blender/extern/bullet/Bullet/NarrowPhaseCollision/GjkPairDetector.cpp
Erwin Coumans feb4f51103 Added Bullet library.
Only windows projectfiles for now.
Will ask Hans to get unix makefiles done.
2005-07-16 09:58:01 +00:00

187 lines
4.9 KiB
C++

/*
* Copyright (c) 2005 Erwin Coumans http://www.erwincoumans.com
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies.
* Erwin Coumans makes no representations about the suitability
* of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*/
#include "GjkPairDetector.h"
#include "CollisionShapes/ConvexShape.h"
#include "NarrowPhaseCollision/SimplexSolverInterface.h"
#include "NarrowPhaseCollision/ConvexPenetrationDepthSolver.h"
static const SimdScalar rel_error = SimdScalar(1.0e-3);
SimdScalar rel_error2 = rel_error * rel_error;
float maxdist2 = 1.e30f;
GjkPairDetector::GjkPairDetector(ConvexShape* objectA,ConvexShape* objectB,SimplexSolverInterface* simplexSolver,ConvexPenetrationDepthSolver* penetrationDepthSolver)
:m_cachedSeparatingAxis(0.f,0.f,1.f),
m_penetrationDepthSolver(penetrationDepthSolver),
m_simplexSolver(simplexSolver),
m_minkowskiA(objectA),
m_minkowskiB(objectB)
{
}
void GjkPairDetector::GetClosestPoints(const ClosestPointInput& input,Result& output)
{
SimdScalar distance;
SimdVector3 normalInB(0.f,0.f,0.f);
SimdVector3 pointOnA,pointOnB;
float marginA = m_minkowskiA->GetMargin();
float marginB = m_minkowskiB->GetMargin();
bool isValid = false;
bool checkSimplex = false;
bool checkPenetration = true;
{
SimdScalar squaredDistance = SIMD_INFINITY;
SimdScalar delta = 0.f;
SimdScalar margin = marginA + marginB;
m_simplexSolver->reset();
while (true)
{
SimdVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* input.m_transformA.getBasis();
SimdVector3 seperatingAxisInB = m_cachedSeparatingAxis* input.m_transformB.getBasis();
SimdVector3 pInA = m_minkowskiA->LocalGetSupportingVertexWithoutMargin(seperatingAxisInA);
SimdVector3 qInB = m_minkowskiB->LocalGetSupportingVertexWithoutMargin(seperatingAxisInB);
SimdPoint3 pWorld = input.m_transformA(pInA);
SimdPoint3 qWorld = input.m_transformB(qInB);
SimdVector3 w = pWorld - qWorld;
delta = m_cachedSeparatingAxis.dot(w);
// potential exit, they don't overlap
if ((delta > SimdScalar(0.0)) && (delta * delta > squaredDistance * input.m_maximumDistanceSquared))
{
checkPenetration = false;
break;
}
//exit 0: the new point is already in the simplex, or we didn't come any closer
if (m_simplexSolver->inSimplex(w))
{
checkSimplex = true;
break;
}
// are we getting any closer ?
if (squaredDistance - delta <= squaredDistance * rel_error2)
{
checkSimplex = true;
break;
}
//add current vertex to simplex
m_simplexSolver->addVertex(w, pWorld, qWorld);
//calculate the closest point to the origin (update vector v)
if (!m_simplexSolver->closest(m_cachedSeparatingAxis))
{
checkSimplex = true;
break;
}
SimdScalar previousSquaredDistance = squaredDistance;
squaredDistance = m_cachedSeparatingAxis.length2();
//redundant m_simplexSolver->compute_points(pointOnA, pointOnB);
//are we getting any closer ?
if (previousSquaredDistance - squaredDistance <= SIMD_EPSILON * previousSquaredDistance)
{
m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
checkSimplex = true;
break;
}
bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > SIMD_EPSILON * m_simplexSolver->maxVertex());
if (!check)
{
//do we need this backup_closest here ?
m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
break;
}
}
if (checkSimplex)
{
m_simplexSolver->compute_points(pointOnA, pointOnB);
normalInB = pointOnA-pointOnB;
float lenSqr = m_cachedSeparatingAxis.length2();
//valid normal
if (lenSqr > SIMD_EPSILON)
{
float rlen = 1.f / sqrtf(lenSqr );
normalInB *= rlen; //normalize
SimdScalar s = sqrtf(squaredDistance);
ASSERT(s > SimdScalar(0.0));
pointOnA -= m_cachedSeparatingAxis * (marginA / s);
pointOnB += m_cachedSeparatingAxis * (marginB / s);
distance = ((1.f/rlen) - margin);
isValid = true;
}
}
if (checkPenetration && !isValid)
{
//penetration case
m_minkowskiA->SetMargin(marginA);
m_minkowskiB->SetMargin(marginB);
//if there is no way to handle penetrations, bail out
if (m_penetrationDepthSolver)
{
// Penetration depth case.
isValid = m_penetrationDepthSolver->CalcPenDepth(
*m_simplexSolver,
m_minkowskiA,m_minkowskiB,
input.m_transformA,input.m_transformB,
m_cachedSeparatingAxis, pointOnA, pointOnB);
if (isValid)
{
normalInB = pointOnB-pointOnA;
float lenSqr = normalInB.length2();
if (lenSqr > SIMD_EPSILON)
{
normalInB /= sqrtf(lenSqr);
distance = -(pointOnA-pointOnB).length();
} else
{
isValid = false;
}
}
}
}
}
if (isValid)
{
output.AddContactPoint(
normalInB,
pointOnB,
distance);
}
}