Two wonderful new NLA & Armature editing features!
authorTon Roosendaal <ton@blender.org>
Tue, 31 Oct 2006 15:51:57 +0000 (15:51 +0000)
committerTon Roosendaal <ton@blender.org>
Tue, 31 Oct 2006 15:51:57 +0000 (15:51 +0000)
- FORWARD CYCLING & MATCHING

Up to no now, adding multiple actions in NLA with walkcycles required to
animate them standing still, as if walking on a conveyor belt. The stride
option then makes the object itself move forward, trying to keep the foot
stuck on the floor (with poor results!).
This option now allows to make walk cycles moving forward. By
indicating a reference Offset Bone, the NLA system will use that bone to
detect the correct offset for the Armature Pose to make it seamlessly going
forward.

Best of all, this option works as for cyclic Action Strips as well as for
individual Action Strips. Note that for individual strips, you have to set
the strip on "Hold". (Might become automatic detected later).

Here's an example edit image for NLA:
http://www.blender.org/bf/nla_match-cycle.jpg
And the animation for it:
http://download.blender.org/demo/test/2.43/0001_0150_match.avi
Blender file:
http://download.blender.org/demo/test/2.43/mancandy_matching.blend

Using this kind of cycling works pretty straightforward, and is a lot
easier to setup than Stride Bones.

To be further tested:
- Blending cycles
- matching rotation for the bones as well.

- ACTION MODIFIERS (motion deformors)

The above option was actually required for this feature. Typically walk
cycles are constructed with certain Bones to be the handles, controlling
for example the torso or feet.
An Action Modifier allows you to use a Curve Path to deform the motion of
these controlling bones. This uses the existing Curve Deformation option.
Modifiers can be added per Action Strip, each controlling a channel (bone)
by choice, and even allows to layer multiple modifiers on top of each other
(several paths deforming motion). This option is using the dependency graph,
so editing the Curve will give realtime changes in the Armature.

The previous walkcycle, controlled by two curves:
http://download.blender.org/demo/test/2.43/0001_0150_deform.avi
Blender file:
http://download.blender.org/demo/test/2.43/mancandy_actiondeform.blend

Action Modifiers can be added in the NLA Properties Panel. Per Modifier you
have to indicate the channel and a Curve Object. You can copy modifiers from
one strip to another using CTRL+C (only copies to active Object strips).

Setting up a correct Curve Path has to be carefully done:
- Use SHIFT+A "Curve Path" in top view, or ensure the path is not rotated.
- make sure the center point of the Curve Object is at the center of the
  Armature (or above)
- move the first point of the curve to the center point as well.
- check if the path starts from this first point, you can change it using
  (in Curve EditMode) the option Wkey -> "Switch Direction"
- Make sure alignment uses the correct axis; if the Armature walks into
  the negative Y direction, you have to set in Object Buttons, "Anim settings"
  Panel, the correct Track option. (Note; option will probably move to the
  Modifier later).

This is a good reason to make such paths automatic (on a command). Is on the
todo list.

Also note this:
- the Curve Path extends in beginning and ending, that's (for now) the default,
  and allows to use multiple paths. Make sure paths begin and end horizontal.
- Moving the Curve in Object Mode will change the "mapping" (as if the landscape
  a character walks over moves). Moving the Curve in Edit Mode will change the
  actual position of the deformation.
- Speed (Ipos) on paths is not supported yet, will be done.
- The Curve "Stretch" deform option doesn't work.
- Modifiers are executed *after* all actions in NLA are evaluated, there's no
  support yet for blending multiple strips with Modifiers.
- This doesn't work yet for time-mapping...

This commit is mostly for review by character animators... some details or
working methods might change.
This feature can also be used for other modifiers, such as noise (Perlin) or
the mythical "Oomph" (frequency control) and of course Python.

Special thanks to Bassam & Matt for research & design help. Have fun!

14 files changed:
source/blender/blenkernel/BKE_lattice.h
source/blender/blenkernel/intern/action.c
source/blender/blenkernel/intern/armature.c
source/blender/blenkernel/intern/depsgraph.c
source/blender/blenkernel/intern/lattice.c
source/blender/blenkernel/intern/nla.c
source/blender/blenkernel/intern/object.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/include/BIF_editnla.h
source/blender/makesdna/DNA_action_types.h
source/blender/makesdna/DNA_nla_types.h
source/blender/src/drawnla.c
source/blender/src/editnla.c

index 6e738bd53fbcad49633ee254585f206255402c38..bcf65fcd54075c27873a12e5a8c253d2c0c23e98 100644 (file)
@@ -53,7 +53,12 @@ void calc_latt_deform(float *co, float weight);
 void end_latt_deform(void);
 int object_deform_mball(struct Object *ob);
 void outside_lattice(struct Lattice *lt);
-void curve_deform_verts(struct Object *cuOb, struct Object *target, struct DerivedMesh *dm, float (*vertexCos)[3], int numVerts, char *vgroup, short defaxis);
+void curve_deform_verts(struct Object *cuOb, struct Object *target, 
+                                               struct DerivedMesh *dm, float (*vertexCos)[3], 
+                                               int numVerts, char *vgroup, short defaxis);
+void curve_deform_vector(struct Object *cuOb, struct Object *target, 
+                                                float *orco, float *vec, float mat[][3], int no_rot_axis);
+
 void lattice_deform_verts(struct Object *laOb, struct Object *target,
                           struct DerivedMesh *dm, float (*vertexCos)[3],
                           int numVerts, char *vgroup);
index 084624724067534e4336e9b54c5a7f3d210bd5a7..6d0a5f672cc2add148380e7836877991e7eff69e 100644 (file)
@@ -57,6 +57,7 @@
 #include "BKE_global.h"
 #include "BKE_ipo.h"
 #include "BKE_key.h"
+#include "BKE_lattice.h"
 #include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_object.h"
@@ -386,6 +387,68 @@ bActionChannel *verify_action_channel(bAction *act, const char *name)
        return chan;
 }
 
+/* ************** time ****************** */
+
+static bActionStrip *get_active_strip(Object *ob)
+{
+       bActionStrip *strip;
+       
+       if(ob->action==NULL)
+               return NULL;
+       
+       for (strip=ob->nlastrips.first; strip; strip=strip->next)
+               if(strip->flag & ACTSTRIP_ACTIVE)
+                       break;
+       
+       if(strip && strip->act==ob->action)
+               return strip;
+       return NULL;
+}
+
+/* non clipped mapping of strip */
+static float get_actionstrip_frame(bActionStrip *strip, float cframe, int invert)
+{
+       float length, actlength, repeat;
+       
+       if (strip->flag & ACTSTRIP_USESTRIDE)
+               repeat= 1.0f;
+       else
+               repeat= strip->repeat;
+       
+       length = strip->end-strip->start;
+       if(length==0.0f)
+               length= 1.0f;
+       actlength = strip->actend-strip->actstart;
+       
+       
+       
+       if(invert)
+               return length*(cframe - strip->actstart)/(repeat*actlength) + strip->start;
+       else
+               return repeat*actlength*(cframe - strip->start)/length + strip->actstart;
+}
+
+/* if the conditions match, it converts current time to strip time */
+float get_action_frame(Object *ob, float cframe)
+{
+       bActionStrip *strip= get_active_strip(ob);
+       
+       if(strip)
+               return get_actionstrip_frame(strip, cframe, 0);
+       return cframe;
+}
+
+/* inverted, strip time to current time */
+float get_action_frame_inv(Object *ob, float cframe)
+{
+       bActionStrip *strip= get_active_strip(ob);
+       
+       if(strip)
+               return get_actionstrip_frame(strip, cframe, 1);
+       return cframe;
+}
+
+
 /* ************************ Blending with NLA *************** */
 
 static void blend_pose_strides(bPose *dst, bPose *src, float srcweight, short mode)
@@ -406,6 +469,87 @@ static void blend_pose_strides(bPose *dst, bPose *src, float srcweight, short mo
        VecLerpf(dst->stride_offset, dst->stride_offset, src->stride_offset, srcweight);
 }
 
+
+/* 
+
+bone matching diagram, strips A and B
+
+                 .------------------------.
+                 |         A              |
+                 '------------------------'
+                                .          .             b2
+                 .          .-------------v----------.
+                 .             |         B   .          |
+                 .          '------------------------'
+                 .          .             .
+                 .          .             .
+offset:          .    0     .    A-B      .  A-b2+B     
+                 .          .             .
+
+*/
+
+
+static void blend_pose_offset_bone(bActionStrip *strip, bPose *dst, bPose *src, float srcweight, short mode)
+{
+       /* matching offset bones */
+       /* take dst offset, and put src on on that location */
+       
+       if(strip->offs_bone[0]==0)
+               return;
+       
+       /* are we also blending with matching bones? */
+       if(strip->prev && strip->start>=strip->prev->start) {
+               bPoseChannel *dpchan= get_pose_channel(dst, strip->offs_bone);
+               if(dpchan) {
+                       bPoseChannel *spchan= get_pose_channel(src, strip->offs_bone);
+                       if(spchan) {
+                               float vec[3];
+                               
+                               /* dst->ctime has the internal strip->prev action time */
+                               /* map this time to nla time */
+                               
+                               float ctime= get_actionstrip_frame(strip, src->ctime, 1);
+                               
+                               if( ctime > strip->prev->end) {
+                                       bActionChannel *achan;
+                                       
+                                       /* add src to dest, minus the position of src on strip->prev->end */
+                                       
+                                       ctime= get_actionstrip_frame(strip, strip->prev->end, 0);
+                                       
+                                       achan= get_action_channel(strip->act, strip->offs_bone);
+                                       if(achan && achan->ipo) {
+                                               bPoseChannel pchan;
+                                               /* Evaluates and sets the internal ipo value */
+                                               calc_ipo(achan->ipo, ctime);
+                                               /* This call also sets the pchan flags */
+                                               execute_action_ipo(achan, &pchan);
+                                               
+                                               /* store offset that moves src to location of pchan */
+                                               VecSubf(vec, dpchan->loc, pchan.loc);
+                                               
+                                               Mat4Mul3Vecfl(dpchan->bone->arm_mat, vec);
+                                       }
+                               }
+                               else {
+                                       /* store offset that moves src to location of dst */
+                                       
+                                       VecSubf(vec, dpchan->loc, spchan->loc);
+                                       Mat4Mul3Vecfl(dpchan->bone->arm_mat, vec);
+                               }
+                               
+                               /* if blending, we only add with factor scrweight */
+                               VecMulf(vec, srcweight);
+                               
+                               VecAddf(dst->cyclic_offset, dst->cyclic_offset, vec);
+                       }
+               }
+       }
+       
+       VecAddf(dst->cyclic_offset, dst->cyclic_offset, src->cyclic_offset);
+}
+
+
 /* Only allowed for Poses with identical channels */
 void blend_poses(bPose *dst, bPose *src, float srcweight, short mode)
 {
@@ -457,6 +601,9 @@ void blend_poses(bPose *dst, bPose *src, float srcweight, short mode)
                        dcon->enforce= dcon->enforce*(1.0f-srcweight) + scon->enforce*srcweight;
                }
        }
+       
+       /* this pose is now in src time */
+       dst->ctime= src->ctime;
 }
 
 
@@ -545,6 +692,8 @@ void extract_pose_from_action(bPose *pose, bAction *act, float ctime)
                        do_constraint_channels(&pchan->constraints, &achan->constraintChannels, ctime);
                }
        }
+       
+       pose->ctime= ctime;     /* used for cyclic offset matching */
 }
 
 /* for do_all_pose_actions, clears the pose */
@@ -556,17 +705,16 @@ static void rest_pose(bPose *pose)
        if (!pose)
                return;
        
-       pose->stride_offset[0]= 0.0f;
-       pose->stride_offset[1]= 0.0f;
-       pose->stride_offset[2]= 0.0f;
+       memset(pose->stride_offset, 0, sizeof(pose->stride_offset));
+       memset(pose->cyclic_offset, 0, sizeof(pose->cyclic_offset));
        
-       for (pchan=pose->chanbase.first; pchan; pchan=pchan->next){
-               for (i=0; i<3; i++){
-                       pchan->loc[i]=0.0;
-                       pchan->quat[i+1]=0.0;
-                       pchan->size[i]=1.0;
+       for (pchan=pose->chanbase.first; pchan; pchan= pchan->next){
+               for (i=0; i<3; i++) {
+                       pchan->loc[i]= 0.0f;
+                       pchan->quat[i+1]= 0.0f;
+                       pchan->size[i]= 1.0f;
                }
-               pchan->quat[0]=1.0;
+               pchan->quat[0]= 1.0f;
                
                pchan->flag &= ~(POSE_LOC|POSE_ROT|POSE_SIZE);
        }
@@ -685,69 +833,7 @@ static void execute_ipochannels(ListBase *lb)
        }
 }
 
-
-/* ************** time ****************** */
-
-static bActionStrip *get_active_strip(Object *ob)
-{
-       bActionStrip *strip;
-       
-       if(ob->action==NULL)
-               return NULL;
-       
-       for (strip=ob->nlastrips.first; strip; strip=strip->next)
-               if(strip->flag & ACTSTRIP_ACTIVE)
-                       break;
-       
-       if(strip && strip->act==ob->action)
-               return strip;
-       return NULL;
-}
-
-/* non clipped mapping of strip */
-static float get_actionstrip_frame(bActionStrip *strip, float cframe, int invert)
-{
-       float length, actlength, repeat;
-       
-       if (strip->flag & ACTSTRIP_USESTRIDE)
-               repeat= 1.0f;
-       else
-               repeat= strip->repeat;
-       
-       length = strip->end-strip->start;
-       if(length==0.0f)
-               length= 1.0f;
-       actlength = strip->actend-strip->actstart;
-
-       
-       
-       if(invert)
-               return length*(cframe - strip->actstart)/(repeat*actlength) + strip->start;
-       else
-               return repeat*actlength*(cframe - strip->start)/length + strip->actstart;
-}
-
-/* if the conditions match, it converts current time to strip time */
-float get_action_frame(Object *ob, float cframe)
-{
-       bActionStrip *strip= get_active_strip(ob);
-       
-       if(strip)
-               return get_actionstrip_frame(strip, cframe, 0);
-       return cframe;
-}
-
-/* inverted, strip time to current time */
-float get_action_frame_inv(Object *ob, float cframe)
-{
-       bActionStrip *strip= get_active_strip(ob);
-       
-       if(strip)
-               return get_actionstrip_frame(strip, cframe, 1);
-       return cframe;
-}
-
-
+/* nla timing */
 
 /* this now only used for repeating cycles, to enable fields and blur. */
 /* the whole time control in blender needs serious thinking... */
@@ -840,6 +926,54 @@ static float stridechannel_frame(Object *ob, float sizecorr, bActionStrip *strip
        return 0.0f;
 }
 
+static void cyclic_offs_bone(Object *ob, bPose *pose, bActionStrip *strip, float time)
+{
+       
+       if(time > 1.0f) {
+               bActionChannel *achan= get_action_channel(strip->act, strip->offs_bone);
+
+               if(achan && achan->ipo) {
+                       IpoCurve *icu= NULL;
+                       Bone *bone;
+                       float min[3]={0.0f, 0.0f, 0.0f}, max[3]={0.0f, 0.0f, 0.0f};
+                       int index=0, foundvert= 0;
+                       
+                       /* calculate the min/max */
+                       for (icu=achan->ipo->curve.first; icu; icu=icu->next) {
+                               if(icu->totvert>1) {
+                                       
+                                       if(icu->adrcode==AC_LOC_X)
+                                               index= 0;
+                                       else if(icu->adrcode==AC_LOC_Y)
+                                               index= 1;
+                                       else if(icu->adrcode==AC_LOC_Z)
+                                               index= 2;
+                                       else
+                                               continue;
+                               
+                                       foundvert= 1;
+                                       min[index]= icu->bezt[0].vec[1][1];
+                                       max[index]= icu->bezt[icu->totvert-1].vec[1][1];
+                               }
+                       }
+                       if(foundvert) {
+                               /* bring it into armature space */
+                               VecSubf(min, max, min);
+                               bone= get_named_bone(ob->data, strip->offs_bone);       /* weak */
+                               Mat4Mul3Vecfl(bone->arm_mat, min);
+                               
+                               /* dominant motion, cyclic_offset was cleared in rest_pose */
+                               if( fabs(min[0]) >= fabs(min[1]) && fabs(min[0]) >= fabs(min[2]))
+                                       pose->cyclic_offset[0]= time*min[0];
+                               else if( fabs(min[1]) >= fabs(min[0]) && fabs(min[1]) >= fabs(min[2]))
+                                       pose->cyclic_offset[1]= time*min[1];
+                               else
+                                       pose->cyclic_offset[2]= time*min[2];
+                       }
+               }
+       }
+}
+
 /* simple case for now; only the curve path with constraint value > 0.5 */
 /* blending we might do later... */
 static Object *get_parent_path(Object *ob)
@@ -917,7 +1051,7 @@ static void do_nla(Object *ob, int blocktype)
                        actlength = strip->actend-strip->actstart;
                        striptime = (scene_cfra-(strip->start)) / length;
                        stripframe = (scene_cfra-(strip->start)) ;
-                       
+
                        if (striptime>=0.0){
                                
                                if(blocktype==ID_AR) 
@@ -982,25 +1116,32 @@ static void do_nla(Object *ob, int blocktype)
                                                
                                                /* Mod to repeat */
                                                if(strip->repeat!=1.0f) {
-                                                       striptime*= strip->repeat;
-                                                       striptime = (float)fmod (striptime, 1.0f + 0.1f/length);
+                                                       float cycle= striptime*strip->repeat;
+                                                       
+                                                       striptime = (float)fmod (cycle, 1.0f + 0.1f/length);
+                                                       cycle-= striptime;
+                                                       
+                                                       if(blocktype==ID_AR)
+                                                               cyclic_offs_bone(ob, tpose, strip, cycle);
                                                }
 
                                                frametime = (striptime * actlength) + strip->actstart;
                                                frametime= nla_time(frametime, (float)strip->repeat);
                                                        
-                                               if(blocktype==ID_AR)
+                                               if(blocktype==ID_AR) {
                                                        extract_pose_from_action (tpose, strip->act, frametime);
+                                               }
                                                else if(blocktype==ID_OB) {
                                                        extract_ipochannels_from_action(&tchanbase, &ob->id, strip->act, "Object", frametime);
                                                        if(key)
                                                                extract_ipochannels_from_action(&tchanbase, &key->id, strip->act, "Shape", frametime);
-                                               }                                               
+                                               }
+                                               
                                                doit=1;
                                        }
                                }
                                /* Handle extend */
-                               else{
+                               else {
                                        if (strip->flag & ACTSTRIP_HOLDLASTFRAME){
                                                /* we want the strip to hold on the exact fraction of the repeat value */
                                                
@@ -1015,6 +1156,13 @@ static void do_nla(Object *ob, int blocktype)
                                                        if(key)
                                                                extract_ipochannels_from_action(&tchanbase, &key->id, strip->act, "Shape", frametime);
                                                }
+                                               
+                                               /* handle cycle hold */
+                                               if(strip->repeat!=1.0f) {
+                                                       if(blocktype==ID_AR)
+                                                               cyclic_offs_bone(ob, tpose, strip, strip->repeat-1.0f);
+                                               }
+                                               
                                                doit=1;
                                        }
                                }
@@ -1033,6 +1181,9 @@ static void do_nla(Object *ob, int blocktype)
                                                blendfac = 1;
                                        
                                        if(blocktype==ID_AR) {/* Blend this pose with the accumulated pose */
+                                               /* offset bone, for matching cycles */
+                                               blend_pose_offset_bone (strip, ob->pose, tpose, blendfac, strip->mode);
+                                               
                                                blend_poses (ob->pose, tpose, blendfac, strip->mode);
                                                if(dostride)
                                                        blend_pose_strides (ob->pose, tpose, blendfac, strip->mode);
@@ -1061,7 +1212,6 @@ static void do_nla(Object *ob, int blocktype)
        }
        if(chanbase.first)
                BLI_freelistN(&chanbase);
-       
 }
 
 void do_all_pose_actions(Object *ob)
index aaa8e638ec9bc91aaaffd331e79b19185c4283d5..fe703007e50547144b2db132119d170ae774538d 100644 (file)
@@ -43,6 +43,7 @@
 #include "DNA_mesh_types.h"
 #include "DNA_lattice_types.h"
 #include "DNA_meshdata_types.h"
+#include "DNA_nla_types.h"
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_view3d_types.h"
@@ -58,6 +59,7 @@
 #include "BKE_displist.h"
 #include "BKE_global.h"
 #include "BKE_library.h"
+#include "BKE_lattice.h"
 #include "BKE_main.h"
 #include "BKE_object.h"
 #include "BKE_object.h"
@@ -1592,6 +1594,35 @@ static void do_local_constraint(bPoseChannel *pchan, bConstraint *con)
        }
 }
 
+static void do_strip_modifiers(Object *armob, Bone *bone, bPoseChannel *pchan)
+{
+       bActionModifier *amod;
+       bActionStrip *strip;
+       float scene_cfra= G.scene->r.cfra;
+
+       for (strip=armob->nlastrips.first; strip; strip=strip->next) {
+               if(scene_cfra>=strip->start && scene_cfra<=strip->end) {
+                       
+                       for(amod= strip->modifiers.first; amod; amod= amod->next) {
+                               if(amod->type==ACTSTRIP_MOD_DEFORM) {
+                                       /* validate first */
+                                       if(amod->ob && amod->ob->type==OB_CURVE && amod->channel[0]) {
+                                               
+                                               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);
+                                                       Mat4CpyMat4(mat4, pchan->pose_mat);
+                                                       Mat4MulMat34(pchan->pose_mat, mat3, mat4);
+                                                       
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
 
 /* The main armature solver, does all constraints excluding IK */
 /* pchan is validated, as having bone and parent pointer */
@@ -1657,9 +1688,13 @@ static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime)
                else 
                        Mat4MulSerie(pchan->pose_mat, parchan->pose_mat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL);
        }
-       else 
+       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);
+       }
        
+       do_strip_modifiers(ob, bone, pchan);
        
        /* Do constraints */
        if(pchan->constraints.first) {
index 5847b913004805734ee24fad77763fcfdd38de03..c65e9363274244511495d413e8853ce83f51fa3e 100644 (file)
@@ -389,7 +389,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int
                }
        }
        
-       /* driver dependencies */
+       /* driver dependencies, nla modifiers */
        if(ob->ipo) 
                dag_add_driver_relation(ob->ipo, dag, node, 0);
        
@@ -416,6 +416,15 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int
                                for (chan = strip->act->chanbase.first; chan; chan=chan->next)
                                        if(chan->ipo)
                                                dag_add_driver_relation(chan->ipo, dag, node, 1);
+                       if(strip->modifiers.first) {
+                               bActionModifier *amod;
+                               for(amod= strip->modifiers.first; amod; amod= amod->next) {
+                                       if(amod->ob) {
+                                               node2 = dag_get_node(dag, amod->ob);
+                                               dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA);
+                                       }
+                               }
+                       }
                }
        }
        if (ob->modifiers.first) {
index 3d8fd7d13112e6c466ae3faf37f618139b217354..d751169d115bc0eed4c281fc03c3ca062ffea9b2 100644 (file)
@@ -450,19 +450,25 @@ void end_latt_deform()
         */
 typedef struct {
        float dmin[3], dmax[3], dsize, dloc[3];
-       float curvespace[4][4], objectspace[4][4];
+       float curvespace[4][4], objectspace[4][4], objectspace3[3][3];
+       int no_rot_axis;
 } CurveDeform;
 
-static void init_curve_deform(Object *par, Object *ob, CurveDeform *cd)
+static void init_curve_deform(Object *par, Object *ob, CurveDeform *cd, int dloc)
 {
        Mat4Invert(ob->imat, ob->obmat);
        Mat4MulMat4(cd->objectspace, par->obmat, ob->imat);
        Mat4Invert(cd->curvespace, cd->objectspace);
-
+       Mat3CpyMat4(cd->objectspace3, cd->objectspace);
+       
        // offset vector for 'no smear'
-       Mat4Invert(par->imat, par->obmat);
-       VecMat4MulVecfl(cd->dloc, par->imat, ob->obmat[3]);
-
+       if(dloc) {
+               Mat4Invert(par->imat, par->obmat);
+               VecMat4MulVecfl(cd->dloc, par->imat, ob->obmat[3]);
+       }
+       else cd->dloc[0]=cd->dloc[1]=cd->dloc[2]= 0.0f;
+       
+       cd->no_rot_axis= 0;
 }
 
 /* this makes sure we can extend for non-cyclic. *vec needs 4 items! */
@@ -508,10 +514,12 @@ static int where_on_path_deform(Object *ob, float ctime, float *vec, float *dir)
        /* for each point, rotate & translate to curve */
        /* use path, since it has constant distances */
        /* co: local coord, result local too */
-static void calc_curve_deform(Object *par, float *co, short axis, CurveDeform *cd)
+       /* returns quaternion for rotation, using cd->no_rot_axis */
+       /* axis is using another define!!! */
+static float *calc_curve_deform(Object *par, float *co, short axis, CurveDeform *cd)
 {
        Curve *cu= par->data;
-       float fac, loc[4], dir[3], *quat, q[4], mat[3][3], cent[3];
+       float fac, loc[4], dir[3], cent[3];
        short upflag, index;
        
        if(axis==MOD_CURVE_POSX || axis==MOD_CURVE_NEGX) {
@@ -538,17 +546,33 @@ static void calc_curve_deform(Object *par, float *co, short axis, CurveDeform *c
        /* to be sure, mostly after file load */
        if(cu->path==NULL) {
                makeDispListCurveTypes(par, 0);
-               if(cu->path==NULL) return;      // happens on append...
+               if(cu->path==NULL) return NULL; // happens on append...
        }
+       
        /* options */
-       if(cu->flag & CU_STRETCH)
-               fac= (co[index]-cd->dmin[index])/(cd->dmax[index] - cd->dmin[index]);
-       else
-               fac= (cd->dloc[index])/(cu->path->totdist) + (co[index]-cd->dmin[index])/(cu->path->totdist);
+       if(ELEM3(axis, OB_NEGX, OB_NEGY, OB_NEGZ)) {
+               if(cu->flag & CU_STRETCH)
+                       fac= (-co[index]-cd->dmax[index])/(cd->dmax[index] - cd->dmin[index]);
+               else
+                       fac= (cd->dloc[index])/(cu->path->totdist) - (co[index]-cd->dmax[index])/(cu->path->totdist);
+       }
+       else {
+               if(cu->flag & CU_STRETCH)
+                       fac= (co[index]-cd->dmin[index])/(cd->dmax[index] - cd->dmin[index]);
+               else
+                       fac= (cd->dloc[index])/(cu->path->totdist) + (co[index]-cd->dmin[index])/(cu->path->totdist);
+       }
+       
        
        if( where_on_path_deform(par, fac, loc, dir)) { /* returns OK */
-
-               quat= vectoquat(dir, axis-1, upflag);   /* -1 for compatibility with old track defines */
+               float q[4], mat[3][3];
+               float *quat;
+               
+               if(cd->no_rot_axis)     /* set by caller */
+                       dir[cd->no_rot_axis-1]= 0.0f;
+               
+               /* -1 for compatibility with old track defines */
+               quat= vectoquat(dir, axis-1, upflag);   /* gives static quat */
                
                /* the tilt */
                if(loc[3]!=0.0) {
@@ -568,8 +592,9 @@ static void calc_curve_deform(Object *par, float *co, short axis, CurveDeform *c
                /* translation */
                VECADD(co, cent, loc);
                
+               return quat;
        }
-
+       return NULL;
 }
 
 void curve_deform_verts(Object *cuOb, Object *target, DerivedMesh *dm, float (*vertexCos)[3], int numVerts, char *vgroup, short defaxis)
@@ -581,7 +606,7 @@ void curve_deform_verts(Object *cuOb, Object *target, DerivedMesh *dm, float (*v
        
        cu->flag |= (CU_PATH|CU_FOLLOW); // needed for path & bevlist
 
-       init_curve_deform(cuOb, target, &cd);
+       init_curve_deform(cuOb, target, &cd, (cu->flag & CU_STRETCH)==0);
                
        /* check whether to use vertex groups (only possible if target is a Mesh)
         * we want either a Mesh with no derived data, or derived data with
@@ -658,6 +683,36 @@ void curve_deform_verts(Object *cuOb, Object *target, DerivedMesh *dm, float (*v
        cu->flag = flag;
 }
 
+/* input vec and orco = local coord in armature space */
+/* orco is original not-animated or deformed reference point */
+/* result written in vec and mat */
+void curve_deform_vector(Object *cuOb, Object *target, float *orco, float *vec, float mat[][3], int no_rot_axis)
+{
+       CurveDeform cd;
+       float *quat;
+       
+       init_curve_deform(cuOb, target, &cd, 0);        /* 0 no dloc */
+       cd.no_rot_axis= no_rot_axis;                            /* option to only rotate for XY, for example */
+       
+       VECCOPY(cd.dmin, orco);
+       VECCOPY(cd.dmax, orco);
+
+       Mat4MulVecfl(cd.curvespace, vec);
+       
+       quat= calc_curve_deform(cuOb, vec, target->trackflag+1, &cd);
+       if(quat) {
+               float qmat[3][3];
+               
+               QuatToMat3(quat, qmat);
+               Mat3MulMat3(mat, qmat, cd.objectspace3);
+       }
+       else
+               Mat3One(mat);
+       
+       Mat4MulVecfl(cd.objectspace, vec);
+
+}
+
 void lattice_deform_verts(Object *laOb, Object *target, DerivedMesh *dm,
                           float (*vertexCos)[3], int numVerts, char *vgroup)
 {
index a8f1f2406f108b0705bf264d960538e2512aba5f..a348aef03996ddb9501fa8d5445334f4fa8db600 100644 (file)
@@ -74,6 +74,11 @@ void copy_actionstrip (bActionStrip **dst, bActionStrip **src){
 
        if (dstrip->ipo)
                dstrip->ipo->id.us++;
+       
+       if (dstrip->modifiers.first) {
+               duplicatelist (&dstrip->modifiers, &sstrip->modifiers);
+       }
+       
 }
 
 void copy_nlastrips (ListBase *dst, ListBase *src)
@@ -93,6 +98,11 @@ void copy_nlastrips (ListBase *dst, ListBase *src)
                        strip->act->id.us++;
                if (strip->ipo)
                        strip->ipo->id.us++;
+               if (strip->modifiers.first) {
+                       ListBase listb;
+                       duplicatelist (&listb, &strip->modifiers);
+                       strip->modifiers= listb;
+               }
        }
 }
 
@@ -139,7 +149,7 @@ bActionStrip *convert_action_to_strip (Object *ob)
 }
 
 
-
+/* not strip itself! */
 void free_actionstrip(bActionStrip* strip)
 {
        if (!strip)
@@ -153,6 +163,10 @@ void free_actionstrip(bActionStrip* strip)
                strip->ipo->id.us--;
                strip->ipo = NULL;
        }
+       if (strip->modifiers.first) {
+               BLI_freelistN(&strip->modifiers);
+       }
+       
 }
 
 void free_nlastrips (ListBase *nlalist)
index fd0bd47430e7d1db0e4180d60a9cb8692ae5a191..a7404fa8f51f2d0a3ebde91cef760d229cf6e7f7 100644 (file)
@@ -333,6 +333,13 @@ void unlink_object(Object *ob)
                        for(strip= ob->nlastrips.first; strip; strip= strip->next) {
                                if(strip->object==ob)
                                        strip->object= NULL;
+                               
+                               if(strip->modifiers.first) {
+                                       bActionModifier *amod;
+                                       for(amod= strip->modifiers.first; amod; amod= amod->next)
+                                               if(amod->ob==ob)
+                                                       amod->ob= NULL;
+                               }
                        }
                }
                obt= obt->id.next;
index e66a888aa4776fce4c9e74e0f248897671f1f098..99b778b549ebceda79c930f311a59bb05cd75800 100644 (file)
@@ -1390,11 +1390,14 @@ static void direct_link_scriptlink(FileData *fd, ScriptLink *slink)
 static void lib_link_nlastrips(FileData *fd, ID *id, ListBase *striplist)
 {
        bActionStrip *strip;
-
+       bActionModifier *amod;
+       
        for (strip=striplist->first; strip; strip=strip->next){
                strip->object = newlibadr(fd, id->lib, strip->object);
                strip->act = newlibadr_us(fd, id->lib, strip->act);
                strip->ipo = newlibadr(fd, id->lib, strip->ipo);
+               for(amod= strip->modifiers.first; amod; amod= amod->next)
+                       amod->ob= newlibadr(fd, id->lib, amod->ob);
        }
 }
 
@@ -2502,6 +2505,16 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
        }
 }
 
+static void direct_link_nlastrips(FileData *fd, ListBase *strips)
+{
+       bActionStrip *strip;
+       
+       link_list(fd, strips);
+       
+       for(strip= strips->first; strip; strip= strip->next)
+               link_list(fd, &strip->modifiers);
+}
+
 static void direct_link_object(FileData *fd, Object *ob)
 {
        PartEff *paf;
@@ -2520,7 +2533,7 @@ static void direct_link_object(FileData *fd, Object *ob)
        direct_link_pose(fd, ob->pose);
 
        link_list(fd, &ob->defbase);
-       link_list(fd, &ob->nlastrips);
+       direct_link_nlastrips(fd, &ob->nlastrips);
        link_list(fd, &ob->constraintChannels);
 
        direct_link_scriptlink(fd, &ob->scriptlink);
index 96c1b2ceb9b6b7034a8dda0f71370ad4d80e08ca..3955530b3b32132926bf43f99b231d8bd782cf48 100644 (file)
@@ -632,9 +632,14 @@ static void write_actuators(WriteData *wd, ListBase *lb)
 static void write_nlastrips(WriteData *wd, ListBase *nlabase)
 {
        bActionStrip *strip;
-
+       bActionModifier *amod;
+       
        for (strip=nlabase->first; strip; strip=strip->next)
                writestruct(wd, DATA, "bActionStrip", 1, strip);
+       for (strip=nlabase->first; strip; strip=strip->next) {
+               for(amod= strip->modifiers.first; amod; amod= amod->next)
+                       writestruct(wd, DATA, "bActionModifier", 1, amod);
+       }
 }
 
 static void write_constraints(WriteData *wd, ListBase *conlist)
index f4fa4629d9f07babb501948f85b9fc1ffb2d9b55..fafc95fb03f0c1a50015eeba7b16cff9329c4e98 100644 (file)
@@ -54,6 +54,7 @@ void reset_action_strips(int val);
 void synchronize_action_strips(void);
 void snap_action_strips(void);
 void add_nlablock(void);
+void copy_action_modifiers(void);
 
 /* Baking */
 void bake_all_to_action(void);
index e688b778894cca67134837d6f1ea37c6e930fbe3..2c6122198b8f61147f8fb5026d5e4c331e0e42df 100644 (file)
@@ -83,7 +83,9 @@ typedef struct bPoseChannel {
 typedef struct bPose{
        ListBase                        chanbase;
        int flag;
-       float stride_offset[3];
+       float ctime;                            /* local action time of this pose */
+       float stride_offset[3];         /* applied to object */
+       float cyclic_offset[3];         /* result of match and cycles, applied in where_is_pose() */
 } bPose;
 
 typedef struct bActionChannel {
index fe01fa91eb2d694b544918ad583b1b31a2a8fd74..c97ef5ab197e94f43b66bd6e06aea56792155f31 100644 (file)
 #ifndef DNA_NLA_TYPES_H
 #define DNA_NLA_TYPES_H
 
+#include "DNA_listBase.h"
+
 struct bAction;
 struct Ipo;
 struct Object;
 
+/* simple uniform modifier structure, assumed it can hold all type info */
+typedef struct bActionModifier {
+       struct bActionModifier *next, *prev;
+       short type, flag;
+       char channel[32];
+       
+       /* path deform modifier */
+       short pad, no_rot_axis;
+       struct Object *ob;
+       
+} bActionModifier;
+
+#define ACTSTRIP_MOD_DEFORM            0
+#define ACTSTRIP_MOD_NOISE             1
+#define ACTSTRIP_MOD_OOMPH             2
+
 typedef struct bActionStrip {
        struct bActionStrip *next, *prev;
        short   flag, mode;
-       short   stride_axis, pad;       /* axis 0=x, 1=y, 2=z */
+       short   stride_axis;            /* axis 0=x, 1=y, 2=z */
+       short   curmod;                         /* current modifier for buttons */
 
        struct  Ipo *ipo;                       /* Blending ipo */
        struct  bAction *act;           /* The action referenced by this strip */
@@ -54,6 +73,10 @@ typedef struct bActionStrip {
        float   blendin, blendout;
        
        char    stridechannel[32];      /* Instead of stridelen, it uses an action channel */
+       char    offs_bone[32];          /* if repeat, use this bone/channel for defining offset */
+       
+       struct ListBase modifiers;      /* modifier stack */
+       
 } bActionStrip;
 
 #define ACTSTRIPMODE_BLEND             0
index 9c66eae794bccb72f9f6b3db712c6c9297576a00..a1a5bebe8d60e584c881a8ab8ed26a2e2bf4f7f2 100644 (file)
@@ -41,6 +41,7 @@
 #include "BMF_Api.h"
 
 #include <stdlib.h>
+#include <string.h>
 #include <stdio.h>
 
 #include "DNA_view3d_types.h"
@@ -183,7 +184,13 @@ static void draw_nla_channels(void)
                                                BIF_icon_draw(x+16, y-8, ICON_DOT);
                                                glDisable(GL_BLEND);
                                        }
+                                       if(strip->modifiers.first) {
+                                               glEnable(GL_BLEND);
+                                               BIF_icon_draw(x+34, y-8, ICON_MODIFIER);
+                                               glDisable(GL_BLEND);
+                                       }
                                }
+                               
                                y-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
                        }
                }
@@ -398,6 +405,11 @@ static void draw_nla_strips_keys(SpaceNla *snla)
 
 #define B_NLA_PANEL            121
 #define B_NLA_LOCK             122
+#define B_NLA_MOD_ADD  123
+#define B_NLA_MOD_NEXT 124
+#define B_NLA_MOD_PREV 125
+#define B_NLA_MOD_DEL  126
+#define B_NLA_MOD_DEPS 127
 
 /* For now just returns the first selected strip */
 bActionStrip *get_active_nlastrip(Object **obpp)
@@ -442,19 +454,73 @@ void do_nlabuts(unsigned short event)
                allqueue (REDRAWACTION, 0);
                allqueue (REDRAWVIEW3D, 0);
                break;
+               
+       case B_NLA_MOD_ADD:
+               {
+                       bActionModifier *amod= MEM_callocN(sizeof(bActionModifier), "bActionModifier");
+                       
+                       BLI_addtail(&strip->modifiers, amod);
+                       strip->curmod= BLI_countlist(&strip->modifiers)-1;
+                       allqueue (REDRAWNLA, 0);
+               }
+               break;
+       case B_NLA_MOD_DEL:
+               if(strip->modifiers.first) {
+                       bActionModifier *amod= BLI_findlink(&strip->modifiers, strip->curmod);
+                       BLI_remlink(&strip->modifiers, amod);
+                       MEM_freeN(amod);
+                       if(strip->curmod) strip->curmod--;
+                       allqueue (REDRAWNLA, 0);
+               }
+               break;
+       case B_NLA_MOD_NEXT:
+               if(strip->curmod < BLI_countlist(&strip->modifiers)-1)
+                       strip->curmod++;
+               allqueue (REDRAWNLA, 0);
+               break;
+       case B_NLA_MOD_PREV:
+               if(strip->curmod > 0)
+                       strip->curmod--;
+               allqueue (REDRAWNLA, 0);
+               break;
+       case B_NLA_MOD_DEPS:
+               DAG_scene_sort(G.scene);
+               DAG_object_flush_update(G.scene, ob, OB_RECALC_OB|OB_RECALC_DATA);
+               break;
        }
 }
 
+static char *make_modifier_menu(ListBase *lb)
+{
+       bActionModifier *amod;
+       int index= 1;
+       char *str, item[64], *types[3]={"Deform", "Noise", "Oomph"};
+       
+       for (amod = lb->first; amod; amod=amod->next, index++);
+       str= MEM_mallocN(index*64, "key string");
+       str[0]= 0;
+       
+       index= 0;
+       for (amod = lb->first; amod; amod=amod->next, index++) {
+               sprintf (item,  "|%s %s%%x%d", types[amod->type], amod->channel, index);
+               strcat(str, item);
+       }
+       
+       return str;
+}
+
+
 static void nla_panel_properties(short cntrl)  // NLA_HANDLER_PROPERTIES
 {
        Object *ob;
        bActionStrip *strip;
        uiBlock *block;
+       uiBut *but;
 
        block= uiNewBlock(&curarea->uiblocks, "nla_panel_properties", UI_EMBOSS, UI_HELV, curarea->win);
        uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
        uiSetPanelHandler(NLA_HANDLER_PROPERTIES);  // for close and esc
-       if(uiNewPanel(curarea, block, "Transform Properties", "NLA", 10, 230, 318, 204)==0) return;
+       if(uiNewPanel(curarea, block, "Transform Properties", "NLA", 10, 230, 318, 224)==0) return;
 
        /* Determine if an nla strip has been selected */
        strip = get_active_nlastrip(&ob);
@@ -488,26 +554,61 @@ static void nla_panel_properties(short cntrl)     // NLA_HANDLER_PROPERTIES
 
        uiBlockBeginAlign(block);
        uiDefButF(block, NUM, B_NLA_PANEL, "Repeat:",   160,100,150,19, &strip->repeat, 0.001, 1000.0f, 100, 0, "Number of times the action should repeat");
-       uiDefButBitS(block, TOG, ACTSTRIP_HOLDLASTFRAME, B_NLA_PANEL, "Hold",   160,80,75,19, &strip->flag, 0, 0, 0, 0, "Toggles whether to continue displaying the last frame past the end of the strip");
-       uiDefButS(block, TOG, B_NLA_PANEL, "Add",                                                               235,80,75,19, &strip->mode, 0, 0, 0, 0, "Toggles additive blending mode");
+       but= uiDefButC(block, TEX, B_NLA_PANEL, "OffsBone:", 160,80,150,19, strip->offs_bone, 0, 31.0f, 0, 0, "Name of Bone that defines offset for repeat");
+       uiButSetCompleteFunc(but, autocomplete_bone, (void *)ob);
+       uiDefButBitS(block, TOG, ACTSTRIP_HOLDLASTFRAME, B_NLA_PANEL, "Hold",   160,60,75,19, &strip->flag, 0, 0, 0, 0, "Toggles whether to continue displaying the last frame past the end of the strip");
+       uiDefButS(block, TOG, B_NLA_PANEL, "Add",                                                               235,60,75,19, &strip->mode, 0, 0, 0, 0, "Toggles additive blending mode");
        uiBlockEndAlign(block);
        
-       uiDefButBitS(block, TOG, ACTSTRIP_USESTRIDE, B_NLA_PANEL, "Stride Path",        10, 50,140,19, &strip->flag, 0, 0, 0, 0, "Plays action based on path position & stride");
-       if(ob->dup_group)
-               uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_NLA_PANEL, "Target:",   160,50, 150, 19, &strip->object, "Target Object in this group"); 
-       
-       uiBlockBeginAlign(block);
-       uiDefButBitS(block, TOG, OB_DISABLE_PATH, B_NLA_PANEL, "Disable",                       10,20,60,19, &ob->ipoflag, 0, 0, 0, 0, "Disable path temporally, for editing cycles");
-       
-       uiDefButF(block, NUM, B_NLA_PANEL, "Offs:",                     70,20,120,19, &strip->actoffs, -500, 500.0, 100, 0, "Action offset in frames to tweak cycle of the action within the stride");
-       uiDefButF(block, NUM, B_NLA_PANEL, "Stri:",                     190,20,120,19, &strip->stridelen, 0.0001, 1000.0, 100, 0, "Distance covered by one complete cycle of the action specified in the Action Range");
+       uiDefButBitS(block, TOG, ACTSTRIP_USESTRIDE, B_NLA_PANEL, "Stride Path",        10, 30,140,19, &strip->flag, 0, 0, 0, 0, "Plays action based on path position & stride");
        
-       uiDefButS(block, ROW, B_NLA_PANEL, "X",                         10, 0, 33, 19, &strip->stride_axis, 1, 0, 0, 0, "Dominant axis for Stride Bone");
-       uiDefButS(block, ROW, B_NLA_PANEL, "Y",                         43, 0, 33, 19, &strip->stride_axis, 1, 1, 0, 0, "Dominant axis for Stride Bone");
-       uiDefButS(block, ROW, B_NLA_PANEL, "Z",                         76, 0, 34, 19, &strip->stride_axis, 1, 2, 0, 0, "Dominant axis for Stride Bone");
+       if(ob->dup_group)
+               uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_NLA_PANEL, "Target:",   160,30, 150, 19, &strip->object, "Target Object in this group"); 
        
-       uiDefBut(block, TEX, B_NLA_PANEL, "Stride Bone:",       110, 0, 200, 19, strip->stridechannel, 1, 31, 0, 0, "Name of Bone used for stride");
-
+       if(strip->flag & ACTSTRIP_USESTRIDE) {
+               uiBlockBeginAlign(block);
+               uiDefButBitS(block, TOG, OB_DISABLE_PATH, B_NLA_PANEL, "Disable",       10,0,60,19, &ob->ipoflag, 0, 0, 0, 0, "Disable path temporally, for editing cycles");
+               
+               uiDefButF(block, NUM, B_NLA_PANEL, "Offs:",                     70,0,120,19, &strip->actoffs, -500, 500.0, 100, 0, "Action offset in frames to tweak cycle of the action within the stride");
+               uiDefButF(block, NUM, B_NLA_PANEL, "Stri:",                     190,0,120,19, &strip->stridelen, 0.0001, 1000.0, 100, 0, "Distance covered by one complete cycle of the action specified in the Action Range");
+               
+               uiDefButS(block, ROW, B_NLA_PANEL, "X",                         10, -20, 33, 19, &strip->stride_axis, 1, 0, 0, 0, "Dominant axis for Stride Bone");
+               uiDefButS(block, ROW, B_NLA_PANEL, "Y",                         43, -20, 33, 19, &strip->stride_axis, 1, 1, 0, 0, "Dominant axis for Stride Bone");
+               uiDefButS(block, ROW, B_NLA_PANEL, "Z",                         76, -20, 34, 19, &strip->stride_axis, 1, 2, 0, 0, "Dominant axis for Stride Bone");
+               
+               but= uiDefBut(block, TEX, B_NLA_PANEL, "Stride Bone:",  110, -20, 200, 19, strip->stridechannel, 1, 31, 0, 0, "Name of Bone used for stride");
+               uiButSetCompleteFunc(but, autocomplete_bone, (void *)ob);
+       }
+       else {  /* modifiers */
+               bActionModifier *amod= BLI_findlink(&strip->modifiers, strip->curmod);
+               
+               uiBlockBeginAlign(block);
+               uiDefBut(block, BUT, B_NLA_MOD_ADD, "Add Modifier",                             10,0,140,19, NULL, 0, 0, 0, 0, "");
+               if(amod) {
+                       char *strp= make_modifier_menu(&strip->modifiers);
+                       
+                       uiDefIconBut(block, BUT, B_NLA_MOD_NEXT, ICON_TRIA_LEFT,        150,0,20,19, NULL, 0, 0, 0, 0, "Previous Modifier");
+                       uiDefButS(block, MENU, B_NLA_PANEL, strp,                                       170,0,20,19, &strip->curmod, 0, 0, 0, 0, "Browse modifier");
+                       MEM_freeN(strp);
+                       uiDefIconBut(block, BUT, B_NLA_MOD_PREV, ICON_TRIA_RIGHT,       190,0,20,19, NULL, 0, 0, 0, 0, "Next Modifier");
+                       uiDefButS(block, MENU, B_REDR, "Deform %x0|Noise %x1|Oomph %x2",        210,0,80,19, &amod->type, 0, 0, 0, 0, "Modifier type");
+                       uiDefIconBut(block, BUT, B_NLA_MOD_DEL, ICON_X,                         290,0,20,19, NULL, 0, 0, 0, 0, "Delete Modifier");
+                       
+                       if(amod->type==ACTSTRIP_MOD_DEFORM) {
+                               but= uiDefBut(block, TEX, B_NLA_PANEL, "Chan:",                         10, -20, 130, 19, amod->channel, 1, 31, 0, 0, "Name of channel used for modifier");
+                               uiButSetCompleteFunc(but, autocomplete_bone, (void *)ob);
+                               uiDefButS(block, MENU, B_REDR, "All%x0|XY%x3|XZ%x2|YZ%x1",      140,-20,40,19, &amod->no_rot_axis, 0, 0, 0, 0, "Enable rotation axes (local for curve)");
+                               uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_NLA_MOD_DEPS, "Ob:",    180,-20, 130, 19, &amod->ob, "Curve Object"); 
+                       }
+                       else
+                               uiDefBut(block, LABEL, B_NOP, "Ack! Not implemented.",  10, -20, 150, 19, NULL, 0, 0, 0, 0, "");
+                               
+               }
+               else { /* for panel aligning */
+                       uiBlockEndAlign(block);
+                       uiDefBut(block, LABEL, B_NOP, " ",                              10, -20, 150, 19, NULL, 0, 0, 0, 0, "");
+               }
+       }
 }
 
 static void nla_blockhandlers(ScrArea *sa)
index ac415cbed53f2d448b4169a9aa1df479787032f9..6bb42502f5aa036cf226a643124fc9ec312035a2 100644 (file)
@@ -54,6 +54,7 @@
 #include "DNA_userdef_types.h"
 
 #include "BKE_action.h"
+#include "BKE_blender.h"
 #include "BKE_depsgraph.h"
 #include "BKE_group.h"
 #include "BKE_global.h"
@@ -1647,7 +1648,11 @@ void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                          break;
                                
                        case CKEY:
-                               convert_nla(mval);
+                               if(G.qual==LR_CTRLKEY) {
+                                       if(okee("Copy Modifiers"))
+                                               copy_action_modifiers();
+                               }
+                               else convert_nla(mval);
                                break;
                                
                        case DKEY:
@@ -1848,3 +1853,36 @@ void bake_all_to_action(void)
                }
        }
 }
+
+void copy_action_modifiers(void)
+{
+       bActionStrip *strip, *actstrip;
+       Object *ob= OBACT;
+       
+       if(ob==NULL)
+               return;
+       
+       /* active strip */
+       for (actstrip=ob->nlastrips.first; actstrip; actstrip=actstrip->next)
+               if(actstrip->flag & ACTSTRIP_ACTIVE)
+                       break;
+       if(actstrip==NULL)
+               return;
+       
+       /* copy to selected items */
+       for (strip=ob->nlastrips.first; strip; strip=strip->next){
+               if (strip->flag & ACTSTRIP_SELECT) {
+                       if(strip!=actstrip) {
+                               if (strip->modifiers.first)
+                                       BLI_freelistN(&strip->modifiers);
+                               if (actstrip->modifiers.first)
+                                       duplicatelist (&strip->modifiers, &actstrip->modifiers);
+                       }
+               }
+       }
+       
+       BIF_undo_push("Copy Action Modifiers");
+       allqueue(REDRAWNLA, 0);
+       DAG_scene_flush_update(G.scene, screen_view3d_layers());
+}
+