1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
// Geometric Tools, LLC
// Copyright (c) 1998-2014
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
// http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
//
// File Version: 5.2.0 (2010/06/21)
#include "Colliders.h"
//----------------------------------------------------------------------------
Colliders::Colliders ()
{
SetDerivativeTimeStep(1e-03f);
SetPseudodistanceThreshold(1e-06f);
SetMaxIterations(8);
mContactTime = Mathf::MAX_REAL;
}
//----------------------------------------------------------------------------
Colliders::~Colliders ()
{
}
//----------------------------------------------------------------------------
void Colliders::SetDerivativeTimeStep (float timeStep)
{
if (timeStep > 0.0f)
{
mDerivativeTimeStep = timeStep;
}
else
{
mDerivativeTimeStep = 1e-03f;
}
mInvDerivativeTimeStep = 0.5f/mDerivativeTimeStep;
}
//----------------------------------------------------------------------------
float Colliders::GetDerivativeTimeStep () const
{
return mDerivativeTimeStep;
}
//----------------------------------------------------------------------------
void Colliders::SetPseudodistanceThreshold (float threshold)
{
if (threshold >= 0.0f)
{
mPseudodistanceThreshold = threshold;
}
}
//----------------------------------------------------------------------------
float Colliders::GetPseudodistanceThreshold () const
{
return mPseudodistanceThreshold;
}
//----------------------------------------------------------------------------
void Colliders::SetMaxIterations (int maxIterations)
{
if (maxIterations > 0)
{
mMaxIterations = maxIterations;
}
}
//----------------------------------------------------------------------------
int Colliders::GetMaxIterations () const
{
return mMaxIterations;
}
//----------------------------------------------------------------------------
Colliders::CollisionType Colliders::Test (float maxTime,
const Vector3f& velocity0, const Vector3f& velocity1, float& contactTime)
{
return Find(maxTime, velocity0, velocity1, contactTime);
}
//----------------------------------------------------------------------------
Colliders::CollisionType Colliders::Find (float maxTime,
const Vector3f& velocity0, const Vector3f& velocity1, float& contactTime)
{
float f0, fder0;
CollisionType type = FastNoIntersection(maxTime, velocity0, velocity1,
f0, fder0);
if (type == SEPARATED)
{
contactTime = Mathf::MAX_REAL;
return SEPARATED;
}
if (type == TOUCHING)
{
contactTime = 0.0f;
return TOUCHING;
}
// Use Newtons method for root finding when the derivative is calculated
// but Secant method when the derivative is estimated.
float t0 = 0.0f;
for (int i = 1; i <= mMaxIterations; ++i)
{
t0 -= f0/fder0;
if (t0 > maxTime)
{
// The objects do not intersect during the specified time
// interval.
contactTime = Mathf::MAX_REAL;
return TOUCHING;
}
f0 = Pseudodistance(t0, velocity0, velocity1);
if (f0 <= mPseudodistanceThreshold)
{
contactTime = t0;
ComputeContactInformation(TOUCHING, contactTime, velocity0,
velocity1);
return TOUCHING;
}
fder0 = PseudodistanceDerivative(t0, f0, velocity0, velocity1);
if (fder0 >= 0.0f)
{
// The objects are moving apart.
contactTime = Mathf::MAX_REAL;
return SEPARATED;
}
}
// Newtons method failed to converge, but we already tested earlier
// whether the objects were moving apart or not intersecting during the
// specified time interval. To reach here, the number of iterations was
// not large enough for the desired pseudodistance threshold. Most
// likely this occurs when the relative speed is very large and the time
// step for the derivative estimation needs to be smaller.
contactTime = t0;
ComputeContactInformation(TOUCHING, contactTime, velocity0, velocity1);
return TOUCHING;
}
//----------------------------------------------------------------------------
float Colliders::GetContactTime () const
{
return mContactTime;
}
//----------------------------------------------------------------------------
float Colliders::PseudodistanceDerivative (float t0, float f0,
const Vector3f& velocity0, const Vector3f& velocity1) const
{
float t1 = t0 - mDerivativeTimeStep;
float f1 = Pseudodistance(t1,velocity0,velocity1);
float fder0 = (f0 - f1)*mInvDerivativeTimeStep;
return fder0;
}
//----------------------------------------------------------------------------
Colliders::CollisionType Colliders::FastNoIntersection (float maxTime,
const Vector3f& velocity0, const Vector3f& velocity1, float& f0,
float& fder0)
{
// Analyze the initial configuration of the objects.
f0 = Pseudodistance(0.0f, velocity0, velocity1);
fder0 = PseudodistanceDerivative(0.0f, f0, velocity0, velocity1);
if (f0 <= -mPseudodistanceThreshold)
{
// Objects are (significantly) overlapping.
ComputeContactInformation(OVERLAPPING, 0.0f, velocity0, velocity1);
return (fder0 >= 0.0f ? SEPARATED : OVERLAPPING);
}
if (f0 <= mPseudodistanceThreshold)
{
// Objects are (nearly) in tangential contact.
ComputeContactInformation(TOUCHING, 0.0f, velocity0, velocity1);
return (fder0 >= 0.0f ? SEPARATED : TOUCHING);
}
// The objects are not currently in contact or overlapping. If the
// objects are moving apart or the relative velocity between them is
// zero, they cannot intersect at a later time.
if (fder0 >= 0.0f || velocity0 == velocity1)
{
return SEPARATED;
}
// Check if the objects are not intersecting, yet still moving toward each
// other at maximum time. If this is the case, the objects do not
// intersect on the specified time interval.
float f1 = Pseudodistance(maxTime, velocity0, velocity1);
if (f1 > 0.0f)
{
// Compute or estimate the derivative F(tmax).
float fder1 = PseudodistanceDerivative(maxTime, f1, velocity0,
velocity1);
if (fder1 < 0.0f)
{
// The objects are moving toward each other and do not intersect
// during the specified time interval.
return SEPARATED;
}
}
return UNKNOWN;
}
//----------------------------------------------------------------------------
|