Fix T64283: Gizmo doesn't update after click-extrude
[blender.git] / source / blender / editors / mesh / editmesh_extrude.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2004 by Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edmesh
22  */
23
24 #include "DNA_modifier_types.h"
25 #include "DNA_object_types.h"
26
27 #include "BLI_math.h"
28 #include "BLI_listbase.h"
29
30 #include "BKE_layer.h"
31 #include "BKE_context.h"
32 #include "BKE_report.h"
33 #include "BKE_editmesh.h"
34
35 #include "RNA_define.h"
36 #include "RNA_access.h"
37
38 #include "WM_types.h"
39 #include "WM_api.h"
40
41 #include "ED_mesh.h"
42 #include "ED_screen.h"
43 #include "ED_transform.h"
44 #include "ED_view3d.h"
45
46 #include "MEM_guardedalloc.h"
47
48 #include "mesh_intern.h" /* own include */
49
50 /* -------------------------------------------------------------------- */
51 /** \name Extrude Internal Utilities
52  * \{ */
53
54 static void edbm_extrude_edge_exclude_mirror(
55     Object *obedit, BMEditMesh *em, const char hflag, BMOperator *op, BMOpSlot *slot_edges_exclude)
56 {
57   BMesh *bm = em->bm;
58   ModifierData *md;
59
60   /* If a mirror modifier with clipping is on, we need to adjust some
61    * of the cases above to handle edges on the line of symmetry.
62    */
63   for (md = obedit->modifiers.first; md; md = md->next) {
64     if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
65       MirrorModifierData *mmd = (MirrorModifierData *)md;
66
67       if (mmd->flag & MOD_MIR_CLIPPING) {
68         BMIter iter;
69         BMEdge *edge;
70
71         float mtx[4][4];
72         if (mmd->mirror_ob) {
73           float imtx[4][4];
74           invert_m4_m4(imtx, mmd->mirror_ob->obmat);
75           mul_m4_m4m4(mtx, imtx, obedit->obmat);
76         }
77
78         BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
79           if (BM_elem_flag_test(edge, hflag) && BM_edge_is_boundary(edge) &&
80               BM_elem_flag_test(edge->l->f, hflag)) {
81             float co1[3], co2[3];
82
83             copy_v3_v3(co1, edge->v1->co);
84             copy_v3_v3(co2, edge->v2->co);
85
86             if (mmd->mirror_ob) {
87               mul_v3_m4v3(co1, mtx, co1);
88               mul_v3_m4v3(co2, mtx, co2);
89             }
90
91             if (mmd->flag & MOD_MIR_AXIS_X) {
92               if ((fabsf(co1[0]) < mmd->tolerance) && (fabsf(co2[0]) < mmd->tolerance)) {
93                 BMO_slot_map_empty_insert(op, slot_edges_exclude, edge);
94               }
95             }
96             if (mmd->flag & MOD_MIR_AXIS_Y) {
97               if ((fabsf(co1[1]) < mmd->tolerance) && (fabsf(co2[1]) < mmd->tolerance)) {
98                 BMO_slot_map_empty_insert(op, slot_edges_exclude, edge);
99               }
100             }
101             if (mmd->flag & MOD_MIR_AXIS_Z) {
102               if ((fabsf(co1[2]) < mmd->tolerance) && (fabsf(co2[2]) < mmd->tolerance)) {
103                 BMO_slot_map_empty_insert(op, slot_edges_exclude, edge);
104               }
105             }
106           }
107         }
108       }
109     }
110   }
111 }
112
113 /* individual face extrude */
114 /* will use vertex normals for extrusion directions, so *nor is unaffected */
115 static bool edbm_extrude_discrete_faces(BMEditMesh *em, wmOperator *op, const char hflag)
116 {
117   BMOIter siter;
118   BMIter liter;
119   BMFace *f;
120   BMLoop *l;
121   BMOperator bmop;
122
123   EDBM_op_init(
124       em, &bmop, op, "extrude_discrete_faces faces=%hf use_select_history=%b", hflag, true);
125
126   /* deselect original verts */
127   EDBM_flag_disable_all(em, BM_ELEM_SELECT);
128
129   BMO_op_exec(em->bm, &bmop);
130
131   BMO_ITER (f, &siter, bmop.slots_out, "faces.out", BM_FACE) {
132     BM_face_select_set(em->bm, f, true);
133
134     /* set face vertex normals to face normal */
135     BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
136       copy_v3_v3(l->v->no, f->no);
137     }
138   }
139
140   if (!EDBM_op_finish(em, &bmop, op, true)) {
141     return false;
142   }
143
144   return true;
145 }
146
147 /* extrudes individual edges */
148 static bool edbm_extrude_edges_indiv(BMEditMesh *em,
149                                      wmOperator *op,
150                                      const char hflag,
151                                      const bool use_normal_flip)
152 {
153   BMesh *bm = em->bm;
154   BMOperator bmop;
155
156   EDBM_op_init(em,
157                &bmop,
158                op,
159                "extrude_edge_only edges=%he use_normal_flip=%b use_select_history=%b",
160                hflag,
161                use_normal_flip,
162                true);
163
164   /* deselect original verts */
165   BM_SELECT_HISTORY_BACKUP(bm);
166   EDBM_flag_disable_all(em, BM_ELEM_SELECT);
167   BM_SELECT_HISTORY_RESTORE(bm);
168
169   BMO_op_exec(em->bm, &bmop);
170   BMO_slot_buffer_hflag_enable(
171       em->bm, bmop.slots_out, "geom.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true);
172
173   if (!EDBM_op_finish(em, &bmop, op, true)) {
174     return false;
175   }
176
177   return true;
178 }
179
180 /* extrudes individual vertices */
181 static bool edbm_extrude_verts_indiv(BMEditMesh *em, wmOperator *op, const char hflag)
182 {
183   BMOperator bmop;
184
185   EDBM_op_init(em, &bmop, op, "extrude_vert_indiv verts=%hv use_select_history=%b", hflag, true);
186
187   /* deselect original verts */
188   BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_in, "verts", BM_VERT, BM_ELEM_SELECT, true);
189
190   BMO_op_exec(em->bm, &bmop);
191   BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
192
193   if (!EDBM_op_finish(em, &bmop, op, true)) {
194     return false;
195   }
196
197   return true;
198 }
199
200 static char edbm_extrude_htype_from_em_select(BMEditMesh *em)
201 {
202   char htype = BM_ALL_NOLOOP;
203
204   if (em->selectmode & SCE_SELECT_VERTEX) {
205     /* pass */
206   }
207   else if (em->selectmode & SCE_SELECT_EDGE) {
208     htype &= ~BM_VERT;
209   }
210   else {
211     htype &= ~(BM_VERT | BM_EDGE);
212   }
213
214   if (em->bm->totedgesel == 0) {
215     htype &= ~(BM_EDGE | BM_FACE);
216   }
217   else if (em->bm->totfacesel == 0) {
218     htype &= ~BM_FACE;
219   }
220
221   return htype;
222 }
223
224 static bool edbm_extrude_ex(Object *obedit,
225                             BMEditMesh *em,
226                             char htype,
227                             const char hflag,
228                             const bool use_normal_flip,
229                             const bool use_mirror,
230                             const bool use_select_history)
231 {
232   BMesh *bm = em->bm;
233   BMOIter siter;
234   BMOperator extop;
235   BMElem *ele;
236
237   /* needed to remove the faces left behind */
238   if (htype & BM_FACE) {
239     htype |= BM_EDGE;
240   }
241
242   BMO_op_init(bm, &extop, BMO_FLAG_DEFAULTS, "extrude_face_region");
243   BMO_slot_bool_set(extop.slots_in, "use_normal_flip", use_normal_flip);
244   BMO_slot_bool_set(extop.slots_in, "use_select_history", use_select_history);
245   BMO_slot_buffer_from_enabled_hflag(bm, &extop, extop.slots_in, "geom", htype, hflag);
246
247   if (use_mirror) {
248     BMOpSlot *slot_edges_exclude;
249     slot_edges_exclude = BMO_slot_get(extop.slots_in, "edges_exclude");
250
251     edbm_extrude_edge_exclude_mirror(obedit, em, hflag, &extop, slot_edges_exclude);
252   }
253
254   BM_SELECT_HISTORY_BACKUP(bm);
255   EDBM_flag_disable_all(em, BM_ELEM_SELECT);
256   BM_SELECT_HISTORY_RESTORE(bm);
257
258   BMO_op_exec(bm, &extop);
259
260   BMO_ITER (ele, &siter, extop.slots_out, "geom.out", BM_ALL_NOLOOP) {
261     BM_elem_select_set(bm, ele, true);
262   }
263
264   BMO_op_finish(bm, &extop);
265
266   return true;
267 }
268
269 /** \} */
270
271 /* -------------------------------------------------------------------- */
272 /** \name Extrude Repeat Operator
273  * \{ */
274
275 static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op)
276 {
277   RegionView3D *rv3d = CTX_wm_region_view3d(C);
278   const int steps = RNA_int_get(op->ptr, "steps");
279   const float offs = RNA_float_get(op->ptr, "offset");
280   float dvec[3], tmat[3][3], bmat[3][3];
281   short a;
282
283   ViewLayer *view_layer = CTX_data_view_layer(C);
284   uint objects_len = 0;
285   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
286       view_layer, CTX_wm_view3d(C), &objects_len);
287
288   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
289
290     Object *obedit = objects[ob_index];
291     BMEditMesh *em = BKE_editmesh_from_object(obedit);
292
293     /* dvec */
294     normalize_v3_v3_length(dvec, rv3d->persinv[2], offs);
295
296     /* base correction */
297     copy_m3_m4(bmat, obedit->obmat);
298     invert_m3_m3(tmat, bmat);
299     mul_m3_v3(tmat, dvec);
300
301     for (a = 0; a < steps; a++) {
302       edbm_extrude_ex(obedit, em, BM_ALL_NOLOOP, BM_ELEM_SELECT, false, false, false);
303
304       BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS, "translate vec=%v verts=%hv", dvec, BM_ELEM_SELECT);
305     }
306
307     EDBM_mesh_normals_update(em);
308
309     EDBM_update_generic(em, true, true);
310   }
311
312   MEM_freeN(objects);
313
314   return OPERATOR_FINISHED;
315 }
316
317 void MESH_OT_extrude_repeat(wmOperatorType *ot)
318 {
319   /* identifiers */
320   ot->name = "Extrude Repeat Mesh";
321   ot->description = "Extrude selected vertices, edges or faces repeatedly";
322   ot->idname = "MESH_OT_extrude_repeat";
323
324   /* api callbacks */
325   ot->exec = edbm_extrude_repeat_exec;
326   ot->poll = ED_operator_editmesh_view3d;
327
328   /* flags */
329   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
330
331   /* props */
332   RNA_def_float_distance(ot->srna, "offset", 2.0f, 0.0f, 1e12f, "Offset", "", 0.0f, 100.0f);
333   RNA_def_int(ot->srna, "steps", 10, 0, 1000000, "Steps", "", 0, 180);
334 }
335
336 /** \} */
337
338 /* -------------------------------------------------------------------- */
339 /** \name Extrude Operator
340  * \{ */
341
342 /* generic extern called extruder */
343 static bool edbm_extrude_mesh(Object *obedit, BMEditMesh *em, wmOperator *op)
344 {
345   const bool use_normal_flip = RNA_boolean_get(op->ptr, "use_normal_flip");
346   const char htype = edbm_extrude_htype_from_em_select(em);
347   enum { NONE = 0, ELEM_FLAG, VERT_ONLY, EDGE_ONLY } nr;
348   bool changed = false;
349
350   if (em->selectmode & SCE_SELECT_VERTEX) {
351     if (em->bm->totvertsel == 0) {
352       nr = NONE;
353     }
354     else if (em->bm->totvertsel == 1) {
355       nr = VERT_ONLY;
356     }
357     else if (em->bm->totedgesel == 0) {
358       nr = VERT_ONLY;
359     }
360     else {
361       nr = ELEM_FLAG;
362     }
363   }
364   else if (em->selectmode & SCE_SELECT_EDGE) {
365     if (em->bm->totedgesel == 0) {
366       nr = NONE;
367     }
368     else if (em->bm->totfacesel == 0) {
369       nr = EDGE_ONLY;
370     }
371     else {
372       nr = ELEM_FLAG;
373     }
374   }
375   else {
376     if (em->bm->totfacesel == 0) {
377       nr = NONE;
378     }
379     else {
380       nr = ELEM_FLAG;
381     }
382   }
383
384   switch (nr) {
385     case NONE:
386       return false;
387     case ELEM_FLAG:
388       changed = edbm_extrude_ex(obedit, em, htype, BM_ELEM_SELECT, use_normal_flip, true, true);
389       break;
390     case VERT_ONLY:
391       changed = edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT);
392       break;
393     case EDGE_ONLY:
394       changed = edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT, use_normal_flip);
395       break;
396   }
397
398   if (changed) {
399     return true;
400   }
401   else {
402     BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude");
403     return false;
404   }
405 }
406
407 /* extrude without transform */
408 static int edbm_extrude_region_exec(bContext *C, wmOperator *op)
409 {
410   ViewLayer *view_layer = CTX_data_view_layer(C);
411   uint objects_len = 0;
412   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
413       view_layer, CTX_wm_view3d(C), &objects_len);
414
415   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
416     Object *obedit = objects[ob_index];
417     BMEditMesh *em = BKE_editmesh_from_object(obedit);
418     if (em->bm->totvertsel == 0) {
419       continue;
420     }
421
422     if (!edbm_extrude_mesh(obedit, em, op)) {
423       continue;
424     }
425     /* This normally happens when pushing undo but modal operators
426      * like this one don't push undo data until after modal mode is
427      * done.*/
428     EDBM_mesh_normals_update(em);
429
430     EDBM_update_generic(em, true, true);
431   }
432   MEM_freeN(objects);
433   return OPERATOR_FINISHED;
434 }
435
436 void MESH_OT_extrude_region(wmOperatorType *ot)
437 {
438   /* identifiers */
439   ot->name = "Extrude Region";
440   ot->idname = "MESH_OT_extrude_region";
441   ot->description = "Extrude region of faces";
442
443   /* api callbacks */
444   // ot->invoke = mesh_extrude_region_invoke;
445   ot->exec = edbm_extrude_region_exec;
446   ot->poll = ED_operator_editmesh;
447
448   /* flags */
449   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
450
451   RNA_def_boolean(ot->srna, "use_normal_flip", false, "Flip Normals", "");
452   Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
453 }
454
455 /** \} */
456
457 /* -------------------------------------------------------------------- */
458 /** \name Extrude Context Operator
459  *
460  * Guess what to do based on selection.
461  * \{ */
462
463 /* extrude without transform */
464 static int edbm_extrude_context_exec(bContext *C, wmOperator *op)
465 {
466   ViewLayer *view_layer = CTX_data_view_layer(C);
467   uint objects_len = 0;
468   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
469       view_layer, CTX_wm_view3d(C), &objects_len);
470
471   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
472     Object *obedit = objects[ob_index];
473     BMEditMesh *em = BKE_editmesh_from_object(obedit);
474     if (em->bm->totvertsel == 0) {
475       continue;
476     }
477
478     edbm_extrude_mesh(obedit, em, op);
479     /* This normally happens when pushing undo but modal operators
480      * like this one don't push undo data until after modal mode is
481      * done.*/
482
483     EDBM_mesh_normals_update(em);
484
485     EDBM_update_generic(em, true, true);
486   }
487   MEM_freeN(objects);
488   return OPERATOR_FINISHED;
489 }
490
491 void MESH_OT_extrude_context(wmOperatorType *ot)
492 {
493   /* identifiers */
494   ot->name = "Extrude Context";
495   ot->idname = "MESH_OT_extrude_context";
496   ot->description = "Extrude selection";
497
498   /* api callbacks */
499   ot->exec = edbm_extrude_context_exec;
500   ot->poll = ED_operator_editmesh;
501
502   /* flags */
503   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
504
505   RNA_def_boolean(ot->srna, "use_normal_flip", false, "Flip Normals", "");
506   Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
507 }
508
509 /** \} */
510
511 /* -------------------------------------------------------------------- */
512 /** \name Extrude Verts Operator
513  * \{ */
514
515 static int edbm_extrude_verts_exec(bContext *C, wmOperator *op)
516 {
517   ViewLayer *view_layer = CTX_data_view_layer(C);
518   uint objects_len = 0;
519   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
520       view_layer, CTX_wm_view3d(C), &objects_len);
521
522   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
523     Object *obedit = objects[ob_index];
524     BMEditMesh *em = BKE_editmesh_from_object(obedit);
525     if (em->bm->totvertsel == 0) {
526       continue;
527     }
528
529     edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT);
530
531     EDBM_update_generic(em, true, true);
532   }
533   MEM_freeN(objects);
534
535   return OPERATOR_FINISHED;
536 }
537
538 void MESH_OT_extrude_verts_indiv(wmOperatorType *ot)
539 {
540   /* identifiers */
541   ot->name = "Extrude Only Vertices";
542   ot->idname = "MESH_OT_extrude_verts_indiv";
543   ot->description = "Extrude individual vertices only";
544
545   /* api callbacks */
546   ot->exec = edbm_extrude_verts_exec;
547   ot->poll = ED_operator_editmesh;
548
549   /* flags */
550   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
551
552   /* to give to transform */
553   Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
554 }
555
556 /** \} */
557
558 /* -------------------------------------------------------------------- */
559 /** \name Extrude Edges Operator
560  * \{ */
561
562 static int edbm_extrude_edges_exec(bContext *C, wmOperator *op)
563 {
564   const bool use_normal_flip = RNA_boolean_get(op->ptr, "use_normal_flip");
565   ViewLayer *view_layer = CTX_data_view_layer(C);
566   uint objects_len = 0;
567   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
568       view_layer, CTX_wm_view3d(C), &objects_len);
569
570   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
571     Object *obedit = objects[ob_index];
572     BMEditMesh *em = BKE_editmesh_from_object(obedit);
573     if (em->bm->totedgesel == 0) {
574       continue;
575     }
576
577     edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT, use_normal_flip);
578
579     EDBM_update_generic(em, true, true);
580   }
581   MEM_freeN(objects);
582
583   return OPERATOR_FINISHED;
584 }
585
586 void MESH_OT_extrude_edges_indiv(wmOperatorType *ot)
587 {
588   /* identifiers */
589   ot->name = "Extrude Only Edges";
590   ot->idname = "MESH_OT_extrude_edges_indiv";
591   ot->description = "Extrude individual edges only";
592
593   /* api callbacks */
594   ot->exec = edbm_extrude_edges_exec;
595   ot->poll = ED_operator_editmesh;
596
597   /* flags */
598   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
599
600   /* to give to transform */
601   RNA_def_boolean(ot->srna, "use_normal_flip", false, "Flip Normals", "");
602   Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
603 }
604
605 /** \} */
606
607 /* -------------------------------------------------------------------- */
608 /** \name Extrude Faces Operator
609  * \{ */
610
611 static int edbm_extrude_faces_exec(bContext *C, wmOperator *op)
612 {
613   ViewLayer *view_layer = CTX_data_view_layer(C);
614   uint objects_len = 0;
615   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
616       view_layer, CTX_wm_view3d(C), &objects_len);
617
618   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
619     Object *obedit = objects[ob_index];
620     BMEditMesh *em = BKE_editmesh_from_object(obedit);
621     if (em->bm->totfacesel == 0) {
622       continue;
623     }
624
625     edbm_extrude_discrete_faces(em, op, BM_ELEM_SELECT);
626
627     EDBM_update_generic(em, true, true);
628   }
629   MEM_freeN(objects);
630
631   return OPERATOR_FINISHED;
632 }
633
634 void MESH_OT_extrude_faces_indiv(wmOperatorType *ot)
635 {
636   /* identifiers */
637   ot->name = "Extrude Individual Faces";
638   ot->idname = "MESH_OT_extrude_faces_indiv";
639   ot->description = "Extrude individual faces only";
640
641   /* api callbacks */
642   ot->exec = edbm_extrude_faces_exec;
643   ot->poll = ED_operator_editmesh;
644
645   /* flags */
646   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
647
648   Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
649 }
650
651 /** \} */
652
653 /* -------------------------------------------------------------------- */
654 /** \name Dupli-Extrude Operator
655  *
656  * Add-click-mesh (extrude) operator.
657  * \{ */
658
659 static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
660 {
661   ViewContext vc;
662   BMVert *v1;
663   BMIter iter;
664   float center[3];
665   uint verts_len;
666
667   em_setup_viewcontext(C, &vc);
668   const Object *object_active = vc.obact;
669
670   const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source");
671   const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
672                          (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
673
674   /* First calculate the center of transformation. */
675   zero_v3(center);
676   verts_len = 0;
677
678   uint objects_len = 0;
679   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
680       vc.view_layer, vc.v3d, &objects_len);
681   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
682     Object *obedit = objects[ob_index];
683     ED_view3d_viewcontext_init_object(&vc, obedit);
684     const int local_verts_len = vc.em->bm->totvertsel;
685
686     if (vc.em->bm->totvertsel == 0) {
687       continue;
688     }
689
690     float local_center[3];
691     zero_v3(local_center);
692
693     BM_ITER_MESH (v1, &iter, vc.em->bm, BM_VERTS_OF_MESH) {
694       if (BM_elem_flag_test(v1, BM_ELEM_SELECT)) {
695         add_v3_v3(local_center, v1->co);
696       }
697     }
698
699     mul_v3_fl(local_center, 1.0f / (float)local_verts_len);
700     mul_m4_v3(vc.obedit->obmat, local_center);
701     mul_v3_fl(local_center, (float)local_verts_len);
702
703     add_v3_v3(center, local_center);
704     verts_len += local_verts_len;
705   }
706
707   if (verts_len != 0) {
708     mul_v3_fl(center, 1.0f / (float)verts_len);
709   }
710
711   /* Then we process the meshes. */
712   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
713     Object *obedit = objects[ob_index];
714     ED_view3d_viewcontext_init_object(&vc, obedit);
715
716     if (verts_len != 0) {
717       if (vc.em->bm->totvertsel == 0) {
718         continue;
719       }
720     }
721     else if (obedit != object_active) {
722       continue;
723     }
724
725     invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
726     ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
727
728     float local_center[3];
729     mul_v3_m4v3(local_center, vc.obedit->imat, center);
730
731     /* call extrude? */
732     if (verts_len != 0) {
733       const char extrude_htype = edbm_extrude_htype_from_em_select(vc.em);
734       BMEdge *eed;
735       float mat[3][3];
736       float vec[3], ofs[3];
737       float nor[3] = {0.0, 0.0, 0.0};
738
739       /* 2D normal calc */
740       const float mval_f[2] = {(float)event->mval[0], (float)event->mval[1]};
741
742       /* check for edges that are half selected, use for rotation */
743       bool done = false;
744       BM_ITER_MESH (eed, &iter, vc.em->bm, BM_EDGES_OF_MESH) {
745         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
746           float co1[2], co2[2];
747
748           if ((ED_view3d_project_float_object(vc.ar, eed->v1->co, co1, V3D_PROJ_TEST_NOP) ==
749                V3D_PROJ_RET_OK) &&
750               (ED_view3d_project_float_object(vc.ar, eed->v2->co, co2, V3D_PROJ_TEST_NOP) ==
751                V3D_PROJ_RET_OK)) {
752             /* 2D rotate by 90d while adding.
753              *  (x, y) = (y, -x)
754              *
755              * accumulate the screenspace normal in 2D,
756              * with screenspace edge length weighting the result. */
757             if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
758               nor[0] += (co1[1] - co2[1]);
759               nor[1] += -(co1[0] - co2[0]);
760             }
761             else {
762               nor[0] += (co2[1] - co1[1]);
763               nor[1] += -(co2[0] - co1[0]);
764             }
765             done = true;
766           }
767         }
768       }
769
770       if (done) {
771         float view_vec[3], cross[3];
772
773         /* convert the 2D normal into 3D */
774         mul_mat3_m4_v3(vc.rv3d->viewinv, nor); /* worldspace */
775         mul_mat3_m4_v3(vc.obedit->imat, nor);  /* local space */
776
777         /* correct the normal to be aligned on the view plane */
778         mul_v3_mat3_m4v3(view_vec, vc.obedit->imat, vc.rv3d->viewinv[2]);
779         cross_v3_v3v3(cross, nor, view_vec);
780         cross_v3_v3v3(nor, view_vec, cross);
781         normalize_v3(nor);
782       }
783
784       /* center */
785       copy_v3_v3(ofs, local_center);
786
787       mul_m4_v3(vc.obedit->obmat, ofs); /* view space */
788       ED_view3d_win_to_3d_int(vc.v3d, vc.ar, ofs, event->mval, ofs);
789       mul_m4_v3(vc.obedit->imat, ofs);  // back in object space
790
791       sub_v3_v3(ofs, local_center);
792
793       /* calculate rotation */
794       unit_m3(mat);
795       if (done) {
796         float angle;
797
798         normalize_v3_v3(vec, ofs);
799
800         angle = angle_normalized_v3v3(vec, nor);
801
802         if (angle != 0.0f) {
803           float axis[3];
804
805           cross_v3_v3v3(axis, nor, vec);
806
807           /* halve the rotation if its applied twice */
808           if (rot_src) {
809             angle *= 0.5f;
810           }
811
812           axis_angle_to_mat3(mat, axis, angle);
813         }
814       }
815
816       if (rot_src) {
817         EDBM_op_callf(
818             vc.em, op, "rotate verts=%hv cent=%v matrix=%m3", BM_ELEM_SELECT, local_center, mat);
819
820         /* also project the source, for retopo workflow */
821         if (use_proj) {
822           EDBM_project_snap_verts(C, vc.ar, vc.em);
823         }
824       }
825
826       edbm_extrude_ex(vc.obedit, vc.em, extrude_htype, BM_ELEM_SELECT, false, true, true);
827       EDBM_op_callf(
828           vc.em, op, "rotate verts=%hv cent=%v matrix=%m3", BM_ELEM_SELECT, local_center, mat);
829       EDBM_op_callf(vc.em, op, "translate verts=%hv vec=%v", BM_ELEM_SELECT, ofs);
830     }
831     else {
832       /* This only runs for the active object. */
833       const float *cursor = vc.scene->cursor.location;
834       BMOperator bmop;
835       BMOIter oiter;
836
837       copy_v3_v3(local_center, cursor);
838       ED_view3d_win_to_3d_int(vc.v3d, vc.ar, local_center, event->mval, local_center);
839
840       mul_m4_v3(vc.obedit->imat, local_center);  // back in object space
841
842       EDBM_op_init(vc.em, &bmop, op, "create_vert co=%v", local_center);
843       BMO_op_exec(vc.em->bm, &bmop);
844
845       BMO_ITER (v1, &oiter, bmop.slots_out, "vert.out", BM_VERT) {
846         BM_vert_select_set(vc.em->bm, v1, true);
847       }
848
849       if (!EDBM_op_finish(vc.em, &bmop, op, true)) {
850         continue;
851       }
852     }
853
854     if (use_proj) {
855       EDBM_project_snap_verts(C, vc.ar, vc.em);
856     }
857
858     /* This normally happens when pushing undo but modal operators
859      * like this one don't push undo data until after modal mode is
860      * done. */
861     EDBM_mesh_normals_update(vc.em);
862
863     EDBM_update_generic(vc.em, true, true);
864
865     WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
866     WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
867   }
868   MEM_freeN(objects);
869
870   return OPERATOR_FINISHED;
871 }
872
873 void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
874 {
875   /* identifiers */
876   ot->name = "Duplicate or Extrude to Cursor";
877   ot->idname = "MESH_OT_dupli_extrude_cursor";
878   ot->description =
879       "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor";
880
881   /* api callbacks */
882   ot->invoke = edbm_dupli_extrude_cursor_invoke;
883   ot->poll = ED_operator_editmesh_region_view3d;
884
885   /* flags */
886   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
887
888   RNA_def_boolean(ot->srna,
889                   "rotate_source",
890                   true,
891                   "Rotate Source",
892                   "Rotate initial selection giving better shape");
893 }
894
895 /** \} */