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