Fix T58113 Multiple problems with bevel harden normals.
[blender.git] / source / blender / editors / mesh / editmesh_bevel.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Joseph Eagar, Howard Trickey, Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/mesh/editmesh_bevel.c
24  *  \ingroup edmesh
25  */
26
27 #include "MEM_guardedalloc.h"
28
29 #include "DNA_object_types.h"
30
31 #include "BLI_string.h"
32 #include "BLI_math.h"
33 #include "BLI_linklist_stack.h"
34
35 #include "BLT_translation.h"
36
37 #include "BKE_context.h"
38 #include "BKE_global.h"
39 #include "BKE_editmesh.h"
40 #include "BKE_unit.h"
41 #include "BKE_layer.h"
42 #include "BKE_mesh.h"
43
44 #include "RNA_define.h"
45 #include "RNA_access.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "UI_interface.h"
51
52 #include "ED_mesh.h"
53 #include "ED_numinput.h"
54 #include "ED_screen.h"
55 #include "ED_space_api.h"
56 #include "ED_transform.h"
57 #include "ED_view3d.h"
58
59 #include "mesh_intern.h"  /* own include */
60
61
62 #define MVAL_PIXEL_MARGIN  5.0f
63
64 #define PROFILE_HARD_MIN 0.0f
65
66 #define SEGMENTS_HARD_MAX 1000
67
68 /* which value is mouse movement and numeric input controlling? */
69 #define OFFSET_VALUE 0
70 #define OFFSET_VALUE_PERCENT 1
71 #define PROFILE_VALUE 2
72 #define SEGMENTS_VALUE 3
73 #define NUM_VALUE_KINDS 4
74
75 static const char *value_rna_name[NUM_VALUE_KINDS] = {"offset", "offset", "profile", "segments"};
76 static const float value_clamp_min[NUM_VALUE_KINDS] = {0.0f, 0.0f, PROFILE_HARD_MIN, 1.0f};
77 static const float value_clamp_max[NUM_VALUE_KINDS] = {1e6, 100.0f, 1.0f, SEGMENTS_HARD_MAX};
78 static const float value_start[NUM_VALUE_KINDS] = {0.0f, 0.0f, 0.5f, 1.0f};
79 static const float value_scale_per_inch[NUM_VALUE_KINDS] = { 0.0f, 100.0f, 1.0f, 4.0f};
80
81 typedef struct {
82         BMEditMesh *em;
83         BMBackup mesh_backup;
84 } BevelObjectStore;
85
86
87 typedef struct {
88         float initial_length[NUM_VALUE_KINDS];
89         float scale[NUM_VALUE_KINDS];
90         NumInput num_input[NUM_VALUE_KINDS];
91         float shift_value[NUM_VALUE_KINDS]; /* The current value when shift is pressed. Negative when shift not active. */
92         bool is_modal;
93
94         BevelObjectStore *ob_store;
95         uint ob_store_len;
96
97         /* modal only */
98         float mcenter[2];
99         void *draw_handle_pixel;
100         short gizmo_flag;
101         short value_mode;  /* Which value does mouse movement and numeric input affect? */
102         float segments;     /* Segments as float so smooth mouse pan works in small increments */
103 } BevelData;
104
105 static void edbm_bevel_update_header(bContext *C, wmOperator *op)
106 {
107         const char *str = IFACE_("Confirm: (Enter/LMB), Cancel: (Esc/RMB), Mode: %s (M), Clamp Overlap: %s (C), "
108                                  "Vertex Only: %s (V), Profile Control: %s (P), Offset: %s, Segments: %d, Profile: %.3f");
109
110         char msg[UI_MAX_DRAW_STR];
111         ScrArea *sa = CTX_wm_area(C);
112         Scene *sce = CTX_data_scene(C);
113
114         if (sa) {
115                 BevelData *opdata = op->customdata;
116                 char offset_str[NUM_STR_REP_LEN];
117                 const char *type_str;
118                 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "offset_type");
119
120                 if (hasNumInput(&opdata->num_input[OFFSET_VALUE])) {
121                         outputNumInput(&opdata->num_input[OFFSET_VALUE], offset_str, &sce->unit);
122                 }
123                 else {
124                         BLI_snprintf(offset_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "offset"));
125                 }
126
127                 RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &type_str);
128
129                 BLI_snprintf(msg, sizeof(msg), str, type_str,
130                              WM_bool_as_string(RNA_boolean_get(op->ptr, "clamp_overlap")),
131                              WM_bool_as_string(RNA_boolean_get(op->ptr, "vertex_only")),
132                              WM_bool_as_string(opdata->value_mode == PROFILE_VALUE),
133                              offset_str, RNA_int_get(op->ptr, "segments"), RNA_float_get(op->ptr, "profile"));
134
135                 ED_area_status_text(sa, msg);
136         }
137 }
138
139 static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
140 {
141         Scene *scene = CTX_data_scene(C);
142         BevelData *opdata;
143         ViewLayer *view_layer = CTX_data_view_layer(C);
144         float pixels_per_inch;
145         int i;
146
147         if (is_modal) {
148                 RNA_float_set(op->ptr, "offset", 0.0f);
149         }
150
151         op->customdata = opdata = MEM_mallocN(sizeof(BevelData), "beveldata_mesh_operator");
152         uint objects_used_len = 0;
153
154         {
155                 uint ob_store_len = 0;
156                 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &ob_store_len);
157                 opdata->ob_store = MEM_malloc_arrayN(ob_store_len, sizeof(*opdata->ob_store), __func__);
158                 for (uint ob_index = 0; ob_index < ob_store_len; ob_index++) {
159                         Object *obedit = objects[ob_index];
160                         BMEditMesh *em = BKE_editmesh_from_object(obedit);
161                         if (em->bm->totvertsel > 0) {
162                                 opdata->ob_store[objects_used_len].em = em;
163                                 objects_used_len++;
164                         }
165                 }
166                 MEM_freeN(objects);
167                 opdata->ob_store_len = objects_used_len;
168         }
169
170         opdata->is_modal = is_modal;
171         opdata->value_mode = OFFSET_VALUE;
172         opdata->segments = (float) RNA_int_get(op->ptr, "segments");
173         pixels_per_inch = U.dpi * U.pixelsize;
174
175         for (i = 0; i < NUM_VALUE_KINDS; i++) {
176                 opdata->shift_value[i] = -1.0f;
177                 opdata->initial_length[i] = -1.0f;
178                 /* note: scale for OFFSET_VALUE will get overwritten in edbm_bevel_invoke */
179                 opdata->scale[i] = value_scale_per_inch[i] / pixels_per_inch;
180
181                 initNumInput(&opdata->num_input[i]);
182                 opdata->num_input[i].idx_max = 0;
183                 opdata->num_input[i].val_flag[0] |= NUM_NO_NEGATIVE;
184                 if (i == SEGMENTS_VALUE) {
185                         opdata->num_input[i].val_flag[0] |= NUM_NO_FRACTION | NUM_NO_ZERO;
186                 }
187                 if (i == OFFSET_VALUE) {
188                         opdata->num_input[i].unit_sys = scene->unit.system;
189                 }
190                 opdata->num_input[i].unit_type[0] = B_UNIT_NONE;  /* Not sure this is a factor or a unit? */
191         }
192
193         /* avoid the cost of allocating a bm copy */
194         if (is_modal) {
195                 View3D *v3d = CTX_wm_view3d(C);
196                 ARegion *ar = CTX_wm_region(C);
197
198                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
199                         opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store(opdata->ob_store[ob_index].em);
200                 }
201                 opdata->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ED_region_draw_mouse_line_cb,
202                         opdata->mcenter, REGION_DRAW_POST_PIXEL);
203                 G.moving = G_TRANSFORM_EDIT;
204
205                 if (v3d) {
206                         opdata->gizmo_flag = v3d->gizmo_flag;
207                         v3d->gizmo_flag = V3D_GIZMO_HIDE;
208                 }
209         }
210
211         return true;
212 }
213
214 static bool edbm_bevel_calc(wmOperator *op)
215 {
216         BevelData *opdata = op->customdata;
217         BMEditMesh *em;
218         BMOperator bmop;
219         bool changed = false;
220
221         const float offset = RNA_float_get(op->ptr, "offset");
222         const int offset_type = RNA_enum_get(op->ptr, "offset_type");
223         const int segments = RNA_int_get(op->ptr, "segments");
224         const float profile = RNA_float_get(op->ptr, "profile");
225         const bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
226         const bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap");
227         int material = RNA_int_get(op->ptr, "material");
228         const bool loop_slide = RNA_boolean_get(op->ptr, "loop_slide");
229         const bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam");
230         const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
231         const bool harden_normals = RNA_boolean_get(op->ptr, "harden_normals");
232         const int face_strength_mode = RNA_enum_get(op->ptr, "face_strength_mode");
233
234         for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
235                 em = opdata->ob_store[ob_index].em;
236
237                 /* revert to original mesh */
238                 if (opdata->is_modal) {
239                         EDBM_redo_state_restore(opdata->ob_store[ob_index].mesh_backup, em, false);
240                 }
241
242                 if (em->ob) {
243                         material = CLAMPIS(material, -1, em->ob->totcol - 1);
244                 }
245
246                 EDBM_op_init(
247                         em, &bmop, op,
248                         "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f clamp_overlap=%b "
249                         "material=%i loop_slide=%b mark_seam=%b mark_sharp=%b harden_normals=%b face_strength_mode=%i",
250                         BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile,
251                         clamp_overlap, material, loop_slide, mark_seam, mark_sharp, harden_normals, face_strength_mode);
252
253                 BMO_op_exec(em->bm, &bmop);
254
255                 if (offset != 0.0f) {
256                         /* not essential, but we may have some loose geometry that
257                          * won't get bevel'd and better not leave it selected */
258                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
259                         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
260                 }
261
262                 /* no need to de-select existing geometry */
263                 if (!EDBM_op_finish(em, &bmop, op, true)) {
264                         continue;
265                 }
266
267                 EDBM_mesh_normals_update(em);
268
269                 EDBM_update_generic(em, true, true);
270                 changed = true;
271         }
272         return changed;
273 }
274
275 static void edbm_bevel_exit(bContext *C, wmOperator *op)
276 {
277         BevelData *opdata = op->customdata;
278
279         ScrArea *sa = CTX_wm_area(C);
280
281         if (sa) {
282                 ED_area_status_text(sa, NULL);
283         }
284
285         if (opdata->is_modal) {
286                 View3D *v3d = CTX_wm_view3d(C);
287                 ARegion *ar = CTX_wm_region(C);
288                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
289                         EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, NULL, false);
290                 }
291                 ED_region_draw_cb_exit(ar->type, opdata->draw_handle_pixel);
292                 if (v3d) {
293                         v3d->gizmo_flag = opdata->gizmo_flag;
294                 }
295                 G.moving = 0;
296         }
297         MEM_SAFE_FREE(opdata->ob_store);
298         MEM_SAFE_FREE(op->customdata);
299         op->customdata = NULL;
300 }
301
302 static void edbm_bevel_cancel(bContext *C, wmOperator *op)
303 {
304         BevelData *opdata = op->customdata;
305         if (opdata->is_modal) {
306                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
307                         EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, opdata->ob_store[ob_index].em, true);
308                         EDBM_update_generic(opdata->ob_store[ob_index].em, false, true);
309                 }
310         }
311
312         edbm_bevel_exit(C, op);
313
314         /* need to force redisplay or we may still view the modified result */
315         ED_region_tag_redraw(CTX_wm_region(C));
316 }
317
318 /* bevel! yay!!*/
319 static int edbm_bevel_exec(bContext *C, wmOperator *op)
320 {
321         if (!edbm_bevel_init(C, op, false)) {
322                 return OPERATOR_CANCELLED;
323         }
324
325         if (!edbm_bevel_calc(op)) {
326                 edbm_bevel_cancel(C, op);
327                 return OPERATOR_CANCELLED;
328         }
329
330         edbm_bevel_exit(C, op);
331
332         return OPERATOR_FINISHED;
333 }
334
335 static void edbm_bevel_calc_initial_length(wmOperator *op, const wmEvent *event, bool mode_changed)
336 {
337         BevelData *opdata;
338         float mlen[2], len, value, sc, st;
339         int vmode;
340
341         opdata = op->customdata;
342         mlen[0] = opdata->mcenter[0] - event->mval[0];
343         mlen[1] = opdata->mcenter[1] - event->mval[1];
344         len = len_v2(mlen);
345         vmode = opdata->value_mode;
346         if (mode_changed || opdata->initial_length[vmode] == -1.0f) {
347                 /* If current value is not default start value, adjust len so that
348                  * the scaling and offset in edbm_bevel_mouse_set_value will
349                  * start at current value */
350                 value = (vmode == SEGMENTS_VALUE) ?
351                         opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
352                 sc = opdata->scale[vmode];
353                 st = value_start[vmode];
354                 if (value != value_start[vmode]) {
355                         len = (st + sc * (len - MVAL_PIXEL_MARGIN) - value) / sc;
356                 }
357         }
358         opdata->initial_length[opdata->value_mode] = len;
359 }
360
361 static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event)
362 {
363         /* TODO make modal keymap (see fly mode) */
364         RegionView3D *rv3d = CTX_wm_region_view3d(C);
365         BevelData *opdata;
366         float center_3d[3];
367
368         if (!edbm_bevel_init(C, op, true)) {
369                 return OPERATOR_CANCELLED;
370         }
371
372         opdata = op->customdata;
373
374         /* initialize mouse values */
375         if (!calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, center_3d, opdata->mcenter)) {
376                 /* in this case the tool will likely do nothing,
377                  * ideally this will never happen and should be checked for above */
378                 opdata->mcenter[0] = opdata->mcenter[1] = 0;
379         }
380
381         /* for OFFSET_VALUE only, the scale is the size of a pixel under the mouse in 3d space */
382         opdata->scale[OFFSET_VALUE] = rv3d ? ED_view3d_pixel_size(rv3d, center_3d) : 1.0f;
383
384         edbm_bevel_calc_initial_length(op, event, false);
385
386         edbm_bevel_update_header(C, op);
387
388         if (!edbm_bevel_calc(op)) {
389                 edbm_bevel_cancel(C, op);
390                 return OPERATOR_CANCELLED;
391         }
392
393         WM_event_add_modal_handler(C, op);
394
395         return OPERATOR_RUNNING_MODAL;
396 }
397
398 static void edbm_bevel_mouse_set_value(wmOperator *op, const wmEvent *event)
399 {
400         BevelData *opdata = op->customdata;
401         int vmode = opdata->value_mode;
402         float mdiff[2];
403         float value;
404
405         mdiff[0] = opdata->mcenter[0] - event->mval[0];
406         mdiff[1] = opdata->mcenter[1] - event->mval[1];
407
408         value = ((len_v2(mdiff) - MVAL_PIXEL_MARGIN) - opdata->initial_length[vmode]);
409
410         /* Scale according to value mode */
411         value = value_start[vmode] + value * opdata->scale[vmode];
412
413         /* Fake shift-transform... */
414         if (event->shift) {
415                 if (opdata->shift_value[vmode] < 0.0f) {
416                         opdata->shift_value[vmode] = (vmode == SEGMENTS_VALUE) ?
417                                 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
418                 }
419                 value = (value - opdata->shift_value[vmode]) * 0.1f + opdata->shift_value[vmode];
420         }
421         else if (opdata->shift_value[vmode] >= 0.0f) {
422                 opdata->shift_value[vmode] = -1.0f;
423         }
424
425         /* clamp accordingto value mode, and store value back */
426         CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
427         if (vmode == SEGMENTS_VALUE) {
428                 opdata->segments = value;
429                 RNA_int_set(op->ptr, "segments", (int)(value + 0.5f));
430         }
431         else {
432                 RNA_float_set(op->ptr, value_rna_name[vmode], value);
433         }
434 }
435
436 static void edbm_bevel_numinput_set_value(wmOperator *op)
437 {
438         BevelData *opdata = op->customdata;
439         float value;
440         int vmode;
441
442         vmode = opdata->value_mode;
443         value = (vmode == SEGMENTS_VALUE) ?
444                 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
445         applyNumInput(&opdata->num_input[vmode], &value);
446         CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
447         if (vmode == SEGMENTS_VALUE) {
448                 opdata->segments = value;
449                 RNA_int_set(op->ptr, "segments", (int)value);
450         }
451         else {
452                 RNA_float_set(op->ptr, value_rna_name[vmode], value);
453         }
454 }
455
456 static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
457 {
458         BevelData *opdata = op->customdata;
459         const bool has_numinput = hasNumInput(&opdata->num_input[opdata->value_mode]);
460
461         /* Modal numinput active, try to handle numeric inputs first... */
462         if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
463                 edbm_bevel_numinput_set_value(op);
464                 edbm_bevel_calc(op);
465                 edbm_bevel_update_header(C, op);
466                 return OPERATOR_RUNNING_MODAL;
467         }
468         else {
469                 bool handled = false;
470                 switch (event->type) {
471                         case ESCKEY:
472                         case RIGHTMOUSE:
473                                 edbm_bevel_cancel(C, op);
474                                 return OPERATOR_CANCELLED;
475
476                         case MOUSEMOVE:
477                                 if (!has_numinput) {
478                                         edbm_bevel_mouse_set_value(op, event);
479                                         edbm_bevel_calc(op);
480                                         edbm_bevel_update_header(C, op);
481                                         handled = true;
482                                 }
483                                 break;
484
485                         case LEFTMOUSE:
486                         case PADENTER:
487                         case RETKEY:
488                                 if ((event->val == KM_PRESS) ||
489                                     ((event->val == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm")))
490                                 {
491                                         edbm_bevel_calc(op);
492                                         edbm_bevel_exit(C, op);
493                                         return OPERATOR_FINISHED;
494                                 }
495                                 break;
496
497                         case MOUSEPAN: {
498                                 float delta = 0.02f * (event->y - event->prevy);
499                                 if (opdata->segments >= 1 && opdata->segments + delta < 1)
500                                         opdata->segments = 1;
501                                 else
502                                         opdata->segments += delta;
503                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
504                                 edbm_bevel_calc(op);
505                                 edbm_bevel_update_header(C, op);
506                                 handled = true;
507                                 break;
508                         }
509
510                         /* Note this will prevent padplus and padminus to ever activate modal numinput.
511                          * This is not really an issue though, as we only expect positive values here...
512                          * Else we could force them to only modify segments number when shift is pressed, or so.
513                          */
514
515                         case WHEELUPMOUSE:  /* change number of segments */
516                         case PADPLUSKEY:
517                                 if (event->val == KM_RELEASE)
518                                         break;
519
520                                 opdata->segments = opdata->segments + 1;
521                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
522                                 edbm_bevel_calc(op);
523                                 edbm_bevel_update_header(C, op);
524                                 handled = true;
525                                 break;
526
527                         case WHEELDOWNMOUSE:  /* change number of segments */
528                         case PADMINUS:
529                                 if (event->val == KM_RELEASE)
530                                         break;
531
532                                 opdata->segments = max_ff(opdata->segments - 1, 1);
533                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
534                                 edbm_bevel_calc(op);
535                                 edbm_bevel_update_header(C, op);
536                                 handled = true;
537                                 break;
538
539                         case MKEY:
540                                 if (event->val == KM_RELEASE)
541                                         break;
542
543                                 {
544                                         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "offset_type");
545                                         int type = RNA_property_enum_get(op->ptr, prop);
546                                         type++;
547                                         if (type > BEVEL_AMT_PERCENT) {
548                                                 type = BEVEL_AMT_OFFSET;
549                                         }
550                                         if (opdata->value_mode == OFFSET_VALUE && type == BEVEL_AMT_PERCENT)
551                                                 opdata->value_mode = OFFSET_VALUE_PERCENT;
552                                         else if (opdata->value_mode == OFFSET_VALUE_PERCENT && type != BEVEL_AMT_PERCENT)
553                                                 opdata->value_mode = OFFSET_VALUE;
554                                         RNA_property_enum_set(op->ptr, prop, type);
555                                         if (opdata->initial_length[opdata->value_mode] == -1.0f)
556                                                 edbm_bevel_calc_initial_length(op, event, true);
557                                 }
558                                 /* Update offset accordingly to new offset_type. */
559                                 if (!has_numinput &&
560                                     (opdata->value_mode == OFFSET_VALUE || opdata->value_mode == OFFSET_VALUE_PERCENT))
561                                 {
562                                         edbm_bevel_mouse_set_value(op, event);
563                                 }
564                                 edbm_bevel_calc(op);
565                                 edbm_bevel_update_header(C, op);
566                                 handled = true;
567                                 break;
568                         case CKEY:
569                                 if (event->val == KM_RELEASE)
570                                         break;
571
572                                 {
573                                         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "clamp_overlap");
574                                         RNA_property_boolean_set(op->ptr, prop, !RNA_property_boolean_get(op->ptr, prop));
575                                 }
576                                 edbm_bevel_calc(op);
577                                 edbm_bevel_update_header(C, op);
578                                 handled = true;
579                                 break;
580                         case PKEY:
581                                 if (event->val == KM_RELEASE)
582                                         break;
583                                 if (opdata->value_mode == PROFILE_VALUE) {
584                                         opdata->value_mode = OFFSET_VALUE;
585                                 }
586                                 else {
587                                         opdata->value_mode = PROFILE_VALUE;
588                                 }
589                                 edbm_bevel_calc_initial_length(op, event, true);
590                                 break;
591                         case SKEY:
592                                 if (event->val == KM_RELEASE)
593                                         break;
594                                 if (opdata->value_mode == SEGMENTS_VALUE) {
595                                         opdata->value_mode = OFFSET_VALUE;
596                                 }
597                                 else {
598                                         opdata->value_mode = SEGMENTS_VALUE;
599                                 }
600                                 edbm_bevel_calc_initial_length(op, event, true);
601                                 break;
602                         case VKEY:
603                                 if (event->val == KM_RELEASE)
604                                         break;
605
606                                 {
607                                         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "vertex_only");
608                                         RNA_property_boolean_set(op->ptr, prop, !RNA_property_boolean_get(op->ptr, prop));
609                                 }
610                                 edbm_bevel_calc(op);
611                                 edbm_bevel_update_header(C, op);
612                                 handled = true;
613                                 break;
614                         case UKEY:
615                                 if (event->val == KM_RELEASE)
616                                         break;
617                                 else {
618                                         bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam");
619                                         RNA_boolean_set(op->ptr, "mark_seam", !mark_seam);
620                                         edbm_bevel_calc(op);
621                                         handled = true;
622                                         break;
623                                 }
624                         case KKEY:
625                                 if (event->val == KM_RELEASE)
626                                         break;
627                                 else {
628                                         bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
629                                         RNA_boolean_set(op->ptr, "mark_sharp", !mark_sharp);
630                                         edbm_bevel_calc(op);
631                                         handled = true;
632                                         break;
633                                 }
634
635                 }
636
637                 /* Modal numinput inactive, try to handle numeric inputs last... */
638                 if (!handled && event->val == KM_PRESS && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
639                         edbm_bevel_numinput_set_value(op);
640                         edbm_bevel_calc(op);
641                         edbm_bevel_update_header(C, op);
642                         return OPERATOR_RUNNING_MODAL;
643                 }
644         }
645
646         return OPERATOR_RUNNING_MODAL;
647 }
648
649 static void mesh_ot_bevel_offset_range_func(PointerRNA *ptr, PropertyRNA *UNUSED(prop),
650                                             float *min, float *max, float *softmin, float *softmax)
651 {
652         const int offset_type = RNA_enum_get(ptr, "offset_type");
653
654         *min = -FLT_MAX;
655         *max = FLT_MAX;
656         *softmin = 0.0f;
657         *softmax = (offset_type == BEVEL_AMT_PERCENT) ? 100.0f : 1.0f;
658 }
659
660 void MESH_OT_bevel(wmOperatorType *ot)
661 {
662         PropertyRNA *prop;
663
664         static const EnumPropertyItem offset_type_items[] = {
665                 {BEVEL_AMT_OFFSET, "OFFSET", 0, "Offset", "Amount is offset of new edges from original"},
666                 {BEVEL_AMT_WIDTH, "WIDTH", 0, "Width", "Amount is width of new face"},
667                 {BEVEL_AMT_DEPTH, "DEPTH", 0, "Depth", "Amount is perpendicular distance from original edge to bevel face"},
668                 {BEVEL_AMT_PERCENT, "PERCENT", 0, "Percent", "Amount is percent of adjacent edge length"},
669                 {0, NULL, 0, NULL, NULL},
670         };
671
672         static const EnumPropertyItem face_strength_mode_items[] = {
673                 {BEVEL_FACE_STRENGTH_NONE, "NONE", 0, "None", "Do not set face strength"},
674                 {BEVEL_FACE_STRENGTH_NEW, "NEW", 0, "New", "Set face strength on new faces only"},
675                 {BEVEL_FACE_STRENGTH_AFFECTED, "AFFECTED", 0, "Affected", "Set face strength on new and modified faces only"},
676                 {BEVEL_FACE_STRENGTH_ALL, "ALL", 0, "All", "Set face strength on all faces"},
677                 {0, NULL, 0, NULL, NULL},
678         };
679
680         /* identifiers */
681         ot->name = "Bevel";
682         ot->description = "Edge Bevel";
683         ot->idname = "MESH_OT_bevel";
684
685         /* api callbacks */
686         ot->exec = edbm_bevel_exec;
687         ot->invoke = edbm_bevel_invoke;
688         ot->modal = edbm_bevel_modal;
689         ot->cancel = edbm_bevel_cancel;
690         ot->poll = ED_operator_editmesh;
691
692         /* flags */
693         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING;
694
695         RNA_def_enum(ot->srna, "offset_type", offset_type_items, 0, "Amount Type", "What distance Amount measures");
696         prop = RNA_def_float(ot->srna, "offset", 0.0f, -1e6f, 1e6f, "Amount", "", 0.0f, 100.0f);
697         RNA_def_property_float_array_funcs_runtime(prop, NULL, NULL, mesh_ot_bevel_offset_range_func);
698         RNA_def_int(ot->srna, "segments", 1, 1, SEGMENTS_HARD_MAX, "Segments", "Segments for curved edge", 1, 8);
699         RNA_def_float(ot->srna, "profile", 0.5f, PROFILE_HARD_MIN, 1.0f, "Profile",
700                 "Controls profile shape (0.5 = round)", PROFILE_HARD_MIN, 1.0f);
701         RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex Only", "Bevel only vertices");
702         RNA_def_boolean(ot->srna, "clamp_overlap", false, "Clamp Overlap",
703                 "Do not allow beveled edges/vertices to overlap each other");
704         RNA_def_boolean(ot->srna, "loop_slide", true, "Loop Slide", "Prefer slide along edge to even widths");
705         RNA_def_boolean(ot->srna, "mark_seam", false, "Mark Seams", "Mark Seams along beveled edges");
706         RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark beveled edges as sharp");
707         RNA_def_int(ot->srna, "material", -1, -1, INT_MAX, "Material",
708                 "Material for bevel faces (-1 means use adjacent faces)", -1, 100);
709         RNA_def_boolean(ot->srna, "harden_normals", false, "Harden Normals", "Match normals of new faces to adjacent faces");
710         RNA_def_enum(ot->srna, "face_strength_mode", face_strength_mode_items, BEVEL_FACE_STRENGTH_NONE,
711                 "Face Strength Mode", "Whether to set face strength, and which faces to set face strength on");
712         prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", "");
713         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
714
715 }