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