99acec63f1
This updates our local copy to commit 5ec8339b6fc491e3f09a34a4516e82787f053fcc. We need a recent master commit for some new features that we use in Godot (see #25543 and #28909). To avoid warnings generated by Bullet headers included in our own module, we include those headers with -isystem on GCC and Clang. Fixes #29503.
338 lines
7.9 KiB
C++
338 lines
7.9 KiB
C++
/*
|
|
Bullet Continuous Collision Detection and Physics Library
|
|
Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org
|
|
|
|
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.
|
|
*/
|
|
///original version written by Erwin Coumans, October 2013
|
|
|
|
#ifndef BT_LEMKE_SOLVER_H
|
|
#define BT_LEMKE_SOLVER_H
|
|
|
|
#include "btMLCPSolverInterface.h"
|
|
#include "btLemkeAlgorithm.h"
|
|
|
|
///The btLemkeSolver is based on "Fast Implementation of Lemke's Algorithm for Rigid Body Contact Simulation (John E. Lloyd) "
|
|
///It is a slower but more accurate solver. Increase the m_maxLoops for better convergence, at the cost of more CPU time.
|
|
///The original implementation of the btLemkeAlgorithm was done by Kilian Grundl from the MBSim team
|
|
class btLemkeSolver : public btMLCPSolverInterface
|
|
{
|
|
protected:
|
|
public:
|
|
btScalar m_maxValue;
|
|
int m_debugLevel;
|
|
int m_maxLoops;
|
|
bool m_useLoHighBounds;
|
|
|
|
btLemkeSolver()
|
|
: m_maxValue(100000),
|
|
m_debugLevel(0),
|
|
m_maxLoops(1000),
|
|
m_useLoHighBounds(true)
|
|
{
|
|
}
|
|
virtual bool solveMLCP(const btMatrixXu& A, const btVectorXu& b, btVectorXu& x, const btVectorXu& lo, const btVectorXu& hi, const btAlignedObjectArray<int>& limitDependency, int numIterations, bool useSparsity = true)
|
|
{
|
|
if (m_useLoHighBounds)
|
|
{
|
|
BT_PROFILE("btLemkeSolver::solveMLCP");
|
|
int n = A.rows();
|
|
if (0 == n)
|
|
return true;
|
|
|
|
bool fail = false;
|
|
|
|
btVectorXu solution(n);
|
|
btVectorXu q1;
|
|
q1.resize(n);
|
|
for (int row = 0; row < n; row++)
|
|
{
|
|
q1[row] = -b[row];
|
|
}
|
|
|
|
// cout << "A" << endl;
|
|
// cout << A << endl;
|
|
|
|
/////////////////////////////////////
|
|
|
|
//slow matrix inversion, replace with LU decomposition
|
|
btMatrixXu A1;
|
|
btMatrixXu B(n, n);
|
|
{
|
|
//BT_PROFILE("inverse(slow)");
|
|
A1.resize(A.rows(), A.cols());
|
|
for (int row = 0; row < A.rows(); row++)
|
|
{
|
|
for (int col = 0; col < A.cols(); col++)
|
|
{
|
|
A1.setElem(row, col, A(row, col));
|
|
}
|
|
}
|
|
|
|
btMatrixXu matrix;
|
|
matrix.resize(n, 2 * n);
|
|
for (int row = 0; row < n; row++)
|
|
{
|
|
for (int col = 0; col < n; col++)
|
|
{
|
|
matrix.setElem(row, col, A1(row, col));
|
|
}
|
|
}
|
|
|
|
btScalar ratio, a;
|
|
int i, j, k;
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
for (j = n; j < 2 * n; j++)
|
|
{
|
|
if (i == (j - n))
|
|
matrix.setElem(i, j, 1.0);
|
|
else
|
|
matrix.setElem(i, j, 0.0);
|
|
}
|
|
}
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
for (j = 0; j < n; j++)
|
|
{
|
|
if (i != j)
|
|
{
|
|
btScalar v = matrix(i, i);
|
|
if (btFuzzyZero(v))
|
|
{
|
|
a = 0.000001f;
|
|
}
|
|
ratio = matrix(j, i) / matrix(i, i);
|
|
for (k = 0; k < 2 * n; k++)
|
|
{
|
|
matrix.addElem(j, k, -ratio * matrix(i, k));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
a = matrix(i, i);
|
|
if (btFuzzyZero(a))
|
|
{
|
|
a = 0.000001f;
|
|
}
|
|
btScalar invA = 1.f / a;
|
|
for (j = 0; j < 2 * n; j++)
|
|
{
|
|
matrix.mulElem(i, j, invA);
|
|
}
|
|
}
|
|
|
|
for (int row = 0; row < n; row++)
|
|
{
|
|
for (int col = 0; col < n; col++)
|
|
{
|
|
B.setElem(row, col, matrix(row, n + col));
|
|
}
|
|
}
|
|
}
|
|
|
|
btMatrixXu b1(n, 1);
|
|
|
|
btMatrixXu M(n * 2, n * 2);
|
|
for (int row = 0; row < n; row++)
|
|
{
|
|
b1.setElem(row, 0, -b[row]);
|
|
for (int col = 0; col < n; col++)
|
|
{
|
|
btScalar v = B(row, col);
|
|
M.setElem(row, col, v);
|
|
M.setElem(n + row, n + col, v);
|
|
M.setElem(n + row, col, -v);
|
|
M.setElem(row, n + col, -v);
|
|
}
|
|
}
|
|
|
|
btMatrixXu Bb1 = B * b1;
|
|
// q = [ (-B*b1 - lo)' (hi + B*b1)' ]'
|
|
|
|
btVectorXu qq;
|
|
qq.resize(n * 2);
|
|
for (int row = 0; row < n; row++)
|
|
{
|
|
qq[row] = -Bb1(row, 0) - lo[row];
|
|
qq[n + row] = Bb1(row, 0) + hi[row];
|
|
}
|
|
|
|
btVectorXu z1;
|
|
|
|
btMatrixXu y1;
|
|
y1.resize(n, 1);
|
|
btLemkeAlgorithm lemke(M, qq, m_debugLevel);
|
|
{
|
|
//BT_PROFILE("lemke.solve");
|
|
lemke.setSystem(M, qq);
|
|
z1 = lemke.solve(m_maxLoops);
|
|
}
|
|
for (int row = 0; row < n; row++)
|
|
{
|
|
y1.setElem(row, 0, z1[2 * n + row] - z1[3 * n + row]);
|
|
}
|
|
btMatrixXu y1_b1(n, 1);
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
y1_b1.setElem(i, 0, y1(i, 0) - b1(i, 0));
|
|
}
|
|
|
|
btMatrixXu x1;
|
|
|
|
x1 = B * (y1_b1);
|
|
|
|
for (int row = 0; row < n; row++)
|
|
{
|
|
solution[row] = x1(row, 0); //n];
|
|
}
|
|
|
|
int errorIndexMax = -1;
|
|
int errorIndexMin = -1;
|
|
float errorValueMax = -1e30;
|
|
float errorValueMin = 1e30;
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
x[i] = solution[i];
|
|
volatile btScalar check = x[i];
|
|
if (x[i] != check)
|
|
{
|
|
//printf("Lemke result is #NAN\n");
|
|
x.setZero();
|
|
return false;
|
|
}
|
|
|
|
//this is some hack/safety mechanism, to discard invalid solutions from the Lemke solver
|
|
//we need to figure out why it happens, and fix it, or detect it properly)
|
|
if (x[i] > m_maxValue)
|
|
{
|
|
if (x[i] > errorValueMax)
|
|
{
|
|
fail = true;
|
|
errorIndexMax = i;
|
|
errorValueMax = x[i];
|
|
}
|
|
////printf("x[i] = %f,",x[i]);
|
|
}
|
|
if (x[i] < -m_maxValue)
|
|
{
|
|
if (x[i] < errorValueMin)
|
|
{
|
|
errorIndexMin = i;
|
|
errorValueMin = x[i];
|
|
fail = true;
|
|
//printf("x[i] = %f,",x[i]);
|
|
}
|
|
}
|
|
}
|
|
if (fail)
|
|
{
|
|
int m_errorCountTimes = 0;
|
|
if (errorIndexMin < 0)
|
|
errorValueMin = 0.f;
|
|
if (errorIndexMax < 0)
|
|
errorValueMax = 0.f;
|
|
m_errorCountTimes++;
|
|
// printf("Error (x[%d] = %f, x[%d] = %f), resetting %d times\n", errorIndexMin,errorValueMin, errorIndexMax, errorValueMax, errorCountTimes++);
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
x[i] = 0.f;
|
|
}
|
|
}
|
|
return !fail;
|
|
}
|
|
else
|
|
|
|
{
|
|
int dimension = A.rows();
|
|
if (0 == dimension)
|
|
return true;
|
|
|
|
// printf("================ solving using Lemke/Newton/Fixpoint\n");
|
|
|
|
btVectorXu q;
|
|
q.resize(dimension);
|
|
for (int row = 0; row < dimension; row++)
|
|
{
|
|
q[row] = -b[row];
|
|
}
|
|
|
|
btLemkeAlgorithm lemke(A, q, m_debugLevel);
|
|
|
|
lemke.setSystem(A, q);
|
|
|
|
btVectorXu solution = lemke.solve(m_maxLoops);
|
|
|
|
//check solution
|
|
|
|
bool fail = false;
|
|
int errorIndexMax = -1;
|
|
int errorIndexMin = -1;
|
|
float errorValueMax = -1e30;
|
|
float errorValueMin = 1e30;
|
|
|
|
for (int i = 0; i < dimension; i++)
|
|
{
|
|
x[i] = solution[i + dimension];
|
|
volatile btScalar check = x[i];
|
|
if (x[i] != check)
|
|
{
|
|
x.setZero();
|
|
return false;
|
|
}
|
|
|
|
//this is some hack/safety mechanism, to discard invalid solutions from the Lemke solver
|
|
//we need to figure out why it happens, and fix it, or detect it properly)
|
|
if (x[i] > m_maxValue)
|
|
{
|
|
if (x[i] > errorValueMax)
|
|
{
|
|
fail = true;
|
|
errorIndexMax = i;
|
|
errorValueMax = x[i];
|
|
}
|
|
////printf("x[i] = %f,",x[i]);
|
|
}
|
|
if (x[i] < -m_maxValue)
|
|
{
|
|
if (x[i] < errorValueMin)
|
|
{
|
|
errorIndexMin = i;
|
|
errorValueMin = x[i];
|
|
fail = true;
|
|
//printf("x[i] = %f,",x[i]);
|
|
}
|
|
}
|
|
}
|
|
if (fail)
|
|
{
|
|
static int errorCountTimes = 0;
|
|
if (errorIndexMin < 0)
|
|
errorValueMin = 0.f;
|
|
if (errorIndexMax < 0)
|
|
errorValueMax = 0.f;
|
|
printf("Error (x[%d] = %f, x[%d] = %f), resetting %d times\n", errorIndexMin, errorValueMin, errorIndexMax, errorValueMax, errorCountTimes++);
|
|
for (int i = 0; i < dimension; i++)
|
|
{
|
|
x[i] = 0.f;
|
|
}
|
|
}
|
|
|
|
return !fail;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
#endif //BT_LEMKE_SOLVER_H
|