Add clippling segment/polygons methods, fix issues and add convex vs capsule algorithm

This commit is contained in:
Daniel Chappuis 2017-04-16 22:09:59 +02:00
parent 57da79492f
commit f61fea8b8a
16 changed files with 452 additions and 83 deletions

View File

@ -86,6 +86,8 @@ SET (REACTPHYSICS3D_SOURCES
"src/collision/narrowphase/ConcaveVsConvexAlgorithm.cpp"
"src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h"
"src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp"
"src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h"
"src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp"
"src/collision/shapes/AABB.h"
"src/collision/shapes/AABB.cpp"
"src/collision/shapes/ConvexShape.h"

View File

@ -288,17 +288,6 @@ void CollisionDetection::broadPhaseNotifyOverlappingPair(ProxyShape* shape1, Pro
if ((shape1->getCollideWithMaskBits() & shape2->getCollisionCategoryBits()) == 0 ||
(shape1->getCollisionCategoryBits() & shape2->getCollideWithMaskBits()) == 0) return;
// Make sure the shape with the smallest collision shape type comes first
const uint shape1TypeIndex = static_cast<const uint>(shape1->getCollisionShape()->getType());
const uint shape2TypeIndex = static_cast<const uint>(shape2->getCollisionShape()->getType());
if (shape1TypeIndex > shape2TypeIndex) {
// Swap the two shapes
ProxyShape* temp = shape1;
shape1 = shape2;
shape2 = temp;
}
// Compute the overlapping pair ID
overlappingpairid pairID = OverlappingPair::computeID(shape1, shape2);

View File

@ -266,8 +266,15 @@ inline void CollisionDetection::updateProxyCollisionShape(ProxyShape* shape, con
inline NarrowPhaseAlgorithm* CollisionDetection::selectNarrowPhaseAlgorithm(const CollisionShapeType& shape1Type,
const CollisionShapeType& shape2Type) const {
const unsigned int shape1Index = static_cast<unsigned int>(shape1Type);
const unsigned int shape2Index = static_cast<unsigned int>(shape2Type);
uint shape1Index = static_cast<unsigned int>(shape1Type);
uint shape2Index = static_cast<unsigned int>(shape2Type);
// Swap the shape types if necessary
if (shape1Index > shape2Index) {
const uint tempIndex = shape1Index;
shape1Index = shape2Index;
shape2Index = tempIndex;
}
assert(shape1Index <= shape2Index);

View File

@ -0,0 +1,61 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2016 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
// Libraries
#include "CapsuleVsConvexPolyhedronAlgorithm.h"
#include "SAT/SATAlgorithm.h"
#include "GJK/GJKAlgorithm.h"
// We want to use the ReactPhysics3D namespace
using namespace reactphysics3d;
bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(const NarrowPhaseInfo* narrowPhaseInfo,
ContactManifoldInfo& contactManifoldInfo) {
// Get the local-space to world-space transforms
const Transform& transform1 = narrowPhaseInfo->shape1ToWorldTransform;
const Transform& transform2 = narrowPhaseInfo->shape2ToWorldTransform;
// First, we run the GJK algorithm
GJKAlgorithm gjkAlgorithm;
GJKAlgorithm::GJKResult result = gjkAlgorithm.testCollision(narrowPhaseInfo, contactManifoldInfo);
// If we have found a contact point inside the margins (shallow penetration)
if (result == GJKAlgorithm::GJKResult::COLLIDE_IN_MARGIN) {
// Return true
return true;
}
// If we have overlap even without the margins (deep penetration)
if (result == GJKAlgorithm::GJKResult::INTERPENETRATE) {
// Run the SAT algorithm to find the separating axis and compute contact point
SATAlgorithm satAlgorithm;
return satAlgorithm.testCollisionCapsuleVsConvexPolyhedron(narrowPhaseInfo, contactManifoldInfo);
}
return false;
}

View File

@ -0,0 +1,71 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2016 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_CAPSULE_VS_CONVEX_POLYHEDRON_ALGORITHM_H
#define REACTPHYSICS3D_CAPSULE_VS_CONVEX_POLYHEDRON_ALGORITHM_H
// Libraries
#include "body/Body.h"
#include "constraint/ContactPoint.h"
#include "NarrowPhaseAlgorithm.h"
/// Namespace ReactPhysics3D
namespace reactphysics3d {
// Class CapsuleVsConvexPolyhedronAlgorithm
/**
* This class is used to compute the narrow-phase collision detection
* between a capsule and a convex polyhedron.
*/
class CapsuleVsConvexPolyhedronAlgorithm : public NarrowPhaseAlgorithm {
protected :
public :
// -------------------- Methods -------------------- //
/// Constructor
CapsuleVsConvexPolyhedronAlgorithm() = default;
/// Destructor
virtual ~CapsuleVsConvexPolyhedronAlgorithm() override = default;
/// Deleted copy-constructor
CapsuleVsConvexPolyhedronAlgorithm(const CapsuleVsConvexPolyhedronAlgorithm& algorithm) = delete;
/// Deleted assignment operator
CapsuleVsConvexPolyhedronAlgorithm& operator=(const CapsuleVsConvexPolyhedronAlgorithm& algorithm) = delete;
/// Compute a contact info if the two bounding volume collide
virtual bool testCollision(const NarrowPhaseInfo* narrowPhaseInfo,
ContactManifoldInfo& contactManifoldInfo) override;
};
}
#endif

View File

@ -39,39 +39,25 @@
// We want to use the ReactPhysics3D namespace
using namespace reactphysics3d;
bool SATAlgorithm::testCollision(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) {
assert(narrowPhaseInfo->collisionShape2->getType() == CollisionShapeType::CONVEX_POLYHEDRON);
switch (narrowPhaseInfo->collisionShape1->getType()) {
case CollisionShapeType::CONVEX_POLYHEDRON:
return testCollisionConvexMeshVsConvexMesh(narrowPhaseInfo, contactManifoldInfo);
case CollisionShapeType::SPHERE:
return testCollisionSphereVsConvexPolyhedron(narrowPhaseInfo, contactManifoldInfo);
case CollisionShapeType::CAPSULE:
return testCollisionCapsuleVsConvexMesh(narrowPhaseInfo, contactManifoldInfo);
case CollisionShapeType::TRIANGLE:
return testCollisionTriangleVsConvexMesh(narrowPhaseInfo, contactManifoldInfo);
default: assert(false);
}
return false;
}
// Test collision between a sphere and a convex mesh
bool SATAlgorithm::testCollisionSphereVsConvexPolyhedron(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const {
assert(narrowPhaseInfo->collisionShape1->getType() == CollisionShapeType::SPHERE);
assert(narrowPhaseInfo->collisionShape2->getType() == CollisionShapeType::CONVEX_POLYHEDRON);
bool isSphereShape1 = narrowPhaseInfo->collisionShape1->getType() == CollisionShapeType::SPHERE;
assert(isSphereShape1 || narrowPhaseInfo->collisionShape1->getType() == CollisionShapeType::CONVEX_POLYHEDRON);
assert(!isSphereShape1 || narrowPhaseInfo->collisionShape1->getType() == CollisionShapeType::CONVEX_POLYHEDRON);
assert(!isSphereShape1 || narrowPhaseInfo->collisionShape2->getType() == CollisionShapeType::SPHERE);
// Get the capsule collision shapes
const SphereShape* sphere = static_cast<const SphereShape*>(narrowPhaseInfo->collisionShape1);
const ConvexPolyhedronShape* polyhedron = static_cast<const ConvexPolyhedronShape*>(narrowPhaseInfo->collisionShape2);
const SphereShape* sphere = static_cast<const SphereShape*>(isSphereShape1 ? narrowPhaseInfo->collisionShape1 : narrowPhaseInfo->collisionShape2);
const ConvexPolyhedronShape* polyhedron = static_cast<const ConvexPolyhedronShape*>(isSphereShape1 ? narrowPhaseInfo->collisionShape2 : narrowPhaseInfo->collisionShape1);
const Transform& sphereToWorldTransform = isSphereShape1 ? narrowPhaseInfo->shape1ToWorldTransform : narrowPhaseInfo->shape2ToWorldTransform;
const Transform& polyhedronToWorldTransform = isSphereShape1 ? narrowPhaseInfo->shape2ToWorldTransform : narrowPhaseInfo->shape1ToWorldTransform;
// Get the transform from sphere local-space to polyhedron local-space
const Transform sphereToPolyhedronSpaceTransform = narrowPhaseInfo->shape2ToWorldTransform.getInverse() *
narrowPhaseInfo->shape1ToWorldTransform;
const Transform worldToPolyhedronTransform = polyhedronToWorldTransform.getInverse();
const Transform sphereToPolyhedronSpaceTransform = worldToPolyhedronTransform * sphereToWorldTransform;
// Transform the center of the sphere into the local-space of the convex polyhedron
const Vector3 sphereCenter = sphereToPolyhedronSpaceTransform.getPosition();
@ -105,18 +91,24 @@ bool SATAlgorithm::testCollisionSphereVsConvexPolyhedron(const NarrowPhaseInfo*
}
const Vector3 minFaceNormal = polyhedron->getFaceNormal(minFaceIndex);
const Vector3 normalWorld = -(narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * minFaceNormal);
const Vector3 contactPointSphereLocal = narrowPhaseInfo->shape1ToWorldTransform.getInverse() * normalWorld * sphere->getRadius();
Vector3 normalWorld = -(polyhedronToWorldTransform.getOrientation() * minFaceNormal);
const Vector3 contactPointSphereLocal = sphereToWorldTransform.getInverse() * normalWorld * sphere->getRadius();
const Vector3 contactPointPolyhedronLocal = sphereCenter + minFaceNormal * (minPenetrationDepth - sphere->getRadius());
if (!isSphereShape1) {
normalWorld = -normalWorld;
}
// Create the contact info object
contactManifoldInfo.addContactPoint(normalWorld, minPenetrationDepth, contactPointSphereLocal, contactPointPolyhedronLocal);
contactManifoldInfo.addContactPoint(normalWorld, minPenetrationDepth,
isSphereShape1 ? contactPointSphereLocal : contactPointPolyhedronLocal,
isSphereShape1 ? contactPointPolyhedronLocal : contactPointSphereLocal);
return true;
}
// Test collision between a capsule and a convex mesh
bool SATAlgorithm::testCollisionCapsuleVsConvexMesh(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const {
bool SATAlgorithm::testCollisionCapsuleVsConvexPolyhedron(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const {
assert(narrowPhaseInfo->collisionShape1->getType() == CollisionShapeType::CAPSULE);
assert(narrowPhaseInfo->collisionShape2->getType() == CollisionShapeType::CONVEX_POLYHEDRON);

View File

@ -46,18 +46,6 @@ class SATAlgorithm {
bool testGaussMapArcsIntersect(const Vector3& a, const Vector3& b,
const Vector3& c, const Vector3& d) const;
/// Test collision between a sphere and a convex mesh
bool testCollisionSphereVsConvexPolyhedron(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const;
/// Test collision between a capsule and a convex mesh
bool testCollisionCapsuleVsConvexMesh(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const;
/// Test collision between a triangle and a convex mesh
bool testCollisionTriangleVsConvexMesh(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const;
/// Test collision between two convex meshes
bool testCollisionConvexMeshVsConvexMesh(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const;
public :
// -------------------- Methods -------------------- //
@ -74,9 +62,18 @@ class SATAlgorithm {
/// Deleted assignment operator
SATAlgorithm& operator=(const SATAlgorithm& algorithm) = delete;
/// Compute a contact info if the two bounding volumes collide.
bool testCollision(const NarrowPhaseInfo* narrowPhaseInfo,
ContactManifoldInfo& contactManifoldInfo);
/// Test collision between a sphere and a convex mesh
bool testCollisionSphereVsConvexPolyhedron(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const;
/// Test collision between a capsule and a convex mesh
bool testCollisionCapsuleVsConvexPolyhedron(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const;
/// Test collision between a triangle and a convex mesh
bool testCollisionTriangleVsConvexMesh(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const;
/// Test collision between two convex meshes
bool testCollisionConvexMeshVsConvexMesh(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) const;
};
}

View File

@ -32,23 +32,30 @@
using namespace reactphysics3d;
bool SphereVsCapsuleAlgorithm::testCollision(const NarrowPhaseInfo* narrowPhaseInfo, ContactManifoldInfo& contactManifoldInfo) {
assert(narrowPhaseInfo->collisionShape1->getType() == CollisionShapeType::SPHERE);
assert(narrowPhaseInfo->collisionShape2->getType() == CollisionShapeType::CAPSULE);
bool isSphereShape1 = narrowPhaseInfo->collisionShape1->getType() == CollisionShapeType::SPHERE;
assert(isSphereShape1 || narrowPhaseInfo->collisionShape1->getType() == CollisionShapeType::CAPSULE);
assert(!isSphereShape1 || narrowPhaseInfo->collisionShape1->getType() == CollisionShapeType::CAPSULE);
assert(!isSphereShape1 || narrowPhaseInfo->collisionShape2->getType() == CollisionShapeType::SPHERE);
// Get the collision shapes
const SphereShape* sphereShape = static_cast<const SphereShape*>(narrowPhaseInfo->collisionShape1);
const CapsuleShape* capsuleShape = static_cast<const CapsuleShape*>(narrowPhaseInfo->collisionShape2);
const SphereShape* sphereShape = static_cast<const SphereShape*>(isSphereShape1 ? narrowPhaseInfo->collisionShape1 : narrowPhaseInfo->collisionShape2);
const CapsuleShape* capsuleShape = static_cast<const CapsuleShape*>(isSphereShape1 ? narrowPhaseInfo->collisionShape2 : narrowPhaseInfo->collisionShape1);
// Get the transform from sphere local-space to capsule local-space
const Transform sphereToCapsuleSpaceTransform = narrowPhaseInfo->shape2ToWorldTransform.getInverse() * narrowPhaseInfo->shape1ToWorldTransform;
const Transform& sphereToWorldTransform = isSphereShape1 ? narrowPhaseInfo->shape1ToWorldTransform : narrowPhaseInfo->shape2ToWorldTransform;
const Transform& capsuleToWorldTransform = isSphereShape1 ? narrowPhaseInfo->shape2ToWorldTransform : narrowPhaseInfo->shape1ToWorldTransform;
const Transform worldToCapsuleTransform = capsuleToWorldTransform.getInverse();
const Transform sphereToCapsuleSpaceTransform = worldToCapsuleTransform * sphereToWorldTransform;
// Transform the center of the sphere into the local-space of the capsule shape
const Vector3 sphereCenter = sphereToCapsuleSpaceTransform.getPosition();
// Compute the end-points of the inner segment of the capsule
const Vector3 capsuleSegA(0, -capsuleShape->getHeight() * decimal(0.5), 0);
const Vector3 capsuleSegB(0, capsuleShape->getHeight() * decimal(0.5), 0);
const decimal capsuleHalfHeight = capsuleShape->getHeight() * decimal(0.5);
const Vector3 capsuleSegA(0, -capsuleHalfHeight, 0);
const Vector3 capsuleSegB(0, capsuleHalfHeight, 0);
// Compute the point on the inner capsule segment that is the closes to center of sphere
const Vector3 closestPointOnSegment = computeClosestPointOnSegment(capsuleSegA, capsuleSegB, sphereCenter);
@ -69,12 +76,18 @@ bool SphereVsCapsuleAlgorithm::testCollision(const NarrowPhaseInfo* narrowPhaseI
const Vector3 contactPointSphereLocal = sphereToCapsuleSpaceTransform.getInverse() * (sphereCenter + sphereCenterToSegment * sphereShape->getRadius());
const Vector3 contactPointCapsuleLocal = closestPointOnSegment - sphereCenterToSegment * capsuleShape->getRadius();
const Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * sphereCenterToSegment;
Vector3 normalWorld = capsuleToWorldTransform.getOrientation() * sphereCenterToSegment;
decimal penetrationDepth = sumRadius - sphereSegmentDistance;
if (!isSphereShape1) {
normalWorld = -normalWorld;
}
// Create the contact info object
contactManifoldInfo.addContactPoint(normalWorld, penetrationDepth, contactPointSphereLocal, contactPointCapsuleLocal);
contactManifoldInfo.addContactPoint(normalWorld, penetrationDepth,
isSphereShape1 ? contactPointSphereLocal : contactPointCapsuleLocal,
isSphereShape1 ? contactPointCapsuleLocal : contactPointSphereLocal);
return true;
}

View File

@ -25,15 +25,14 @@
// Libraries
#include "SphereVsConvexPolyhedronAlgorithm.h"
#include "GJK/GJKAlgorithm.h"
#include "SAT/SATAlgorithm.h"
#include "collision/shapes/SphereShape.h"
#include "collision/shapes/ConvexMeshShape.h"
// We want to use the ReactPhysics3D namespace
using namespace reactphysics3d;
bool SphereVsConvexPolyhedronAlgorithm::testCollision(const NarrowPhaseInfo* narrowPhaseInfo,
ContactManifoldInfo& contactManifoldInfo) {
ContactManifoldInfo& contactManifoldInfo) {
// Get the local-space to world-space transforms
const Transform& transform1 = narrowPhaseInfo->shape1ToWorldTransform;
@ -55,7 +54,7 @@ bool SphereVsConvexPolyhedronAlgorithm::testCollision(const NarrowPhaseInfo* nar
// Run the SAT algorithm to find the separating axis and compute contact point
SATAlgorithm satAlgorithm;
return satAlgorithm.testCollision(narrowPhaseInfo, contactManifoldInfo);
return satAlgorithm.testCollisionSphereVsConvexPolyhedron(narrowPhaseInfo, contactManifoldInfo);
}
return false;

View File

@ -23,8 +23,8 @@
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_SPHERE_VS_CONVEX_MESH_ALGORITHM_H
#define REACTPHYSICS3D_SPHERE_VS_CONVEX_MESH_ALGORITHM_H
#ifndef REACTPHYSICS3D_SPHERE_VS_CONVEX_POLYHEDRON_ALGORITHM_H
#define REACTPHYSICS3D_SPHERE_VS_CONVEX_POLYHEDRON_ALGORITHM_H
// Libraries
#include "body/Body.h"

View File

@ -35,6 +35,4 @@ OverlappingPair::OverlappingPair(ProxyShape* shape1, ProxyShape* shape2,
: mContactManifoldSet(shape1, shape2, memoryAllocator, nbMaxContactManifolds),
mCachedSeparatingAxis(0.0, 1.0, 0.0) {
assert(static_cast<uint>(shape1->getCollisionShape()->getType()) <=
static_cast<uint>(shape2->getCollisionShape()->getType()));
}

View File

@ -27,6 +27,7 @@
#include "mathematics_functions.h"
#include "Vector3.h"
#include <cassert>
#include <vector>
using namespace reactphysics3d;
@ -187,7 +188,7 @@ decimal reactphysics3d::computePlaneSegmentIntersection(const Vector3& segA, con
return t;
}
/// Compute the distance between a point "point" and a line given by the points "linePointA" and "linePointB"
// Compute the distance between a point "point" and a line given by the points "linePointA" and "linePointB"
decimal reactphysics3d::computeDistancePointToLineDistance(const Vector3& linePointA, const Vector3& linePointB, const Vector3& point) {
decimal distAB = (linePointB - linePointA).length();
@ -199,4 +200,148 @@ decimal reactphysics3d::computeDistancePointToLineDistance(const Vector3& linePo
return ((point - linePointA).cross(point - linePointB)).length() / distAB;
}
// Clip a segment against multiple planes and return the clipped segment vertices
// This method implements the SutherlandHodgman clipping algorithm
std::vector<Vector3> reactphysics3d::clipSegmentWithPlanes(const Vector3& segA, const Vector3& segB,
const std::vector<Vector3>& planesPoints,
const std::vector<Vector3>& planesNormals) {
assert(planesPoints.size() == planesNormals.size());
std::vector<Vector3> inputVertices = {segA, segB};
std::vector<Vector3> outputVertices;
// For each clipping plane
for (uint p=0; p<planesPoints.size(); p++) {
// If there is no more vertices, stop
if (inputVertices.empty()) return inputVertices;
assert(inputVertices.size() == 2);
outputVertices.clear();
Vector3& v1 = inputVertices[0];
Vector3& v2 = inputVertices[1];
decimal v1DotN = (v1 - planesPoints[p]).dot(planesNormals[p]);
decimal v2DotN = (v2 - planesPoints[p]).dot(planesNormals[p]);
// If the second vertex is in front of the clippling plane
if (v2DotN >= decimal(0.0)) {
// If the first vertex is not in front of the clippling plane
if (v1DotN < decimal(0.0)) {
// The second point we keep is the intersection between the segment v1, v2 and the clipping plane
decimal t = computePlaneSegmentIntersection(v1, v2, planesNormals[p].dot(planesPoints[p]), planesNormals[p]);
if (t >= decimal(0) && t <= decimal(1.0)) {
outputVertices.push_back(v1 + t * (v2 - v1));
}
else {
outputVertices.push_back(v2);
}
}
else {
outputVertices.push_back(v1);
}
// Add the second vertex
outputVertices.push_back(v2);
}
else { // If the second vertex is behind the clipping plane
// If the first vertex is in front of the clippling plane
if (v1DotN >= decimal(0.0)) {
outputVertices.push_back(v1);
// The first point we keep is the intersection between the segment v1, v2 and the clipping plane
decimal t = computePlaneSegmentIntersection(v1, v2, -planesNormals[p].dot(planesPoints[p]), -planesNormals[p]);
if (t >= decimal(0.0) && t <= decimal(1.0)) {
outputVertices.push_back(v1 + t * (v2 - v1));
}
}
}
inputVertices = outputVertices;
}
return outputVertices;
}
/// Clip a polygon against multiple planes and return the clipped polygon vertices
/// This method implements the SutherlandHodgman clipping algorithm
std::vector<Vector3> reactphysics3d::clipPolygonWithPlanes(const std::vector<Vector3>& polygonVertices, const std::vector<Vector3>& planesPoints,
const std::vector<Vector3>& planesNormals) {
assert(planesPoints.size() == planesNormals.size());
std::vector<Vector3> inputVertices(polygonVertices);
std::vector<Vector3> outputVertices;
// For each clipping plane
for (uint p=0; p<planesPoints.size(); p++) {
outputVertices.clear();
uint vStart = inputVertices.size() - 1;
// For each edge of the polygon
for (uint vEnd = 0; vEnd<inputVertices.size(); vEnd++) {
Vector3& v1 = inputVertices[vStart];
Vector3& v2 = inputVertices[vEnd];
decimal v1DotN = (v1 - planesPoints[p]).dot(planesNormals[p]);
decimal v2DotN = (v2 - planesPoints[p]).dot(planesNormals[p]);
// If the second vertex is in front of the clippling plane
if (v2DotN >= decimal(0.0)) {
// If the first vertex is not in front of the clippling plane
if (v1DotN < decimal(0.0)) {
// The second point we keep is the intersection between the segment v1, v2 and the clipping plane
decimal t = computePlaneSegmentIntersection(v1, v2, planesNormals[p].dot(planesPoints[p]), planesNormals[p]);
if (t >= decimal(0) && t <= decimal(1.0)) {
outputVertices.push_back(v1 + t * (v2 - v1));
}
else {
outputVertices.push_back(v2);
}
}
// Add the second vertex
outputVertices.push_back(v2);
}
else { // If the second vertex is behind the clipping plane
// If the first vertex is in front of the clippling plane
if (v1DotN >= decimal(0.0)) {
// The first point we keep is the intersection between the segment v1, v2 and the clipping plane
decimal t = computePlaneSegmentIntersection(v1, v2, -planesNormals[p].dot(planesPoints[p]), -planesNormals[p]);
if (t >= decimal(0.0) && t <= decimal(1.0)) {
outputVertices.push_back(v1 + t * (v2 - v1));
}
else {
outputVertices.push_back(v1);
}
}
}
vStart = vEnd;
}
inputVertices = outputVertices;
}
return outputVertices;
}

View File

@ -32,6 +32,7 @@
#include <algorithm>
#include <cassert>
#include <cmath>
#include <vector>
/// ReactPhysics3D namespace
namespace reactphysics3d {
@ -96,8 +97,16 @@ decimal computePlaneSegmentIntersection(const Vector3& segA, const Vector3& segB
/// Compute the distance between a point and a line
decimal computeDistancePointToLineDistance(const Vector3& linePointA, const Vector3& linePointB, const Vector3& point);
/// Clip a segment against multiple planes and return the clipped segment vertices
std::vector<Vector3> clipSegmentWithPlanes(const Vector3& segA, const Vector3& segB,
const std::vector<Vector3>& planesPoints,
const std::vector<Vector3>& planesNormals);
/// Clip a polygon against multiple planes and return the clipped polygon vertices
std::vector<Vector3> clipPolygonWithPlanes(const std::vector<Vector3>& polygonVertices, const std::vector<Vector3>& planesPoints,
const std::vector<Vector3>& planesNormals);
}
#endif

View File

@ -35,7 +35,7 @@
namespace reactphysics3d {
// Macros
#define test(condition) applyTest(condition, #condition, __FILE__, __LINE__)
#define test(condition) applyTest(condition, #condition, __FILE__, __LINE__);
#define fail(text) applyFail(text, __FILE__, __LINE__);
// Class Test

View File

@ -300,8 +300,8 @@ class TestRaycast : public Test {
mBoxProxyShape->setCollisionCategoryBits(CATEGORY1);
mSphereProxyShape->setCollisionCategoryBits(CATEGORY1);
mCapsuleProxyShape->setCollisionCategoryBits(CATEGORY1);
mConvexMeshProxyShape->setCollisionCategoryBits(CATEGORY2);
mConvexMeshProxyShapeEdgesInfo->setCollisionCategoryBits(CATEGORY2);
//mConvexMeshProxyShape->setCollisionCategoryBits(CATEGORY2);
//mConvexMeshProxyShapeEdgesInfo->setCollisionCategoryBits(CATEGORY2);
mCompoundSphereProxyShape->setCollisionCategoryBits(CATEGORY2);
mCompoundCapsuleProxyShape->setCollisionCategoryBits(CATEGORY2);
mTriangleProxyShape->setCollisionCategoryBits(CATEGORY1);
@ -1787,7 +1787,7 @@ class TestRaycast : public Test {
// ----- Test raycast miss ----- //
test(!mConcaveMeshBody->raycast(ray1, raycastInfo3));
test(!mConvexMeshProxyShape->raycast(ray1, raycastInfo3));
//test(!mConvexMeshProxyShape->raycast(ray1, raycastInfo3));
mCallback.reset();
mWorld->raycast(ray1, &mCallback);
test(!mCallback.isHit);

View File

@ -27,8 +27,6 @@
#define TEST_MATHEMATICS_FUNCTIONS_H
// Libraries
#include "Test.h"
#include "mathematics/mathematics_functions.h"
/// Reactphysics3D namespace
namespace reactphysics3d {
@ -170,6 +168,94 @@ class TestMathematicsFunctions : public Test {
test(approxEqual(computeDistancePointToLineDistance(Vector3(6, -5, 0), Vector3(10, -5, 0), Vector3(-43, 254, 0)), 259.0, 0.000001));
test(approxEqual(computeDistancePointToLineDistance(Vector3(6, -5, 8), Vector3(10, -5, -5), Vector3(6, -5, 8)), 0.0, 0.000001));
test(approxEqual(computeDistancePointToLineDistance(Vector3(6, -5, 8), Vector3(10, -5, -5), Vector3(10, -5, -5)), 0.0, 0.000001));
// Test clipSegmentWithPlanes()
std::vector<Vector3> segmentVertices;
segmentVertices.push_back(Vector3(-6, 3, 0));
segmentVertices.push_back(Vector3(8, 3, 0));
std::vector<Vector3> planesNormals;
std::vector<Vector3> planesPoints;
planesNormals.push_back(Vector3(-1, 0, 0));
planesPoints.push_back(Vector3(4, 0, 0));
std::vector<Vector3> clipSegmentVertices = clipSegmentWithPlanes(segmentVertices[0], segmentVertices[1],
planesPoints, planesNormals);
test(clipSegmentVertices.size() == 2);
test(approxEqual(clipSegmentVertices[0].x, -6, 0.000001));
test(approxEqual(clipSegmentVertices[0].y, 3, 0.000001));
test(approxEqual(clipSegmentVertices[0].z, 0, 0.000001));
test(approxEqual(clipSegmentVertices[1].x, 4, 0.000001));
test(approxEqual(clipSegmentVertices[1].y, 3, 0.000001));
test(approxEqual(clipSegmentVertices[1].z, 0, 0.000001));
segmentVertices.clear();
segmentVertices.push_back(Vector3(8, 3, 0));
segmentVertices.push_back(Vector3(-6, 3, 0));
clipSegmentVertices = clipSegmentWithPlanes(segmentVertices[0], segmentVertices[1], planesPoints, planesNormals);
test(clipSegmentVertices.size() == 2);
test(approxEqual(clipSegmentVertices[0].x, 4, 0.000001));
test(approxEqual(clipSegmentVertices[0].y, 3, 0.000001));
test(approxEqual(clipSegmentVertices[0].z, 0, 0.000001));
test(approxEqual(clipSegmentVertices[1].x, -6, 0.000001));
test(approxEqual(clipSegmentVertices[1].y, 3, 0.000001));
test(approxEqual(clipSegmentVertices[1].z, 0, 0.000001));
segmentVertices.clear();
segmentVertices.push_back(Vector3(-6, 3, 0));
segmentVertices.push_back(Vector3(3, 3, 0));
clipSegmentVertices = clipSegmentWithPlanes(segmentVertices[0], segmentVertices[1], planesPoints, planesNormals);
test(clipSegmentVertices.size() == 2);
test(approxEqual(clipSegmentVertices[0].x, -6, 0.000001));
test(approxEqual(clipSegmentVertices[0].y, 3, 0.000001));
test(approxEqual(clipSegmentVertices[0].z, 0, 0.000001));
test(approxEqual(clipSegmentVertices[1].x, 3, 0.000001));
test(approxEqual(clipSegmentVertices[1].y, 3, 0.000001));
test(approxEqual(clipSegmentVertices[1].z, 0, 0.000001));
segmentVertices.clear();
segmentVertices.push_back(Vector3(5, 3, 0));
segmentVertices.push_back(Vector3(8, 3, 0));
clipSegmentVertices = clipSegmentWithPlanes(segmentVertices[0], segmentVertices[1], planesPoints, planesNormals);
test(clipSegmentVertices.size() == 0);
// Test clipPolygonWithPlanes()
std::vector<Vector3> polygonVertices;
polygonVertices.push_back(Vector3(-4, 2, 0));
polygonVertices.push_back(Vector3(7, 2, 0));
polygonVertices.push_back(Vector3(7, 4, 0));
polygonVertices.push_back(Vector3(-4, 4, 0));
planesNormals.clear();
planesPoints.clear();
planesNormals.push_back(Vector3(1, 0, 0));
planesPoints.push_back(Vector3(0, 0, 0));
planesNormals.push_back(Vector3(0, 1, 0));
planesPoints.push_back(Vector3(0, 0, 0));
planesNormals.push_back(Vector3(-1, 0, 0));
planesPoints.push_back(Vector3(10, 0, 0));
planesNormals.push_back(Vector3(0, -1, 0));
planesPoints.push_back(Vector3(10, 5, 0));
clipSegmentVertices = clipPolygonWithPlanes(polygonVertices, planesPoints, planesNormals);
test(clipSegmentVertices.size() == 4);
test(approxEqual(clipSegmentVertices[0].x, 0, 0.000001));
test(approxEqual(clipSegmentVertices[0].y, 2, 0.000001));
test(approxEqual(clipSegmentVertices[0].z, 0, 0.000001));
test(approxEqual(clipSegmentVertices[1].x, 7, 0.000001));
test(approxEqual(clipSegmentVertices[1].y, 2, 0.000001));
test(approxEqual(clipSegmentVertices[1].z, 0, 0.000001));
test(approxEqual(clipSegmentVertices[2].x, 7, 0.000001));
test(approxEqual(clipSegmentVertices[2].y, 4, 0.000001));
test(approxEqual(clipSegmentVertices[2].z, 0, 0.000001));
test(approxEqual(clipSegmentVertices[3].x, 0, 0.000001));
test(approxEqual(clipSegmentVertices[3].y, 4, 0.000001));
test(approxEqual(clipSegmentVertices[3].z, 0, 0.000001));
}
};