Orientation for 3D cursor
[blender.git] / source / blender / editors / mesh / editmesh_extrude.c
index 950dc92d87ced7925394eb2441f163f5adae7911..41053d423debe46948516678ea5e70509b9f0f64 100644 (file)
 #include "BKE_context.h"
 #include "BKE_report.h"
 #include "BKE_editmesh.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
 
 #include "RNA_define.h"
 #include "RNA_access.h"
 
+#include "WM_api.h"
 #include "WM_types.h"
+#include "WM_message.h"
 
 #include "ED_mesh.h"
 #include "ED_screen.h"
 #include "ED_transform.h"
 #include "ED_view3d.h"
+#include "ED_manipulator_library.h"
 
+#include "UI_resources.h"
 
 #include "MEM_guardedalloc.h"
 
@@ -57,9 +63,6 @@
 
 #define USE_MANIPULATOR
 
-#ifdef USE_MANIPULATOR
-#endif
-
 /* -------------------------------------------------------------------- */
 /** \name Extrude Internal Utilities
  * \{ */
@@ -292,36 +295,44 @@ static bool edbm_extrude_ex(
 
 static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op)
 {
-       Object *obedit = CTX_data_edit_object(C);
-       BMEditMesh *em = BKE_editmesh_from_object(obedit);
        RegionView3D *rv3d = CTX_wm_region_view3d(C);
-
        const int steps = RNA_int_get(op->ptr, "steps");
-
        const float offs = RNA_float_get(op->ptr, "offset");
        float dvec[3], tmat[3][3], bmat[3][3];
        short a;
 
-       /* dvec */
-       normalize_v3_v3_length(dvec, rv3d->persinv[2], offs);
+       ViewLayer *view_layer = CTX_data_view_layer(C);
+       uint objects_len = 0;
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
+
+       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
 
-       /* base correction */
-       copy_m3_m4(bmat, obedit->obmat);
-       invert_m3_m3(tmat, bmat);
-       mul_m3_v3(tmat, dvec);
+               Object *obedit = objects[ob_index];
+               BMEditMesh *em = BKE_editmesh_from_object(obedit);
 
-       for (a = 0; a < steps; a++) {
-               edbm_extrude_ex(obedit, em, BM_ALL_NOLOOP, BM_ELEM_SELECT, false, false);
+               /* dvec */
+               normalize_v3_v3_length(dvec, rv3d->persinv[2], offs);
 
-               BMO_op_callf(
-                       em->bm, BMO_FLAG_DEFAULTS,
-                       "translate vec=%v verts=%hv",
-                       dvec, BM_ELEM_SELECT);
-       }
+               /* base correction */
+               copy_m3_m4(bmat, obedit->obmat);
+               invert_m3_m3(tmat, bmat);
+               mul_m3_v3(tmat, dvec);
 
-       EDBM_mesh_normals_update(em);
+               for (a = 0; a < steps; a++) {
+                       edbm_extrude_ex(obedit, em, BM_ALL_NOLOOP, BM_ELEM_SELECT, false, false);
 
-       EDBM_update_generic(em, true, true);
+                       BMO_op_callf(
+                               em->bm, BMO_FLAG_DEFAULTS,
+                               "translate vec=%v verts=%hv",
+                               dvec, BM_ELEM_SELECT);
+               }
+
+               EDBM_mesh_normals_update(em);
+
+               EDBM_update_generic(em, true, true);
+       }
+
+       MEM_freeN(objects);
 
        return OPERATOR_FINISHED;
 }
@@ -347,6 +358,192 @@ void MESH_OT_extrude_repeat(wmOperatorType *ot)
 
 /** \} */
 
+
+/* -------------------------------------------------------------------- */
+/** \name Extrude Manipulator
+ * \{ */
+
+#ifdef USE_MANIPULATOR
+
+typedef struct ManipulatorExtrudeGroup {
+       /* Extrude . */
+       struct wmManipulator *axis_arrow;
+       /* Redo Z-axis translation. */
+       struct wmManipulator *axis_redo;
+
+       wmOperatorType *ot_extrude;
+} ManipulatorExtrudeGroup;
+
+static bool manipulator_mesh_extrude_poll(const bContext *C, wmManipulatorGroupType *wgt)
+{
+       WorkSpace *workspace = CTX_wm_workspace(C);
+       if (!STREQ(workspace->tool.manipulator_group, "MESH_WGT_extrude") ||
+           !ED_operator_editmesh_view3d((bContext *)C))
+       {
+               WM_manipulator_group_type_unlink_delayed_ptr(wgt);
+               return false;
+       }
+       return true;
+}
+
+static void manipulator_mesh_extrude_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
+{
+       const int constraint_z[3] = {0, 0, 1};
+
+       struct ManipulatorExtrudeGroup *man = MEM_callocN(sizeof(ManipulatorExtrudeGroup), __func__);
+       mgroup->customdata = man;
+
+       const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true);
+       const wmManipulatorType *wt_grab = WM_manipulatortype_find("MANIPULATOR_WT_grab_3d", true);
+
+       man->axis_arrow = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL);
+       man->axis_redo = WM_manipulator_new_ptr(wt_grab, mgroup, NULL);
+
+       man->ot_extrude = WM_operatortype_find("MESH_OT_extrude_context_move", true);
+
+       UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->axis_arrow->color);
+       UI_GetThemeColor3fv(TH_MANIPULATOR_SECONDARY, man->axis_redo->color);
+
+       WM_manipulator_set_scale(man->axis_arrow, 2.0);
+       WM_manipulator_set_scale(man->axis_redo, 2.0);
+
+       RNA_enum_set(man->axis_arrow->ptr, "draw_style", ED_MANIPULATOR_ARROW_STYLE_NORMAL);
+       RNA_enum_set(man->axis_redo->ptr, "draw_style", ED_MANIPULATOR_GRAB_STYLE_RING_2D);
+
+       WM_manipulator_set_flag(man->axis_redo, WM_MANIPULATOR_DRAW_VALUE, true);
+
+       /* New extrude. */
+       {
+               PointerRNA *ptr = WM_manipulator_operator_set(man->axis_arrow, 0, man->ot_extrude, NULL);
+               {
+                       PointerRNA macroptr = RNA_pointer_get(ptr, "TRANSFORM_OT_translate");
+                       RNA_boolean_set(&macroptr, "release_confirm", true);
+                       RNA_boolean_set_array(&macroptr, "constraint_axis", constraint_z);
+               }
+       }
+       /* Adjust extrude. */
+       {
+               PointerRNA *ptr = WM_manipulator_operator_set(man->axis_redo, 0, man->ot_extrude, NULL);
+               {
+                       PointerRNA macroptr = RNA_pointer_get(ptr, "TRANSFORM_OT_translate");
+                       RNA_boolean_set(&macroptr, "release_confirm", true);
+                       RNA_boolean_set_array(&macroptr, "constraint_axis", constraint_z);
+               }
+               wmManipulatorOpElem *mpop = WM_manipulator_operator_get(man->axis_redo, 0);
+               mpop->is_redo = true;
+       }
+}
+
+static void manipulator_mesh_extrude_refresh(const bContext *C, wmManipulatorGroup *mgroup)
+{
+       ManipulatorExtrudeGroup *man = mgroup->customdata;
+
+       WM_manipulator_set_flag(man->axis_arrow, WM_MANIPULATOR_HIDDEN, true);
+       WM_manipulator_set_flag(man->axis_redo, WM_MANIPULATOR_HIDDEN, true);
+
+       if (G.moving) {
+               return;
+       }
+
+       int orientation_type;
+       {
+               PointerRNA ot_ptr;
+               WM_operator_last_properties_ensure(man->ot_extrude, &ot_ptr);
+               PointerRNA macroptr = RNA_pointer_get(&ot_ptr, "TRANSFORM_OT_translate");
+               orientation_type = RNA_enum_get(&macroptr, "constraint_orientation");
+       }
+
+       struct TransformBounds tbounds;
+       if (!ED_transform_calc_manipulator_stats(
+                   C, &(struct TransformCalcParams){
+                       .orientation_type = orientation_type + 1,
+                   }, &tbounds))
+       {
+               return;
+       }
+
+       /* Needed for normal orientation. */
+       copy_m4_m3(man->axis_arrow->matrix_basis, tbounds.axis);
+       copy_m4_m3(man->axis_redo->matrix_basis, tbounds.axis);
+
+       WM_manipulator_set_matrix_location(man->axis_arrow, tbounds.center);
+       WM_manipulator_set_matrix_location(man->axis_redo, tbounds.center);
+
+       wmOperator *op = WM_operator_last_redo(C);
+       bool has_redo = (op && op->type == man->ot_extrude);
+
+       WM_manipulator_set_flag(man->axis_arrow, WM_MANIPULATOR_HIDDEN, false);
+       WM_manipulator_set_flag(man->axis_redo, WM_MANIPULATOR_HIDDEN, !has_redo);
+
+       {
+               wmManipulatorOpElem *mpop = WM_manipulator_operator_get(man->axis_arrow, 0);
+               PointerRNA macroptr = RNA_pointer_get(&mpop->ptr, "TRANSFORM_OT_translate");
+               RNA_enum_set(&macroptr, "constraint_orientation", orientation_type);
+       }
+
+       /* Redo with current settings. */
+       if (has_redo) {
+               /* We could also access this from 'ot->last_properties' */
+
+               wmOperator *op_transform = op->macro.last;
+               wmManipulatorOpElem *mpop = WM_manipulator_operator_get(man->axis_redo, 0);
+
+               PointerRNA macroptr = RNA_pointer_get(&mpop->ptr, "TRANSFORM_OT_translate");
+
+               float value[4];
+               RNA_float_get_array(op_transform->ptr, "value", value);
+               RNA_float_set_array(&macroptr, "value", value);
+
+               /* Currently has glitch in re-applying. */
+#if 0
+               int constraint_axis[3];
+               RNA_boolean_get_array(op_transform->ptr, "constraint_axis", constraint_axis);
+               RNA_boolean_set_array(&macroptr, "constraint_axis", constraint_axis);
+#endif
+       }
+}
+
+static void manipulator_mesh_extrude_message_subscribe(
+        const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus)
+{
+       ManipulatorExtrudeGroup *man = mgroup->customdata;
+       ARegion *ar = CTX_wm_region(C);
+
+       /* Subscribe to view properties */
+       wmMsgSubscribeValue msg_sub_value_mpr_tag_refresh = {
+               .owner = ar,
+               .user_data = mgroup->parent_mmap,
+               .notify = WM_manipulator_do_msg_notify_tag_refresh,
+       };
+
+       {
+               PointerRNA ot_ptr;
+               WM_operator_last_properties_ensure(man->ot_extrude, &ot_ptr);
+               PointerRNA macroptr = RNA_pointer_get(&ot_ptr, "TRANSFORM_OT_translate");
+               WM_msg_subscribe_rna(mbus, &macroptr, NULL, &msg_sub_value_mpr_tag_refresh, __func__);
+       }
+}
+
+static void MESH_WGT_extrude(struct wmManipulatorGroupType *wgt)
+{
+       wgt->name = "Mesh Extrude";
+       wgt->idname = "MESH_WGT_extrude";
+
+       wgt->flag = WM_MANIPULATORGROUPTYPE_3D;
+
+       wgt->mmap_params.spaceid = SPACE_VIEW3D;
+       wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
+
+       wgt->poll = manipulator_mesh_extrude_poll;
+       wgt->setup = manipulator_mesh_extrude_setup;
+       wgt->refresh = manipulator_mesh_extrude_refresh;
+       wgt->message_subscribe = manipulator_mesh_extrude_message_subscribe;
+}
+
+#endif  /* USE_MANIPULATOR */
+
+/** \} */
+
 /* -------------------------------------------------------------------- */
 /** \name Extrude Operator
  * \{ */
@@ -411,7 +608,9 @@ static int edbm_extrude_region_exec(bContext *C, wmOperator *op)
                        continue;
                }
 
-               edbm_extrude_mesh(obedit, em, op);
+               if (!edbm_extrude_mesh(obedit, em, op)) {
+                       continue;
+               }
                /* This normally happens when pushing undo but modal operators
                 * like this one don't push undo data until after modal mode is
                 * done.*/
@@ -443,6 +642,62 @@ void MESH_OT_extrude_region(wmOperatorType *ot)
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name Extrude Context Operator
+ *
+ * Guess what to do based on selection.
+ * \{ */
+
+/* extrude without transform */
+static int edbm_extrude_context_exec(bContext *C, wmOperator *op)
+{
+       ViewLayer *view_layer = CTX_data_view_layer(C);
+       uint objects_len = 0;
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
+
+       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+               Object *obedit = objects[ob_index];
+               BMEditMesh *em = BKE_editmesh_from_object(obedit);
+               if (em->bm->totvertsel == 0) {
+                       continue;
+               }
+
+               edbm_extrude_mesh(obedit, em, op);
+               /* This normally happens when pushing undo but modal operators
+                * like this one don't push undo data until after modal mode is
+                * done.*/
+
+               EDBM_mesh_normals_update(em);
+
+               EDBM_update_generic(em, true, true);
+       }
+       MEM_freeN(objects);
+       return OPERATOR_FINISHED;
+}
+
+void MESH_OT_extrude_context(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Extrude Context";
+       ot->idname = "MESH_OT_extrude_context";
+       ot->description = "Extrude selection";
+
+       /* api callbacks */
+       ot->exec = edbm_extrude_context_exec;
+       ot->poll = ED_operator_editmesh;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
+
+#ifdef USE_MANIPULATOR
+       WM_manipulatorgrouptype_append(MESH_WGT_extrude);
+#endif
+}
+
+/** \} */
+
 /* -------------------------------------------------------------------- */
 /** \name Extrude Verts Operator
  * \{ */
@@ -718,7 +973,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w
                              BM_ELEM_SELECT, ofs);
        }
        else {
-               const float *cursor = ED_view3d_cursor3d_get(vc.scene, vc.v3d);
+               const float *cursor = ED_view3d_cursor3d_get(vc.scene, vc.v3d)->location;
                BMOperator bmop;
                BMOIter oiter;