Pole Target for IK
================== This adds an extra target to the IK solver constraint to define the roll of the IK chain. http://www.blender.org/development/current-projects/changes-since-244/inverse-kinematics/ Also fixes a crashes using ctrl+I to set an IK constraint on a bone due to the recent constraints refactor.
This commit is contained in:
2
intern/iksolver/extern/IK_solver.h
vendored
2
intern/iksolver/extern/IK_solver.h
vendored
@@ -158,6 +158,8 @@ void IK_FreeSolver(IK_Solver *solver);
|
|||||||
|
|
||||||
void IK_SolverAddGoal(IK_Solver *solver, IK_Segment *tip, float goal[3], float weight);
|
void IK_SolverAddGoal(IK_Solver *solver, IK_Segment *tip, float goal[3], float weight);
|
||||||
void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[][3], float weight);
|
void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[][3], float weight);
|
||||||
|
void IK_SolverSetPoleVectorConstraint(IK_Solver *solver, IK_Segment *tip, float goal[3], float polegoal[3], float poleangle, int getangle);
|
||||||
|
float IK_SolverGetPoleAngle(IK_Solver *solver);
|
||||||
|
|
||||||
int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations);
|
int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations);
|
||||||
|
|
||||||
|
|||||||
@@ -42,11 +42,10 @@ IK_QJacobian::~IK_QJacobian()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void IK_QJacobian::ArmMatrices(int dof, int task_size, int tasks)
|
void IK_QJacobian::ArmMatrices(int dof, int task_size)
|
||||||
{
|
{
|
||||||
m_dof = dof;
|
m_dof = dof;
|
||||||
m_task_size = task_size;
|
m_task_size = task_size;
|
||||||
m_tasks = tasks;
|
|
||||||
|
|
||||||
m_jacobian.newsize(task_size, dof);
|
m_jacobian.newsize(task_size, dof);
|
||||||
m_jacobian = 0;
|
m_jacobian = 0;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public:
|
|||||||
~IK_QJacobian();
|
~IK_QJacobian();
|
||||||
|
|
||||||
// Call once to initialize
|
// Call once to initialize
|
||||||
void ArmMatrices(int dof, int task_size, int tasks);
|
void ArmMatrices(int dof, int task_size);
|
||||||
void SetDoFWeight(int dof, MT_Scalar weight);
|
void SetDoFWeight(int dof, MT_Scalar weight);
|
||||||
|
|
||||||
// Iteratively called
|
// Iteratively called
|
||||||
@@ -75,7 +75,7 @@ private:
|
|||||||
void InvertSDLS();
|
void InvertSDLS();
|
||||||
void InvertDLS();
|
void InvertDLS();
|
||||||
|
|
||||||
int m_dof, m_task_size, m_tasks;
|
int m_dof, m_task_size;
|
||||||
bool m_transpose;
|
bool m_transpose;
|
||||||
|
|
||||||
// the jacobian matrix and it's null space projector
|
// the jacobian matrix and it's null space projector
|
||||||
|
|||||||
@@ -32,6 +32,15 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "IK_QJacobianSolver.h"
|
#include "IK_QJacobianSolver.h"
|
||||||
|
#include "MT_Quaternion.h"
|
||||||
|
|
||||||
|
//#include "analyze.h"
|
||||||
|
IK_QJacobianSolver::IK_QJacobianSolver()
|
||||||
|
{
|
||||||
|
m_poleconstraint = false;
|
||||||
|
m_getpoleangle = false;
|
||||||
|
m_rootmatrix.setIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
void IK_QJacobianSolver::AddSegmentList(IK_QSegment *seg)
|
void IK_QJacobianSolver::AddSegmentList(IK_QSegment *seg)
|
||||||
{
|
{
|
||||||
@@ -47,7 +56,7 @@ bool IK_QJacobianSolver::Setup(IK_QSegment *root, std::list<IK_QTask*>& tasks)
|
|||||||
m_segments.clear();
|
m_segments.clear();
|
||||||
AddSegmentList(root);
|
AddSegmentList(root);
|
||||||
|
|
||||||
// assing each segment a unique id for the jacobian
|
// assign each segment a unique id for the jacobian
|
||||||
std::vector<IK_QSegment*>::iterator seg;
|
std::vector<IK_QSegment*>::iterator seg;
|
||||||
int num_dof = 0;
|
int num_dof = 0;
|
||||||
|
|
||||||
@@ -105,9 +114,9 @@ bool IK_QJacobianSolver::Setup(IK_QSegment *root, std::list<IK_QTask*>& tasks)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set matrix sizes
|
// set matrix sizes
|
||||||
m_jacobian.ArmMatrices(num_dof, primary_size, primary);
|
m_jacobian.ArmMatrices(num_dof, primary_size);
|
||||||
if (secondary > 0)
|
if (secondary > 0)
|
||||||
m_jacobian_sub.ArmMatrices(num_dof, secondary_size, secondary);
|
m_jacobian_sub.ArmMatrices(num_dof, secondary_size);
|
||||||
|
|
||||||
// set dof weights
|
// set dof weights
|
||||||
int i;
|
int i;
|
||||||
@@ -119,6 +128,109 @@ bool IK_QJacobianSolver::Setup(IK_QSegment *root, std::list<IK_QTask*>& tasks)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IK_QJacobianSolver::SetPoleVectorConstraint(IK_QSegment *tip, MT_Vector3& goal, MT_Vector3& polegoal, float poleangle, bool getangle)
|
||||||
|
{
|
||||||
|
m_poleconstraint = true;
|
||||||
|
m_poletip = tip;
|
||||||
|
m_goal = goal;
|
||||||
|
m_polegoal = polegoal;
|
||||||
|
m_poleangle = (getangle)? 0.0f: poleangle;
|
||||||
|
m_getpoleangle = getangle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MT_Scalar safe_acos(MT_Scalar f)
|
||||||
|
{
|
||||||
|
// acos that does not return NaN with rounding errors
|
||||||
|
if (f <= -1.0f) return MT_PI;
|
||||||
|
else if (f >= 1.0f) return 0.0;
|
||||||
|
else return acos(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MT_Vector3 normalize(const MT_Vector3& v)
|
||||||
|
{
|
||||||
|
// a sane normalize function that doesn't give (1, 0, 0) in case
|
||||||
|
// of a zero length vector, like MT_Vector3.normalize
|
||||||
|
MT_Scalar len = v.length();
|
||||||
|
return MT_fuzzyZero(len)? MT_Vector3(0, 0, 0): v/len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float angle(const MT_Vector3& v1, const MT_Vector3& v2)
|
||||||
|
{
|
||||||
|
return safe_acos(v1.dot(v2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IK_QJacobianSolver::ConstrainPoleVector(IK_QSegment *root, std::list<IK_QTask*>& tasks)
|
||||||
|
{
|
||||||
|
// this function will be called before and after solving. calling it before
|
||||||
|
// solving gives predictable solutions by rotating towards the solution,
|
||||||
|
// and calling it afterwards ensures the solution is exact.
|
||||||
|
|
||||||
|
if(!m_poleconstraint)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// disable pole vector constraint in case of multiple position tasks
|
||||||
|
std::list<IK_QTask*>::iterator task;
|
||||||
|
int positiontasks = 0;
|
||||||
|
|
||||||
|
for (task = tasks.begin(); task != tasks.end(); task++)
|
||||||
|
if((*task)->PositionTask())
|
||||||
|
positiontasks++;
|
||||||
|
|
||||||
|
if (positiontasks >= 2) {
|
||||||
|
m_poleconstraint = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get positions and rotations
|
||||||
|
root->UpdateTransform(m_rootmatrix);
|
||||||
|
|
||||||
|
const MT_Vector3 rootpos = root->GlobalStart();
|
||||||
|
const MT_Vector3 endpos = m_poletip->GlobalEnd();
|
||||||
|
const MT_Matrix3x3& rootbasis = root->GlobalTransform().getBasis();
|
||||||
|
|
||||||
|
// construct "lookat" matrices (like gluLookAt), based on a direction and
|
||||||
|
// an up vector, with the direction going from the root to the end effector
|
||||||
|
// and the up vector going from the root to the pole constraint position.
|
||||||
|
MT_Vector3 dir = normalize(endpos - rootpos);
|
||||||
|
MT_Vector3 rootx= rootbasis.getColumn(0);
|
||||||
|
MT_Vector3 rootz= rootbasis.getColumn(2);
|
||||||
|
MT_Vector3 up = rootx*cos(m_poleangle) + rootz*sin(m_poleangle);
|
||||||
|
|
||||||
|
// in post, don't rotate towards the goal but only correct the pole up
|
||||||
|
MT_Vector3 poledir = (m_getpoleangle)? dir: normalize(m_goal - rootpos);
|
||||||
|
MT_Vector3 poleup = normalize(m_polegoal - rootpos);
|
||||||
|
|
||||||
|
MT_Matrix3x3 mat, polemat;
|
||||||
|
|
||||||
|
mat[0] = normalize(MT_cross(dir, up));
|
||||||
|
mat[1] = MT_cross(mat[0], dir);
|
||||||
|
mat[2] = -dir;
|
||||||
|
|
||||||
|
polemat[0] = normalize(MT_cross(poledir, poleup));
|
||||||
|
polemat[1] = MT_cross(polemat[0], poledir);
|
||||||
|
polemat[2] = -poledir;
|
||||||
|
|
||||||
|
if(m_getpoleangle) {
|
||||||
|
// we compute the pole angle that to rotate towards the target
|
||||||
|
m_poleangle = angle(mat[1], polemat[1]);
|
||||||
|
|
||||||
|
if(rootz.dot(mat[1]*cos(m_poleangle) + mat[0]*sin(m_poleangle)) > 0.0f)
|
||||||
|
m_poleangle = -m_poleangle;
|
||||||
|
|
||||||
|
// solve again, with the pole angle we just computed
|
||||||
|
m_getpoleangle = false;
|
||||||
|
ConstrainPoleVector(root, tasks);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// now we set as root matrix the difference between the current and
|
||||||
|
// desired rotation based on the pole vector constraint. we use
|
||||||
|
// transpose instead of inverse because we have orthogonal matrices
|
||||||
|
// anyway, and in case of a singular matrix we don't get NaN's.
|
||||||
|
MT_Transform trans(MT_Point3(0, 0, 0), polemat.transposed()*mat);
|
||||||
|
m_rootmatrix = trans*m_rootmatrix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool IK_QJacobianSolver::UpdateAngles(MT_Scalar& norm)
|
bool IK_QJacobianSolver::UpdateAngles(MT_Scalar& norm)
|
||||||
{
|
{
|
||||||
// assing each segment a unique id for the jacobian
|
// assing each segment a unique id for the jacobian
|
||||||
@@ -181,15 +293,17 @@ bool IK_QJacobianSolver::Solve(
|
|||||||
const int max_iterations
|
const int max_iterations
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
bool solved = false;
|
||||||
//double dt = analyze_time();
|
//double dt = analyze_time();
|
||||||
|
|
||||||
if (!Setup(root, tasks))
|
ConstrainPoleVector(root, tasks);
|
||||||
return false;
|
|
||||||
|
root->UpdateTransform(m_rootmatrix);
|
||||||
|
|
||||||
// iterate
|
// iterate
|
||||||
for (int iterations = 0; iterations < max_iterations; iterations++) {
|
for (int iterations = 0; iterations < max_iterations; iterations++) {
|
||||||
// update transform
|
// update transform
|
||||||
root->UpdateTransform(MT_Transform::Identity());
|
root->UpdateTransform(m_rootmatrix);
|
||||||
|
|
||||||
std::list<IK_QTask*>::iterator task;
|
std::list<IK_QTask*>::iterator task;
|
||||||
|
|
||||||
@@ -211,7 +325,7 @@ bool IK_QJacobianSolver::Solve(
|
|||||||
m_jacobian.SubTask(m_jacobian_sub);
|
m_jacobian.SubTask(m_jacobian_sub);
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
printf("IK Exception\n");
|
fprintf(stderr, "IK Exception\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,12 +344,19 @@ bool IK_QJacobianSolver::Solve(
|
|||||||
|
|
||||||
// check for convergence
|
// check for convergence
|
||||||
if (norm < 1e-3) {
|
if (norm < 1e-3) {
|
||||||
|
solved = true;
|
||||||
|
break;
|
||||||
//analyze_add_run(iterations, analyze_time()-dt);
|
//analyze_add_run(iterations, analyze_time()-dt);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(m_poleconstraint)
|
||||||
|
root->PrependBasis(m_rootmatrix.getBasis());
|
||||||
|
|
||||||
//analyze_add_run(max_iterations, analyze_time()-dt);
|
//analyze_add_run(max_iterations, analyze_time()-dt);
|
||||||
return false;
|
|
||||||
|
return solved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "MT_Vector3.h"
|
#include "MT_Vector3.h"
|
||||||
|
#include "MT_Transform.h"
|
||||||
#include "IK_QJacobian.h"
|
#include "IK_QJacobian.h"
|
||||||
#include "IK_QSegment.h"
|
#include "IK_QSegment.h"
|
||||||
#include "IK_QTask.h"
|
#include "IK_QTask.h"
|
||||||
@@ -50,11 +51,18 @@
|
|||||||
class IK_QJacobianSolver
|
class IK_QJacobianSolver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IK_QJacobianSolver() {};
|
IK_QJacobianSolver();
|
||||||
~IK_QJacobianSolver() {};
|
~IK_QJacobianSolver() {};
|
||||||
|
|
||||||
// returns true if converged, false if max number of iterations was used
|
// setup pole vector constraint
|
||||||
|
void SetPoleVectorConstraint(IK_QSegment *tip, MT_Vector3& goal,
|
||||||
|
MT_Vector3& polegoal, float poleangle, bool getangle);
|
||||||
|
float GetPoleAngle() { return m_poleangle; };
|
||||||
|
|
||||||
|
// call setup once before solving, if it fails don't solve
|
||||||
|
bool Setup(IK_QSegment *root, std::list<IK_QTask*>& tasks);
|
||||||
|
|
||||||
|
// returns true if converged, false if max number of iterations was used
|
||||||
bool Solve(
|
bool Solve(
|
||||||
IK_QSegment *root,
|
IK_QSegment *root,
|
||||||
std::list<IK_QTask*> tasks,
|
std::list<IK_QTask*> tasks,
|
||||||
@@ -64,8 +72,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void AddSegmentList(IK_QSegment *seg);
|
void AddSegmentList(IK_QSegment *seg);
|
||||||
bool Setup(IK_QSegment *root, std::list<IK_QTask*>& tasks);
|
|
||||||
bool UpdateAngles(MT_Scalar& norm);
|
bool UpdateAngles(MT_Scalar& norm);
|
||||||
|
void ConstrainPoleVector(IK_QSegment *root, std::list<IK_QTask*>& tasks);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -75,6 +83,15 @@ private:
|
|||||||
bool m_secondary_enabled;
|
bool m_secondary_enabled;
|
||||||
|
|
||||||
std::vector<IK_QSegment*> m_segments;
|
std::vector<IK_QSegment*> m_segments;
|
||||||
|
|
||||||
|
MT_Transform m_rootmatrix;
|
||||||
|
|
||||||
|
bool m_poleconstraint;
|
||||||
|
bool m_getpoleangle;
|
||||||
|
MT_Vector3 m_goal;
|
||||||
|
MT_Vector3 m_polegoal;
|
||||||
|
float m_poleangle;
|
||||||
|
IK_QSegment *m_poletip;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -236,6 +236,18 @@ IK_QSegment::IK_QSegment(int num_DoF, bool translational)
|
|||||||
m_orig_translation = m_translation;
|
m_orig_translation = m_translation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IK_QSegment::Reset()
|
||||||
|
{
|
||||||
|
m_locked[0] = m_locked[1] = m_locked[2] = false;
|
||||||
|
|
||||||
|
m_basis = m_orig_basis;
|
||||||
|
m_translation = m_orig_translation;
|
||||||
|
SetBasis(m_basis);
|
||||||
|
|
||||||
|
for (IK_QSegment *seg = m_child; seg; seg = seg->m_sibling)
|
||||||
|
seg->Reset();
|
||||||
|
}
|
||||||
|
|
||||||
void IK_QSegment::SetTransform(
|
void IK_QSegment::SetTransform(
|
||||||
const MT_Vector3& start,
|
const MT_Vector3& start,
|
||||||
const MT_Matrix3x3& rest_basis,
|
const MT_Matrix3x3& rest_basis,
|
||||||
@@ -326,6 +338,11 @@ void IK_QSegment::UpdateTransform(const MT_Transform& global)
|
|||||||
seg->UpdateTransform(m_global_transform);
|
seg->UpdateTransform(m_global_transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IK_QSegment::PrependBasis(const MT_Matrix3x3& mat)
|
||||||
|
{
|
||||||
|
m_basis = m_rest_basis.inverse() * mat * m_rest_basis * m_basis;
|
||||||
|
}
|
||||||
|
|
||||||
// IK_QSphericalSegment
|
// IK_QSphericalSegment
|
||||||
|
|
||||||
IK_QSphericalSegment::IK_QSphericalSegment()
|
IK_QSphericalSegment::IK_QSphericalSegment()
|
||||||
|
|||||||
@@ -165,6 +165,10 @@ public:
|
|||||||
|
|
||||||
virtual void SetBasis(const MT_Matrix3x3& basis) { m_basis = basis; }
|
virtual void SetBasis(const MT_Matrix3x3& basis) { m_basis = basis; }
|
||||||
|
|
||||||
|
// functions needed for pole vector constraint
|
||||||
|
void PrependBasis(const MT_Matrix3x3& mat);
|
||||||
|
void Reset();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// num_DoF: number of degrees of freedom
|
// num_DoF: number of degrees of freedom
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ public:
|
|||||||
|
|
||||||
virtual MT_Scalar Distance() const=0;
|
virtual MT_Scalar Distance() const=0;
|
||||||
|
|
||||||
|
virtual bool PositionTask() const { return false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int m_id;
|
int m_id;
|
||||||
int m_size;
|
int m_size;
|
||||||
@@ -97,6 +99,8 @@ public:
|
|||||||
|
|
||||||
MT_Scalar Distance() const;
|
MT_Scalar Distance() const;
|
||||||
|
|
||||||
|
bool PositionTask() const { return true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MT_Vector3 m_goal;
|
MT_Vector3 m_goal;
|
||||||
MT_Scalar m_clamp_length;
|
MT_Scalar m_clamp_length;
|
||||||
|
|||||||
@@ -318,6 +318,31 @@ void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[
|
|||||||
qsolver->tasks.push_back(orient);
|
qsolver->tasks.push_back(orient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IK_SolverSetPoleVectorConstraint(IK_Solver *solver, IK_Segment *tip, float goal[3], float polegoal[3], float poleangle, int getangle)
|
||||||
|
{
|
||||||
|
if (solver == NULL || tip == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IK_QSolver *qsolver = (IK_QSolver*)solver;
|
||||||
|
IK_QSegment *qtip = (IK_QSegment*)tip;
|
||||||
|
|
||||||
|
MT_Vector3 qgoal(goal);
|
||||||
|
MT_Vector3 qpolegoal(polegoal);
|
||||||
|
|
||||||
|
qsolver->solver.SetPoleVectorConstraint(
|
||||||
|
qtip, qgoal, qpolegoal, poleangle, getangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
float IK_SolverGetPoleAngle(IK_Solver *solver)
|
||||||
|
{
|
||||||
|
if (solver == NULL)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
IK_QSolver *qsolver = (IK_QSolver*)solver;
|
||||||
|
|
||||||
|
return qsolver->solver.GetPoleAngle();
|
||||||
|
}
|
||||||
|
|
||||||
void IK_SolverAddCenterOfMass(IK_Solver *solver, IK_Segment *root, float goal[3], float weight)
|
void IK_SolverAddCenterOfMass(IK_Solver *solver, IK_Segment *root, float goal[3], float weight)
|
||||||
{
|
{
|
||||||
if (solver == NULL || root == NULL)
|
if (solver == NULL || root == NULL)
|
||||||
@@ -346,6 +371,9 @@ int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations)
|
|||||||
std::list<IK_QTask*>& tasks = qsolver->tasks;
|
std::list<IK_QTask*>& tasks = qsolver->tasks;
|
||||||
MT_Scalar tol = tolerance;
|
MT_Scalar tol = tolerance;
|
||||||
|
|
||||||
|
if(!jacobian.Setup(root, tasks))
|
||||||
|
return 0;
|
||||||
|
|
||||||
bool result = jacobian.Solve(root, tasks, tol, max_iterations);
|
bool result = jacobian.Solve(root, tasks, tol, max_iterations);
|
||||||
|
|
||||||
return ((result)? 1: 0);
|
return ((result)? 1: 0);
|
||||||
|
|||||||
@@ -126,14 +126,13 @@ void copy_constraint_channels(struct ListBase *dst, struct ListBase *src);
|
|||||||
void clone_constraint_channels(struct ListBase *dst, struct ListBase *src);
|
void clone_constraint_channels(struct ListBase *dst, struct ListBase *src);
|
||||||
void free_constraint_channels(struct ListBase *chanbase);
|
void free_constraint_channels(struct ListBase *chanbase);
|
||||||
|
|
||||||
|
|
||||||
/* Constraint Evaluation function prototypes */
|
/* Constraint Evaluation function prototypes */
|
||||||
struct bConstraintOb *constraints_make_evalob(struct Object *ob, void *subdata, short datatype);
|
struct bConstraintOb *constraints_make_evalob(struct Object *ob, void *subdata, short datatype);
|
||||||
void constraints_clear_evalob(struct bConstraintOb *cob);
|
void constraints_clear_evalob(struct bConstraintOb *cob);
|
||||||
|
|
||||||
void constraint_mat_convertspace(struct Object *ob, struct bPoseChannel *pchan, float mat[][4], short from, short to);
|
void constraint_mat_convertspace(struct Object *ob, struct bPoseChannel *pchan, float mat[][4], short from, short to);
|
||||||
|
|
||||||
void get_constraint_target_matrix(struct bConstraint *con, short ownertype, void *ownerdata, float mat[][4], float ctime);
|
void get_constraint_target_matrix(struct bConstraint *con, int n, short ownertype, void *ownerdata, float mat[][4], float ctime);
|
||||||
void solve_constraints(struct ListBase *conlist, struct bConstraintOb *cob, float ctime);
|
void solve_constraints(struct ListBase *conlist, struct bConstraintOb *cob, float ctime);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1512,7 +1512,7 @@ static void execute_posetree(Object *ob, PoseTree *tree)
|
|||||||
IK_Segment *seg, *parent, **iktree, *iktarget;
|
IK_Segment *seg, *parent, **iktree, *iktarget;
|
||||||
IK_Solver *solver;
|
IK_Solver *solver;
|
||||||
PoseTarget *target;
|
PoseTarget *target;
|
||||||
bKinematicConstraint *data;
|
bKinematicConstraint *data, *poleangledata=NULL;
|
||||||
Bone *bone;
|
Bone *bone;
|
||||||
|
|
||||||
if (tree->totchannel == 0)
|
if (tree->totchannel == 0)
|
||||||
@@ -1624,12 +1624,15 @@ static void execute_posetree(Object *ob, PoseTree *tree)
|
|||||||
Mat4Invert (goalinv, imat);
|
Mat4Invert (goalinv, imat);
|
||||||
|
|
||||||
for (target=tree->targets.first; target; target=target->next) {
|
for (target=tree->targets.first; target; target=target->next) {
|
||||||
|
float polepos[3];
|
||||||
|
int poleconstrain= 0;
|
||||||
|
|
||||||
data= (bKinematicConstraint*)target->con->data;
|
data= (bKinematicConstraint*)target->con->data;
|
||||||
|
|
||||||
/* 1.0=ctime, we pass on object for auto-ik (owner-type here is object, even though
|
/* 1.0=ctime, we pass on object for auto-ik (owner-type here is object, even though
|
||||||
* strictly speaking, it is a posechannel)
|
* strictly speaking, it is a posechannel)
|
||||||
*/
|
*/
|
||||||
get_constraint_target_matrix(target->con, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0);
|
get_constraint_target_matrix(target->con, 0, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0);
|
||||||
|
|
||||||
/* and set and transform goal */
|
/* and set and transform goal */
|
||||||
Mat4MulMat4(goal, rootmat, goalinv);
|
Mat4MulMat4(goal, rootmat, goalinv);
|
||||||
@@ -1637,6 +1640,26 @@ static void execute_posetree(Object *ob, PoseTree *tree)
|
|||||||
VECCOPY(goalpos, goal[3]);
|
VECCOPY(goalpos, goal[3]);
|
||||||
Mat3CpyMat4(goalrot, goal);
|
Mat3CpyMat4(goalrot, goal);
|
||||||
|
|
||||||
|
/* same for pole vector target */
|
||||||
|
if(data->poletar) {
|
||||||
|
get_constraint_target_matrix(target->con, 1, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0);
|
||||||
|
|
||||||
|
if(data->flag & CONSTRAINT_IK_SETANGLE) {
|
||||||
|
/* don't solve IK when we are setting the pole angle */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Mat4MulMat4(goal, rootmat, goalinv);
|
||||||
|
VECCOPY(polepos, goal[3]);
|
||||||
|
poleconstrain= 1;
|
||||||
|
|
||||||
|
if(data->flag & CONSTRAINT_IK_GETANGLE) {
|
||||||
|
poleangledata= data;
|
||||||
|
data->flag &= ~CONSTRAINT_IK_GETANGLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* do we need blending? */
|
/* do we need blending? */
|
||||||
if (target->con->enforce!=1.0) {
|
if (target->con->enforce!=1.0) {
|
||||||
float q1[4], q2[4], q[4];
|
float q1[4], q2[4], q[4];
|
||||||
@@ -1664,14 +1687,24 @@ static void execute_posetree(Object *ob, PoseTree *tree)
|
|||||||
|
|
||||||
iktarget= iktree[target->tip];
|
iktarget= iktree[target->tip];
|
||||||
|
|
||||||
if(data->weight != 0.0)
|
if(data->weight != 0.0) {
|
||||||
|
if(poleconstrain)
|
||||||
|
IK_SolverSetPoleVectorConstraint(solver, iktarget, goalpos,
|
||||||
|
polepos, data->poleangle*M_PI/180, (poleangledata == data));
|
||||||
IK_SolverAddGoal(solver, iktarget, goalpos, data->weight);
|
IK_SolverAddGoal(solver, iktarget, goalpos, data->weight);
|
||||||
if((data->flag & CONSTRAINT_IK_ROT) && (data->orientweight != 0.0) && (data->flag & CONSTRAINT_IK_AUTO)==0)
|
}
|
||||||
IK_SolverAddGoalOrientation(solver, iktarget, goalrot, data->orientweight);
|
if((data->flag & CONSTRAINT_IK_ROT) && (data->orientweight != 0.0))
|
||||||
|
if((data->flag & CONSTRAINT_IK_AUTO)==0)
|
||||||
|
IK_SolverAddGoalOrientation(solver, iktarget, goalrot,
|
||||||
|
data->orientweight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* solve */
|
/* solve */
|
||||||
IK_Solve(solver, 0.0f, tree->iterations);
|
IK_Solve(solver, 0.0f, tree->iterations);
|
||||||
|
|
||||||
|
if(poleangledata)
|
||||||
|
poleangledata->poleangle= IK_SolverGetPoleAngle(solver)*180/M_PI;
|
||||||
|
|
||||||
IK_FreeSolver(solver);
|
IK_FreeSolver(solver);
|
||||||
|
|
||||||
/* gather basis changes */
|
/* gather basis changes */
|
||||||
|
|||||||
@@ -804,12 +804,12 @@ static void default_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstrain
|
|||||||
* (Hopefully all compilers will be happy with the lines with just a space on them. Those are
|
* (Hopefully all compilers will be happy with the lines with just a space on them. Those are
|
||||||
* really just to help this code easier to read)
|
* really just to help this code easier to read)
|
||||||
*/
|
*/
|
||||||
#define SINGLETARGET_GET_TARS(con, data, ct, list) \
|
#define SINGLETARGET_GET_TARS(con, datatar, datasubtarget, ct, list) \
|
||||||
{ \
|
{ \
|
||||||
ct= MEM_callocN(sizeof(bConstraintTarget), "tempConstraintTarget"); \
|
ct= MEM_callocN(sizeof(bConstraintTarget), "tempConstraintTarget"); \
|
||||||
\
|
\
|
||||||
ct->tar= data->tar; \
|
ct->tar= datatar; \
|
||||||
strcpy(ct->subtarget, data->subtarget); \
|
strcpy(ct->subtarget, datasubtarget); \
|
||||||
ct->space= con->tarspace; \
|
ct->space= con->tarspace; \
|
||||||
ct->flag= CONSTRAINT_TAR_TEMP; \
|
ct->flag= CONSTRAINT_TAR_TEMP; \
|
||||||
\
|
\
|
||||||
@@ -827,11 +827,11 @@ static void default_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstrain
|
|||||||
* (Hopefully all compilers will be happy with the lines with just a space on them. Those are
|
* (Hopefully all compilers will be happy with the lines with just a space on them. Those are
|
||||||
* really just to help this code easier to read)
|
* really just to help this code easier to read)
|
||||||
*/
|
*/
|
||||||
#define SINGLETARGETNS_GET_TARS(con, data, ct, list) \
|
#define SINGLETARGETNS_GET_TARS(con, datatar, ct, list) \
|
||||||
{ \
|
{ \
|
||||||
ct= MEM_callocN(sizeof(bConstraintTarget), "tempConstraintTarget"); \
|
ct= MEM_callocN(sizeof(bConstraintTarget), "tempConstraintTarget"); \
|
||||||
\
|
\
|
||||||
ct->tar= data->tar; \
|
ct->tar= datatar; \
|
||||||
ct->space= con->tarspace; \
|
ct->space= con->tarspace; \
|
||||||
ct->flag= CONSTRAINT_TAR_TEMP; \
|
ct->flag= CONSTRAINT_TAR_TEMP; \
|
||||||
\
|
\
|
||||||
@@ -845,16 +845,16 @@ static void default_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstrain
|
|||||||
* (Hopefully all compilers will be happy with the lines with just a space on them. Those are
|
* (Hopefully all compilers will be happy with the lines with just a space on them. Those are
|
||||||
* really just to help this code easier to read)
|
* really just to help this code easier to read)
|
||||||
*/
|
*/
|
||||||
#define SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy) \
|
#define SINGLETARGET_FLUSH_TARS(con, datatar, datasubtarget, ct, list, nocopy) \
|
||||||
{ \
|
{ \
|
||||||
if (ct) { \
|
if (ct) { \
|
||||||
if (nocopy == 0) { \
|
if (nocopy == 0) { \
|
||||||
data->tar= ct->tar; \
|
datatar= ct->tar; \
|
||||||
strcpy(data->subtarget, ct->subtarget); \
|
strcpy(datasubtarget, ct->subtarget); \
|
||||||
con->tarspace= ct->space; \
|
con->tarspace= ct->space; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
BLI_freelistN(list); \
|
BLI_freelinkN(list, ct); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,15 +863,15 @@ static void default_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstrain
|
|||||||
* (Hopefully all compilers will be happy with the lines with just a space on them. Those are
|
* (Hopefully all compilers will be happy with the lines with just a space on them. Those are
|
||||||
* really just to help this code easier to read)
|
* really just to help this code easier to read)
|
||||||
*/
|
*/
|
||||||
#define SINGLETARGETNS_FLUSH_TARS(con, data, ct, list, nocopy) \
|
#define SINGLETARGETNS_FLUSH_TARS(con, datatar, ct, list, nocopy) \
|
||||||
{ \
|
{ \
|
||||||
if (ct) { \
|
if (ct) { \
|
||||||
if (nocopy == 0) { \
|
if (nocopy == 0) { \
|
||||||
data->tar= ct->tar; \
|
datatar= ct->tar; \
|
||||||
con->tarspace= ct->space; \
|
con->tarspace= ct->space; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
BLI_freelistN(list); \
|
BLI_freelinkN(list, ct); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -894,7 +894,7 @@ static void childof_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -905,7 +905,7 @@ static void childof_flush_tars (bConstraint *con, ListBase *list, short nocopy)
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -992,7 +992,7 @@ static void trackto_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1003,7 +1003,7 @@ static void trackto_flush_tars (bConstraint *con, ListBase *list, short nocopy)
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1169,7 +1169,8 @@ static void kinematic_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
|
SINGLETARGET_GET_TARS(con, data->poletar, data->polesubtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,7 +1181,9 @@ static void kinematic_flush_tars (bConstraint *con, ListBase *list, short nocopy
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
|
ct= ct->next;
|
||||||
|
SINGLETARGET_FLUSH_TARS(con, data->poletar, data->polesubtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1245,7 +1248,7 @@ static void followpath_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints without subtargets */
|
/* standard target-getting macro for single-target constraints without subtargets */
|
||||||
SINGLETARGETNS_GET_TARS(con, data, ct, list)
|
SINGLETARGETNS_GET_TARS(con, data->tar, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1256,7 +1259,7 @@ static void followpath_flush_tars (bConstraint *con, ListBase *list, short nocop
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGETNS_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGETNS_FLUSH_TARS(con, data->tar, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1283,7 +1286,7 @@ static void followpath_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstr
|
|||||||
|
|
||||||
if (cu->path && cu->path->data) {
|
if (cu->path && cu->path->data) {
|
||||||
curvetime= bsystem_time(ct->tar, (float)ctime, 0.0) - data->offset;
|
curvetime= bsystem_time(ct->tar, (float)ctime, 0.0) - data->offset;
|
||||||
|
|
||||||
if (calc_ipo_spec(cu->ipo, CU_SPEED, &curvetime)==0) {
|
if (calc_ipo_spec(cu->ipo, CU_SPEED, &curvetime)==0) {
|
||||||
curvetime /= cu->pathlen;
|
curvetime /= cu->pathlen;
|
||||||
CLAMP(curvetime, 0.0, 1.0);
|
CLAMP(curvetime, 0.0, 1.0);
|
||||||
@@ -1546,7 +1549,7 @@ static void loclike_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1557,7 +1560,7 @@ static void loclike_flush_tars (bConstraint *con, ListBase *list, short nocopy)
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1661,7 +1664,7 @@ static void rotlike_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1672,7 +1675,7 @@ static void rotlike_flush_tars (bConstraint *con, ListBase *list, short nocopy)
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1749,7 +1752,7 @@ static void sizelike_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1760,7 +1763,7 @@ static void sizelike_flush_tars (bConstraint *con, ListBase *list, short nocopy)
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1928,7 +1931,7 @@ static void actcon_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1939,7 +1942,7 @@ static void actcon_flush_tars (bConstraint *con, ListBase *list, short nocopy)
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2068,7 +2071,7 @@ static void locktrack_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2079,7 +2082,7 @@ static void locktrack_flush_tars (bConstraint *con, ListBase *list, short nocopy
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2419,7 +2422,7 @@ static void stretchto_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2430,7 +2433,7 @@ static void stretchto_flush_tars (bConstraint *con, ListBase *list, short nocopy
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2595,7 +2598,7 @@ static void minmax_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2606,7 +2609,7 @@ static void minmax_flush_tars (bConstraint *con, ListBase *list, short nocopy)
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2725,7 +2728,7 @@ static void rbj_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints without subtargets */
|
/* standard target-getting macro for single-target constraints without subtargets */
|
||||||
SINGLETARGETNS_GET_TARS(con, data, ct, list)
|
SINGLETARGETNS_GET_TARS(con, data->tar, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2736,7 +2739,7 @@ static void rbj_flush_tars (bConstraint *con, ListBase *list, short nocopy)
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGETNS_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGETNS_FLUSH_TARS(con, data->tar, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2764,7 +2767,7 @@ static void clampto_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints without subtargets */
|
/* standard target-getting macro for single-target constraints without subtargets */
|
||||||
SINGLETARGETNS_GET_TARS(con, data, ct, list)
|
SINGLETARGETNS_GET_TARS(con, data->tar, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2775,7 +2778,7 @@ static void clampto_flush_tars (bConstraint *con, ListBase *list, short nocopy)
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGETNS_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGETNS_FLUSH_TARS(con, data->tar, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2937,7 +2940,7 @@ static void transform_get_tars (bConstraint *con, ListBase *list)
|
|||||||
bConstraintTarget *ct;
|
bConstraintTarget *ct;
|
||||||
|
|
||||||
/* standard target-getting macro for single-target constraints */
|
/* standard target-getting macro for single-target constraints */
|
||||||
SINGLETARGET_GET_TARS(con, data, ct, list)
|
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2948,7 +2951,7 @@ static void transform_flush_tars (bConstraint *con, ListBase *list, short nocopy
|
|||||||
bConstraintTarget *ct= list->first;
|
bConstraintTarget *ct= list->first;
|
||||||
|
|
||||||
/* the following macro is used for all standard single-target constraints */
|
/* the following macro is used for all standard single-target constraints */
|
||||||
SINGLETARGET_FLUSH_TARS(con, data, ct, list, nocopy)
|
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3225,7 +3228,7 @@ void copy_constraints (ListBase *dst, ListBase *src)
|
|||||||
* None of the actual calculations of the matricies should be done here! Also, this function is
|
* None of the actual calculations of the matricies should be done here! Also, this function is
|
||||||
* not to be used by any new constraints, particularly any that have multiple targets.
|
* not to be used by any new constraints, particularly any that have multiple targets.
|
||||||
*/
|
*/
|
||||||
void get_constraint_target_matrix (bConstraint *con, short ownertype, void *ownerdata, float mat[][4], float ctime)
|
void get_constraint_target_matrix (bConstraint *con, int n, short ownertype, void *ownerdata, float mat[][4], float ctime)
|
||||||
{
|
{
|
||||||
bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
|
bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
|
||||||
ListBase targets = {NULL, NULL};
|
ListBase targets = {NULL, NULL};
|
||||||
@@ -3272,6 +3275,9 @@ void get_constraint_target_matrix (bConstraint *con, short ownertype, void *owne
|
|||||||
|
|
||||||
/* only calculate the target matrix on the first target */
|
/* only calculate the target matrix on the first target */
|
||||||
ct= (bConstraintTarget *)targets.first;
|
ct= (bConstraintTarget *)targets.first;
|
||||||
|
while(ct && n-- > 0)
|
||||||
|
ct= ct->next;
|
||||||
|
|
||||||
if (ct) {
|
if (ct) {
|
||||||
if (cti->get_target_matrix)
|
if (cti->get_target_matrix)
|
||||||
cti->get_target_matrix(con, cob, ct, ctime);
|
cti->get_target_matrix(con, cob, ct, ctime);
|
||||||
|
|||||||
@@ -1664,6 +1664,7 @@ static void lib_link_constraints(FileData *fd, ID *id, ListBase *conlist)
|
|||||||
bKinematicConstraint *data;
|
bKinematicConstraint *data;
|
||||||
data = ((bKinematicConstraint*)con->data);
|
data = ((bKinematicConstraint*)con->data);
|
||||||
data->tar = newlibadr(fd, id->lib, data->tar);
|
data->tar = newlibadr(fd, id->lib, data->tar);
|
||||||
|
data->poletar = newlibadr(fd, id->lib, data->poletar);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CONSTRAINT_TYPE_TRACKTO:
|
case CONSTRAINT_TYPE_TRACKTO:
|
||||||
@@ -7289,6 +7290,7 @@ static void expand_constraints(FileData *fd, Main *mainvar, ListBase *lb)
|
|||||||
{
|
{
|
||||||
bKinematicConstraint *data = (bKinematicConstraint*)curcon->data;
|
bKinematicConstraint *data = (bKinematicConstraint*)curcon->data;
|
||||||
expand_doit(fd, mainvar, data->tar);
|
expand_doit(fd, mainvar, data->tar);
|
||||||
|
expand_doit(fd, mainvar, data->poletar);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CONSTRAINT_TYPE_TRACKTO:
|
case CONSTRAINT_TYPE_TRACKTO:
|
||||||
|
|||||||
@@ -125,10 +125,13 @@ typedef struct bKinematicConstraint {
|
|||||||
int rootbone; /* index to rootbone, if zero go all the way to mother bone */
|
int rootbone; /* index to rootbone, if zero go all the way to mother bone */
|
||||||
char subtarget[32]; /* String to specify sub-object target */
|
char subtarget[32]; /* String to specify sub-object target */
|
||||||
|
|
||||||
|
Object *poletar; /* Pole vector target */
|
||||||
|
char polesubtarget[32]; /* Pole vector sub-object target */
|
||||||
|
float poleangle; /* Pole vector rest angle */
|
||||||
|
|
||||||
float weight; /* Weight of goal in IK tree */
|
float weight; /* Weight of goal in IK tree */
|
||||||
float orientweight; /* Amount of rotation a target applies on chain */
|
float orientweight; /* Amount of rotation a target applies on chain */
|
||||||
float grabtarget[3]; /* for target-less IK */
|
float grabtarget[3]; /* for target-less IK */
|
||||||
int pad;
|
|
||||||
} bKinematicConstraint;
|
} bKinematicConstraint;
|
||||||
|
|
||||||
/* Track To Constraint */
|
/* Track To Constraint */
|
||||||
@@ -439,6 +442,8 @@ typedef enum B_CONSTRAINTCHANNEL_FLAG {
|
|||||||
#define CONSTRAINT_IK_TEMP 8
|
#define CONSTRAINT_IK_TEMP 8
|
||||||
#define CONSTRAINT_IK_STRETCH 16
|
#define CONSTRAINT_IK_STRETCH 16
|
||||||
#define CONSTRAINT_IK_POS 32
|
#define CONSTRAINT_IK_POS 32
|
||||||
|
#define CONSTRAINT_IK_SETANGLE 64
|
||||||
|
#define CONSTRAINT_IK_GETANGLE 128
|
||||||
|
|
||||||
/* MinMax (floor) flags */
|
/* MinMax (floor) flags */
|
||||||
#define MINMAX_STICKY 0x01
|
#define MINMAX_STICKY 0x01
|
||||||
|
|||||||
@@ -410,6 +410,24 @@ void autocomplete_vgroup(char *str, void *arg_v)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* pole angle callback */
|
||||||
|
void con_kinematic_set_pole_angle(void *ob_v, void *con_v)
|
||||||
|
{
|
||||||
|
bConstraint *con= con_v;
|
||||||
|
bKinematicConstraint *data = con->data;
|
||||||
|
|
||||||
|
if(data->poletar) {
|
||||||
|
if(data->flag & CONSTRAINT_IK_SETANGLE) {
|
||||||
|
data->flag |= CONSTRAINT_IK_GETANGLE;
|
||||||
|
data->flag &= ~CONSTRAINT_IK_SETANGLE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data->flag &= ~CONSTRAINT_IK_GETANGLE;
|
||||||
|
data->flag |= CONSTRAINT_IK_SETANGLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* some commonly used macros in the constraints drawing code */
|
/* some commonly used macros in the constraints drawing code */
|
||||||
#define is_armature_target(target) (target && target->type==OB_ARMATURE)
|
#define is_armature_target(target) (target && target->type==OB_ARMATURE)
|
||||||
#define is_armature_owner(ob) ((ob->type == OB_ARMATURE) && (ob->flag & OB_POSEMODE))
|
#define is_armature_owner(ob) ((ob->type == OB_ARMATURE) && (ob->flag & OB_POSEMODE))
|
||||||
@@ -878,47 +896,76 @@ static void draw_constraint (uiBlock *block, ListBase *list, bConstraint *con, s
|
|||||||
{
|
{
|
||||||
bKinematicConstraint *data = con->data;
|
bKinematicConstraint *data = con->data;
|
||||||
|
|
||||||
height = 111;
|
height = 146;
|
||||||
|
if(data->poletar)
|
||||||
|
height += 30;
|
||||||
|
|
||||||
uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-10, *yco-height, width+40,height-1, NULL, 5.0, 0.0, 12, rb_col, "");
|
uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-10, *yco-height, width+40,height-1, NULL, 5.0, 0.0, 12, rb_col, "");
|
||||||
|
|
||||||
uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Target:", *xco+65, *yco-24, 50, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
|
/* IK Target */
|
||||||
|
uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Target:", *xco, *yco-24, 50, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
|
||||||
|
|
||||||
/* Draw target parameters */
|
/* Draw target parameters */
|
||||||
uiDefButBitS(block, TOG, CONSTRAINT_IK_ROT, B_CONSTRAINT_TEST, "Rot", *xco, *yco-24,60,19, &data->flag, 0, 0, 0, 0, "Chain follows rotation of target");
|
|
||||||
|
|
||||||
uiBlockBeginAlign(block);
|
uiBlockBeginAlign(block);
|
||||||
uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_CONSTRAINT_CHANGETARGET, "OB:", *xco+120, *yco-24, 135, 19, &data->tar, "Target Object");
|
uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_CONSTRAINT_CHANGETARGET, "OB:", *xco, *yco-44, 137, 19, &data->tar, "Target Object");
|
||||||
|
|
||||||
if (is_armature_target(data->tar)) {
|
if (is_armature_target(data->tar)) {
|
||||||
but=uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "BO:", *xco+120, *yco-42,135,19, &data->subtarget, 0, 24, 0, 0, "Subtarget Bone");
|
but=uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "BO:", *xco, *yco-62,137,19, &data->subtarget, 0, 24, 0, 0, "Subtarget Bone");
|
||||||
uiButSetCompleteFunc(but, autocomplete_bone, (void *)data->tar);
|
uiButSetCompleteFunc(but, autocomplete_bone, (void *)data->tar);
|
||||||
}
|
}
|
||||||
else if (is_geom_target(data->tar)) {
|
else if (is_geom_target(data->tar)) {
|
||||||
but= uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "VG:", *xco+120, *yco-42,135,18, &data->subtarget, 0, 24, 0, 0, "Name of Vertex Group defining 'target' points");
|
but= uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "VG:", *xco, *yco-62,137,18, &data->subtarget, 0, 24, 0, 0, "Name of Vertex Group defining 'target' points");
|
||||||
uiButSetCompleteFunc(but, autocomplete_vgroup, (void *)data->tar);
|
uiButSetCompleteFunc(but, autocomplete_vgroup, (void *)data->tar);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
strcpy(data->subtarget, "");
|
strcpy (data->subtarget, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
uiBlockEndAlign(block);
|
uiBlockEndAlign(block);
|
||||||
|
|
||||||
|
/* Settings */
|
||||||
uiBlockBeginAlign(block);
|
uiBlockBeginAlign(block);
|
||||||
uiDefButBitS(block, TOG, CONSTRAINT_IK_TIP, B_CONSTRAINT_TEST, "Use Tail", *xco, *yco-64, 137, 19, &data->flag, 0, 0, 0, 0, "Include Bone's tail as last element in Chain");
|
uiDefButBitS(block, TOG, CONSTRAINT_IK_TIP, B_CONSTRAINT_TEST, "Use Tail", *xco, *yco-92, 137, 19, &data->flag, 0, 0, 0, 0, "Include Bone's tail als last element in Chain");
|
||||||
uiDefButI(block, NUM, B_CONSTRAINT_TEST, "ChainLen:", *xco, *yco-84,137,19, &data->rootbone, 0, 255, 0, 0, "If not zero, the amount of bones in this chain");
|
uiDefButI(block, NUM, B_CONSTRAINT_TEST, "ChainLen:", *xco, *yco-112,137,19, &data->rootbone, 0, 255, 0, 0, "If not zero, the amount of bones in this chain");
|
||||||
uiBlockEndAlign(block);
|
|
||||||
|
|
||||||
uiBlockBeginAlign(block);
|
uiBlockBeginAlign(block);
|
||||||
uiDefButF(block, NUMSLI, B_CONSTRAINT_TEST, "PosW ", *xco+147, *yco-64, 137, 19, &data->weight, 0.01, 1.0, 2, 2, "For Tree-IK: weight of position control for this target");
|
uiDefButF(block, NUMSLI, B_CONSTRAINT_TEST, "PosW ", *xco+147, *yco-92, 137, 19, &data->weight, 0.01, 1.0, 2, 2, "For Tree-IK: weight of position control for this target");
|
||||||
uiDefButF(block, NUMSLI, B_CONSTRAINT_TEST, "RotW ", *xco+147, *yco-84, 137, 19, &data->orientweight, 0.01, 1.0, 2, 2, "For Tree-IK: Weight of orientation control for this target");
|
uiDefButBitS(block, TOG, CONSTRAINT_IK_ROT, B_CONSTRAINT_TEST, "Rot", *xco+147, *yco-112, 40,19, &data->flag, 0, 0, 0, 0, "Chain follows rotation of target");
|
||||||
uiBlockEndAlign(block);
|
uiDefButF(block, NUMSLI, B_CONSTRAINT_TEST, "W ", *xco+187, *yco-112, 97, 19, &data->orientweight, 0.01, 1.0, 2, 2, "For Tree-IK: Weight of orientation control for this target");
|
||||||
|
|
||||||
uiBlockBeginAlign(block);
|
uiBlockBeginAlign(block);
|
||||||
uiDefButS(block, NUM, B_CONSTRAINT_TEST, "Iterations:", *xco, *yco-109, 137, 19, &data->iterations, 1, 10000, 0, 0, "Maximum number of solving iterations");
|
|
||||||
uiBlockEndAlign(block);
|
uiDefButBitS(block, TOG, CONSTRAINT_IK_STRETCH, B_CONSTRAINT_TEST, "Stretch", *xco, *yco-137,137,19, &data->flag, 0, 0, 0, 0, "Enable IK stretching");
|
||||||
|
|
||||||
uiBlockBeginAlign(block);
|
uiBlockBeginAlign(block);
|
||||||
uiDefButBitS(block, TOG, CONSTRAINT_IK_STRETCH, B_CONSTRAINT_TEST, "Stretch", *xco+147, *yco-109,137,19, &data->flag, 0, 0, 0, 0, "Enable IK stretching");
|
uiDefButS(block, NUM, B_CONSTRAINT_TEST, "Iterations:", *xco+147, *yco-137, 137, 19, &data->iterations, 1, 10000, 0, 0, "Maximum number of solving iterations");
|
||||||
uiBlockEndAlign(block);
|
uiBlockEndAlign(block);
|
||||||
|
|
||||||
|
/* Pole Vector */
|
||||||
|
uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Pole Target:", *xco+147, *yco-24, 100, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
|
||||||
|
|
||||||
|
uiBlockBeginAlign(block);
|
||||||
|
uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_CONSTRAINT_CHANGETARGET, "OB:", *xco+147, *yco-44, 137, 19, &data->poletar, "Pole Target Object");
|
||||||
|
if (is_armature_target(data->poletar)) {
|
||||||
|
but=uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "BO:", *xco+147, *yco-62,137,19, &data->polesubtarget, 0, 24, 0, 0, "Pole Subtarget Bone");
|
||||||
|
uiButSetCompleteFunc(but, autocomplete_bone, (void *)data->poletar);
|
||||||
|
}
|
||||||
|
else if (is_geom_target(data->poletar)) {
|
||||||
|
but= uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "VG:", *xco+147, *yco-62,137,18, &data->polesubtarget, 0, 24, 0, 0, "Name of Vertex Group defining pole 'target' points");
|
||||||
|
uiButSetCompleteFunc(but, autocomplete_vgroup, (void *)data->poletar);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strcpy (data->polesubtarget, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data->poletar) {
|
||||||
|
uiBlockBeginAlign(block);
|
||||||
|
#if 0
|
||||||
|
but = uiDefBut(block, BUT, B_CONSTRAINT_TEST, (data->flag & CONSTRAINT_IK_SETANGLE)? "Set Pole Offset": "Clear Pole Offset", *xco, *yco-167, 137, 19, 0, 0.0, 1.0, 0.0, 0.0, "Set the pole rotation offset from the current pose");
|
||||||
|
uiButSetFunc(but, con_kinematic_set_pole_angle, ob, con);
|
||||||
|
if(!(data->flag & CONSTRAINT_IK_SETANGLE))
|
||||||
|
#endif
|
||||||
|
uiDefButF(block, NUM, B_CONSTRAINT_TEST, "Pole Offset ", *xco, *yco-167, 137, 19, &data->poleangle, -180.0, 180.0, 0, 0, "Pole rotation offset");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CONSTRAINT_TYPE_TRACKTO:
|
case CONSTRAINT_TYPE_TRACKTO:
|
||||||
|
|||||||
@@ -417,18 +417,26 @@ static void test_constraints (Object *owner, const char substring[])
|
|||||||
case CONSTRAINT_TYPE_KINEMATIC:
|
case CONSTRAINT_TYPE_KINEMATIC:
|
||||||
{
|
{
|
||||||
bKinematicConstraint *data = curcon->data;
|
bKinematicConstraint *data = curcon->data;
|
||||||
|
|
||||||
if (!exist_object(data->tar)) {
|
if (!exist_object(data->tar)) {
|
||||||
data->tar = NULL;
|
data->tar = NULL;
|
||||||
curcon->flag |= CONSTRAINT_DISABLE;
|
curcon->flag |= CONSTRAINT_DISABLE;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
else if ( (data->tar == owner) &&
|
||||||
if ( (data->tar == owner) &&
|
|
||||||
(!get_named_bone(get_armature(owner),
|
(!get_named_bone(get_armature(owner),
|
||||||
data->subtarget))) {
|
data->subtarget))) {
|
||||||
curcon->flag |= CONSTRAINT_DISABLE;
|
curcon->flag |= CONSTRAINT_DISABLE;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data->poletar && !exist_object(data->poletar)) {
|
||||||
|
data->poletar = NULL;
|
||||||
|
}
|
||||||
|
else if ( (data->poletar == owner) &&
|
||||||
|
(!get_named_bone(get_armature(owner),
|
||||||
|
data->polesubtarget))) {
|
||||||
|
curcon->flag |= CONSTRAINT_DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CONSTRAINT_TYPE_TRACKTO:
|
case CONSTRAINT_TYPE_TRACKTO:
|
||||||
@@ -816,7 +824,7 @@ void add_constraint(int only_IK)
|
|||||||
set_constraint_nth_target(con, ob, pchansel->name, 0);
|
set_constraint_nth_target(con, ob, pchansel->name, 0);
|
||||||
}
|
}
|
||||||
else if(obsel) {
|
else if(obsel) {
|
||||||
set_constraint_nth_target(con, obsel, NULL, 0);
|
set_constraint_nth_target(con, obsel, "", 0);
|
||||||
}
|
}
|
||||||
else if (ELEM4(nr, 11, 13, 14, 15)==0) { /* add new empty as target */
|
else if (ELEM4(nr, 11, 13, 14, 15)==0) { /* add new empty as target */
|
||||||
Base *base= BASACT, *newbase;
|
Base *base= BASACT, *newbase;
|
||||||
@@ -838,7 +846,7 @@ void add_constraint(int only_IK)
|
|||||||
else
|
else
|
||||||
VECCOPY(obt->loc, ob->obmat[3]);
|
VECCOPY(obt->loc, ob->obmat[3]);
|
||||||
|
|
||||||
set_constraint_nth_target(con, obt, NULL, 0);
|
set_constraint_nth_target(con, obt, "", 0);
|
||||||
|
|
||||||
/* restore, add_object sets active */
|
/* restore, add_object sets active */
|
||||||
BASACT= base;
|
BASACT= base;
|
||||||
|
|||||||
@@ -1416,7 +1416,7 @@ void make_parent(void)
|
|||||||
|
|
||||||
add_constraint_to_object(con, base->object);
|
add_constraint_to_object(con, base->object);
|
||||||
|
|
||||||
get_constraint_target_matrix(con, CONSTRAINT_OBTYPE_OBJECT, NULL, cmat, G.scene->r.cfra - base->object->sf);
|
get_constraint_target_matrix(con, 0, CONSTRAINT_OBTYPE_OBJECT, NULL, cmat, G.scene->r.cfra - base->object->sf);
|
||||||
VecSubf(vec, base->object->obmat[3], cmat[3]);
|
VecSubf(vec, base->object->obmat[3], cmat[3]);
|
||||||
|
|
||||||
base->object->loc[0] = vec[0];
|
base->object->loc[0] = vec[0];
|
||||||
|
|||||||
Reference in New Issue
Block a user