diff options
-rw-r--r-- | intern/iksolver/extern/IK_solver.h | 29 | ||||
-rw-r--r-- | intern/iksolver/intern/IK_QJacobian.cpp | 9 | ||||
-rw-r--r-- | intern/iksolver/intern/IK_QSegment.cpp | 260 | ||||
-rw-r--r-- | intern/iksolver/intern/IK_QSegment.h | 19 | ||||
-rw-r--r-- | intern/iksolver/intern/IK_QTask.cpp | 3 | ||||
-rw-r--r-- | intern/iksolver/intern/IK_Solver.cpp | 109 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_armature.h | 35 | ||||
-rw-r--r-- | source/blender/blenkernel/bad_level_call_stubs/stubs.c | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/action.c | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/armature.c | 252 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_action_types.h | 4 | ||||
-rw-r--r-- | source/blender/src/buttons_editing.c | 26 | ||||
-rw-r--r-- | source/blender/src/drawarmature.c | 193 |
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 } } |