Proportional edit for object mode.
authorMartin Poirier <theeth@yahoo.com>
Fri, 30 Oct 2009 17:48:50 +0000 (17:48 +0000)
committerMartin Poirier <theeth@yahoo.com>
Fri, 30 Oct 2009 17:48:50 +0000 (17:48 +0000)
Limitations:

1) Parents and children of selected objects are excluded from the pool (siblings are ok) Making it work with that would required unparenting and reparenting after transform, that would turn nasty really quick.

2) Does not support Connected (this could be done through parent links, but see 3 first).

3) Parent relationships in affected objects aren't taken into account. When parent and children in the area of effect, remember that the children will also take the motion of the parents (with additive results). This could perhaps be fixed, but it could be nasty.

Other stuff:
New BASE_EDITABLE macro that checks if base is editable (like TESTBASELIB except it doesn't check for selection)
Add scene parameter to TESTBASELIB_BGMODE macro (using it from current scope is nasty)

source/blender/blenkernel/intern/multires.c
source/blender/editors/object/object_ops.c
source/blender/editors/space_view3d/view3d_header.c
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_generics.c
source/blender/makesdna/DNA_object_types.h
source/blender/makesdna/DNA_scene_types.h

index ecffbb3d15fd1a6613a53c6244ed94e468fc2a89..e1a51a05ca408b9ab6a7d9ff805b01d5decf6843 100644 (file)
@@ -101,7 +101,7 @@ void multiresModifier_join(Object *ob)
        /* First find the highest level of subdivision */
        base = FIRSTBASE;
        while(base) {
-               if(TESTBASELIB_BGMODE(v3d, base) && base->object->type==OB_MESH) {
+               if(TESTBASELIB_BGMODE(v3d, scene, base) && base->object->type==OB_MESH) {
                        ModifierData *md;
                        for(md = base->object->modifiers.first; md; md = md->next) {
                                if(md->type == eModifierType_Multires) {
@@ -124,7 +124,7 @@ void multiresModifier_join(Object *ob)
        /* Subdivide all the displacements to the highest level */
        base = FIRSTBASE;
        while(base) {
-               if(TESTBASELIB_BGMODE(v3d, base) && base->object->type==OB_MESH) {
+               if(TESTBASELIB_BGMODE(v3d, scene, base) && base->object->type==OB_MESH) {
                        ModifierData *md = NULL;
                        MultiresModifierData *mmd = NULL;
 
index 2daa95577e145e7972815261de03be801f2fcb8b..85211f12a0cb1974ddaf787133bb25485c853338 100644 (file)
@@ -196,7 +196,8 @@ void ED_operatormacros_object(void)
        ot= WM_operatortype_append_macro("OBJECT_OT_duplicate_move", "Duplicate", OPTYPE_UNDO|OPTYPE_REGISTER);
        if(ot) {
                WM_operatortype_macro_define(ot, "OBJECT_OT_duplicate");
-               WM_operatortype_macro_define(ot, "TFM_OT_translate");
+               otmacro= WM_operatortype_macro_define(ot, "TFM_OT_translate");
+               RNA_enum_set(otmacro->ptr, "proportional", PROP_EDIT_OFF);
        }
 
        /* grr, should be able to pass options on... */
@@ -204,7 +205,8 @@ void ED_operatormacros_object(void)
        if(ot) {
                otmacro= WM_operatortype_macro_define(ot, "OBJECT_OT_duplicate");
                RNA_boolean_set(otmacro->ptr, "linked", 1);
-               WM_operatortype_macro_define(ot, "TFM_OT_translate");
+               otmacro= WM_operatortype_macro_define(ot, "TFM_OT_translate");
+               RNA_enum_set(otmacro->ptr, "proportional", PROP_EDIT_OFF);
        }
 }
 
@@ -238,6 +240,9 @@ void ED_keymap_object(wmKeyConfig *keyconf)
        keymap= WM_keymap_find(keyconf, "Object Mode", 0, 0);
        keymap->poll= object_mode_poll;
        
+       /* object mode supports PET now */
+       ED_object_generic_keymap(keyconf, keymap, TRUE);
+
        WM_keymap_add_item(keymap, "OBJECT_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "OBJECT_OT_select_inverse", IKEY, KM_PRESS, KM_CTRL, 0);
        WM_keymap_add_item(keymap, "OBJECT_OT_select_linked", LKEY, KM_PRESS, KM_SHIFT, 0);
index 7bf50dcd5af51654228dd943958ce606e3719233..6e8c1b602782999a2a1d61ddaad81f0d86046d4a 100644 (file)
@@ -2162,7 +2162,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
                }
        
                /* proportional falloff */
-               if((obedit && (obedit->type == OB_MESH || obedit->type == OB_CURVE || obedit->type == OB_SURF || obedit->type == OB_LATTICE)) || (ob && ob->mode & OB_MODE_PARTICLE_EDIT)) {
+               if((obedit == NULL || (obedit->type == OB_MESH || obedit->type == OB_CURVE || obedit->type == OB_SURF || obedit->type == OB_LATTICE)) || (ob && ob->mode & OB_MODE_PARTICLE_EDIT)) {
                
                        uiBlockBeginAlign(block);
                        uiDefIconTextButS(block, ICONTEXTROW,B_REDR, ICON_PROP_OFF, "Proportional %t|Off %x0|On %x1|Connected %x2", xco,yco,XIC+10,YIC, &(ts->proportional), 0, 1.0, 0, 0, "Proportional Edit Falloff (Hotkeys: O, Alt O) ");
index 0ff0e400d26ecc66d5a3485d3c4f17c111b55385..449994f2cec5761188adde5c58758fd2ca84d5c4 100644 (file)
@@ -4314,7 +4314,7 @@ static void ObjectToTransData(bContext *C, TransInfo *t, TransData *td, Object *
 /* it deselects Bases, so we have to call the clear function always after */
 static void set_trans_object_base_flags(bContext *C, TransInfo *t)
 {
-       Scene *scene = CTX_data_scene(C);
+       Scene *scene = t->scene;
        View3D *v3d = t->view;
 
        /*
@@ -4339,7 +4339,7 @@ static void set_trans_object_base_flags(bContext *C, TransInfo *t)
        for (base= scene->base.first; base; base= base->next) {
                base->flag &= ~BA_WAS_SEL;
 
-               if(TESTBASELIB_BGMODE(v3d, base)) {
+               if(TESTBASELIB_BGMODE(v3d, scene, base)) {
                        Object *ob= base->object;
                        Object *parsel= ob->parent;
 
@@ -4351,12 +4351,10 @@ static void set_trans_object_base_flags(bContext *C, TransInfo *t)
 
                        if(parsel)
                        {
-                               if ((t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL)  && t->around == V3D_LOCAL)
-                               {
+                               /* rotation around local centers are allowed to propagate */
+                               if ((t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL)  && t->around == V3D_LOCAL) {
                                        base->flag |= BA_TRANSFORM_CHILD;
-                               }
-                               else
-                               {
+                               } else {
                                        base->flag &= ~SELECT;
                                        base->flag |= BA_WAS_SEL;
                                }
@@ -4379,6 +4377,86 @@ static void set_trans_object_base_flags(bContext *C, TransInfo *t)
        }
 }
 
+static int mark_children(Object *ob)
+{
+       if (ob->flag & (SELECT|BA_TRANSFORM_CHILD))
+               return 1;
+
+       if (ob->parent)
+       {
+               if (mark_children(ob->parent))
+               {
+                       ob->flag |= BA_TRANSFORM_CHILD;
+                       return 1;
+               }
+       }
+       
+       return 0;
+}
+
+static int count_proportional_objects(TransInfo *t)
+{
+       int total = 0;
+       Scene *scene = t->scene;
+       View3D *v3d = t->view;
+       Base *base;
+
+       /* rotations around local centers are allowed to propagate, so we take all objects */
+       if (!((t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL)  && t->around == V3D_LOCAL))
+       {
+               /* mark all parents */
+               for (base= scene->base.first; base; base= base->next) {
+                       if(TESTBASELIB_BGMODE(v3d, scene, base)) {
+                               Object *parent = base->object->parent;
+       
+                               /* flag all parents */
+                               while(parent) {
+                                       parent->flag |= BA_TRANSFORM_PARENT;
+                                       parent = parent->parent;
+                               }
+                       }
+               }
+
+               /* mark all children */
+               for (base= scene->base.first; base; base= base->next) {
+                       /* all base not already selected or marked that is editable */
+                       if ((base->object->flag & (SELECT|BA_TRANSFORM_CHILD|BA_TRANSFORM_PARENT)) == 0 && BASE_EDITABLE_BGMODE(v3d, scene, base))
+                       {
+                               mark_children(base->object);
+                       }
+               }
+       }
+       
+       for (base= scene->base.first; base; base= base->next) {
+               Object *ob= base->object;
+
+               /* if base is not selected, not a parent of selection or not a child of selection and it is editable */
+               if ((ob->flag & (SELECT|BA_TRANSFORM_CHILD|BA_TRANSFORM_PARENT)) == 0 && BASE_EDITABLE_BGMODE(v3d, scene, base))
+               {
+
+                       /* used for flush, depgraph will change recalcs if needed :) */
+                       ob->recalc |= OB_RECALC_OB;
+
+                       total += 1;
+               }
+       }
+       
+
+       /* all recalc flags get flushed to all layers, so a layer flip later on works fine */
+       DAG_scene_flush_update(t->scene, -1, 0);
+
+       /* and we store them temporal in base (only used for transform code) */
+       /* this because after doing updates, the object->recalc is cleared */
+       for (base= scene->base.first; base; base= base->next) {
+               if(base->object->recalc & OB_RECALC_OB)
+                       base->flag |= BA_HAS_RECALC_OB;
+               if(base->object->recalc & OB_RECALC_DATA)
+                       base->flag |= BA_HAS_RECALC_DATA;
+       }
+
+       return total;
+}
+
 static void clear_trans_object_base_flags(TransInfo *t)
 {
        Scene *sce = t->scene;
@@ -4389,7 +4467,7 @@ static void clear_trans_object_base_flags(TransInfo *t)
                if(base->flag & BA_WAS_SEL)
                        base->flag |= SELECT;
 
-               base->flag &= ~(BA_WAS_SEL|BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA|BA_DO_IPO|BA_TRANSFORM_CHILD);
+               base->flag &= ~(BA_WAS_SEL|BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA|BA_DO_IPO|BA_TRANSFORM_CHILD|BA_TRANSFORM_PARENT);
        }
 }
 
@@ -4972,19 +5050,23 @@ static void createTransObject(bContext *C, TransInfo *t)
 {
        TransData *td = NULL;
        TransDataExtension *tx;
-//     IpoKey *ik;
-//     ListBase elems;
+       int propmode = t->flag & T_PROP_EDIT;
 
        set_trans_object_base_flags(C, t);
 
        /* count */
        t->total= CTX_DATA_COUNT(C, selected_objects);
-
+       
        if(!t->total) {
                /* clear here, main transform function escapes too */
                clear_trans_object_base_flags(t);
                return;
        }
+       
+       if (propmode)
+       {
+               t->total += count_proportional_objects(t);
+       }
 
        td = t->data = MEM_callocN(t->total*sizeof(TransData), "TransOb");
        tx = t->ext = MEM_callocN(t->total*sizeof(TransDataExtension), "TransObExtension");
@@ -5015,6 +5097,30 @@ static void createTransObject(bContext *C, TransInfo *t)
                tx++;
        }
        CTX_DATA_END;
+       
+       if (propmode)
+       {
+               Scene *scene = t->scene;
+               View3D *v3d = t->view;
+               Base *base;
+
+               for (base= scene->base.first; base; base= base->next) {
+                       Object *ob= base->object;
+
+                       /* if base is not selected, not a parent of selection or not a child of selection and it is editable */
+                       if ((ob->flag & (SELECT|BA_TRANSFORM_CHILD|BA_TRANSFORM_PARENT)) == 0 && BASE_EDITABLE_BGMODE(v3d, scene, base))
+                       {
+                               td->protectflag= ob->protectflag;
+                               td->ext = tx;
+                               td->rotOrder= ob->rotmode;
+                               
+                               ObjectToTransData(C, t, td, ob);
+                               td->val = NULL;
+                               td++;
+                               tx++;
+                       }
+               }
+       }
 }
 
 /* transcribe given node into TransData2D for Transforming */
@@ -5145,6 +5251,8 @@ void createTransData(bContext *C, TransInfo *t)
                        printf("edit type not implemented!\n");
                }
 
+               t->flag |= T_EDIT|T_POINTS;
+
                if(t->data && t->flag & T_PROP_EDIT) {
                        if (ELEM(t->obedit->type, OB_CURVE, OB_MESH)) {
                                sort_trans_data(t);     // makes selected become first in array
@@ -5158,8 +5266,6 @@ void createTransData(bContext *C, TransInfo *t)
                        }
                }
 
-               t->flag |= T_EDIT|T_POINTS;
-
                /* exception... hackish, we want bonesize to use bone orientation matrix (ton) */
                if(t->mode==TFM_BONESIZE) {
                        t->flag &= ~(T_EDIT|T_POINTS);
@@ -5190,22 +5296,27 @@ void createTransData(bContext *C, TransInfo *t)
        else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) 
                && PE_start_edit(PE_get_current(scene, ob))) {
                createTransParticleVerts(C, t);
+               t->flag |= T_POINTS;
 
                if(t->data && t->flag & T_PROP_EDIT) {
                        sort_trans_data(t);     // makes selected become first in array
                        set_prop_dist(t, 1);
                        sort_trans_data_dist(t);
                }
-
-               t->flag |= T_POINTS;
        }
        else {
-               t->flag &= ~T_PROP_EDIT; /* no proportional edit in object mode */
+               // t->flag &= ~T_PROP_EDIT; /* no proportional edit in object mode */
                t->options |= CTX_NO_PET;
                
                createTransObject(C, t);
                t->flag |= T_OBJECT;
 
+               if(t->data && t->flag & T_PROP_EDIT) {
+                       // selected objects are already first, no need to presort
+                       set_prop_dist(t, 1);
+                       sort_trans_data_dist(t);
+               }
+
                if (t->ar->regiontype == RGN_TYPE_WINDOW)
                {
                        View3D *v3d = t->view;
index 32078cb582693a11703e17e0f269a7997ff792ea..fc3661c1102d5b740b2a20dcdc28e0884659bc08 100644 (file)
@@ -1004,7 +1004,7 @@ int initTransInfo (bContext *C, TransInfo *t, wmOperator *op, wmEvent *event)
                                t->flag |= T_PROP_EDIT;
                                
                                if(ts->proportional == PROP_EDIT_CONNECTED)
-                                       t->flag |= T_PROP_CONNECTED;    // yes i know, has to become define
+                                       t->flag |= T_PROP_CONNECTED;
                        }
                }
                
index 267e01922477807f444791d6482a5be6f65e6e83..d780aec78954d4374f5968efb5f673b33b8350fe 100644 (file)
@@ -413,9 +413,10 @@ extern Object workob;
 #define BA_FROMSET                     128
 
 #define BA_TRANSFORM_CHILD     256 /* child of a transformed object */
+#define BA_TRANSFORM_PARENT    8192 /* parent of a transformed object */
 
 /* an initial attempt as making selection more specific! */
-#define BA_DESELECT    0
+#define BA_DESELECT            0
 #define BA_SELECT              1
 
 
index 952f62942ef0a3f57c188345984902f0dc64341c..bafe8cc316225ea3770f33c506ea8e7c69d51372 100644 (file)
@@ -956,7 +956,8 @@ typedef struct Scene {
 /* depricate this! */
 #define TESTBASE(v3d, base)    ( ((base)->flag & SELECT) && ((base)->lay & v3d->lay) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0) )
 #define TESTBASELIB(v3d, base) ( ((base)->flag & SELECT) && ((base)->lay & v3d->lay) && ((base)->object->id.lib==0) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0))
-#define TESTBASELIB_BGMODE(v3d, base)   ( ((base)->flag & SELECT) && ((base)->lay & (v3d ? v3d->lay : scene->lay)) && ((base)->object->id.lib==0) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0))
+#define TESTBASELIB_BGMODE(v3d, scene, base)   ( ((base)->flag & SELECT) && ((base)->lay & (v3d ? v3d->lay : scene->lay)) && ((base)->object->id.lib==0) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0))
+#define BASE_EDITABLE_BGMODE(v3d, scene, base)   (((base)->lay & (v3d ? v3d->lay : scene->lay)) && ((base)->object->id.lib==0) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0))
 #define BASE_SELECTABLE(v3d, base)      ((base->lay & v3d->lay) && (base->object->restrictflag & (OB_RESTRICT_SELECT|OB_RESTRICT_VIEW))==0)
 #define FIRSTBASE              scene->base.first
 #define LASTBASE               scene->base.last