NLA SoC: Start of 'Meta' Strips
[blender.git] / source / blender / blenkernel / intern / armature.c
index 10b2a3648271148f6350a2861c5a5df71233233f..1b930a74449b7e0353507643de2a48e7b81c87d3 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * $Id$
  *
 /**
  * $Id$
  *
- * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
+ * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -22,7 +22,7 @@
  *
  * Contributor(s): Full recode, Ton Roosendaal, Crete 2005
  *
  *
  * Contributor(s): Full recode, Ton Roosendaal, Crete 2005
  *
- * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ * ***** END GPL LICENSE BLOCK *****
  */
 
 #include <ctype.h>
  */
 
 #include <ctype.h>
 #include <math.h>
 #include <string.h>
 #include <stdio.h>
 #include <math.h>
 #include <string.h>
 #include <stdio.h>
+#include <float.h>
+
 #include "MEM_guardedalloc.h"
 
 #include "MEM_guardedalloc.h"
 
-#include "nla.h"
+//XXX #include "nla.h"
 
 #include "BLI_arithb.h"
 #include "BLI_blenlib.h"
 
 #include "BLI_arithb.h"
 #include "BLI_blenlib.h"
@@ -65,7 +67,7 @@
 #include "BKE_object.h"
 #include "BKE_utildefines.h"
 
 #include "BKE_object.h"
 #include "BKE_utildefines.h"
 
-#include "BIF_editdeform.h"
+//XXX #include "BIF_editdeform.h"
 
 #include "IK_solver.h"
 
 
 #include "IK_solver.h"
 
 
 /*     **************** Generic Functions, data level *************** */
 
 
 /*     **************** Generic Functions, data level *************** */
 
-bArmature *get_armature(Object *ob)
-{
-       if(ob==NULL) return NULL;
-       if(ob->type==OB_ARMATURE) return ob->data;
-       else return NULL;
-}
-
 bArmature *add_armature(char *name)
 {
        bArmature *arm;
 bArmature *add_armature(char *name)
 {
        bArmature *arm;
@@ -92,6 +87,12 @@ bArmature *add_armature(char *name)
        return arm;
 }
 
        return arm;
 }
 
+bArmature *get_armature(Object *ob)
+{
+       if(ob->type==OB_ARMATURE)
+               return (bArmature *)ob->data;
+       return NULL;
+}
 
 void free_boneChildren(Bone *bone)
 { 
 
 void free_boneChildren(Bone *bone)
 { 
@@ -131,6 +132,14 @@ void free_armature(bArmature *arm)
        if (arm) {
                /*              unlink_armature(arm);*/
                free_bones(arm);
        if (arm) {
                /*              unlink_armature(arm);*/
                free_bones(arm);
+               
+               /* free editmode data */
+               if (arm->edbo) {
+                       BLI_freelistN(arm->edbo);
+                       
+                       MEM_freeN(arm->edbo);
+                       arm->edbo= NULL;
+               }
        }
 }
 
        }
 }
 
@@ -178,7 +187,7 @@ static void copy_bonechildren (Bone* newBone, Bone* oldBone)
        Bone    *curBone, *newChildBone;
        
        /*      Copy this bone's list*/
        Bone    *curBone, *newChildBone;
        
        /*      Copy this bone's list*/
-       duplicatelist (&newBone->childbase, &oldBone->childbase);
+       BLI_duplicatelist(&newBone->childbase, &oldBone->childbase);
        
        /*      For each child in the list, update it's children*/
        newChildBone=newBone->childbase.first;
        
        /*      For each child in the list, update it's children*/
        newChildBone=newBone->childbase.first;
@@ -195,7 +204,7 @@ bArmature *copy_armature(bArmature *arm)
        Bone            *oldBone, *newBone;
        
        newArm= copy_libblock (arm);
        Bone            *oldBone, *newBone;
        
        newArm= copy_libblock (arm);
-       duplicatelist(&newArm->bonebase, &arm->bonebase);
+       BLI_duplicatelist(&newArm->bonebase, &arm->bonebase);
        
        /*      Duplicate the childrens' lists*/
        newBone=newArm->bonebase.first;
        
        /*      Duplicate the childrens' lists*/
        newBone=newArm->bonebase.first;
@@ -354,6 +363,117 @@ void bone_flip_name (char *name, int strip_number)
        sprintf (name, "%s%s%s%s", prefix, replace, suffix, number);
 }
 
        sprintf (name, "%s%s%s%s", prefix, replace, suffix, number);
 }
 
+/* Finds the best possible extension to the name on a particular axis. (For renaming, check for unique names afterwards)
+ * This assumes that bone names are at most 32 chars long!
+ *     strip_number: removes number extensions  (TODO: not used)
+ *     axis: the axis to name on
+ *     head/tail: the head/tail co-ordinate of the bone on the specified axis
+ */
+void bone_autoside_name (char *name, int strip_number, short axis, float head, float tail)
+{
+       unsigned int len;
+       char    basename[32]={""};
+       char    extension[5]={""};
+
+       len= strlen(name);
+       if (len == 0) return;
+       strcpy(basename, name);
+       
+       /* Figure out extension to append: 
+        *      - The extension to append is based upon the axis that we are working on.
+        *      - If head happens to be on 0, then we must consider the tail position as well to decide
+        *        which side the bone is on
+        *              -> If tail is 0, then it's bone is considered to be on axis, so no extension should be added
+        *              -> Otherwise, extension is added from perspective of object based on which side tail goes to
+        *      - If head is non-zero, extension is added from perspective of object based on side head is on
+        */
+       if (axis == 2) {
+               /* z-axis - vertical (top/bottom) */
+               if (IS_EQ(head, 0)) {
+                       if (tail < 0)
+                               strcpy(extension, "Bot");
+                       else if (tail > 0)
+                               strcpy(extension, "Top");
+               }
+               else {
+                       if (head < 0)
+                               strcpy(extension, "Bot");
+                       else
+                               strcpy(extension, "Top");
+               }
+       }
+       else if (axis == 1) {
+               /* y-axis - depth (front/back) */
+               if (IS_EQ(head, 0)) {
+                       if (tail < 0)
+                               strcpy(extension, "Fr");
+                       else if (tail > 0)
+                               strcpy(extension, "Bk");
+               }
+               else {
+                       if (head < 0)
+                               strcpy(extension, "Fr");
+                       else
+                               strcpy(extension, "Bk");
+               }
+       }
+       else {
+               /* x-axis - horizontal (left/right) */
+               if (IS_EQ(head, 0)) {
+                       if (tail < 0)
+                               strcpy(extension, "R");
+                       else if (tail > 0)
+                               strcpy(extension, "L");
+               }
+               else {
+                       if (head < 0)
+                               strcpy(extension, "R");
+                       else if (head > 0)
+                               strcpy(extension, "L");
+               }
+       }
+
+       /* Simple name truncation 
+        *      - truncate if there is an extension and it wouldn't be able to fit
+        *      - otherwise, just append to end
+        */
+       if (extension[0]) {
+               int change = 1;
+               
+               while (change) { /* remove extensions */
+                       change = 0;
+                       if (len > 2 && basename[len-2]=='.') {
+                               if (basename[len-1]=='L' || basename[len-1] == 'R' ) { /* L R */
+                                       basename[len-2] = '\0';
+                                       len-=2;
+                                       change= 1;
+                               }
+                       } else if (len > 3 && basename[len-3]=='.') {
+                               if (    (basename[len-2]=='F' && basename[len-1] == 'r') ||     /* Fr */
+                                               (basename[len-2]=='B' && basename[len-1] == 'k')        /* Bk */
+                               ) {
+                                       basename[len-3] = '\0';
+                                       len-=3;
+                                       change= 1;
+                               }
+                       } else if (len > 4 && basename[len-4]=='.') {
+                               if (    (basename[len-3]=='T' && basename[len-2]=='o' && basename[len-1] == 'p') ||     /* Top */
+                                               (basename[len-3]=='B' && basename[len-2]=='o' && basename[len-1] == 't')        /* Bot */
+                               ) {
+                                       basename[len-4] = '\0';
+                                       len-=4;
+                                       change= 1;
+                               }
+                       }
+               }
+               
+               if ((32 - len) < strlen(extension) + 1) { /* add 1 for the '.' */
+                       strncpy(name, basename, len-strlen(extension));
+               }
+       }
+
+       sprintf(name, "%s.%s", basename, extension);
+}
 
 /* ************* B-Bone support ******************* */
 
 
 /* ************* B-Bone support ******************* */
 
@@ -411,12 +531,31 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan, int rest)
        Mat4 *result_array= (rest)? bbone_rest_array: bbone_array;
        bPoseChannel *next, *prev;
        Bone *bone= pchan->bone;
        Mat4 *result_array= (rest)? bbone_rest_array: bbone_array;
        bPoseChannel *next, *prev;
        Bone *bone= pchan->bone;
-       float h1[3], h2[3], length, hlength1, hlength2, roll1, roll2;
-       float mat3[3][3], imat[4][4];
+       float h1[3], h2[3], scale[3], length, hlength1, hlength2, roll1=0.0f, roll2;
+       float mat3[3][3], imat[4][4], posemat[4][4], scalemat[4][4], iscalemat[4][4];
        float data[MAX_BBONE_SUBDIV+1][4], *fp;
        float data[MAX_BBONE_SUBDIV+1][4], *fp;
-       int a;
-       
+       int a, doscale= 0;
+
        length= bone->length;
        length= bone->length;
+
+       if(!rest) {
+               /* check if we need to take non-uniform bone scaling into account */
+               scale[0]= VecLength(pchan->pose_mat[0]);
+               scale[1]= VecLength(pchan->pose_mat[1]);
+               scale[2]= VecLength(pchan->pose_mat[2]);
+
+               if(fabs(scale[0] - scale[1]) > 1e-6f || fabs(scale[1] - scale[2]) > 1e-6f) {
+                       Mat4One(scalemat);
+                       scalemat[0][0]= scale[0];
+                       scalemat[1][1]= scale[1];
+                       scalemat[2][2]= scale[2];
+                       Mat4Invert(iscalemat, scalemat);
+
+                       length *= scale[1];
+                       doscale = 1;
+               }
+       }
+       
        hlength1= bone->ease1*length*0.390464f;         // 0.5*sqrt(2)*kappa, the handle length for near-perfect circles
        hlength2= bone->ease2*length*0.390464f;
        
        hlength1= bone->ease1*length*0.390464f;         // 0.5*sqrt(2)*kappa, the handle length for near-perfect circles
        hlength2= bone->ease2*length*0.390464f;
        
@@ -432,8 +571,14 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan, int rest)
                first point = (0,0,0)
                last point =  (0, length, 0) */
        
                first point = (0,0,0)
                last point =  (0, length, 0) */
        
-       if(rest)
+       if(rest) {
                Mat4Invert(imat, pchan->bone->arm_mat);
                Mat4Invert(imat, pchan->bone->arm_mat);
+       }
+       else if(doscale) {
+               Mat4CpyMat4(posemat, pchan->pose_mat);
+               Mat4Ortho(posemat);
+               Mat4Invert(imat, posemat);
+       }
        else
                Mat4Invert(imat, pchan->pose_mat);
        
        else
                Mat4Invert(imat, pchan->pose_mat);
        
@@ -469,7 +614,7 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan, int rest)
                        Mat3Inv(imat3, mat3);
                        Mat3MulMat3(mat3, result, imat3);                       // the matrix transforming vec_roll to desired roll
                        
                        Mat3Inv(imat3, mat3);
                        Mat3MulMat3(mat3, result, imat3);                       // the matrix transforming vec_roll to desired roll
                        
-                       roll1= atan2(mat3[2][0], mat3[2][2]);
+                       roll1= (float)atan2(mat3[2][0], mat3[2][2]);
                }
        }
        else {
                }
        }
        else {
@@ -502,7 +647,7 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan, int rest)
                Mat3Inv(imat3, mat3);
                Mat3MulMat3(mat3, imat3, result);                       // the matrix transforming vec_roll to desired roll
                
                Mat3Inv(imat3, mat3);
                Mat3MulMat3(mat3, imat3, result);                       // the matrix transforming vec_roll to desired roll
                
-               roll2= atan2(mat3[2][0], mat3[2][2]);
+               roll2= (float)atan2(mat3[2][0], mat3[2][2]);
                
                /* and only now negate handle */
                VecMulf(h2, -hlength2);
                
                /* and only now negate handle */
                VecMulf(h2, -hlength2);
@@ -527,8 +672,15 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan, int rest)
        for(a=0, fp= data[0]; a<bone->segments; a++, fp+=4) {
                VecSubf(h1, fp+4, fp);
                vec_roll_to_mat3(h1, fp[3], mat3);              // fp[3] is roll
        for(a=0, fp= data[0]; a<bone->segments; a++, fp+=4) {
                VecSubf(h1, fp+4, fp);
                vec_roll_to_mat3(h1, fp[3], mat3);              // fp[3] is roll
+
                Mat4CpyMat3(result_array[a].mat, mat3);
                VECCOPY(result_array[a].mat[3], fp);
                Mat4CpyMat3(result_array[a].mat, mat3);
                VECCOPY(result_array[a].mat[3], fp);
+
+               if(doscale) {
+                       /* correct for scaling when this matrix is used in scaled space */
+                       Mat4MulSerie(result_array[a].mat, iscalemat, result_array[a].mat,
+                               scalemat, NULL, NULL, NULL, NULL, NULL);
+               }
        }
        
        return result_array;
        }
        
        return result_array;
@@ -640,7 +792,7 @@ float distfactor_to_bone (float vec[3], float b1[3], float b2[3], float rad1, fl
                
                if(l!=0.0f) {
                        rad= a/l;
                
                if(l!=0.0f) {
                        rad= a/l;
-                       rad= rad*rad2 + (1.0-rad)*rad1;
+                       rad= rad*rad2 + (1.0f-rad)*rad1;
                }
                else rad= rad1;
        }
                }
                else rad= rad1;
        }
@@ -654,8 +806,8 @@ float distfactor_to_bone (float vec[3], float b1[3], float b2[3], float rad1, fl
                if(rdist==0.0f || dist >= l) 
                        return 0.0f;
                else {
                if(rdist==0.0f || dist >= l) 
                        return 0.0f;
                else {
-                       a= sqrt(dist)-rad;
-                       return 1.0-( a*a )/( rdist*rdist );
+                       a= (float)sqrt(dist)-rad;
+                       return 1.0f-( a*a )/( rdist*rdist );
                }
        }
 }
                }
        }
 }
@@ -758,8 +910,10 @@ static void pchan_bone_deform(bPoseChannel *pchan, float weight, float *vec, Dua
 
 void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
                            float (*vertexCos)[3], float (*defMats)[3][3],
 
 void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
                            float (*vertexCos)[3], float (*defMats)[3][3],
-                                                  int numVerts, int deformflag, const char *defgrp_name)
+                                                  int numVerts, int deformflag, 
+                                                  float (*prevCos)[3], const char *defgrp_name)
 {
 {
+       bArmature *arm= armOb->data;
        bPoseChannel *pchan, **defnrToPC = NULL;
        MDeformVert *dverts = NULL;
        bDeformGroup *dg;
        bPoseChannel *pchan, **defnrToPC = NULL;
        MDeformVert *dverts = NULL;
        bDeformGroup *dg;
@@ -768,13 +922,14 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
        int use_envelope = deformflag & ARM_DEF_ENVELOPE;
        int use_quaternion = deformflag & ARM_DEF_QUATERNION;
        int bbone_rest_def = deformflag & ARM_DEF_B_BONE_REST;
        int use_envelope = deformflag & ARM_DEF_ENVELOPE;
        int use_quaternion = deformflag & ARM_DEF_QUATERNION;
        int bbone_rest_def = deformflag & ARM_DEF_B_BONE_REST;
+       int invert_vgroup= deformflag & ARM_DEF_INVERT_VGROUP;
        int numGroups = 0;              /* safety for vertexgroup index overflow */
        int i, target_totvert = 0;      /* safety for vertexgroup overflow */
        int use_dverts = 0;
        int armature_def_nr = -1;
        int totchan;
 
        int numGroups = 0;              /* safety for vertexgroup index overflow */
        int i, target_totvert = 0;      /* safety for vertexgroup overflow */
        int use_dverts = 0;
        int armature_def_nr = -1;
        int totchan;
 
-       if(armOb == G.obedit) return;
+       if(arm->edbo) return;
        
        Mat4Invert(obinv, target->obmat);
        Mat4CpyMat4(premat, target->obmat);
        
        Mat4Invert(obinv, target->obmat);
        Mat4CpyMat4(premat, target->obmat);
@@ -849,11 +1004,12 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
        for(i = 0; i < numVerts; i++) {
                MDeformVert *dvert;
                DualQuat sumdq, *dq = NULL;
        for(i = 0; i < numVerts; i++) {
                MDeformVert *dvert;
                DualQuat sumdq, *dq = NULL;
-               float *co = vertexCos[i];
+               float *co, dco[3];
                float sumvec[3], summat[3][3];
                float *vec = NULL, (*smat)[3] = NULL;
                float contrib = 0.0f;
                float sumvec[3], summat[3][3];
                float *vec = NULL, (*smat)[3] = NULL;
                float contrib = 0.0f;
-               float armature_weight = 1.0f; /* default to 1 if no overall def group */
+               float armature_weight = 1.0f;   /* default to 1 if no overall def group */
+               float prevco_weight = 1.0f;             /* weight for optional cached vertexcos */
                int       j;
 
                if(use_quaternion) {
                int       j;
 
                if(use_quaternion) {
@@ -885,11 +1041,22 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
                                        break;
                                }
                        }
                                        break;
                                }
                        }
+                       /* hackish: the blending factor can be used for blending with prevCos too */
+                       if(prevCos) {
+                               if(invert_vgroup)
+                                       prevco_weight= 1.0f-armature_weight;
+                               else
+                                       prevco_weight= armature_weight;
+                               armature_weight= 1.0f;
+                       }
                }
 
                /* check if there's any  point in calculating for this vert */
                if(armature_weight == 0.0f) continue;
                
                }
 
                /* check if there's any  point in calculating for this vert */
                if(armature_weight == 0.0f) continue;
                
+               /* get the coord we work on */
+               co= prevCos?prevCos[i]:vertexCos[i];
+               
                /* Apply the object's matrix */
                Mat4MulVecfl(premat, co);
                
                /* Apply the object's matrix */
                Mat4MulVecfl(premat, co);
                
@@ -937,8 +1104,18 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
                /* actually should be EPSILON? weight values and contrib can be like 10e-39 small */
                if(contrib > 0.0001f) {
                        if(use_quaternion) {
                /* actually should be EPSILON? weight values and contrib can be like 10e-39 small */
                if(contrib > 0.0001f) {
                        if(use_quaternion) {
-                               DQuatNormalize(dq, contrib, armature_weight);
-                               DQuatMulVecfl(dq, co, (defMats)? summat: NULL);
+                               DQuatNormalize(dq, contrib);
+
+                               if(armature_weight != 1.0f) {
+                                       VECCOPY(dco, co);
+                                       DQuatMulVecfl(dq, dco, (defMats)? summat: NULL);
+                                       VecSubf(dco, dco, co);
+                                       VecMulf(dco, armature_weight);
+                                       VecAddf(co, co, dco);
+                               }
+                               else
+                                       DQuatMulVecfl(dq, co, (defMats)? summat: NULL);
+
                                smat = summat;
                        }
                        else {
                                smat = summat;
                        }
                        else {
@@ -963,6 +1140,15 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm,
                
                /* always, check above code */
                Mat4MulVecfl(postmat, co);
                
                /* always, check above code */
                Mat4MulVecfl(postmat, co);
+               
+               
+               /* interpolate with previous modifier position using weight group */
+               if(prevCos) {
+                       float mw= 1.0f - prevco_weight;
+                       vertexCos[i][0]= prevco_weight*vertexCos[i][0] + mw*co[0];
+                       vertexCos[i][1]= prevco_weight*vertexCos[i][1] + mw*co[1];
+                       vertexCos[i][2]= prevco_weight*vertexCos[i][2] + mw*co[2];
+               }
        }
 
        if(dualquats) MEM_freeN(dualquats);
        }
 
        if(dualquats) MEM_freeN(dualquats);
@@ -1038,7 +1224,10 @@ void armature_mat_pose_to_bone(bPoseChannel *pchan, float inmat[][4], float outm
        if (pchan==NULL) return;
        
        /* get the inverse matrix of the pchan's transforms */
        if (pchan==NULL) return;
        
        /* get the inverse matrix of the pchan's transforms */
-       LocQuatSizeToMat4(pc_trans, pchan->loc, pchan->quat, pchan->size);
+       if (pchan->rotmode)
+               LocEulSizeToMat4(pc_trans, pchan->loc, pchan->eul, pchan->size);
+       else
+               LocQuatSizeToMat4(pc_trans, pchan->loc, pchan->quat, pchan->size);
        Mat4Invert(inv_trans, pc_trans);
        
        /* Remove the pchan's transforms from it's pose_mat.
        Mat4Invert(inv_trans, pc_trans);
        
        /* Remove the pchan's transforms from it's pose_mat.
@@ -1120,7 +1309,7 @@ void mat3_to_vec_roll(float mat[][3], float *vec, float *roll)
         Mat3Inv(vecmatinv, vecmat);
         Mat3MulMat3(rollmat, vecmatinv, mat);
 
         Mat3Inv(vecmatinv, vecmat);
         Mat3MulMat3(rollmat, vecmatinv, mat);
 
-        *roll= atan2(rollmat[2][0], rollmat[2][2]);
+        *roll= (float)atan2(rollmat[2][0], rollmat[2][2]);
     }
 }
 
     }
 }
 
@@ -1152,7 +1341,7 @@ void vec_roll_to_mat3(float *vec, float roll, float mat[][3])
                float updown;
                
                /* point same direction, or opposite? */
                float updown;
                
                /* point same direction, or opposite? */
-               updown = ( Inpf (target,nor) > 0 ) ? 1.0 : -1.0;
+               updown = ( Inpf (target,nor) > 0 ) ? 1.0f : -1.0f;
                
                /* I think this should work ... */
                bMatrix[0][0]=updown; bMatrix[0][1]=0.0;    bMatrix[0][2]=0.0;
                
                /* I think this should work ... */
                bMatrix[0][0]=updown; bMatrix[0][1]=0.0;    bMatrix[0][2]=0.0;
@@ -1239,20 +1428,28 @@ static void pose_proxy_synchronize(Object *ob, Object *from, int layer_protected
        bPose *pose= ob->pose, *frompose= from->pose;
        bPoseChannel *pchan, *pchanp, pchanw;
        bConstraint *con;
        bPose *pose= ob->pose, *frompose= from->pose;
        bPoseChannel *pchan, *pchanp, pchanw;
        bConstraint *con;
-       char *str;
        
        
-       if(frompose==NULL) return;
+       if (frompose==NULL) return;
        
        /* exception, armature local layer should be proxied too */
        
        /* exception, armature local layer should be proxied too */
-       if(pose->proxy_layer)
+       if (pose->proxy_layer)
                ((bArmature *)ob->data)->layer= pose->proxy_layer;
        
        /* clear all transformation values from library */
        rest_pose(frompose);
        
                ((bArmature *)ob->data)->layer= pose->proxy_layer;
        
        /* clear all transformation values from library */
        rest_pose(frompose);
        
-       pchan= pose->chanbase.first;
-       for(; pchan; pchan= pchan->next) {
-               if(pchan->bone->layer & layer_protected) {
+       /* copy over all of the proxy's bone groups */
+               /* TODO for later - implement 'local' bone groups as for constraints
+                *      Note: this isn't trivial, as bones reference groups by index not by pointer, 
+                *               so syncing things correctly needs careful attention
+                */
+       BLI_freelistN(&pose->agroups);
+       BLI_duplicatelist(&pose->agroups, &frompose->agroups);
+       pose->active_group= frompose->active_group;
+       
+       for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
+               if (pchan->bone->layer & layer_protected) {
+                       ListBase proxylocal_constraints = {NULL, NULL};
                        pchanp= get_pose_channel(frompose, pchan->name);
                        
                        /* copy posechannel to temp, but restore important pointers */
                        pchanp= get_pose_channel(frompose, pchan->name);
                        
                        /* copy posechannel to temp, but restore important pointers */
@@ -1263,16 +1460,36 @@ static void pose_proxy_synchronize(Object *ob, Object *from, int layer_protected
                        pchanw.child= pchan->child;
                        pchanw.path= NULL;
                        
                        pchanw.child= pchan->child;
                        pchanw.path= NULL;
                        
-                       /* constraints, set target ob pointer to own object */
+                       /* constraints - proxy constraints are flushed... local ones are added after 
+                        *      1. extract constraints not from proxy (CONSTRAINT_PROXY_LOCAL) from pchan's constraints
+                        *      2. copy proxy-pchan's constraints on-to new
+                        *      3. add extracted local constraints back on top 
+                        */
+                       extract_proxylocal_constraints(&proxylocal_constraints, &pchan->constraints);
                        copy_constraints(&pchanw.constraints, &pchanp->constraints);
                        copy_constraints(&pchanw.constraints, &pchanp->constraints);
-
-                       for(con= pchanw.constraints.first; con; con= con->next) {
-                               if(from==get_constraint_target(con, &str))
-                                       set_constraint_target(con, ob, NULL);
+                       addlisttolist(&pchanw.constraints, &proxylocal_constraints);
+                       
+                       /* constraints - set target ob pointer to own object */
+                       for (con= pchanw.constraints.first; con; con= con->next) {
+                               bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
+                               ListBase targets = {NULL, NULL};
+                               bConstraintTarget *ct;
+                               
+                               if (cti && cti->get_constraint_targets) {
+                                       cti->get_constraint_targets(con, &targets);
+                                       
+                                       for (ct= targets.first; ct; ct= ct->next) {
+                                               if (ct->tar == from)
+                                                       ct->tar = ob;
+                                       }
+                                       
+                                       if (cti->flush_constraint_targets)
+                                               cti->flush_constraint_targets(con, &targets, 0);
+                               }
                        }
                        
                        /* free stuff from current channel */
                        }
                        
                        /* free stuff from current channel */
-                       if(pchan->path) MEM_freeN(pchan->path);
+                       if (pchan->path) MEM_freeN(pchan->path);
                        free_constraints(&pchan->constraints);
                        
                        /* the final copy */
                        free_constraints(&pchan->constraints);
                        
                        /* the final copy */
@@ -1362,26 +1579,21 @@ static void initialize_posetree(struct Object *ob, bPoseChannel *pchan_tip)
        PoseTree *tree;
        PoseTarget *target;
        bConstraint *con;
        PoseTree *tree;
        PoseTarget *target;
        bConstraint *con;
-       bKinematicConstraint *data;
+       bKinematicConstraint *data= NULL;
        int a, segcount= 0, size, newsize, *oldparent, parent;
        
        /* find IK constraint, and validate it */
        for(con= pchan_tip->constraints.first; con; con= con->next) {
        int a, segcount= 0, size, newsize, *oldparent, parent;
        
        /* find IK constraint, and validate it */
        for(con= pchan_tip->constraints.first; con; con= con->next) {
-               if(con->type==CONSTRAINT_TYPE_KINEMATIC) break;
+               if(con->type==CONSTRAINT_TYPE_KINEMATIC) {
+                       data=(bKinematicConstraint*)con->data;
+                       if (data->flag & CONSTRAINT_IK_AUTO) break;
+                       if (data->tar==NULL) continue;
+                       if (data->tar->type==OB_ARMATURE && data->subtarget[0]==0) continue;
+                       if ((con->flag & CONSTRAINT_DISABLE)==0 && (con->enforce!=0.0)) break;
+               }
        }
        if(con==NULL) return;
        
        }
        if(con==NULL) return;
        
-       data=(bKinematicConstraint*)con->data;
-       
-       /* two types of targets */
-       if(data->flag & CONSTRAINT_IK_AUTO);
-       else {
-               if(con->flag & CONSTRAINT_DISABLE) return;      /* checked in editconstraint.c */
-               if(con->enforce == 0.0f) return;
-               if(data->tar==NULL) return;
-               if(data->tar->type==OB_ARMATURE && data->subtarget[0]==0) return;
-       }
-       
        /* exclude tip from chain? */
        if(!(data->flag & CONSTRAINT_IK_TIP))
                pchan_tip= pchan_tip->parent;
        /* exclude tip from chain? */
        if(!(data->flag & CONSTRAINT_IK_TIP))
                pchan_tip= pchan_tip->parent;
@@ -1484,7 +1696,7 @@ static void initialize_posetree(struct Object *ob, bPoseChannel *pchan_tip)
 were executed & assigned. Now as last we do an IK pass */
 static void execute_posetree(Object *ob, PoseTree *tree)
 {
 were executed & assigned. Now as last we do an IK pass */
 static void execute_posetree(Object *ob, PoseTree *tree)
 {
-       float R_parmat[3][3];
+       float R_parmat[3][3], identity[3][3];
        float iR_parmat[3][3];
        float R_bonemat[3][3];
        float goalrot[3][3], goalpos[3];
        float iR_parmat[3][3];
        float R_bonemat[3][3];
        float goalrot[3][3], goalpos[3];
@@ -1493,57 +1705,58 @@ static void execute_posetree(Object *ob, PoseTree *tree)
        float irest_basis[3][3], full_basis[3][3];
        float end_pose[4][4], world_pose[4][4];
        float length, basis[3][3], rest_basis[3][3], start[3], *ikstretch=NULL;
        float irest_basis[3][3], full_basis[3][3];
        float end_pose[4][4], world_pose[4][4];
        float length, basis[3][3], rest_basis[3][3], start[3], *ikstretch=NULL;
-       int a, flag, hasstretch=0;
+       float resultinf=0.0f;
+       int a, flag, hasstretch=0, resultblend=0;
        bPoseChannel *pchan;
        IK_Segment *seg, *parent, **iktree, *iktarget;
        IK_Solver *solver;
        PoseTarget *target;
        bPoseChannel *pchan;
        IK_Segment *seg, *parent, **iktree, *iktarget;
        IK_Solver *solver;
        PoseTarget *target;
-       bKinematicConstraint *data;
+       bKinematicConstraint *data, *poleangledata=NULL;
        Bone *bone;
 
        if (tree->totchannel == 0)
                return;
        Bone *bone;
 
        if (tree->totchannel == 0)
                return;
-
+       
        iktree= MEM_mallocN(sizeof(void*)*tree->totchannel, "ik tree");
 
        for(a=0; a<tree->totchannel; a++) {
                pchan= tree->pchan[a];
                bone= pchan->bone;
        iktree= MEM_mallocN(sizeof(void*)*tree->totchannel, "ik tree");
 
        for(a=0; a<tree->totchannel; a++) {
                pchan= tree->pchan[a];
                bone= pchan->bone;
-
+               
                /* set DoF flag */
                flag= 0;
                /* set DoF flag */
                flag= 0;
-               if((pchan->ikflag & BONE_IK_NO_XDOF) == 0)
+               if(!(pchan->ikflag & BONE_IK_NO_XDOF) && !(pchan->ikflag & BONE_IK_NO_XDOF_TEMP))
                        flag |= IK_XDOF;
                        flag |= IK_XDOF;
-               if((pchan->ikflag & BONE_IK_NO_YDOF) == 0)
+               if(!(pchan->ikflag & BONE_IK_NO_YDOF) && !(pchan->ikflag & BONE_IK_NO_YDOF_TEMP))
                        flag |= IK_YDOF;
                        flag |= IK_YDOF;
-               if((pchan->ikflag & BONE_IK_NO_ZDOF) == 0)
+               if(!(pchan->ikflag & BONE_IK_NO_ZDOF) && !(pchan->ikflag & BONE_IK_NO_ZDOF_TEMP))
                        flag |= IK_ZDOF;
                        flag |= IK_ZDOF;
-
+               
                if(tree->stretch && (pchan->ikstretch > 0.0)) {
                        flag |= IK_TRANS_YDOF;
                        hasstretch = 1;
                }
                if(tree->stretch && (pchan->ikstretch > 0.0)) {
                        flag |= IK_TRANS_YDOF;
                        hasstretch = 1;
                }
-
+               
                seg= iktree[a]= IK_CreateSegment(flag);
                seg= iktree[a]= IK_CreateSegment(flag);
-
+               
                /* find parent */
                if(a == 0)
                        parent= NULL;
                else
                        parent= iktree[tree->parent[a]];
                /* find parent */
                if(a == 0)
                        parent= NULL;
                else
                        parent= iktree[tree->parent[a]];
-
+                       
                IK_SetParent(seg, parent);
                IK_SetParent(seg, parent);
-       
+                       
                /* get the matrix that transforms from prevbone into this bone */
                Mat3CpyMat4(R_bonemat, pchan->pose_mat);
                /* get the matrix that transforms from prevbone into this bone */
                Mat3CpyMat4(R_bonemat, pchan->pose_mat);
-
+               
                /* gather transformations for this IK segment */
                /* gather transformations for this IK segment */
-
+               
                if (pchan->parent)
                        Mat3CpyMat4(R_parmat, pchan->parent->pose_mat);
                else
                        Mat3One(R_parmat);
                if (pchan->parent)
                        Mat3CpyMat4(R_parmat, pchan->parent->pose_mat);
                else
                        Mat3One(R_parmat);
-
+               
                /* bone offset */
                if (pchan->parent && (a > 0))
                        VecSubf(start, pchan->pose_head, pchan->parent->pose_tail);
                /* bone offset */
                if (pchan->parent && (a > 0))
                        VecSubf(start, pchan->pose_head, pchan->parent->pose_tail);
@@ -1553,41 +1766,41 @@ static void execute_posetree(Object *ob, PoseTree *tree)
                
                /* change length based on bone size */
                length= bone->length*VecLength(R_bonemat[1]);
                
                /* change length based on bone size */
                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 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(iR_parmat, R_parmat);
                Mat3MulMat3(full_basis, iR_parmat, R_bonemat);
                Mat3MulMat3(basis, irest_basis, full_basis);
                /* compute basis with rest_basis removed */
                Mat3Inv(iR_parmat, R_parmat);
                Mat3MulMat3(full_basis, iR_parmat, R_bonemat);
                Mat3MulMat3(basis, irest_basis, full_basis);
-
+               
                /* basis must be pure rotation */
                Mat3Ortho(basis);
                /* basis must be pure rotation */
                Mat3Ortho(basis);
-
+               
                /* transform offset into local bone space */
                Mat3Ortho(iR_parmat);
                Mat3MulVecfl(iR_parmat, start);
                /* transform offset into local bone space */
                Mat3Ortho(iR_parmat);
                Mat3MulVecfl(iR_parmat, start);
-
+               
                IK_SetTransform(seg, start, rest_basis, basis, length);
                IK_SetTransform(seg, start, rest_basis, basis, length);
-
+               
                if (pchan->ikflag & BONE_IK_XLIMIT)
                        IK_SetLimit(seg, IK_X, pchan->limitmin[0], pchan->limitmax[0]);
                if (pchan->ikflag & BONE_IK_YLIMIT)
                        IK_SetLimit(seg, IK_Y, pchan->limitmin[1], pchan->limitmax[1]);
                if (pchan->ikflag & BONE_IK_ZLIMIT)
                        IK_SetLimit(seg, IK_Z, pchan->limitmin[2], pchan->limitmax[2]);
                if (pchan->ikflag & BONE_IK_XLIMIT)
                        IK_SetLimit(seg, IK_X, pchan->limitmin[0], pchan->limitmax[0]);
                if (pchan->ikflag & BONE_IK_YLIMIT)
                        IK_SetLimit(seg, IK_Y, pchan->limitmin[1], pchan->limitmax[1]);
                if (pchan->ikflag & BONE_IK_ZLIMIT)
                        IK_SetLimit(seg, IK_Z, pchan->limitmin[2], pchan->limitmax[2]);
-
+               
                IK_SetStiffness(seg, IK_X, pchan->stiffness[0]);
                IK_SetStiffness(seg, IK_Y, pchan->stiffness[1]);
                IK_SetStiffness(seg, IK_Z, pchan->stiffness[2]);
                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(tree->stretch && (pchan->ikstretch > 0.0)) {
+               
+               if(tree->stretch && (pchan->ikstretch > 0.0f)) {
                        float ikstretch = pchan->ikstretch*pchan->ikstretch;
                        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);
+                       IK_SetStiffness(seg, IK_TRANS_Y, MIN2(1.0f-ikstretch, 0.99f));
+                       IK_SetLimit(seg, IK_TRANS_Y, 0.001f, 1e10);
                }
        }
 
                }
        }
 
@@ -1609,89 +1822,135 @@ static void execute_posetree(Object *ob, PoseTree *tree)
        Mat4MulMat4 (imat, rootmat, ob->obmat);
        Mat4Invert (goalinv, imat);
        
        Mat4MulMat4 (imat, rootmat, ob->obmat);
        Mat4Invert (goalinv, imat);
        
-       for(target=tree->targets.first; target; target=target->next) {
+       for (target=tree->targets.first; target; target=target->next) {
+               float polepos[3];
+               int poleconstrain= 0;
+               
                data= (bKinematicConstraint*)target->con->data;
                data= (bKinematicConstraint*)target->con->data;
-
-               /* 1.0=ctime, we pass on object for auto-ik */
-               get_constraint_target_matrix(target->con, TARGET_BONE, ob, rootmat, 1.0);
-
+               
+               /* 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, 0, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0);
+               
                /* and set and transform goal */
                Mat4MulMat4(goal, rootmat, goalinv);
                /* and set and transform goal */
                Mat4MulMat4(goal, rootmat, goalinv);
-
+               
                VECCOPY(goalpos, goal[3]);
                Mat3CpyMat4(goalrot, goal);
                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;
+
+                               /* for pole targets, we blend the result of the ik solver
+                                * instead of the target position, otherwise we can't get
+                                * a smooth transition */
+                               resultblend= 1;
+                               resultinf= target->con->enforce;
+                               
+                               if(data->flag & CONSTRAINT_IK_GETANGLE) {
+                                       poleangledata= data;
+                                       data->flag &= ~CONSTRAINT_IK_GETANGLE;
+                               }
+                       }
+               }
 
                /* do we need blending? */
 
                /* do we need blending? */
-               if(target->con->enforce!=1.0) {
+               if (!resultblend && target->con->enforce!=1.0f) {
                        float q1[4], q2[4], q[4];
                        float fac= target->con->enforce;
                        float q1[4], q2[4], q[4];
                        float fac= target->con->enforce;
-                       float mfac= 1.0-fac;
+                       float mfac= 1.0f-fac;
                        
                        pchan= tree->pchan[target->tip];
                        
                        pchan= tree->pchan[target->tip];
-
+                       
                        /* end effector in world space */
                        Mat4CpyMat4(end_pose, pchan->pose_mat);
                        VECCOPY(end_pose[3], pchan->pose_tail);
                        Mat4MulSerie(world_pose, goalinv, ob->obmat, end_pose, 0, 0, 0, 0, 0);
                        /* end effector in world space */
                        Mat4CpyMat4(end_pose, pchan->pose_mat);
                        VECCOPY(end_pose[3], pchan->pose_tail);
                        Mat4MulSerie(world_pose, goalinv, ob->obmat, end_pose, 0, 0, 0, 0, 0);
-
+                       
                        /* blend position */
                        goalpos[0]= fac*goalpos[0] + mfac*world_pose[3][0];
                        goalpos[1]= fac*goalpos[1] + mfac*world_pose[3][1];
                        goalpos[2]= fac*goalpos[2] + mfac*world_pose[3][2];
                        /* blend position */
                        goalpos[0]= fac*goalpos[0] + mfac*world_pose[3][0];
                        goalpos[1]= fac*goalpos[1] + mfac*world_pose[3][1];
                        goalpos[2]= fac*goalpos[2] + mfac*world_pose[3][2];
-
+                       
                        /* blend rotation */
                        Mat3ToQuat(goalrot, q1);
                        Mat4ToQuat(world_pose, q2);
                        QuatInterpol(q, q1, q2, mfac);
                        QuatToMat3(q, goalrot);
                }
                        /* blend rotation */
                        Mat3ToQuat(goalrot, q1);
                        Mat4ToQuat(world_pose, q2);
                        QuatInterpol(q, q1, q2, mfac);
                        QuatToMat3(q, goalrot);
                }
-
+               
                iktarget= iktree[target->tip];
                iktarget= iktree[target->tip];
-
-               if(data->weight != 0.0)
+               
+               if(data->weight != 0.0f) {
+                       if(poleconstrain)
+                               IK_SolverSetPoleVectorConstraint(solver, iktarget, goalpos,
+                                       polepos, data->poleangle*(float)M_PI/180.0f, (poleangledata == data));
                        IK_SolverAddGoal(solver, iktarget, goalpos, data->weight);
                        IK_SolverAddGoal(solver, iktarget, goalpos, data->weight);
-               if((data->flag & CONSTRAINT_IK_ROT) && (data->orientweight != 0.0) && (data->flag & CONSTRAINT_IK_AUTO)==0)
-                       IK_SolverAddGoalOrientation(solver, iktarget, goalrot, data->orientweight);
+               }
+               if((data->flag & CONSTRAINT_IK_ROT) && (data->orientweight != 0.0f))
+                       if((data->flag & CONSTRAINT_IK_AUTO)==0)
+                               IK_SolverAddGoalOrientation(solver, iktarget, goalrot,
+                                       data->orientweight);
        }
 
        /* solve */
        IK_Solve(solver, 0.0f, tree->iterations);
        }
 
        /* solve */
        IK_Solve(solver, 0.0f, tree->iterations);
+
+       if(poleangledata)
+               poleangledata->poleangle= IK_SolverGetPoleAngle(solver)*180.0f/(float)M_PI;
+
        IK_FreeSolver(solver);
 
        /* gather basis changes */
        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");
        IK_FreeSolver(solver);
 
        /* gather basis changes */
        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]);
        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;
                if(hasstretch) {
                        /* have to compensate for scaling received from parent */
                        float parentstretch, stretch;
-
+                       
                        pchan= tree->pchan[a];
                        pchan= tree->pchan[a];
-                       parentstretch= (tree->parent[a] >= 0)? ikstretch[tree->parent[a]]: 1.0;
-
-                       if(tree->stretch && (pchan->ikstretch > 0.0)) {
+                       parentstretch= (tree->parent[a] >= 0)? ikstretch[tree->parent[a]]: 1.0f;
+                       
+                       if(tree->stretch && (pchan->ikstretch > 0.0f)) {
                                float trans[3], length;
                                float trans[3], length;
-
+                               
                                IK_GetTranslationChange(iktree[a], trans);
                                length= pchan->bone->length*VecLength(pchan->pose_mat[1]);
                                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;
+                               
+                               ikstretch[a]= (length == 0.0f)? 1.0f: (trans[1]+length)/length;
                        }
                        else
                        }
                        else
-                               ikstretch[a] = 1.0;
-
-                       stretch= (parentstretch == 0.0)? 1.0: ikstretch[a]/parentstretch;
-
+                               ikstretch[a] = 1.0f;
+                       
+                       stretch= (parentstretch == 0.0f)? 1.0f: ikstretch[a]/parentstretch;
+                       
                        VecMulf(tree->basis_change[a][0], stretch);
                        VecMulf(tree->basis_change[a][1], stretch);
                        VecMulf(tree->basis_change[a][2], stretch);
                        VecMulf(tree->basis_change[a][0], stretch);
                        VecMulf(tree->basis_change[a][1], stretch);
                        VecMulf(tree->basis_change[a][2], stretch);
-
                }
 
                }
 
+               if(resultblend && resultinf!=1.0f) {
+                       Mat3One(identity);
+                       Mat3BlendMat3(tree->basis_change[a], identity,
+                               tree->basis_change[a], resultinf);
+               }
+               
                IK_FreeSegment(iktree[a]);
        }
        
                IK_FreeSegment(iktree[a]);
        }
        
@@ -1719,22 +1978,29 @@ void chan_calc_mat(bPoseChannel *chan)
        float rmat[3][3];
        float tmat[3][3];
        
        float rmat[3][3];
        float tmat[3][3];
        
+       /* get scaling matrix */
        SizeToMat3(chan->size, smat);
        
        SizeToMat3(chan->size, smat);
        
-       NormalQuat(chan->quat);
-
-       QuatToMat3(chan->quat, rmat);
+       /* rotations may either be quats or eulers (no rotation modes for now...) */
+       if (chan->rotmode) {
+               /* euler rotations (will cause gimble lock... no rotation order to solve that yet) */
+               EulToMat3(chan->eul, rmat);
+       }
+       else {
+               /* quats are normalised before use to eliminate scaling issues */
+               NormalQuat(chan->quat);
+               QuatToMat3(chan->quat, rmat);
+       }
        
        
+       /* calculate matrix of bone (as 3x3 matrix, but then copy the 4x4) */
        Mat3MulMat3(tmat, rmat, smat);
        Mat3MulMat3(tmat, rmat, smat);
-       
        Mat4CpyMat3(chan->chan_mat, tmat);
        
        /* prevent action channels breaking chains */
        /* need to check for bone here, CONSTRAINT_TYPE_ACTION uses this call */
        Mat4CpyMat3(chan->chan_mat, tmat);
        
        /* prevent action channels breaking chains */
        /* need to check for bone here, CONSTRAINT_TYPE_ACTION uses this call */
-       if (chan->bone==NULL || !(chan->bone->flag & BONE_CONNECTED)) {
+       if ((chan->bone==NULL) || !(chan->bone->flag & BONE_CONNECTED)) {
                VECCOPY(chan->chan_mat[3], chan->loc);
        }
                VECCOPY(chan->chan_mat[3], chan->loc);
        }
-
 }
 
 /* transform from bone(b) to bone(b+1), store in chan_mat */
 }
 
 /* transform from bone(b) to bone(b+1), store in chan_mat */
@@ -1773,15 +2039,45 @@ static void where_is_ik_bone(bPoseChannel *pchan, float ik_mat[][3])   // nr = t
 }
 
 /* NLA strip modifiers */
 }
 
 /* NLA strip modifiers */
-static void do_strip_modifiers(Object *armob, Bone *bone, bPoseChannel *pchan)
+static void do_strip_modifiers(Scene *scene, Object *armob, Bone *bone, bPoseChannel *pchan)
 {
        bActionModifier *amod;
 {
        bActionModifier *amod;
-       bActionStrip *strip;
-       float scene_cfra= G.scene->r.cfra;
+       bActionStrip *strip, *strip2;
+       float scene_cfra= (float)scene->r.cfra;
+       int do_modif;
 
        for (strip=armob->nlastrips.first; strip; strip=strip->next) {
 
        for (strip=armob->nlastrips.first; strip; strip=strip->next) {
-               if(scene_cfra>=strip->start && scene_cfra<=strip->end) {
+               do_modif=0;
+               
+               if (scene_cfra>=strip->start && scene_cfra<=strip->end)
+                       do_modif=1;
+               
+               if ((scene_cfra > strip->end) && (strip->flag & ACTSTRIP_HOLDLASTFRAME)) {
+                       do_modif=1;
+                       
+                       /* if there are any other strips active, ignore modifiers for this strip - 
+                        * 'hold' option should only hold action modifiers if there are 
+                        * no other active strips */
+                       for (strip2=strip->next; strip2; strip2=strip2->next) {
+                               if (strip2 == strip) continue;
+                               
+                               if (scene_cfra>=strip2->start && scene_cfra<=strip2->end) {
+                                       if (!(strip2->flag & ACTSTRIP_MUTE))
+                                               do_modif=0;
+                               }
+                       }
                        
                        
+                       /* if there are any later, activated, strips with 'hold' set, they take precedence, 
+                        * so ignore modifiers for this strip */
+                       for (strip2=strip->next; strip2; strip2=strip2->next) {
+                               if (scene_cfra < strip2->start) continue;
+                               if ((strip2->flag & ACTSTRIP_HOLDLASTFRAME) && !(strip2->flag & ACTSTRIP_MUTE)) {
+                                       do_modif=0;
+                               }
+                       }
+               }
+               
+               if (do_modif) {
                        /* temporal solution to prevent 2 strips accumulating */
                        if(scene_cfra==strip->end && strip->next && strip->next->start==scene_cfra)
                                continue;
                        /* temporal solution to prevent 2 strips accumulating */
                        if(scene_cfra==strip->end && strip->next && strip->next->start==scene_cfra)
                                continue;
@@ -1796,7 +2092,7 @@ static void do_strip_modifiers(Object *armob, Bone *bone, bPoseChannel *pchan)
                                                if( strcmp(pchan->name, amod->channel)==0 ) {
                                                        float mat4[4][4], mat3[3][3];
                                                        
                                                if( strcmp(pchan->name, amod->channel)==0 ) {
                                                        float mat4[4][4], mat3[3][3];
                                                        
-                                                       curve_deform_vector(amod->ob, armob, bone->arm_mat[3], pchan->pose_mat[3], mat3, amod->no_rot_axis);
+                                                       curve_deform_vector(scene, amod->ob, armob, bone->arm_mat[3], pchan->pose_mat[3], mat3, amod->no_rot_axis);
                                                        Mat4CpyMat4(mat4, pchan->pose_mat);
                                                        Mat4MulMat34(pchan->pose_mat, mat3, mat4);
                                                        
                                                        Mat4CpyMat4(mat4, pchan->pose_mat);
                                                        Mat4MulMat34(pchan->pose_mat, mat3, mat4);
                                                        
@@ -1867,7 +2163,7 @@ static void do_strip_modifiers(Object *armob, Bone *bone, bPoseChannel *pchan)
 
 /* The main armature solver, does all constraints excluding IK */
 /* pchan is validated, as having bone and parent pointer */
 
 /* The main armature solver, does all constraints excluding IK */
 /* pchan is validated, as having bone and parent pointer */
-static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime)
+static void where_is_pose_bone(Scene *scene, Object *ob, bPoseChannel *pchan, float ctime)
 {
        Bone *bone, *parbone;
        bPoseChannel *parchan;
 {
        Bone *bone, *parbone;
        bPoseChannel *parchan;
@@ -1910,17 +2206,34 @@ static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime)
                        
                        Mat4MulSerie(pchan->pose_mat, tmat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL);
                }
                        
                        Mat4MulSerie(pchan->pose_mat, tmat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL);
                }
+               else if(bone->flag & BONE_NO_SCALE) {
+                       float orthmat[4][4], vec[3];
+                       
+                       /* get the official transform, but we only use the vector from it (optimize...) */
+                       Mat4MulSerie(pchan->pose_mat, parchan->pose_mat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL);
+                       VECCOPY(vec, pchan->pose_mat[3]);
+                       
+                       /* do this again, but with an ortho-parent matrix */
+                       Mat4CpyMat4(orthmat, parchan->pose_mat);
+                       Mat4Ortho(orthmat);
+                       Mat4MulSerie(pchan->pose_mat, orthmat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL);
+                       
+                       /* copy correct transform */
+                       VECCOPY(pchan->pose_mat[3], vec);
+               }
                else 
                        Mat4MulSerie(pchan->pose_mat, parchan->pose_mat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL);
        }
        else {
                Mat4MulMat4(pchan->pose_mat, pchan->chan_mat, bone->arm_mat);
                else 
                        Mat4MulSerie(pchan->pose_mat, parchan->pose_mat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL);
        }
        else {
                Mat4MulMat4(pchan->pose_mat, pchan->chan_mat, bone->arm_mat);
-               /* only rootbones get the cyclic offset */
-               VecAddf(pchan->pose_mat[3], pchan->pose_mat[3], ob->pose->cyclic_offset);
+               
+               /* only rootbones get the cyclic offset (unless user doesn't want that) */
+               if ((bone->flag & BONE_NO_CYCLICOFFSET) == 0)
+                       VecAddf(pchan->pose_mat[3], pchan->pose_mat[3], ob->pose->cyclic_offset);
        }
        
        /* do NLA strip modifiers - i.e. curve follow */
        }
        
        /* do NLA strip modifiers - i.e. curve follow */
-       do_strip_modifiers(ob, bone, pchan);
+       do_strip_modifiers(scene, ob, bone, pchan);
        
        /* Do constraints */
        if (pchan->constraints.first) {
        
        /* Do constraints */
        if (pchan->constraints.first) {
@@ -1932,7 +2245,7 @@ static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime)
                /* prepare PoseChannel for Constraint solving 
                 * - makes a copy of matrix, and creates temporary struct to use 
                 */
                /* prepare PoseChannel for Constraint solving 
                 * - makes a copy of matrix, and creates temporary struct to use 
                 */
-               cob= constraints_make_evalob(ob, pchan, TARGET_BONE);
+               cob= constraints_make_evalob(scene, ob, pchan, CONSTRAINT_OBTYPE_BONE);
                
                /* Solve PoseChannel's Constraints */
                solve_constraints(&pchan->constraints, cob, ctime);     // ctime doesnt alter objects
                
                /* Solve PoseChannel's Constraints */
                solve_constraints(&pchan->constraints, cob, ctime);     // ctime doesnt alter objects
@@ -1958,22 +2271,25 @@ static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime)
 
 /* This only reads anim data from channels, and writes to channels */
 /* This is the only function adding poses */
 
 /* This only reads anim data from channels, and writes to channels */
 /* This is the only function adding poses */
-void where_is_pose (Object *ob)
+void where_is_pose (Scene *scene, Object *ob)
 {
        bArmature *arm;
        Bone *bone;
        bPoseChannel *pchan;
        float imat[4][4];
 {
        bArmature *arm;
        Bone *bone;
        bPoseChannel *pchan;
        float imat[4][4];
-       float ctime= bsystem_time(ob, (float)G.scene->r.cfra, 0.0);     /* not accurate... */
+       float ctime;
        
        
-       arm = get_armature(ob);
+       if(ob->type!=OB_ARMATURE) return;
+       arm = ob->data;
        
        
-       if(arm==NULL) return;
-       if(ob->pose==NULL || (ob->pose->flag & POSE_RECALC)) 
+       if(ELEM(NULL, arm, scene)) return;
+       if((ob->pose==NULL) || (ob->pose->flag & POSE_RECALC)) 
           armature_rebuild_pose(ob, arm);
           armature_rebuild_pose(ob, arm);
+          
+       ctime= bsystem_time(scene, ob, (float)scene->r.cfra, 0.0);      /* not accurate... */
        
        
-       /* In restposition we read the data from the bones */
-       if(ob==G.obedit || (arm->flag & ARM_RESTPOS)) {
+       /* In editmode or restposition we read the data from the bones */
+       if(arm->edbo || (arm->flag & ARM_RESTPOS)) {
                
                for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
                        bone= pchan->bone;
                
                for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
                        bone= pchan->bone;
@@ -2006,7 +2322,7 @@ void where_is_pose (Object *ob)
                                        /* 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
                                        /* 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], ctime);
+                                                       where_is_pose_bone(scene, ob, tree->pchan[a], ctime);
                                        }
                                        /* 5. execute the IK solver */
                                        execute_posetree(ob, tree);
                                        }
                                        /* 5. execute the IK solver */
                                        execute_posetree(ob, tree);
@@ -2026,7 +2342,7 @@ void where_is_pose (Object *ob)
                                }
                        }
                        else if(!(pchan->flag & POSE_DONE)) {
                                }
                        }
                        else if(!(pchan->flag & POSE_DONE)) {
-                               where_is_pose_bone(ob, pchan, ctime);
+                               where_is_pose_bone(scene, ob, pchan, ctime);
                        }
                }
        }
                        }
                }
        }