2023-06-15 13:09:04 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
2021-12-14 18:35:31 +11:00
|
|
|
* \ingroup intern_iksolver
|
2011-02-25 11:43:19 +00:00
|
|
|
*/
|
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
#include "../extern/IK_solver.h"
|
|
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
#include "IK_QJacobianSolver.h"
|
|
|
|
|
#include "IK_QSegment.h"
|
|
|
|
|
#include "IK_QTask.h"
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
#include <list>
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
2006-03-04 16:23:15 +00:00
|
|
|
class IK_QSolver {
|
|
|
|
|
public:
|
2016-11-15 13:21:01 -07:00
|
|
|
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
2023-03-29 16:50:54 +02:00
|
|
|
IK_QSolver() : root(NULL) {}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
IK_QJacobianSolver solver;
|
|
|
|
|
IK_QSegment *root;
|
2012-07-27 22:35:27 +00:00
|
|
|
std::list<IK_QTask *> tasks;
|
2006-03-04 16:23:15 +00:00
|
|
|
};
|
2005-08-27 12:44:41 +00:00
|
|
|
|
2010-10-17 11:20:12 +00:00
|
|
|
// FIXME: locks still result in small "residual" changes to the locked axes...
|
2012-09-16 01:35:00 +00:00
|
|
|
static IK_QSegment *CreateSegment(int flag, bool translate)
|
2005-08-27 12:44:41 +00:00
|
|
|
{
|
|
|
|
|
int ndof = 0;
|
2012-07-27 22:35:27 +00:00
|
|
|
ndof += (flag & IK_XDOF) ? 1 : 0;
|
|
|
|
|
ndof += (flag & IK_YDOF) ? 1 : 0;
|
|
|
|
|
ndof += (flag & IK_ZDOF) ? 1 : 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
IK_QSegment *seg;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (ndof == 0) {
|
2005-08-31 22:09:44 +00:00
|
|
|
return NULL;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-27 12:44:41 +00:00
|
|
|
else if (ndof == 1) {
|
|
|
|
|
int axis;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (flag & IK_XDOF) {
|
2005-08-27 12:44:41 +00:00
|
|
|
axis = 0;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else if (flag & IK_YDOF) {
|
2005-08-27 12:44:41 +00:00
|
|
|
axis = 1;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2005-08-27 12:44:41 +00:00
|
|
|
axis = 2;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (translate) {
|
2005-08-27 12:44:41 +00:00
|
|
|
seg = new IK_QTranslateSegment(axis);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2005-08-27 12:44:41 +00:00
|
|
|
seg = new IK_QRevoluteSegment(axis);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
|
|
|
|
else if (ndof == 2) {
|
|
|
|
|
int axis1, axis2;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
if (flag & IK_XDOF) {
|
|
|
|
|
axis1 = 0;
|
2012-07-27 22:35:27 +00:00
|
|
|
axis2 = (flag & IK_YDOF) ? 1 : 2;
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
axis1 = 1;
|
|
|
|
|
axis2 = 2;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (translate) {
|
2005-08-27 12:44:41 +00:00
|
|
|
seg = new IK_QTranslateSegment(axis1, axis2);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-27 12:44:41 +00:00
|
|
|
else {
|
2023-09-17 09:01:48 +10:00
|
|
|
if (axis1 + axis2 == 2) {
|
2005-08-27 12:44:41 +00:00
|
|
|
seg = new IK_QSwingSegment();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2012-07-27 22:35:27 +00:00
|
|
|
seg = new IK_QElbowSegment((axis1 == 0) ? 0 : 2);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2005-08-27 12:44:41 +00:00
|
|
|
else {
|
2023-09-17 09:01:48 +10:00
|
|
|
if (translate) {
|
2005-08-27 12:44:41 +00:00
|
|
|
seg = new IK_QTranslateSegment();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2005-08-27 12:44:41 +00:00
|
|
|
seg = new IK_QSphericalSegment();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
return seg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IK_Segment *IK_CreateSegment(int flag)
|
|
|
|
|
{
|
|
|
|
|
IK_QSegment *rot = CreateSegment(flag, false);
|
|
|
|
|
IK_QSegment *trans = CreateSegment(flag >> 3, true);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
IK_QSegment *seg;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (rot == NULL && trans == NULL) {
|
2005-08-31 22:09:44 +00:00
|
|
|
seg = new IK_QNullSegment();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else if (rot == NULL) {
|
2005-08-31 22:09:44 +00:00
|
|
|
seg = trans;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-31 22:09:44 +00:00
|
|
|
else {
|
|
|
|
|
seg = rot;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
// make it seem from the interface as if the rotation and translation
|
|
|
|
|
// segment are one
|
|
|
|
|
if (trans) {
|
|
|
|
|
seg->SetComposite(trans);
|
|
|
|
|
trans->SetParent(seg);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
return seg;
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_FreeSegment(IK_Segment *seg)
|
|
|
|
|
{
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSegment *qseg = (IK_QSegment *)seg;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (qseg->Composite()) {
|
2005-08-31 22:09:44 +00:00
|
|
|
delete qseg->Composite();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-27 12:44:41 +00:00
|
|
|
delete qseg;
|
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_SetParent(IK_Segment *seg, IK_Segment *parent)
|
|
|
|
|
{
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSegment *qseg = (IK_QSegment *)seg;
|
|
|
|
|
IK_QSegment *qparent = (IK_QSegment *)parent;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (qparent && qparent->Composite()) {
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg->SetParent(qparent->Composite());
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg->SetParent(qparent);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_SetTransform(
|
|
|
|
|
IK_Segment *seg, float start[3], float rest[][3], float basis[][3], float length)
|
|
|
|
|
{
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSegment *qseg = (IK_QSegment *)seg;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-12-10 19:12:26 +01:00
|
|
|
Vector3d mstart(start[0], start[1], start[2]);
|
|
|
|
|
// convert from blender column major
|
|
|
|
|
Matrix3d mbasis = CreateMatrix(basis[0][0],
|
|
|
|
|
basis[1][0],
|
|
|
|
|
basis[2][0],
|
|
|
|
|
basis[0][1],
|
|
|
|
|
basis[1][1],
|
|
|
|
|
basis[2][1],
|
|
|
|
|
basis[0][2],
|
|
|
|
|
basis[1][2],
|
|
|
|
|
basis[2][2]);
|
|
|
|
|
Matrix3d mrest = CreateMatrix(rest[0][0],
|
|
|
|
|
rest[1][0],
|
|
|
|
|
rest[2][0],
|
|
|
|
|
rest[0][1],
|
|
|
|
|
rest[1][1],
|
|
|
|
|
rest[2][1],
|
|
|
|
|
rest[0][2],
|
|
|
|
|
rest[1][2],
|
|
|
|
|
rest[2][2]);
|
|
|
|
|
double mlength(length);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
if (qseg->Composite()) {
|
2015-12-10 19:12:26 +01:00
|
|
|
Vector3d cstart(0, 0, 0);
|
|
|
|
|
Matrix3d cbasis;
|
2005-08-31 22:09:44 +00:00
|
|
|
cbasis.setIdentity();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg->SetTransform(mstart, mrest, mbasis, 0.0);
|
|
|
|
|
qseg->Composite()->SetTransform(cstart, cbasis, cbasis, mlength);
|
|
|
|
|
}
|
2023-09-17 09:01:48 +10:00
|
|
|
else {
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg->SetTransform(mstart, mrest, mbasis, mlength);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_SetLimit(IK_Segment *seg, IK_SegmentAxis axis, float lmin, float lmax)
|
|
|
|
|
{
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSegment *qseg = (IK_QSegment *)seg;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
if (axis >= IK_TRANS_X) {
|
2012-07-27 22:35:27 +00:00
|
|
|
if (!qseg->Translational()) {
|
2023-09-17 09:01:48 +10:00
|
|
|
if (qseg->Composite() && qseg->Composite()->Translational()) {
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg = qseg->Composite();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2005-08-31 22:09:44 +00:00
|
|
|
return;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2009-01-17 00:51:42 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (axis == IK_TRANS_X) {
|
2012-07-27 22:35:27 +00:00
|
|
|
axis = IK_X;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else if (axis == IK_TRANS_Y) {
|
2012-07-27 22:35:27 +00:00
|
|
|
axis = IK_Y;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2012-07-27 22:35:27 +00:00
|
|
|
axis = IK_Z;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-31 22:09:44 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg->SetLimit(axis, lmin, lmax);
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_SetStiffness(IK_Segment *seg, IK_SegmentAxis axis, float stiffness)
|
|
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (stiffness < 0.0f) {
|
2005-08-27 12:44:41 +00:00
|
|
|
return;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (stiffness > (1.0 - IK_STRETCH_STIFF_EPS)) {
|
2012-07-27 23:16:33 +00:00
|
|
|
stiffness = (1.0 - IK_STRETCH_STIFF_EPS);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSegment *qseg = (IK_QSegment *)seg;
|
2015-12-10 19:12:26 +01:00
|
|
|
double weight = 1.0f - stiffness;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
if (axis >= IK_TRANS_X) {
|
2012-07-27 22:35:27 +00:00
|
|
|
if (!qseg->Translational()) {
|
2023-09-17 09:01:48 +10:00
|
|
|
if (qseg->Composite() && qseg->Composite()->Translational()) {
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg = qseg->Composite();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2005-08-31 22:09:44 +00:00
|
|
|
return;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2009-01-17 00:51:42 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (axis == IK_TRANS_X) {
|
2012-07-27 22:35:27 +00:00
|
|
|
axis = IK_X;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else if (axis == IK_TRANS_Y) {
|
2012-07-27 22:35:27 +00:00
|
|
|
axis = IK_Y;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2005-08-31 22:09:44 +00:00
|
|
|
axis = IK_Z;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-31 22:09:44 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg->SetWeight(axis, weight);
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_GetBasisChange(IK_Segment *seg, float basis_change[][3])
|
|
|
|
|
{
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSegment *qseg = (IK_QSegment *)seg;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (qseg->Translational() && qseg->Composite()) {
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg = qseg->Composite();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-12-10 19:12:26 +01:00
|
|
|
const Matrix3d &change = qseg->BasisChange();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-12-10 19:12:26 +01:00
|
|
|
// convert to blender column major
|
|
|
|
|
basis_change[0][0] = (float)change(0, 0);
|
|
|
|
|
basis_change[1][0] = (float)change(0, 1);
|
|
|
|
|
basis_change[2][0] = (float)change(0, 2);
|
|
|
|
|
basis_change[0][1] = (float)change(1, 0);
|
|
|
|
|
basis_change[1][1] = (float)change(1, 1);
|
|
|
|
|
basis_change[2][1] = (float)change(1, 2);
|
|
|
|
|
basis_change[0][2] = (float)change(2, 0);
|
|
|
|
|
basis_change[1][2] = (float)change(2, 1);
|
|
|
|
|
basis_change[2][2] = (float)change(2, 2);
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_GetTranslationChange(IK_Segment *seg, float *translation_change)
|
|
|
|
|
{
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSegment *qseg = (IK_QSegment *)seg;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (!qseg->Translational() && qseg->Composite()) {
|
2005-08-31 22:09:44 +00:00
|
|
|
qseg = qseg->Composite();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-31 22:09:44 +00:00
|
|
|
|
2015-12-10 19:12:26 +01:00
|
|
|
const Vector3d &change = qseg->TranslationChange();
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
translation_change[0] = (float)change[0];
|
|
|
|
|
translation_change[1] = (float)change[1];
|
|
|
|
|
translation_change[2] = (float)change[2];
|
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
IK_Solver *IK_CreateSolver(IK_Segment *root)
|
|
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (root == NULL) {
|
2005-08-27 12:44:41 +00:00
|
|
|
return NULL;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
IK_QSolver *solver = new IK_QSolver();
|
2012-07-27 22:35:27 +00:00
|
|
|
solver->root = (IK_QSegment *)root;
|
2005-08-27 12:44:41 +00:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
return (IK_Solver *)solver;
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_FreeSolver(IK_Solver *solver)
|
|
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (solver == NULL) {
|
2005-08-27 12:44:41 +00:00
|
|
|
return;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSolver *qsolver = (IK_QSolver *)solver;
|
|
|
|
|
std::list<IK_QTask *> &tasks = qsolver->tasks;
|
|
|
|
|
std::list<IK_QTask *>::iterator task;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
for (task = tasks.begin(); task != tasks.end(); task++) {
|
2005-08-27 12:44:41 +00:00
|
|
|
delete (*task);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
delete qsolver;
|
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_SolverAddGoal(IK_Solver *solver, IK_Segment *tip, float goal[3], float weight)
|
|
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (solver == NULL || tip == NULL) {
|
2005-08-27 12:44:41 +00:00
|
|
|
return;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSolver *qsolver = (IK_QSolver *)solver;
|
|
|
|
|
IK_QSegment *qtip = (IK_QSegment *)tip;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2015-09-21 01:48:37 +02:00
|
|
|
// in case of composite segment the second segment is the tip
|
2023-09-17 09:01:48 +10:00
|
|
|
if (qtip->Composite()) {
|
2005-08-31 22:09:44 +00:00
|
|
|
qtip = qtip->Composite();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2005-08-31 22:09:44 +00:00
|
|
|
|
2015-12-10 19:12:26 +01:00
|
|
|
Vector3d pos(goal[0], goal[1], goal[2]);
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
IK_QTask *ee = new IK_QPositionTask(true, qtip, pos);
|
|
|
|
|
ee->SetWeight(weight);
|
|
|
|
|
qsolver->tasks.push_back(ee);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[][3], float weight)
|
|
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (solver == NULL || tip == NULL) {
|
2005-08-27 12:44:41 +00:00
|
|
|
return;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSolver *qsolver = (IK_QSolver *)solver;
|
|
|
|
|
IK_QSegment *qtip = (IK_QSegment *)tip;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-09-21 01:48:37 +02:00
|
|
|
// in case of composite segment the second segment is the tip
|
2023-09-17 09:01:48 +10:00
|
|
|
if (qtip->Composite()) {
|
2005-08-31 22:09:44 +00:00
|
|
|
qtip = qtip->Composite();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-12-10 19:12:26 +01:00
|
|
|
// convert from blender column major
|
|
|
|
|
Matrix3d rot = CreateMatrix(goal[0][0],
|
|
|
|
|
goal[1][0],
|
|
|
|
|
goal[2][0],
|
|
|
|
|
goal[0][1],
|
|
|
|
|
goal[1][1],
|
|
|
|
|
goal[2][1],
|
|
|
|
|
goal[0][2],
|
|
|
|
|
goal[1][2],
|
|
|
|
|
goal[2][2]);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Update SConscript.
Fix some warnings.
Merge with latest soc code.
What changed in IK lib:
Fully restructured, with components now as follows:
- IK_Solver: C <=> C++ interface
- IK_QSegment: base class for bone/segment with 0
to 3 DOF
- IK_QTask: base class for a task (currently there's
a position and a rotation task)
- IK_QJacobian: the Jacobian matrix, with SVD
decomposition, damping, etc
- IK_QJacobianSolver: the iterative solver
The exponential map parametrization is no longer used,
instead we have now:
- 3DOF and 2DOF XZ segments: directly update matrix
with Rodrigues' formula
- Other: Euler angles (no worries about singularities
here)
Computation of the Jacobian inverse has also changed:
- The SVD algorithm is now based on LAPACK code,
instead of NR, to avoid some problems with rounding
errors.
- When the problem is underconstrained (as is the case
most of the time), the SVD is computed for the transpose
of the Jacobian (faster).
- A new damping algorithm called the Selectively Damped
Least Squares is used, result in faster and more
stable convergence.
- Stiffness is implemented as if a weighted psuedo-inverse
was used.
Tree structure support.
Rotation limits:
- 3DOF and 2DOF XZ segments limits are based on a swing
(direct axis-angle over XZ) and twist/roll (rotation
over Y) decomposition. The swing region is an ellipse
on a sphere.
- Rotation limits are implemented using an inner clamping
loop: as long as there is a violation, a violating DOF
is clamped and removed from the Jacobian, and the solution
is recomputed.
Convergence checking is based now on the max norm of angle
change, or the maximum number of iterations.
2005-08-27 13:45:19 +00:00
|
|
|
IK_QTask *orient = new IK_QOrientationTask(true, qtip, rot);
|
2005-08-27 12:44:41 +00:00
|
|
|
orient->SetWeight(weight);
|
|
|
|
|
qsolver->tasks.push_back(orient);
|
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2007-10-24 14:58:31 +00:00
|
|
|
void IK_SolverSetPoleVectorConstraint(IK_Solver *solver,
|
|
|
|
|
IK_Segment *tip,
|
|
|
|
|
float goal[3],
|
|
|
|
|
float polegoal[3],
|
|
|
|
|
float poleangle,
|
|
|
|
|
int getangle)
|
|
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (solver == NULL || tip == NULL) {
|
2007-10-24 14:58:31 +00:00
|
|
|
return;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2007-10-24 14:58:31 +00:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSolver *qsolver = (IK_QSolver *)solver;
|
|
|
|
|
IK_QSegment *qtip = (IK_QSegment *)tip;
|
2007-10-24 14:58:31 +00:00
|
|
|
|
2015-09-21 01:48:37 +02:00
|
|
|
// in case of composite segment the second segment is the tip
|
2023-09-17 09:01:48 +10:00
|
|
|
if (qtip->Composite()) {
|
2015-09-21 01:48:37 +02:00
|
|
|
qtip = qtip->Composite();
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2015-09-21 01:48:37 +02:00
|
|
|
|
2015-12-10 19:12:26 +01:00
|
|
|
Vector3d qgoal(goal[0], goal[1], goal[2]);
|
|
|
|
|
Vector3d qpolegoal(polegoal[0], polegoal[1], polegoal[2]);
|
2007-10-24 14:58:31 +00:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
qsolver->solver.SetPoleVectorConstraint(qtip, qgoal, qpolegoal, poleangle, getangle);
|
2007-10-24 14:58:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float IK_SolverGetPoleAngle(IK_Solver *solver)
|
|
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (solver == NULL) {
|
2007-10-24 14:58:31 +00:00
|
|
|
return 0.0f;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2007-10-24 14:58:31 +00:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSolver *qsolver = (IK_QSolver *)solver;
|
2007-10-24 14:58:31 +00:00
|
|
|
|
|
|
|
|
return qsolver->solver.GetPoleAngle();
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-26 21:58:06 +00:00
|
|
|
#if 0
|
2019-04-17 08:16:53 +02:00
|
|
|
static void IK_SolverAddCenterOfMass(IK_Solver *solver,
|
|
|
|
|
IK_Segment *root,
|
|
|
|
|
float goal[3],
|
|
|
|
|
float weight)
|
2005-08-27 12:44:41 +00:00
|
|
|
{
|
|
|
|
|
if (solver == NULL || root == NULL)
|
|
|
|
|
return;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSolver *qsolver = (IK_QSolver *)solver;
|
|
|
|
|
IK_QSegment *qroot = (IK_QSegment *)root;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2015-12-10 19:12:26 +01:00
|
|
|
// convert from blender column major
|
|
|
|
|
Vector3d center(goal);
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
IK_QTask *com = new IK_QCenterOfMassTask(true, qroot, center);
|
|
|
|
|
com->SetWeight(weight);
|
|
|
|
|
qsolver->tasks.push_back(com);
|
|
|
|
|
}
|
2013-02-26 21:58:06 +00:00
|
|
|
#endif
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations)
|
|
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (solver == NULL) {
|
2005-08-27 12:44:41 +00:00
|
|
|
return 0;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
IK_QSolver *qsolver = (IK_QSolver *)solver;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
IK_QSegment *root = qsolver->root;
|
|
|
|
|
IK_QJacobianSolver &jacobian = qsolver->solver;
|
2012-07-27 22:35:27 +00:00
|
|
|
std::list<IK_QTask *> &tasks = qsolver->tasks;
|
2015-12-10 19:12:26 +01:00
|
|
|
double tol = tolerance;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (!jacobian.Setup(root, tasks)) {
|
2007-10-24 14:58:31 +00:00
|
|
|
return 0;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2007-10-24 14:58:31 +00:00
|
|
|
|
2005-08-27 12:44:41 +00:00
|
|
|
bool result = jacobian.Solve(root, tasks, tol, max_iterations);
|
|
|
|
|
|
2012-07-27 22:35:27 +00:00
|
|
|
return ((result) ? 1 : 0);
|
2005-08-27 12:44:41 +00:00
|
|
|
}
|