This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/intern/iksolver/intern/IK_Solver.cpp
Brecht Van Lommel ae9dcb3dc2 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

281 lines
7.0 KiB
C++

/**
* $Id$
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. The Blender
* Foundation also sells licenses for use in proprietary software under
* the Blender License. See http://www.blender.org/BL/ for information
* about this.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Original Author: Laurence
* Contributor(s): Brecht
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*/
#include "../extern/IK_solver.h"
#include "IK_QJacobianSolver.h"
#include "IK_QSegment.h"
#include "IK_QTask.h"
#include <list>
using namespace std;
typedef struct {
IK_QJacobianSolver solver;
IK_QSegment *root;
std::list<IK_QTask*> tasks;
} IK_QSolver;
IK_Segment *IK_CreateSegment(int flag)
{
int ndof = 0;
ndof += (flag & IK_XDOF)? 1: 0;
ndof += (flag & IK_YDOF)? 1: 0;
ndof += (flag & IK_ZDOF)? 1: 0;
IK_QSegment *seg;
if (ndof == 0)
seg = new IK_QNullSegment();
else if (ndof == 1) {
int axis;
if (flag & IK_XDOF) axis = 0;
else if (flag & IK_YDOF) axis = 1;
else axis = 2;
if (flag & IK_TRANSLATIONAL)
seg = new IK_QTranslateSegment(axis);
else
seg = new IK_QRevoluteSegment(axis);
}
else if (ndof == 2) {
int axis1, axis2;
if (flag & IK_XDOF) {
axis1 = 0;
axis2 = (flag & IK_YDOF)? 1: 2;
}
else {
axis1 = 1;
axis2 = 2;
}
if (flag & IK_TRANSLATIONAL)
seg = new IK_QTranslateSegment(axis1, axis2);
else {
if (axis1 + axis2 == 2)
seg = new IK_QSwingSegment();
else
seg = new IK_QElbowSegment((axis1 == 0)? 0: 2);
}
}
else {
if (flag & IK_TRANSLATIONAL)
seg = new IK_QTranslateSegment();
else
seg = new IK_QSphericalSegment();
}
return (IK_Segment*)seg;
}
void IK_FreeSegment(IK_Segment *seg)
{
IK_QSegment *qseg = (IK_QSegment*)seg;
delete qseg;
}
void IK_SetParent(IK_Segment *seg, IK_Segment *parent)
{
IK_QSegment *qseg = (IK_QSegment*)seg;
qseg->SetParent((IK_QSegment*)parent);
}
void IK_SetTransform(IK_Segment *seg, float start[3], float rest[][3], float basis[][3], float length)
{
IK_QSegment *qseg = (IK_QSegment*)seg;
MT_Vector3 mstart(start);
// convert from blender column major to moto row major
MT_Matrix3x3 mbasis(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]);
MT_Matrix3x3 mrest(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]);
MT_Scalar mlength(length);
qseg->SetTransform(mstart, mrest, mbasis, mlength);
}
void IK_SetLimit(IK_Segment *seg, IK_SegmentAxis axis, float lmin, float lmax)
{
IK_QSegment *qseg = (IK_QSegment*)seg;
if (axis == IK_X)
qseg->SetLimit(0, lmin, lmax);
else if (axis == IK_Y)
qseg->SetLimit(1, lmin, lmax);
else if (axis == IK_Z)
qseg->SetLimit(2, lmin, lmax);
}
void IK_SetStiffness(IK_Segment *seg, IK_SegmentAxis axis, float stiffness)
{
if (stiffness < 1.0)
return;
IK_QSegment *qseg = (IK_QSegment*)seg;
MT_Scalar weight = 1.0/stiffness;
if (axis == IK_X)
qseg->SetWeight(0, weight);
else if (axis == IK_Y)
qseg->SetWeight(1, weight);
else if (axis == IK_Z)
qseg->SetWeight(2, weight);
}
void IK_GetBasisChange(IK_Segment *seg, float basis_change[][3])
{
IK_QSegment *qseg = (IK_QSegment*)seg;
const MT_Matrix3x3& change = qseg->BasisChange();
// convert from moto row major 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];
}
void IK_GetTranslationChange(IK_Segment *seg, float *translation_change)
{
IK_QSegment *qseg = (IK_QSegment*)seg;
const MT_Vector3& change = qseg->TranslationChange();
translation_change[0] = (float)change[0];
translation_change[1] = (float)change[1];
translation_change[2] = (float)change[2];
}
IK_Solver *IK_CreateSolver(IK_Segment *root)
{
if (root == NULL)
return NULL;
IK_QSolver *solver = new IK_QSolver();
solver->root = (IK_QSegment*)root;
return (IK_Solver*)solver;
}
void IK_FreeSolver(IK_Solver *solver)
{
if (solver == NULL)
return;
IK_QSolver *qsolver = (IK_QSolver*)solver;
std::list<IK_QTask*>& tasks = qsolver->tasks;
std::list<IK_QTask*>::iterator task;
for (task = tasks.begin(); task != tasks.end(); task++)
delete (*task);
delete qsolver;
}
void IK_SolverAddGoal(IK_Solver *solver, IK_Segment *tip, float goal[3], float weight)
{
if (solver == NULL || tip == NULL)
return;
IK_QSolver *qsolver = (IK_QSolver*)solver;
IK_QSegment *qtip = (IK_QSegment*)tip;
MT_Vector3 pos(goal);
// qsolver->tasks.empty()
IK_QTask *ee = new IK_QPositionTask(true, qtip, pos);
ee->SetWeight(weight);
qsolver->tasks.push_back(ee);
}
void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[][3], float weight)
{
if (solver == NULL || tip == NULL)
return;
IK_QSolver *qsolver = (IK_QSolver*)solver;
IK_QSegment *qtip = (IK_QSegment*)tip;
// convert from blender column major to moto row major
MT_Matrix3x3 rot(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]);
IK_QTask *orient = new IK_QOrientationTask(true, qtip, rot);
orient->SetWeight(weight);
qsolver->tasks.push_back(orient);
}
void IK_SolverAddCenterOfMass(IK_Solver *solver, IK_Segment *root, float goal[3], float weight)
{
if (solver == NULL || root == NULL)
return;
IK_QSolver *qsolver = (IK_QSolver*)solver;
IK_QSegment *qroot = (IK_QSegment*)root;
// convert from blender column major to moto row major
MT_Vector3 center(goal);
IK_QTask *com = new IK_QCenterOfMassTask(true, qroot, center);
com->SetWeight(weight);
qsolver->tasks.push_back(com);
}
int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations)
{
if (solver == NULL)
return 0;
IK_QSolver *qsolver = (IK_QSolver*)solver;
IK_QSegment *root = qsolver->root;
IK_QJacobianSolver& jacobian = qsolver->solver;
std::list<IK_QTask*>& tasks = qsolver->tasks;
MT_Scalar tol = tolerance;
bool result = jacobian.Solve(root, tasks, tol, max_iterations);
return ((result)? 1: 0);
}