Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/iksolver/extern/IK_solver.h29
-rw-r--r--intern/iksolver/intern/IK_QJacobian.cpp9
-rw-r--r--intern/iksolver/intern/IK_QSegment.cpp260
-rw-r--r--intern/iksolver/intern/IK_QSegment.h19
-rw-r--r--intern/iksolver/intern/IK_QTask.cpp3
-rw-r--r--intern/iksolver/intern/IK_Solver.cpp109
-rw-r--r--source/blender/blenkernel/BKE_armature.h35
-rw-r--r--source/blender/blenkernel/bad_level_call_stubs/stubs.c1
-rw-r--r--source/blender/blenkernel/intern/action.c1
-rw-r--r--source/blender/blenkernel/intern/armature.c252
-rw-r--r--source/blender/makesdna/DNA_action_types.h4
-rw-r--r--source/blender/src/buttons_editing.c26
-rw-r--r--source/blender/src/drawarmature.c193
13 files changed, 583 insertions, 358 deletions
diff --git a/intern/iksolver/extern/IK_solver.h b/intern/iksolver/extern/IK_solver.h
index 78633fb0d9a..8626ca22beb 100644
--- a/intern/iksolver/extern/IK_solver.h
+++ b/intern/iksolver/extern/IK_solver.h
@@ -84,14 +84,13 @@ extern "C" {
* - free all segments
*/
-
/**
* IK_Segment defines a single segment of an IK tree.
* - Individual segments are always defined in local coordinates.
* - The segment is assumed to be oriented in the local
- * y-direction.
+ * y-direction.
* - start is the start of the segment relative to the end
- * of the parent segment.
+ * of the parent segment.
* - rest_basis is a column major matrix defineding the rest
* position (w.r.t. which the limits are defined), must
* be a pure rotation
@@ -100,15 +99,12 @@ extern "C" {
* - length is the length of the bone.
*
* - basis_change and translation_change respectively define
- * the change in rotation or translation for rotational joints
- * and translational joints. basis_change is a column major 3x3
- * matrix.
+ * the change in rotation or translation. basis_change is a
+ * column major 3x3 matrix.
*
- * For rotational joints the local transformation is then defined as:
- * start*rest_basis*basis*basis_change*translate(0,length,0)
+ * The local transformation is then defined as:
+ * start * rest_basis * basis * basis_change * translation_change * translate(0,length,0)
*
- * For translational joints:
- * start*rest_basis*basis*translation_change*translate(0,length,0)
*/
typedef void IK_Segment;
@@ -117,13 +113,18 @@ enum IK_SegmentFlag {
IK_XDOF = 1,
IK_YDOF = 2,
IK_ZDOF = 4,
- IK_TRANSLATIONAL = 8
+ IK_TRANS_XDOF = 8,
+ IK_TRANS_YDOF = 16,
+ IK_TRANS_ZDOF = 32
};
typedef enum IK_SegmentAxis {
- IK_X,
- IK_Y,
- IK_Z
+ IK_X = 0,
+ IK_Y = 1,
+ IK_Z = 2,
+ IK_TRANS_X = 3,
+ IK_TRANS_Y = 4,
+ IK_TRANS_Z = 5
} IK_SegmentAxis;
extern IK_Segment *IK_CreateSegment(int flag);
diff --git a/intern/iksolver/intern/IK_QJacobian.cpp b/intern/iksolver/intern/IK_QJacobian.cpp
index 3eb79c89e4d..03c5f9500be 100644
--- a/intern/iksolver/intern/IK_QJacobian.cpp
+++ b/intern/iksolver/intern/IK_QJacobian.cpp
@@ -296,11 +296,16 @@ void IK_QJacobian::InvertSDLS()
MT_Scalar damp = (gamma < max_dtheta)? gamma/max_dtheta: 1.0;
- for (j = 0; j < m_d_theta.size(); j++)
+ for (j = 0; j < m_d_theta.size(); j++) {
// slight hack: we do 0.80*, so that if there is some oscillation,
// the system can still converge (for joint limits). also, it's
// better to go a little to slow than to far
- m_d_theta[j] += 0.80*damp*m_d_theta_tmp[j];
+
+ MT_Scalar dofdamp = damp/m_weight[j];
+ if (dofdamp > 1.0) dofdamp = 1.0;
+
+ m_d_theta[j] += 0.80*dofdamp*m_d_theta_tmp[j];
+ }
if (damp < m_min_damp)
m_min_damp = damp;
diff --git a/intern/iksolver/intern/IK_QSegment.cpp b/intern/iksolver/intern/IK_QSegment.cpp
index 310f3708499..cf9e1615d8c 100644
--- a/intern/iksolver/intern/IK_QSegment.cpp
+++ b/intern/iksolver/intern/IK_QSegment.cpp
@@ -158,11 +158,69 @@ static MT_Vector3 MatrixToAxisAngle(const MT_Matrix3x3& R)
return delta;
}
+static bool EllipseClamp(MT_Scalar& ax, MT_Scalar& az, MT_Scalar *amin, MT_Scalar *amax)
+{
+ MT_Scalar xlim, zlim, x, z;
+
+ if (ax < 0.0) {
+ x = -ax;
+ xlim = -amin[0];
+ }
+ else {
+ x = ax;
+ xlim = amax[0];
+ }
+
+ if (az < 0.0) {
+ z = -az;
+ zlim = -amin[1];
+ }
+ else {
+ z = az;
+ zlim = amax[1];
+ }
+
+ if (MT_fuzzyZero(xlim) || MT_fuzzyZero(zlim)) {
+ if (x <= xlim && z <= zlim)
+ return false;
+
+ if (x > xlim)
+ x = xlim;
+ if (z > zlim)
+ z = zlim;
+ }
+ else {
+ MT_Scalar invx = 1.0/(xlim*xlim);
+ MT_Scalar invz = 1.0/(zlim*zlim);
+
+ if ((x*x*invx + z*z*invz) <= 1.0)
+ return false;
+
+ if (MT_fuzzyZero(x)) {
+ x = 0.0;
+ z = zlim;
+ }
+ else {
+ MT_Scalar rico = z/x;
+ MT_Scalar old_x = x;
+ x = sqrt(1.0/(invx + invz*rico*rico));
+ if (old_x < 0.0)
+ x = -x;
+ z = rico*x;
+ }
+ }
+
+ ax = (ax < 0.0)? -x: x;
+ az = (az < 0.0)? -z: z;
+
+ return true;
+}
+
// IK_QSegment
IK_QSegment::IK_QSegment(int num_DoF, bool translational)
-: m_parent(NULL), m_child(NULL), m_sibling(NULL), m_num_DoF(num_DoF),
- m_translational(translational)
+: m_parent(NULL), m_child(NULL), m_sibling(NULL), m_composite(NULL),
+ m_num_DoF(num_DoF), m_translational(translational)
{
m_locked[0] = m_locked[1] = m_locked[2] = false;
m_weight[0] = m_weight[1] = m_weight[2] = 1.0;
@@ -232,6 +290,11 @@ void IK_QSegment::SetParent(IK_QSegment *parent)
m_parent = parent;
}
+void IK_QSegment::SetComposite(IK_QSegment *seg)
+{
+ m_composite = seg;
+}
+
void IK_QSegment::RemoveChild(IK_QSegment *child)
{
if (m_child == NULL)
@@ -297,19 +360,15 @@ void IK_QSphericalSegment::SetLimit(int axis, MT_Scalar lmin, MT_Scalar lmax)
lmin = sin(MT_radians(lmin)*0.5);
lmax = sin(MT_radians(lmax)*0.5);
- // put center of ellispe in the middle between min and max
- MT_Scalar offset = 0.5*(lmin + lmax);
- lmax = lmax - offset;
-
if (axis == 0) {
+ m_min[0] = -lmax;
+ m_max[0] = -lmin;
m_limit_x = true;
- m_offset_x = offset;
- m_max_x = lmax;
}
else if (axis == 2) {
+ m_min[1] = -lmax;
+ m_max[1] = -lmin;
m_limit_z = true;
- m_offset_z = offset;
- m_max_z = lmax;
}
}
}
@@ -391,62 +450,28 @@ bool IK_QSphericalSegment::UpdateAngle(const IK_QJacobian &jacobian, MT_Vector3&
}
if (m_limit_x && m_limit_z) {
- /* check in ellipsoid region */
- ax = a.x() + m_offset_x;
- az = a.z() + m_offset_z;
-
- MT_Scalar invX = 1.0/(m_max_x*m_max_x);
- MT_Scalar invZ = 1.0/(m_max_z*m_max_z);
-
- if ((ax*ax*invX + az*az*invZ) > 1.0) {
+ if (EllipseClamp(ax, az, m_min, m_max))
clamp[0] = clamp[2] = true;
-
- if (MT_fuzzyZero(ax)) {
- ax = 0.0;
- az = (az > 0)? m_max_z: -m_max_z;
- }
- else {
- MT_Scalar rico = az/ax;
- MT_Scalar old_ax = ax;
- ax = sqrt(1.0/(invX + invZ*rico*rico));
- if (old_ax < 0.0)
- ax = -ax;
- az = rico*ax;
- }
- }
-
- ax = ax - m_offset_x;
- az = az - m_offset_z;
}
else if (m_limit_x) {
- ax = a.x() + m_offset_x;
-
- if (ax < -m_max_x) {
- ax = -m_max_x;
+ if (ax < m_min[0]) {
+ ax = m_min[0];
clamp[0] = true;
}
- else if (ax > m_max_x) {
- ax = m_max_x;
+ else if (ax > m_max[0]) {
+ ax = m_max[0];
clamp[0] = true;
}
-
- ax = ax - m_offset_x;
- az = a.z();
}
else if (m_limit_z) {
- az = a.z() + m_offset_z;
-
- if (az < -m_max_z) {
- az = -m_max_z;
+ if (az < m_min[1]) {
+ az = m_min[1];
clamp[2] = true;
}
- else if (az > m_max_z) {
- az = m_max_z;
+ else if (az > m_max[1]) {
+ az = m_max[1];
clamp[2] = true;
}
-
- ax = a.x();
- az = az - m_offset_z;
}
if (clamp[0] == false && clamp[1] == false && clamp[2] == false) {
@@ -645,62 +670,31 @@ bool IK_QSwingSegment::UpdateAngle(const IK_QJacobian &jacobian, MT_Vector3& del
clamp[0] = clamp[1] = false;
if (m_limit_x && m_limit_z) {
- /* check in ellipsoid region */
- ax = a.x() + m_offset_x;
- az = a.z() + m_offset_z;
-
- MT_Scalar invX = 1.0/(m_max_x*m_max_x);
- MT_Scalar invZ = 1.0/(m_max_z*m_max_z);
+ ax = a.x();
+ az = a.z();
- if ((ax*ax*invX + az*az*invZ) > 1.0) {
+ if (EllipseClamp(ax, az, m_min, m_max))
clamp[0] = clamp[1] = true;
-
- if (MT_fuzzyZero(ax)) {
- ax = 0.0;
- az = (az > 0)? m_max_z: -m_max_z;
- }
- else {
- MT_Scalar rico = az/ax;
- MT_Scalar old_ax = ax;
- ax = sqrt(1.0/(invX + invZ*rico*rico));
- if (old_ax < 0.0)
- ax = -ax;
- az = rico*ax;
- }
- }
-
- ax = ax - m_offset_x;
- az = az - m_offset_z;
}
else if (m_limit_x) {
- ax = a.x() + m_offset_x;
-
- if (ax < -m_max_x) {
- ax = -m_max_x;
+ if (ax < m_min[0]) {
+ ax = m_min[0];
clamp[0] = true;
}
- else if (ax > m_max_x) {
- ax = m_max_x;
+ else if (ax > m_max[0]) {
+ ax = m_max[0];
clamp[0] = true;
}
-
- ax = ax - m_offset_x;
- az = a.z();
}
else if (m_limit_z) {
- az = a.z() + m_offset_z;
-
- if (az < -m_max_z) {
- az = -m_max_z;
+ if (az < m_min[1]) {
+ az = m_min[1];
clamp[1] = true;
}
- else if (az > m_max_z) {
- az = m_max_z;
+ else if (az > m_max[1]) {
+ az = m_max[1];
clamp[1] = true;
}
-
- ax = a.x();
- az = az - m_offset_z;
}
if (clamp[0] == false && clamp[1] == false)
@@ -740,14 +734,20 @@ void IK_QSwingSegment::SetLimit(int axis, MT_Scalar lmin, MT_Scalar lmax)
// put center of ellispe in the middle between min and max
MT_Scalar offset = 0.5*(lmin + lmax);
- lmax = lmax - offset;
+ //lmax = lmax - offset;
if (axis == 0) {
+ m_min[0] = -lmax;
+ m_max[0] = -lmin;
+
m_limit_x = true;
m_offset_x = offset;
m_max_x = lmax;
}
else if (axis == 2) {
+ m_min[1] = -lmax;
+ m_max[1] = -lmin;
+
m_limit_z = true;
m_offset_z = offset;
m_max_z = lmax;
@@ -907,6 +907,8 @@ IK_QTranslateSegment::IK_QTranslateSegment(int axis1)
m_axis_enabled[axis1] = true;
m_axis[0] = axis1;
+
+ m_limit[0] = m_limit[1] = m_limit[2] = false;
}
IK_QTranslateSegment::IK_QTranslateSegment(int axis1, int axis2)
@@ -918,6 +920,8 @@ IK_QTranslateSegment::IK_QTranslateSegment(int axis1, int axis2)
m_axis[0] = axis1;
m_axis[1] = axis2;
+
+ m_limit[0] = m_limit[1] = m_limit[2] = false;
}
IK_QTranslateSegment::IK_QTranslateSegment()
@@ -928,6 +932,8 @@ IK_QTranslateSegment::IK_QTranslateSegment()
m_axis[0] = 0;
m_axis[1] = 1;
m_axis[2] = 2;
+
+ m_limit[0] = m_limit[1] = m_limit[2] = false;
}
MT_Vector3 IK_QTranslateSegment::Axis(int dof) const
@@ -935,18 +941,42 @@ MT_Vector3 IK_QTranslateSegment::Axis(int dof) const
return m_global_transform.getBasis().getColumn(m_axis[dof]);
}
-bool IK_QTranslateSegment::UpdateAngle(const IK_QJacobian &jacobian, MT_Vector3&, bool*)
+bool IK_QTranslateSegment::UpdateAngle(const IK_QJacobian &jacobian, MT_Vector3& delta, bool *clamp)
{
- int dof_id = m_DoF_id;
+ int dof_id = m_DoF_id, dof = 0, i, clamped = false;
- MT_Vector3 dx;
- dx.x() = (m_axis_enabled[0])? jacobian.AngleUpdate(dof_id++): 0.0;
- dx.y() = (m_axis_enabled[1])? jacobian.AngleUpdate(dof_id++): 0.0;
- dx.z() = (m_axis_enabled[2])? jacobian.AngleUpdate(dof_id++): 0.0;
+ MT_Vector3 dx(0.0, 0.0, 0.0);
- m_new_translation = m_translation + dx;
+ for (i = 0; i < 3; i++) {
+ if (!m_axis_enabled[i]) {
+ m_new_translation[i] = m_translation[i];
+ continue;
+ }
- return false;
+ clamp[dof] = false;
+
+ if (!m_locked[dof]) {
+ m_new_translation[i] = m_translation[i] + jacobian.AngleUpdate(dof_id);
+
+ if (m_limit[i]) {
+ if (m_new_translation[i] > m_max[i]) {
+ delta[dof] = m_max[i] - m_translation[i];
+ m_new_translation[i] = m_max[i];
+ clamped = clamp[dof] = true;
+ }
+ else if (m_new_translation[i] < m_min[i]) {
+ delta[dof] = m_min[i] - m_translation[i];
+ m_new_translation[i] = m_min[i];
+ clamped = clamp[dof] = true;
+ }
+ }
+ }
+
+ dof_id++;
+ dof++;
+ }
+
+ return clamped;
}
void IK_QTranslateSegment::UpdateAngleApply()
@@ -954,8 +984,28 @@ void IK_QTranslateSegment::UpdateAngleApply()
m_translation = m_new_translation;
}
+void IK_QTranslateSegment::Lock(int dof, IK_QJacobian& jacobian, MT_Vector3& delta)
+{
+ m_locked[dof] = true;
+ jacobian.Lock(m_DoF_id+dof, delta[dof]);
+}
+
void IK_QTranslateSegment::SetWeight(int axis, MT_Scalar weight)
{
- m_weight[axis] = weight;
+ int i;
+
+ for (i = 0; i < m_num_DoF; i++)
+ if (m_axis[i] == axis)
+ m_weight[i] = weight;
+}
+
+void IK_QTranslateSegment::SetLimit(int axis, MT_Scalar lmin, MT_Scalar lmax)
+{
+ if (lmax < lmin)
+ return;
+
+ m_min[axis]= lmin;
+ m_max[axis]= lmax;
+ m_limit[axis]= true;
}
diff --git a/intern/iksolver/intern/IK_QSegment.h b/intern/iksolver/intern/IK_QSegment.h
index d6082e205ae..e406585bc8b 100644
--- a/intern/iksolver/intern/IK_QSegment.h
+++ b/intern/iksolver/intern/IK_QSegment.h
@@ -91,6 +91,12 @@ public:
IK_QSegment *Parent() const
{ return m_parent; }
+ // for combining two joints into one from the interface
+ void SetComposite(IK_QSegment *seg);
+
+ IK_QSegment *Composite() const
+ { return m_composite; }
+
// number of degrees of freedom
int NumberOfDoF() const
{ return m_num_DoF; }
@@ -171,6 +177,7 @@ protected:
IK_QSegment *m_parent;
IK_QSegment *m_child;
IK_QSegment *m_sibling;
+ IK_QSegment *m_composite;
// full transform =
// start * rest_basis * basis * translation
@@ -217,6 +224,7 @@ public:
private:
MT_Matrix3x3 m_new_basis;
bool m_limit_x, m_limit_y, m_limit_z;
+ MT_Scalar m_min[2], m_max[2];
MT_Scalar m_min_y, m_max_y, m_max_x, m_max_z, m_offset_x, m_offset_z;
MT_Scalar m_locked_ax, m_locked_ay, m_locked_az;
};
@@ -252,7 +260,7 @@ public:
private:
int m_axis;
MT_Scalar m_angle, m_new_angle;
- MT_Scalar m_limit;
+ bool m_limit;
MT_Scalar m_min, m_max;
};
@@ -275,6 +283,7 @@ public:
private:
MT_Matrix3x3 m_new_basis;
bool m_limit_x, m_limit_z;
+ MT_Scalar m_min[2], m_max[2];
MT_Scalar m_max_x, m_max_z, m_offset_x, m_offset_z;
};
@@ -308,7 +317,7 @@ private:
class IK_QTranslateSegment : public IK_QSegment
{
public:
- // Revolute, 2DOF or 3DOF translational segments
+ // 1DOF, 2DOF or 3DOF translational segments
IK_QTranslateSegment(int axis1);
IK_QTranslateSegment(int axis1, int axis2);
IK_QTranslateSegment();
@@ -316,15 +325,17 @@ public:
MT_Vector3 Axis(int dof) const;
bool UpdateAngle(const IK_QJacobian &jacobian, MT_Vector3& delta, bool *clamp);
- void Lock(int, IK_QJacobian&, MT_Vector3&) {};
+ void Lock(int, IK_QJacobian&, MT_Vector3&);
void UpdateAngleApply();
void SetWeight(int axis, MT_Scalar weight);
+ void SetLimit(int axis, MT_Scalar lmin, MT_Scalar lmax);
private:
int m_axis[3];
- bool m_axis_enabled[3];
+ bool m_axis_enabled[3], m_limit[3];
MT_Vector3 m_new_translation;
+ MT_Scalar m_min[3], m_max[3];
};
#endif
diff --git a/intern/iksolver/intern/IK_QTask.cpp b/intern/iksolver/intern/IK_QTask.cpp
index b0e51aec371..c72f146b36e 100644
--- a/intern/iksolver/intern/IK_QTask.cpp
+++ b/intern/iksolver/intern/IK_QTask.cpp
@@ -82,7 +82,6 @@ void IK_QPositionTask::ComputeJacobian(IK_QJacobian& jacobian)
jacobian.SetBetas(m_id, m_size, m_weight*d_pos);
-
// compute derivatives
int i;
const IK_QSegment *seg;
@@ -92,7 +91,7 @@ void IK_QPositionTask::ComputeJacobian(IK_QJacobian& jacobian)
for (i = 0; i < seg->NumberOfDoF(); i++) {
MT_Vector3 axis = seg->Axis(i)*m_weight;
-
+
if (seg->Translational())
jacobian.SetDerivatives(m_id, seg->DoFId()+i, axis);
else {
diff --git a/intern/iksolver/intern/IK_Solver.cpp b/intern/iksolver/intern/IK_Solver.cpp
index 7e9d00f8655..23960a9550b 100644
--- a/intern/iksolver/intern/IK_Solver.cpp
+++ b/intern/iksolver/intern/IK_Solver.cpp
@@ -45,7 +45,7 @@ typedef struct {
std::list<IK_QTask*> tasks;
} IK_QSolver;
-IK_Segment *IK_CreateSegment(int flag)
+IK_QSegment *CreateSegment(int flag, bool translate)
{
int ndof = 0;
ndof += (flag & IK_XDOF)? 1: 0;
@@ -55,7 +55,7 @@ IK_Segment *IK_CreateSegment(int flag)
IK_QSegment *seg;
if (ndof == 0)
- seg = new IK_QNullSegment();
+ return NULL;
else if (ndof == 1) {
int axis;
@@ -63,7 +63,7 @@ IK_Segment *IK_CreateSegment(int flag)
else if (flag & IK_YDOF) axis = 1;
else axis = 2;
- if (flag & IK_TRANSLATIONAL)
+ if (translate)
seg = new IK_QTranslateSegment(axis);
else
seg = new IK_QRevoluteSegment(axis);
@@ -80,7 +80,7 @@ IK_Segment *IK_CreateSegment(int flag)
axis2 = 2;
}
- if (flag & IK_TRANSLATIONAL)
+ if (translate)
seg = new IK_QTranslateSegment(axis1, axis2);
else {
if (axis1 + axis2 == 2)
@@ -90,27 +90,58 @@ IK_Segment *IK_CreateSegment(int flag)
}
}
else {
- if (flag & IK_TRANSLATIONAL)
+ if (translate)
seg = new IK_QTranslateSegment();
else
seg = new IK_QSphericalSegment();
}
- return (IK_Segment*)seg;
+ return seg;
+}
+
+IK_Segment *IK_CreateSegment(int flag)
+{
+ IK_QSegment *rot = CreateSegment(flag, false);
+ IK_QSegment *trans = CreateSegment(flag >> 3, true);
+
+ IK_QSegment *seg;
+
+ if (rot == NULL && trans == NULL)
+ seg = new IK_QNullSegment();
+ else if (rot == NULL)
+ seg = trans;
+ else {
+ seg = rot;
+
+ // make it seem from the interface as if the rotation and translation
+ // segment are one
+ if (trans) {
+ seg->SetComposite(trans);
+ trans->SetParent(seg);
+ }
+ }
+
+ return seg;
}
void IK_FreeSegment(IK_Segment *seg)
{
IK_QSegment *qseg = (IK_QSegment*)seg;
+ if (qseg->Composite())
+ delete qseg->Composite();
delete qseg;
}
void IK_SetParent(IK_Segment *seg, IK_Segment *parent)
{
IK_QSegment *qseg = (IK_QSegment*)seg;
+ IK_QSegment *qparent = (IK_QSegment*)parent;
- qseg->SetParent((IK_QSegment*)parent);
+ if (qparent && qparent->Composite())
+ qseg->SetParent(qparent->Composite());
+ else
+ qseg->SetParent(qparent);
}
void IK_SetTransform(IK_Segment *seg, float start[3], float rest[][3], float basis[][3], float length)
@@ -127,19 +158,35 @@ void IK_SetTransform(IK_Segment *seg, float start[3], float rest[][3], float bas
rest[0][2], rest[1][2], rest[2][2]);
MT_Scalar mlength(length);
- qseg->SetTransform(mstart, mrest, mbasis, mlength);
+ if (qseg->Composite()) {
+ MT_Vector3 cstart(0, 0, 0);
+ MT_Matrix3x3 cbasis;
+ cbasis.setIdentity();
+
+ qseg->SetTransform(mstart, mrest, mbasis, 0.0);
+ qseg->Composite()->SetTransform(cstart, cbasis, cbasis, mlength);
+ }
+ else
+ 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);
+ if (axis >= IK_TRANS_X) {
+ if(!qseg->Translational())
+ if(qseg->Composite() && qseg->Composite()->Translational())
+ qseg = qseg->Composite();
+ else
+ return;
+
+ if(axis == IK_TRANS_X) axis = IK_X;
+ else if(axis == IK_TRANS_Y) axis = IK_Y;
+ else axis = IK_Z;
+ }
+
+ qseg->SetLimit(axis, lmin, lmax);
}
void IK_SetStiffness(IK_Segment *seg, IK_SegmentAxis axis, float stiffness)
@@ -153,12 +200,20 @@ void IK_SetStiffness(IK_Segment *seg, IK_SegmentAxis axis, float stiffness)
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);
+
+ if (axis >= IK_TRANS_X) {
+ if(!qseg->Translational())
+ if(qseg->Composite() && qseg->Composite()->Translational())
+ qseg = qseg->Composite();
+ else
+ return;
+
+ if(axis == IK_TRANS_X) axis = IK_X;
+ else if(axis == IK_TRANS_Y) axis = IK_Y;
+ else axis = IK_Z;
+ }
+
+ qseg->SetWeight(axis, weight);
}
void IK_GetBasisChange(IK_Segment *seg, float basis_change[][3])
@@ -166,6 +221,9 @@ void IK_GetBasisChange(IK_Segment *seg, float basis_change[][3])
IK_QSegment *qseg = (IK_QSegment*)seg;
const MT_Matrix3x3& change = qseg->BasisChange();
+ if (qseg->Translational() && qseg->Composite())
+ qseg = qseg->Composite();
+
// 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];
@@ -181,6 +239,10 @@ void IK_GetBasisChange(IK_Segment *seg, float basis_change[][3])
void IK_GetTranslationChange(IK_Segment *seg, float *translation_change)
{
IK_QSegment *qseg = (IK_QSegment*)seg;
+
+ if (!qseg->Translational() && qseg->Composite())
+ qseg = qseg->Composite();
+
const MT_Vector3& change = qseg->TranslationChange();
translation_change[0] = (float)change[0];
@@ -222,9 +284,11 @@ void IK_SolverAddGoal(IK_Solver *solver, IK_Segment *tip, float goal[3], float w
IK_QSolver *qsolver = (IK_QSolver*)solver;
IK_QSegment *qtip = (IK_QSegment*)tip;
+ if (qtip->Composite())
+ qtip = qtip->Composite();
+
MT_Vector3 pos(goal);
- // qsolver->tasks.empty()
IK_QTask *ee = new IK_QPositionTask(true, qtip, pos);
ee->SetWeight(weight);
qsolver->tasks.push_back(ee);
@@ -238,6 +302,9 @@ void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[
IK_QSolver *qsolver = (IK_QSolver*)solver;
IK_QSegment *qtip = (IK_QSegment*)tip;
+ if (qtip->Composite())
+ qtip = qtip->Composite();
+
// 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],
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index 7d0caa4b182..336ba823520 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -43,27 +43,29 @@ struct bConstraint;
struct Object;
struct MDeformVert;
struct Mesh;
-struct PoseChain;
+struct PoseTree;
struct ListBase;
typedef struct PoseTarget
{
struct PoseTarget *next, *prev;
- struct bConstraint *con;
- int tip;
+
+ struct bConstraint *con; /* the constrait of this target */
+ int tip; /* index of tip pchan in PoseTree */
} PoseTarget;
-typedef struct PoseChain
+typedef struct PoseTree
{
- struct PoseChain *next, *prev;
- struct bPoseChannel **pchanchain;
- struct ListBase targets;
- int totchannel;
- int tree; // true or false
- float (*basis_change)[3][3];
- float tolerance;
- int iterations;
-} PoseChain;
+ struct PoseTree *next, *prev;
+
+ struct ListBase targets; /* list of targets of the tree */
+ struct bPoseChannel **pchan; /* array of pose channels */
+ int *parent; /* and their parents */
+ int totchannel; /* number of pose channels */
+ float (*basis_change)[3][3]; /* basis change result from solver */
+ float tolerance; /* tolerance from the constraint */
+ int iterations; /* iterations from the constraint */
+} PoseTree;
/* Core armature functionality */
#ifdef __cplusplus
@@ -91,13 +93,14 @@ void where_is_pose (struct Object *ob);
/* get_objectspace_bone_matrix has to be removed still */
void get_objectspace_bone_matrix (struct Bone* bone, float M_accumulatedMatrix[][4], int root, int posed);
void vec_roll_to_mat3(float *vec, float roll, float mat[][3]);
+void mat3_to_vec_roll(float mat[][3], float *vec, float *roll);
/* Animation functions */
-struct PoseChain *ik_chain_to_posechain (struct Object *ob, struct Bone *bone);
-void solve_posechain (PoseChain *chain);
-void free_posechain (PoseChain *chain);
+struct PoseTree *ik_tree_to_posetree(struct Object *ob, struct Bone *bone);
+void solve_posetree(PoseTree *tree);
+void free_posetree(PoseTree *tree);
/* Gameblender hacks */
void GB_init_armature_deform(struct ListBase *defbase, float premat[][4], float postmat[][4]);
diff --git a/source/blender/blenkernel/bad_level_call_stubs/stubs.c b/source/blender/blenkernel/bad_level_call_stubs/stubs.c
index aa5748ceb89..26d82324d3f 100644
--- a/source/blender/blenkernel/bad_level_call_stubs/stubs.c
+++ b/source/blender/blenkernel/bad_level_call_stubs/stubs.c
@@ -186,7 +186,6 @@ void IK_FreeSolver(IK_Solver *solver) {};
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_SolverAddCenterOfMass(IK_Solver *solver, IK_Segment *root, float goal[3], float weight) {}
int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations) { return 0; }
/* exotic.c */
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 9ef64de4d0d..2dfbf94aa0b 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -205,7 +205,6 @@ bPoseChannel *verify_pose_channel(bPose* pose, const char* name)
/* init vars to prevent mat errors */
chan->quat[0] = 1.0F;
chan->size[0] = chan->size[1] = chan->size[2] = 1.0F;
- Mat3One(chan->ik_mat);
chan->limitmin[0]= chan->limitmin[1]= chan->limitmin[2]= -180.0f;
chan->limitmax[0]= chan->limitmax[1]= chan->limitmax[2]= 180.0f;
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 827b3963a00..5e36e2ea843 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -968,16 +968,16 @@ void armature_rebuild_pose(Object *ob, bArmature *arm)
-/* allocates PoseChain, and links that to root bone/channel */
+/* allocates PoseTree, and links that to root bone/channel */
/* note; if we got this working, it can become static too? */
-static void initialize_posechain(struct Object *ob, bPoseChannel *pchan_tip)
+static void initialize_posetree(struct Object *ob, bPoseChannel *pchan_tip)
{
bPoseChannel *curchan, *pchan_root=NULL, *chanlist[256], **oldchan;
- PoseChain *chain;
+ PoseTree *tree;
PoseTarget *target;
bConstraint *con;
bKinematicConstraint *data;
- int a, segcount= 0, size, newsize;
+ int a, segcount= 0, size, newsize, *oldparent, parent;
/* find IK constraint, and validate it */
for(con= pchan_tip->constraints.first; con; con= con->next) {
@@ -1009,9 +1009,9 @@ static void initialize_posechain(struct Object *ob, bPoseChannel *pchan_tip)
/* setup the chain data */
/* we make tree-IK, unless all existing targets are in this chain */
- for(chain= pchan_root->chain.first; chain; chain= chain->next) {
- for(target= chain->targets.first; target; target= target->next) {
- curchan= chain->pchanchain[target->tip];
+ for(tree= pchan_root->iktree.first; tree; tree= tree->next) {
+ for(target= tree->targets.first; target; target= target->next) {
+ curchan= tree->pchan[target->tip];
if(curchan->flag & POSE_CHAIN)
curchan->flag &= ~POSE_CHAIN;
else
@@ -1025,62 +1025,72 @@ static void initialize_posechain(struct Object *ob, bPoseChannel *pchan_tip)
target->con= con;
pchan_tip->flag &= ~POSE_CHAIN;
- if(chain==NULL) {
- /* make new chain */
- chain= MEM_callocN(sizeof(PoseChain), "posechain");
+ if(tree==NULL) {
+ /* make new tree */
+ tree= MEM_callocN(sizeof(PoseTree), "posetree");
- chain->tolerance= data->tolerance;
- chain->iterations= data->iterations;
- chain->totchannel= segcount;
+ tree->tolerance= data->tolerance;
+ tree->iterations= data->iterations;
+ tree->totchannel= segcount;
- chain->pchanchain= MEM_callocN(segcount*sizeof(void *), "channel chain");
+ tree->pchan= MEM_callocN(segcount*sizeof(void*), "ik tree pchan");
+ tree->parent= MEM_callocN(segcount*sizeof(int), "ik tree parent");
for(a=0; a<segcount; a++) {
- chain->pchanchain[a]= chanlist[segcount-a-1];
+ tree->pchan[a]= chanlist[segcount-a-1];
+ tree->parent[a]= a-1;
}
target->tip= segcount-1;
- /* AND! link the chain to the root */
- BLI_addtail(&pchan_root->chain, chain);
+ /* AND! link the tree to the root */
+ BLI_addtail(&pchan_root->iktree, tree);
}
else {
- chain->tolerance= MIN2(chain->tolerance, data->tolerance);
- chain->iterations= MAX2(data->iterations, chain->iterations);
+ tree->tolerance= MIN2(tree->tolerance, data->tolerance);
+ tree->iterations= MAX2(data->iterations, tree->iterations);
/* skip common pose channels and add remaining*/
- size= MIN2(segcount, chain->totchannel);
- for(a=0; a<size && chain->pchanchain[a]==chanlist[segcount-a-1]; a++);
+ size= MIN2(segcount, tree->totchannel);
+ for(a=0; a<size && tree->pchan[a]==chanlist[segcount-a-1]; a++);
+ parent= a-1;
segcount= segcount-a;
- target->tip= chain->totchannel + segcount - 1;
+ target->tip= tree->totchannel + segcount - 1;
if (segcount > 0) {
/* resize array */
- newsize= chain->totchannel + segcount;
- oldchan= chain->pchanchain;
-
- chain->pchanchain= MEM_callocN(newsize*sizeof(void*), "channel chain");
- memcpy(chain->pchanchain, oldchan, sizeof(void*)*chain->totchannel);
+ newsize= tree->totchannel + segcount;
+ oldchan= tree->pchan;
+ oldparent= tree->parent;
+
+ tree->pchan= MEM_callocN(newsize*sizeof(void*), "ik tree pchan");
+ tree->parent= MEM_callocN(newsize*sizeof(int), "ik tree parent");
+ memcpy(tree->pchan, oldchan, sizeof(void*)*tree->totchannel);
+ memcpy(tree->parent, oldparent, sizeof(int)*tree->totchannel);
MEM_freeN(oldchan);
+ MEM_freeN(oldparent);
/* add new pose channels at the end, in reverse order */
- for(a=0; a<segcount; a++)
- chain->pchanchain[chain->totchannel+a]= chanlist[segcount-a-1];
+ for(a=0; a<segcount; a++) {
+ tree->pchan[tree->totchannel+a]= chanlist[segcount-a-1];
+ tree->parent[tree->totchannel+a]= tree->totchannel+a-1;
+ }
+ tree->parent[tree->totchannel]= parent;
- chain->totchannel= newsize;
+ tree->totchannel= newsize;
}
- /* move chain to end of list, for correct evaluation order */
- BLI_remlink(&pchan_root->chain, chain);
- BLI_addtail(&pchan_root->chain, chain);
+ /* move tree to end of list, for correct evaluation order */
+ BLI_remlink(&pchan_root->iktree, tree);
+ BLI_addtail(&pchan_root->iktree, tree);
}
- /* add target to the chain */
- BLI_addtail(&chain->targets, target);
+ /* add target to the tree */
+ BLI_addtail(&tree->targets, target);
}
/* called from within the core where_is_pose loop, all animsystems and constraints
were executed & assigned. Now as last we do an IK pass */
-static void execute_posechain(Object *ob, PoseChain *chain)
+static void execute_posetree(Object *ob, PoseTree *tree)
{
float R_parmat[3][3];
float iR_parmat[3][3];
@@ -1088,24 +1098,24 @@ static void execute_posechain(Object *ob, PoseChain *chain)
float goalrot[3][3], goalpos[3];
float rootmat[4][4], imat[4][4];
float goal[4][4], goalinv[4][4];
- float size[3], bonesize[3], irest_basis[3][3], full_basis[3][3];
- float length, basis[3][3], rest_basis[3][3], start[3];
- int a, b, flag;
+ float size[3], irest_basis[3][3], full_basis[3][3];
+ float length, basis[3][3], rest_basis[3][3], start[3], *ikstretch=NULL;
+ int a, flag, hasstretch=0;
bPoseChannel *pchan;
- IK_Segment *seg, *parent, **ikchain, *iktarget;
+ IK_Segment *seg, *parent, **iktree, *iktarget;
IK_Solver *solver;
PoseTarget *target;
bKinematicConstraint *data;
Bone *bone;
- if (chain->totchannel == 0)
+ if (tree->totchannel == 0)
return;
- ikchain= MEM_mallocN(sizeof(void*)*chain->totchannel, "ik chain");
+ iktree= MEM_mallocN(sizeof(void*)*tree->totchannel, "ik tree");
- for(a=0; a<chain->totchannel; a++) {
- pchan= chain->pchanchain[a];
- bone = pchan->bone;
+ for(a=0; a<tree->totchannel; a++) {
+ pchan= tree->pchan[a];
+ bone= pchan->bone;
/* set DoF flag */
flag= 0;
@@ -1116,54 +1126,51 @@ static void execute_posechain(Object *ob, PoseChain *chain)
if((pchan->ikflag & BONE_IK_NO_ZDOF) == 0)
flag |= IK_ZDOF;
- seg= ikchain[a]= IK_CreateSegment(flag);
+ if(pchan->ikstretch > 0.0) {
+ flag |= IK_TRANS_YDOF;
+ hasstretch = 1;
+ }
+
+ seg= iktree[a]= IK_CreateSegment(flag);
/* find parent */
if(a == 0)
parent= NULL;
- else {
- for(b=a-1; chain->pchanchain[b]!=pchan->parent; b--);
- parent= ikchain[b];
- }
+ else
+ parent= iktree[tree->parent[a]];
IK_SetParent(seg, parent);
/* get the matrix that transforms from prevbone into this bone */
Mat3CpyMat4(R_bonemat, pchan->pose_mat);
-
+
+ /* gather transformations for this IK segment */
+
if(a>0 && pchan->parent) {
Mat3CpyMat4(R_parmat, pchan->parent->pose_mat);
+ VECCOPY(start, bone->head); /* bone offset */
+ }
+ else {
+ Mat3One(R_parmat);
+ start[0]= start[1]= start[2]= 0.0f;
}
- else
- Mat3One (R_parmat);
-
- Mat3Inv(iR_parmat, R_parmat);
-
- /* gather transformations for this IK segment */
/* change length based on bone size */
- Mat3ToSize(R_bonemat, bonesize);
- length= bone->length*bonesize[1];
+ length= bone->length*VecLength(R_bonemat[1]);
+ /* compute rest basis and its inverse */
Mat3CpyMat3(rest_basis, bone->bone_mat);
+ Mat3CpyMat3(irest_basis, bone->bone_mat);
+ Mat3Transp(irest_basis);
/* compute basis with rest_basis removed */
- Mat3Inv(irest_basis, rest_basis);
+ Mat3Inv(iR_parmat, R_parmat);
Mat3MulMat3(full_basis, iR_parmat, R_bonemat);
Mat3MulMat3(basis, irest_basis, full_basis);
- /* basis must be pure rotation, size was extracted for length already */
- Mat3Ortho(rest_basis);
+ /* basis must be pure rotation */
Mat3Ortho(basis);
- /* Bone offset */
- if(a>0 && pchan->parent) {
- VECCOPY(start, bone->head);
-// Mat3MulVecfl(R_parmat, start);
- }
- else
- start[0]= start[1]= start[2]= 0.0f;
-
IK_SetTransform(seg, start, rest_basis, basis, length);
if (pchan->ikflag & BONE_IK_XLIMIT)
@@ -1176,21 +1183,27 @@ static void execute_posechain(Object *ob, PoseChain *chain)
IK_SetStiffness(seg, IK_X, pchan->stiffness[0]);
IK_SetStiffness(seg, IK_Y, pchan->stiffness[1]);
IK_SetStiffness(seg, IK_Z, pchan->stiffness[2]);
+
+ if(pchan->ikstretch > 0.0) {
+ float ikstretch = pchan->ikstretch*pchan->ikstretch;
+ IK_SetStiffness(seg, IK_TRANS_Y, MIN2(1.0-ikstretch, 0.99));
+ IK_SetLimit(seg, IK_TRANS_Y, 0.001, 1e10);
+ }
}
- solver= IK_CreateSolver(ikchain[0]);
+ solver= IK_CreateSolver(iktree[0]);
/* set solver goals */
- /* first set the goal inverse transform, assuming the root of chain was done ok! */
- pchan= chain->pchanchain[0];
+ /* first set the goal inverse transform, assuming the root of tree was done ok! */
+ pchan= tree->pchan[0];
Mat4One(rootmat);
VECCOPY(rootmat[3], pchan->pose_head);
Mat4MulMat4 (imat, rootmat, ob->obmat);
Mat4Invert (goalinv, imat);
- for(target=chain->targets.first; target; target=target->next) {
+ for(target=tree->targets.first; target; target=target->next) {
data= (bKinematicConstraint*)target->con->data;
/* 1.0=ctime */
@@ -1208,7 +1221,7 @@ static void execute_posechain(Object *ob, PoseChain *chain)
float fac= target->con->enforce;
float mfac= 1.0-fac;
- pchan= chain->pchanchain[target->tip];
+ pchan= tree->pchan[target->tip];
/* blend position */
VECCOPY(vec, pchan->pose_tail);
@@ -1226,9 +1239,7 @@ static void execute_posechain(Object *ob, PoseChain *chain)
QuatToMat3(q, goalrot);
}
- iktarget= ikchain[target->tip];
-
- /*IK_SolverAddCenterOfMass(solver, ikchain[0], goalpos, data->weight);*/
+ iktarget= iktree[target->tip];
if(data->weight != 0.0)
IK_SolverAddGoal(solver, iktarget, goalpos, data->weight);
@@ -1237,26 +1248,57 @@ static void execute_posechain(Object *ob, PoseChain *chain)
}
/* solve */
- IK_Solve(solver, chain->tolerance, chain->iterations);
+ IK_Solve(solver, tree->tolerance, tree->iterations);
IK_FreeSolver(solver);
/* gather basis changes */
- chain->basis_change= MEM_mallocN(sizeof(float[3][3])*chain->totchannel, "ik basis change");
+ tree->basis_change= MEM_mallocN(sizeof(float[3][3])*tree->totchannel, "ik basis change");
+ if(hasstretch)
+ ikstretch= MEM_mallocN(sizeof(float)*tree->totchannel, "ik stretch");
+
+ for(a=0; a<tree->totchannel; a++) {
+ IK_GetBasisChange(iktree[a], tree->basis_change[a]);
+
+ if(hasstretch) {
+ /* have to compensate for scaling received from parent */
+ float parentstretch, stretch;
+
+ pchan= tree->pchan[a];
+ parentstretch= (tree->parent[a] >= 0)? ikstretch[tree->parent[a]]: 1.0;
+
+ if(pchan->ikstretch > 0.0) {
+ float trans[3], length;
+
+ IK_GetTranslationChange(iktree[a], trans);
+ length= pchan->bone->length*VecLength(pchan->pose_mat[1]);
+
+ ikstretch[a]= (length == 0.0)? 1.0: (trans[1]+length)/length;
+ }
+ else
+ ikstretch[a] = 1.0;
+
+ stretch= (parentstretch == 0.0)? 1.0: ikstretch[a]/parentstretch;
+
+ VecMulf(tree->basis_change[a][0], stretch);
+ VecMulf(tree->basis_change[a][1], stretch);
+ VecMulf(tree->basis_change[a][2], stretch);
+
+ }
- for(a=0; a<chain->totchannel; a++) {
- IK_GetBasisChange(ikchain[a], chain->basis_change[a]);
- IK_FreeSegment(ikchain[a]);
+ IK_FreeSegment(iktree[a]);
}
- MEM_freeN(ikchain);
+ MEM_freeN(iktree);
+ if(ikstretch) MEM_freeN(ikstretch);
}
-void free_posechain (PoseChain *chain)
+void free_posetree(PoseTree *tree)
{
- BLI_freelistN(&chain->targets);
- if(chain->pchanchain) MEM_freeN(chain->pchanchain);
- if(chain->basis_change) MEM_freeN(chain->basis_change);
- MEM_freeN(chain);
+ BLI_freelistN(&tree->targets);
+ if(tree->pchan) MEM_freeN(tree->pchan);
+ if(tree->parent) MEM_freeN(tree->parent);
+ if(tree->basis_change) MEM_freeN(tree->basis_change);
+ MEM_freeN(tree);
}
/* ********************** THE POSE SOLVER ******************* */
@@ -1451,42 +1493,42 @@ void where_is_pose (Object *ob)
else {
Mat4Invert(ob->imat, ob->obmat); // imat is needed
- /* 1. construct the PoseChains, clear flags */
+ /* 1. construct the PoseTrees, clear flags */
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
pchan->flag &= ~(POSE_DONE|POSE_CHAIN);
if(pchan->constflag & PCHAN_HAS_IK) // flag is set on editing constraints
- initialize_posechain(ob, pchan); // will attach it to root!
+ initialize_posetree(ob, pchan); // will attach it to root!
}
/* 2. the main loop, channels are already hierarchical sorted from root to children */
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
/* 3. if we find an IK root, we handle it separated */
- if(pchan->chain.first) {
- while(pchan->chain.first) {
- PoseChain *chain= pchan->chain.first;
+ if(pchan->iktree.first) {
+ while(pchan->iktree.first) {
+ PoseTree *tree= pchan->iktree.first;
int a;
- /* 4. walk over the chain for regular solving */
- for(a=0; a<chain->totchannel; a++) {
- if(!(chain->pchanchain[a]->flag & POSE_DONE)) // successive chains can set the flag
- where_is_pose_bone(ob, chain->pchanchain[a]);
+ /* 4. walk over the tree for regular solving */
+ for(a=0; a<tree->totchannel; a++) {
+ if(!(tree->pchan[a]->flag & POSE_DONE)) // successive trees can set the flag
+ where_is_pose_bone(ob, tree->pchan[a]);
}
/* 5. execute the IK solver */
- execute_posechain(ob, chain);
+ execute_posetree(ob, tree);
/* 6. apply the differences to the channels,
we need to calculate the original differences first */
- for(a=0; a<chain->totchannel; a++)
- make_dmats(chain->pchanchain[a]);
+ for(a=0; a<tree->totchannel; a++)
+ make_dmats(tree->pchan[a]);
- for(a=0; a<chain->totchannel; a++)
+ for(a=0; a<tree->totchannel; a++)
/* sets POSE_DONE */
- where_is_ik_bone(chain->pchanchain[a], chain->basis_change[a]);
+ where_is_ik_bone(tree->pchan[a], tree->basis_change[a]);
/* 7. and free */
- BLI_remlink(&pchan->chain, chain);
- free_posechain(chain);
+ BLI_remlink(&pchan->iktree, tree);
+ free_posetree(tree);
}
}
else if(!(pchan->flag & POSE_DONE)) {
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 91dbb0758ba..9a33e7a15b9 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -53,7 +53,7 @@ typedef struct bPoseChannel {
struct Bone *bone; /* set on read file or rebuild pose */
struct bPoseChannel *parent; /* set on read file or rebuild pose */
struct bPoseChannel *child; /* set on read file or rebuild pose, the 'ik' child, for b-bones */
- struct ListBase chain; /* only while evaluating pose */
+ struct ListBase iktree; /* only while evaluating pose */
void *b_bone_mats; /* only while deform, stores precalculated b_bone deform mats */
float loc[3]; /* written in by actions or transform */
@@ -62,13 +62,13 @@ typedef struct bPoseChannel {
float chan_mat[4][4]; /* matrix result of loc/quat/size , and where we put deform in, see next line */
float pose_mat[4][4]; /* constraints accumulate here. in the end, pose_mat = bone->arm_mat * chan_mat */
- float ik_mat[3][3]; /* for itterative IK */
float pose_head[3]; /* actually pose_mat[3] */
float pose_tail[3]; /* also used for drawing help lines... */
float limitmin[3], limitmax[3]; /* DOF constraint */
float stiffness[3]; /* DOF stiffness */
+ float ikstretch;
} bPoseChannel;
diff --git a/source/blender/src/buttons_editing.c b/source/blender/src/buttons_editing.c
index 3e16f59a4ed..45cf8b814d2 100644
--- a/source/blender/src/buttons_editing.c
+++ b/source/blender/src/buttons_editing.c
@@ -1252,7 +1252,7 @@ void do_fontbuts(unsigned short event)
Object *ob;
ScrArea *sa;
char str[80];
- int i, style;
+ int i, style=0;
ob= OBACT;
@@ -2357,8 +2357,8 @@ static void editing_panel_pose_bones(Object *ob, bArmature *arm)
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Stiff X:", bx-10, by-80, 114, 19, &pchan->stiffness[0], 0.0, 0.99, 1.0, 0.0, "Resistance to bending for X axis");
uiDefButBitS(block, TOG, BONE_IK_XLIMIT, B_ARM_RECALCDATA, "Limit X", bx-10,by-100,114,19, &pchan->ikflag, 0.0, 0.0, 0.0, 0.0, "Limit rotation over X axis");
if ((pchan->ikflag & BONE_IK_XLIMIT)) {
- uiDefButF(block, NUM, B_ARM_RECALCDATA, "Min X:", bx-10, by-120, 114, 19, &pchan->limitmin[0], -180.0f, pchan->limitmax[0], 1000, 1, "Minimum X limit");
- uiDefButF(block, NUM, B_ARM_RECALCDATA, "Max X:", bx-10, by-140, 114, 19, &pchan->limitmax[0], pchan->limitmin[0], 180.0f, 1000, 1, "Maximum X limit");
+ uiDefButF(block, NUM, B_ARM_RECALCDATA, "Min X:", bx-10, by-120, 114, 19, &pchan->limitmin[0], -180.0, 0.0, 1000, 1, "Minimum X limit");
+ uiDefButF(block, NUM, B_ARM_RECALCDATA, "Max X:", bx-10, by-140, 114, 19, &pchan->limitmax[0], 0.0, 180.0f, 1000, 1, "Maximum X limit");
zerolimit = 0;
}
zerodof = 0;
@@ -2368,11 +2368,11 @@ static void editing_panel_pose_bones(Object *ob, bArmature *arm)
uiBlockBeginAlign(block);
uiDefButBitS(block, TOG, BONE_IK_NO_YDOF, B_ARM_RECALCDATA, "Lock Y Rot", bx+104,by-60,113,19, &pchan->ikflag, 0.0, 0.0, 0.0, 0.0, "Disable Y DoF for IK");
if ((pchan->ikflag & BONE_IK_NO_YDOF)==0) {
- uiDefButF(block, NUM, B_ARM_RECALCDATA, "Stiff Y:", bx+104, by-80, 114, 19, &pchan->stiffness[1], 0.0, 0.99, 1.0, 0.0, "Resistance to bending for Y axis");
+ uiDefButF(block, NUM, B_ARM_RECALCDATA, "Stiff Y:", bx+104, by-80, 114, 19, &pchan->stiffness[1], 0.0, 0.99, 1.0, 0.0, "Resistance to twisting over Y axis");
uiDefButBitS(block, TOG, BONE_IK_YLIMIT, B_ARM_RECALCDATA, "Limit Y", bx+104,by-100,113,19, &pchan->ikflag, 0.0, 0.0, 0.0, 0.0, "Limit rotation over Y axis");
if ((pchan->ikflag & BONE_IK_YLIMIT)) {
- uiDefButF(block, NUM, B_ARM_RECALCDATA, "Min Y:", bx+104, by-120, 113, 19, &pchan->limitmin[1], -180.0f, pchan->limitmax[1], 1000, 1, "Minimum Y limit");
- uiDefButF(block, NUM, B_ARM_RECALCDATA, "Max Y:", bx+104, by-140, 113, 19, &pchan->limitmax[1], pchan->limitmin[1], 180.0f, 1000, 1, "Maximum Y limit");
+ uiDefButF(block, NUM, B_ARM_RECALCDATA, "Min Y:", bx+104, by-120, 113, 19, &pchan->limitmin[1], -180.0, 0.0, 1000, 1, "Minimum Y limit");
+ uiDefButF(block, NUM, B_ARM_RECALCDATA, "Max Y:", bx+104, by-140, 113, 19, &pchan->limitmax[1], 0.0, 180.0, 1000, 1, "Maximum Y limit");
zerolimit = 0;
}
zerodof = 0;
@@ -2385,20 +2385,28 @@ static void editing_panel_pose_bones(Object *ob, bArmature *arm)
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Stiff Z:", bx+217, by-80, 114, 19, &pchan->stiffness[2], 0.0, 0.99, 1.0, 0.0, "Resistance to bending for Z axis");
uiDefButBitS(block, TOG, BONE_IK_ZLIMIT, B_ARM_RECALCDATA, "Limit Z", bx+217,by-100,113,19, &pchan->ikflag, 0.0, 0.0, 0.0, 0.0, "Limit rotation over Z axis");
if ((pchan->ikflag & BONE_IK_ZLIMIT)) {
- uiDefButF(block, NUM, B_ARM_RECALCDATA, "Min Z:", bx+217, by-120, 113, 19, &pchan->limitmin[2], -180.0f, pchan->limitmax[2], 1000, 1, "Minimum Z limit");
- uiDefButF(block, NUM, B_ARM_RECALCDATA, "Max Z:", bx+217, by-140, 113, 19, &pchan->limitmax[2], pchan->limitmin[2], 180.0f, 1000, 1, "Maximum Z limit");
+ uiDefButF(block, NUM, B_ARM_RECALCDATA, "Min Z:", bx+217, by-120, 113, 19, &pchan->limitmin[2], -180.0, 0.0, 1000, 1, "Minimum Z limit");
+ uiDefButF(block, NUM, B_ARM_RECALCDATA, "Max Z:", bx+217, by-140, 113, 19, &pchan->limitmax[2], 0.0, 180.0, 1000, 1, "Maximum Z limit");
zerolimit = 0;
}
zerodof = 0;
}
uiBlockEndAlign(block);
+ by -= (zerodof)? 82: (zerolimit)? 122: 162;
+
+ uiBlockBeginAlign(block);
+ uiDefButF(block, NUM, B_ARM_RECALCDATA, "Stretch:", bx-10, by, 113, 19, &pchan->ikstretch, 0.0, 1.0, 1.0, 0.0, "Allow scaling of the bone for IK");
+ uiBlockEndAlign(block);
+
+ by -= 20;
}
else {
uiDefBut(block, LABEL, 0, "(DoF options only for IK chains)", bx-10,by-60, 300, 20, 0, 0, 0, 0, 0, "");
+
+ by -= 82;
}
- by -= (zerodof)? 82: (zerolimit)? 122: 162;
if(by < -200) break; // for time being... extreme long panels are very slow
}
diff --git a/source/blender/src/drawarmature.c b/source/blender/src/drawarmature.c
index 32c92e776e4..e2dfa96a4dd 100644
--- a/source/blender/src/drawarmature.c
+++ b/source/blender/src/drawarmature.c
@@ -1100,6 +1100,81 @@ static void pchan_draw_IK_root_lines(bPoseChannel *pchan)
}
}
+static void bgl_sphere_project(float ax, float az)
+{
+ float dir[3], sine, q3;
+
+ sine= 1.0f-ax*ax-az*az;
+ q3= 2.0*sqrt((sine < 0.0f)? 0.0f: sine);
+
+ dir[0]= -az*q3;
+ dir[1]= 1.0-2.0*sine;
+ dir[2]= ax*q3;
+
+ glVertex3fv(dir);
+}
+
+static void draw_dof_ellipse(float ax, float az)
+{
+ static float staticSine[16] = {
+ 0.0, 0.104528463268, 0.207911690818, 0.309016994375,
+ 0.406736643076, 0.5, 0.587785252292, 0.669130606359,
+ 0.743144825477, 0.809016994375, 0.866025403784,
+ 0.913545457643, 0.951056516295, 0.978147600734,
+ 0.994521895368, 1.0
+ };
+
+ int i, j, n=16;
+ float x, z, px, pz;
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glDepthMask(0);
+
+ glColor4ub(70, 70, 70, 50);
+
+ glBegin(GL_QUADS);
+ pz= 0.0;
+ for(i=1; i<n; i++) {
+ z= staticSine[i];
+
+ px= 0.0;
+ for(j=1; j<n-i+1; j++) {
+ x = staticSine[j];
+
+ if(j == n-i) {
+ glEnd();
+ glBegin(GL_TRIANGLES);
+ bgl_sphere_project(ax*px, az*z);
+ bgl_sphere_project(ax*px, az*pz);
+ bgl_sphere_project(ax*x, az*pz);
+ glEnd();
+ glBegin(GL_QUADS);
+ }
+ else {
+ bgl_sphere_project(ax*x, az*z);
+ bgl_sphere_project(ax*x, az*pz);
+ bgl_sphere_project(ax*px, az*pz);
+ bgl_sphere_project(ax*px, az*z);
+ }
+
+ px= x;
+ }
+ pz= z;
+ }
+ glEnd();
+
+ glDisable(GL_BLEND);
+ glDepthMask(1);
+
+ glColor3ub(0, 0, 0);
+
+ glBegin(GL_LINE_STRIP);
+ for(i=0; i<n; i++)
+ bgl_sphere_project(staticSine[n-i-1]*ax, staticSine[i]*az);
+ glEnd();
+}
+
static void draw_pose_dofs(Object *ob)
{
bPoseChannel *pchan;
@@ -1111,9 +1186,8 @@ static void draw_pose_dofs(Object *ob)
if(bone && !(bone->flag & BONE_HIDDEN_P)) {
if(bone->flag & BONE_SELECTED) {
if(pose_channel_in_IK_chain(ob, pchan)) {
- float corner[4][3], vec[4][3], handle1[3], handle2[3];
- float mat[4][4];
- float phi=0.0f, theta=0.0f;
+ float corner[4][3], mat[4][4];
+ float phi=0.0f, theta=0.0f, scale;
int a, i;
/* in parent-bone pose, but own restspace */
@@ -1125,19 +1199,37 @@ static void draw_pose_dofs(Object *ob)
glTranslatef(bone->head[0], bone->head[1], bone->head[2]);
Mat4CpyMat3(mat, pchan->bone->bone_mat);
glMultMatrixf(mat);
-
- /* center of the cone */
- if(pchan->ikflag & BONE_IK_XLIMIT)
- phi= 0.5*( pchan->limitmin[0]+pchan->limitmax[0]);
- if(pchan->ikflag & BONE_IK_ZLIMIT)
- theta= 0.5*(pchan->limitmin[2]+pchan->limitmax[2]);
-
- /* now move to cone space */
- glRotatef(phi, 1.0f, 0.0f, 0.0f);
- glRotatef(theta, 0.0f, 0.0f, 1.0f);
+
+ scale= bone->length*pchan->size[1];
+ glScalef(scale, scale, scale);
+
+ if(pchan->ikflag & BONE_IK_XLIMIT) {
+ if(pchan->ikflag & BONE_IK_ZLIMIT) {
+ float amin[3], amax[3];
+
+ for(i=0; i<3; i++) {
+ amin[i]= sin(pchan->limitmin[i]*M_PI/360.0);
+ amax[i]= sin(pchan->limitmax[i]*M_PI/360.0);
+ }
+
+ glScalef(1.0, -1.0, 1.0);
+ if (amin[0] != 0.0 && amin[2] != 0.0)
+ draw_dof_ellipse(amin[0], amin[2]);
+ if (amin[0] != 0.0 && amax[2] != 0.0)
+ draw_dof_ellipse(amin[0], amax[2]);
+ if (amax[0] != 0.0 && amin[2] != 0.0)
+ draw_dof_ellipse(amax[0], amin[2]);
+ if (amax[0] != 0.0 && amax[2] != 0.0)
+ draw_dof_ellipse(amax[0], amax[2]);
+ glScalef(1.0, -1.0, 1.0);
+ }
+ }
/* arcs */
if(pchan->ikflag & BONE_IK_ZLIMIT) {
+ theta= 0.5*(pchan->limitmin[2]+pchan->limitmax[2]);
+ glRotatef(theta, 0.0f, 0.0f, 1.0f);
+
glColor3ub(50, 50, 255); // blue, Z axis limit
glBegin(GL_LINE_STRIP);
for(a=-16; a<=16; a++) {
@@ -1145,14 +1237,20 @@ static void draw_pose_dofs(Object *ob)
phi= fac*(M_PI/360.0f)*(pchan->limitmax[2]-pchan->limitmin[2]);
if(a==-16) i= 0; else i= 1;
- corner[i][0]= bone->length*sin(phi);
- corner[i][1]= bone->length*cos(phi);
+ corner[i][0]= sin(phi);
+ corner[i][1]= cos(phi);
corner[i][2]= 0.0f;
glVertex3fv(corner[i]);
}
glEnd();
+
+ glRotatef(-theta, 0.0f, 0.0f, 1.0f);
}
+
if(pchan->ikflag & BONE_IK_XLIMIT) {
+ theta= 0.5*( pchan->limitmin[0]+pchan->limitmax[0]);
+ glRotatef(theta, 1.0f, 0.0f, 0.0f);
+
glColor3ub(255, 50, 50); // Red, X axis limit
glBegin(GL_LINE_STRIP);
for(a=-16; a<=16; a++) {
@@ -1161,72 +1259,15 @@ static void draw_pose_dofs(Object *ob)
if(a==-16) i= 2; else i= 3;
corner[i][0]= 0.0f;
- corner[i][1]= bone->length*sin(phi);
- corner[i][2]= bone->length*cos(phi);
+ corner[i][1]= sin(phi);
+ corner[i][2]= cos(phi);
glVertex3fv(corner[i]);
}
glEnd();
- }
-
- if(pchan->ikflag & BONE_IK_XLIMIT) {
- if(pchan->ikflag & BONE_IK_ZLIMIT) {
-
- /* using corners to draw the influence area */
- /* we set up beziers for it */
-
- VecSubf(handle1, corner[2], corner[3]);
- VecMulf(handle1, 0.27614f); // 0.5 * kappa, 0.5522847498
- VecSubf(handle2, corner[0], corner[1]);
- VecMulf(handle2, 0.27614f);
- cpack(0x0);
- glEnable(GL_MAP1_VERTEX_3);
-
- /* bezier part */
- VECCOPY(vec[0], corner[0]); // 0 and 3 are cv's, 1 and 2 handles
- VecAddf(vec[1], corner[0], handle1);
- VecAddf(vec[2], corner[2], handle2);
- VECCOPY(vec[3], corner[2]);
- glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, vec[0]);
-
- glBegin(GL_LINE_STRIP);
- for(a=0; a<=16; a++) glEvalCoord1f((float)a/16.0);
- glEnd();
-
- /* bezier part */
- VECCOPY(vec[0], corner[2]); // 0 and 3 are cv's, 1 and 2 handles
- VecSubf(vec[1], corner[2], handle2);
- VecAddf(vec[2], corner[1], handle1);
- VECCOPY(vec[3], corner[1]);
- glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, vec[0]);
-
- glBegin(GL_LINE_STRIP);
- for(a=0; a<=16; a++) glEvalCoord1f((float)a/16.0);
- glEnd();
-
- /* bezier part */
- VECCOPY(vec[0], corner[1]); // 0 and 3 are cv's, 1 and 2 handles
- VecSubf(vec[1], corner[1], handle1);
- VecSubf(vec[2], corner[3], handle2);
- VECCOPY(vec[3], corner[3]);
- glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, vec[0]);
-
- glBegin(GL_LINE_STRIP);
- for(a=0; a<=16; a++) glEvalCoord1f((float)a/16.0);
- glEnd();
-
- /* bezier part */
- VECCOPY(vec[0], corner[3]); // 0 and 3 are cv's, 1 and 2 handles
- VecAddf(vec[1], corner[3], handle2);
- VecSubf(vec[2], corner[0], handle1);
- VECCOPY(vec[3], corner[0]);
- glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, vec[0]);
-
- glBegin(GL_LINE_STRIP);
- for(a=0; a<=16; a++) glEvalCoord1f((float)a/16.0);
- glEnd();
- }
+ glRotatef(-theta, 1.0f, 0.0f, 0.0f);
}
+
glPopMatrix(); // out of cone, out of bone
}
}