Blender V4.5
object_remesh.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2019 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cctype>
10#include <cfloat>
11#include <cmath>
12#include <cstdlib>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "BLI_math_geom.h"
18#include "BLI_math_matrix.h"
19#include "BLI_string_utf8.h"
20#include "BLI_utildefines.h"
21
22#include "DNA_object_types.h"
23#include "DNA_userdef_types.h"
24
25#include "BLT_translation.hh"
26
27#include "BKE_attribute.hh"
28#include "BKE_context.hh"
29#include "BKE_global.hh"
30#include "BKE_lib_id.hh"
31#include "BKE_library.hh"
32#include "BKE_main.hh"
33#include "BKE_mesh.hh"
34#include "BKE_mesh_mirror.hh"
36#include "BKE_modifier.hh"
37#include "BKE_object.hh"
38#include "BKE_object_types.hh"
39#include "BKE_paint.hh"
40#include "BKE_report.hh"
41#include "BKE_screen.hh"
42#include "BKE_shrinkwrap.hh"
43#include "BKE_unit.hh"
44
45#include "DEG_depsgraph.hh"
46
47#include "ED_screen.hh"
48#include "ED_sculpt.hh"
49#include "ED_space_api.hh"
50#include "ED_view3d.hh"
51
52#include "RNA_access.hh"
53#include "RNA_define.hh"
54
55#include "GPU_immediate.hh"
56#include "GPU_matrix.hh"
57#include "GPU_state.hh"
58
59#include "WM_api.hh"
60#include "WM_types.hh"
61
62#include "UI_interface.hh"
63
64#include "BLF_api.hh"
65
66#include "object_intern.hh" /* own include */
67
68namespace blender::ed::object {
69
70/* TODO(sebpa): unstable, can lead to unrecoverable errors. */
71// #define USE_MESH_CURVATURE
72
73/* -------------------------------------------------------------------- */
76
78{
80
81 if (ob == nullptr || ob->data == nullptr) {
82 return false;
83 }
84
86 CTX_wm_operator_poll_msg_set(C, "The remesher cannot work on linked or override data");
87 return false;
88 }
89
91 CTX_wm_operator_poll_msg_set(C, "The remesher cannot run from edit mode");
92 return false;
93 }
94
95 if (ob->mode == OB_MODE_SCULPT && ob->sculpt->bm) {
96 CTX_wm_operator_poll_msg_set(C, "The remesher cannot run with dyntopo activated");
97 return false;
98 }
99
102 C, "The remesher cannot run with a Multires modifier in the modifier stack");
103 return false;
104 }
105
107}
108
110{
111 const Scene &scene = *CTX_data_scene(C);
113
114 Mesh *mesh = static_cast<Mesh *>(ob->data);
115
116 if (mesh->remesh_voxel_size <= 0.0f) {
117 BKE_report(op->reports, RPT_ERROR, "Voxel remesher cannot run with a voxel size of 0.0");
118 return OPERATOR_CANCELLED;
119 }
120
121 if (mesh->faces_num == 0) {
122 return OPERATOR_CANCELLED;
123 }
124
125 float isovalue = 0.0f;
126 if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) {
127 isovalue = mesh->remesh_voxel_size * 0.3f;
128 }
129
130 Mesh *new_mesh = BKE_mesh_remesh_voxel(
131 mesh, mesh->remesh_voxel_size, mesh->remesh_voxel_adaptivity, isovalue, op->reports);
132
133 if (!new_mesh) {
134 BKE_report(op->reports, RPT_ERROR, "Voxel remesher failed to create mesh");
135 return OPERATOR_CANCELLED;
136 }
137
138 if (ob->mode == OB_MODE_SCULPT) {
140 }
141
142 if (mesh->flag & ME_REMESH_FIX_POLES && mesh->remesh_voxel_adaptivity <= 0.0f) {
143 Mesh *mesh_fixed_poles = BKE_mesh_remesh_voxel_fix_poles(new_mesh);
144 BKE_id_free(nullptr, new_mesh);
145 new_mesh = mesh_fixed_poles;
146 }
147
148 if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) {
150 }
151
154 }
155 else {
156 const VArray<bool> sharp_face = *mesh->attributes().lookup_or_default<bool>(
157 "sharp_face", bke::AttrDomain::Face, false);
158 bke::mesh_smooth_set(*new_mesh, !sharp_face[0]);
159 }
160
161 BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob);
162
163 if (ob->mode == OB_MODE_SCULPT) {
166 }
167
171
172 return OPERATOR_FINISHED;
173}
174
176{
177 /* identifiers */
178 ot->name = "Voxel Remesh";
179 ot->description =
180 "Calculates a new manifold mesh based on the volume of the current mesh. All data layers "
181 "will be lost";
182 ot->idname = "OBJECT_OT_voxel_remesh";
183
184 /* API callbacks. */
185 ot->poll = object_remesh_poll;
186 ot->exec = voxel_remesh_exec;
187
189}
190
192
193/* -------------------------------------------------------------------- */
196
197#define VOXEL_SIZE_EDIT_MAX_GRIDS_LINES 500
198#define VOXEL_SIZE_EDIT_MAX_STR_LEN 20
199
219
221 const float initial_co[3],
222 const float end_co[3],
223 const float length_co[3],
224 const float spacing)
225{
226 const float total_len = len_v3v3(initial_co, end_co);
227 const int tot_lines = int(total_len / spacing);
228 const int tot_lines_half = (tot_lines / 2) + 1;
229 float spacing_dir[3], lines_start[3];
230 float line_dir[3];
231 sub_v3_v3v3(spacing_dir, end_co, initial_co);
232 normalize_v3(spacing_dir);
233
234 sub_v3_v3v3(line_dir, length_co, initial_co);
235
236 if (tot_lines > VOXEL_SIZE_EDIT_MAX_GRIDS_LINES || tot_lines <= 1) {
237 return;
238 }
239
240 mid_v3_v3v3(lines_start, initial_co, end_co);
241
242 immBegin(GPU_PRIM_LINES, uint(tot_lines_half) * 2);
243 for (int i = 0; i < tot_lines_half; i++) {
244 float line_start[3];
245 float line_end[3];
246 madd_v3_v3v3fl(line_start, lines_start, spacing_dir, spacing * i);
247 add_v3_v3v3(line_end, line_start, line_dir);
248 immVertex3fv(pos3d, line_start);
249 immVertex3fv(pos3d, line_end);
250 }
251 immEnd();
252
253 mul_v3_fl(spacing_dir, -1.0f);
254
255 immBegin(GPU_PRIM_LINES, uint(tot_lines_half - 1) * 2);
256 for (int i = 1; i < tot_lines_half; i++) {
257 float line_start[3];
258 float line_end[3];
259 madd_v3_v3v3fl(line_start, lines_start, spacing_dir, spacing * i);
260 add_v3_v3v3(line_end, line_start, line_dir);
261 immVertex3fv(pos3d, line_start);
262 immVertex3fv(pos3d, line_end);
263 }
264 immEnd();
265}
266
267static void voxel_size_edit_draw(const bContext *C, ARegion * /*region*/, void *arg)
268{
269 VoxelSizeEditCustomData *cd = static_cast<VoxelSizeEditCustomData *>(arg);
270
272 GPU_line_smooth(true);
273
277 GPU_matrix_mul(cd->active_object->object_to_world().ptr());
278
279 /* Draw Rect */
280 immUniformColor4f(0.9f, 0.9f, 0.9f, 0.8f);
281 GPU_line_width(3.0f);
282
284 immVertex3fv(pos3d, cd->preview_plane[0]);
285 immVertex3fv(pos3d, cd->preview_plane[1]);
286
287 immVertex3fv(pos3d, cd->preview_plane[1]);
288 immVertex3fv(pos3d, cd->preview_plane[2]);
289
290 immVertex3fv(pos3d, cd->preview_plane[2]);
291 immVertex3fv(pos3d, cd->preview_plane[3]);
292
293 immVertex3fv(pos3d, cd->preview_plane[3]);
294 immVertex3fv(pos3d, cd->preview_plane[0]);
295 immEnd();
296
297 /* Draw Grid */
298 GPU_line_width(1.0f);
299
300 const float total_len = len_v3v3(cd->preview_plane[0], cd->preview_plane[1]);
301 const int tot_lines = int(total_len / cd->voxel_size);
302
303 /* Smooth-step to reduce the alpha of the grid as the line number increases. */
304 const float a = VOXEL_SIZE_EDIT_MAX_GRIDS_LINES * 0.1f;
306 const float x = clamp_f((tot_lines - a) / (b - a), 0.0f, 1.0);
307 const float alpha_factor = 1.0f - (x * x * (3.0f - 2.0f * x));
308
309 immUniformColor4f(0.9f, 0.9f, 0.9f, 0.75f * alpha_factor);
311 pos3d, cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[3], cd->voxel_size);
313 pos3d, cd->preview_plane[1], cd->preview_plane[2], cd->preview_plane[0], cd->voxel_size);
314
315 /* Draw text */
316 const uiStyle *style = UI_style_get();
317 const uiFontStyle *fstyle = &style->widget;
318 const int fontid = fstyle->uifont_id;
319 float strwidth, strheight;
320 short fstyle_points = fstyle->points;
322 short strdrawlen = 0;
323 Scene *scene = CTX_data_scene(C);
324 const UnitSettings &unit = scene->unit;
325
326 BKE_unit_value_as_string_scaled(str, sizeof(str), cd->voxel_size, -3, B_UNIT_LENGTH, unit, true);
327 strdrawlen = BLI_strlen_utf8(str);
328
330
333 /* (Constant viewport) scale is already accounted for in 'text_mat'. */
334 BLF_size(fontid, 10.0f * fstyle_points);
335 BLF_color3f(fontid, 1.0f, 1.0f, 1.0f);
336 BLF_width_and_height(fontid, str, strdrawlen, &strwidth, &strheight);
337 BLF_position(fontid, -0.5f * strwidth, -0.5f * strheight, 0.0f);
338 BLF_draw(fontid, str, strdrawlen);
340
342
344 GPU_line_smooth(false);
345}
346
348{
349 ARegion *region = CTX_wm_region(C);
351
352 ED_region_draw_cb_exit(region->runtime->type, cd->draw_handle);
353
354 MEM_freeN(cd);
355
356 ED_workspace_status_text(C, nullptr);
357}
358
360{
362 WorkspaceStatus status(C);
363 status.item(IFACE_("Confirm"), ICON_EVENT_RETURN, ICON_MOUSE_LMB);
364 status.item(IFACE_("Cancel"), ICON_EVENT_ESC, ICON_MOUSE_RMB);
365 status.item(IFACE_("Change Size"), ICON_MOUSE_MOVE);
366 status.item_bool(IFACE_("Precision Mode"), cd->slow_mode, ICON_EVENT_SHIFT);
367}
368
370{
371 ARegion *region = CTX_wm_region(C);
373 Object *active_object = cd->active_object;
374 Mesh *mesh = (Mesh *)active_object->data;
375
376 /* Cancel modal operator */
377 if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) ||
378 (event->type == RIGHTMOUSE && event->val == KM_PRESS))
379 {
381 ED_region_tag_redraw(region);
382 return OPERATOR_FINISHED;
383 }
384
385 /* Finish modal operator */
386 if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) ||
387 (event->type == EVT_RETKEY && event->val == KM_PRESS) ||
388 (event->type == EVT_PADENTER && event->val == KM_PRESS))
389 {
390 ED_region_draw_cb_exit(region->runtime->type, cd->draw_handle);
391 mesh->remesh_voxel_size = cd->voxel_size;
392 MEM_freeN(cd);
393 ED_region_tag_redraw(region);
394 ED_workspace_status_text(C, nullptr);
396 return OPERATOR_FINISHED;
397 }
398
399 const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
400
401 float d = cd->init_mval[0] - mval[0];
402
403 if (cd->slow_mode) {
404 d = cd->slow_mval[0] - mval[0];
405 }
406
407 d *= cd->voxel_size_min * 0.25f;
408
409 if (cd->slow_mode) {
410 cd->voxel_size = cd->slow_voxel_size + d * 0.05f;
411 }
412 else {
413 cd->voxel_size = cd->init_voxel_size + d;
414 }
415
416 if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_PRESS) {
417 cd->slow_mode = true;
418 copy_v2_v2(cd->slow_mval, mval);
419 cd->slow_voxel_size = cd->voxel_size;
420 }
421 if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_RELEASE) {
422 cd->slow_mode = false;
423 cd->slow_voxel_size = 0.0f;
424 }
425
426 cd->voxel_size = clamp_f(
427 cd->voxel_size, max_ff(cd->voxel_size_min, 0.0001f), cd->voxel_size_max);
428
429 ED_region_tag_redraw(region);
430
433}
434
436{
437 ARegion *region = CTX_wm_region(C);
438 Object *active_object = CTX_data_active_object(C);
439 Mesh *mesh = (Mesh *)active_object->data;
440
442 "Voxel Size Edit OP Custom Data");
443
444 /* Initial operator Custom Data setup. */
447 cd->active_object = active_object;
448 cd->init_mval[0] = event->mval[0];
449 cd->init_mval[1] = event->mval[1];
450 cd->slow_mode = false;
451 op->customdata = cd;
452
453 /* Select the front facing face of the mesh bounding box. */
454 const Bounds<float3> bounds = *mesh->bounds_min_max();
455 BoundBox bb;
457
458 /* Indices of the Bounding Box faces. */
459 const int BB_faces[6][4] = {
460 {3, 0, 4, 7},
461 {1, 2, 6, 5},
462 {3, 2, 1, 0},
463 {4, 5, 6, 7},
464 {0, 1, 5, 4},
465 {2, 3, 7, 6},
466 };
467
468 copy_v3_v3(cd->preview_plane[0], bb.vec[BB_faces[0][0]]);
469 copy_v3_v3(cd->preview_plane[1], bb.vec[BB_faces[0][1]]);
470 copy_v3_v3(cd->preview_plane[2], bb.vec[BB_faces[0][2]]);
471 copy_v3_v3(cd->preview_plane[3], bb.vec[BB_faces[0][3]]);
472
474
475 float mat[3][3];
476 float current_normal[3];
477 float view_normal[3] = {0.0f, 0.0f, 1.0f};
478
479 /* Calculate the view normal. */
480 invert_m4_m4(active_object->runtime->world_to_object.ptr(),
481 active_object->object_to_world().ptr());
482 copy_m3_m4(mat, rv3d->viewinv);
483 mul_m3_v3(mat, view_normal);
484 copy_m3_m4(mat, active_object->world_to_object().ptr());
485 mul_m3_v3(mat, view_normal);
486 normalize_v3(view_normal);
487
488 normal_tri_v3(current_normal, cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[2]);
489
490 float min_dot = dot_v3v3(current_normal, view_normal);
491 float current_dot = 1;
492
493 /* Check if there is a face that is more aligned towards the view. */
494 for (int i = 0; i < 6; i++) {
496 current_normal, bb.vec[BB_faces[i][0]], bb.vec[BB_faces[i][1]], bb.vec[BB_faces[i][2]]);
497 current_dot = dot_v3v3(current_normal, view_normal);
498
499 if (current_dot < min_dot) {
500 min_dot = current_dot;
501 copy_v3_v3(cd->preview_plane[0], bb.vec[BB_faces[i][0]]);
502 copy_v3_v3(cd->preview_plane[1], bb.vec[BB_faces[i][1]]);
503 copy_v3_v3(cd->preview_plane[2], bb.vec[BB_faces[i][2]]);
504 copy_v3_v3(cd->preview_plane[3], bb.vec[BB_faces[i][3]]);
505 }
506 }
507
508 /* Cap the max/min voxel size based on the point where we cant visually display any more info
509 * with grid lines. */
511 len_v3v3(cd->preview_plane[3], cd->preview_plane[0])) *
512 0.5f;
515 mesh->remesh_voxel_size, max_ff(cd->voxel_size_min, 0.0001f), cd->voxel_size_max);
516 cd->voxel_size = cd->init_voxel_size;
517
518 /* Matrix calculation to position the text in 3D space. */
519 float text_pos[3];
520 float scale_mat[4][4];
521
522 float d_a[3], d_b[3];
523 float d_a_proj[2], d_b_proj[2];
524 float preview_plane_proj[4][2];
525 const float y_axis_proj[2] = {0.0f, 1.0f};
526
527 mid_v3_v3v3(text_pos, cd->preview_plane[0], cd->preview_plane[2]);
528
529 /* Project the selected face in the previous step of the Bounding Box. */
530 for (int i = 0; i < 4; i++) {
531 float preview_plane_world_space[3];
533 preview_plane_world_space, active_object->object_to_world().ptr(), cd->preview_plane[i]);
534 ED_view3d_project_v2(region, preview_plane_world_space, preview_plane_proj[i]);
535 }
536
537 /* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */
538 sub_v3_v3v3(d_a, cd->preview_plane[1], cd->preview_plane[0]);
539 sub_v3_v3v3(d_b, cd->preview_plane[3], cd->preview_plane[0]);
540 normalize_v3(d_a);
541 normalize_v3(d_b);
542
543 /* Project the X and Y axis. */
544 sub_v2_v2v2(d_a_proj, preview_plane_proj[1], preview_plane_proj[0]);
545 sub_v2_v2v2(d_b_proj, preview_plane_proj[3], preview_plane_proj[0]);
546 normalize_v2(d_a_proj);
547 normalize_v2(d_b_proj);
548
549 unit_m4(cd->text_mat);
550
551 /* Select the axis that is aligned with the view Y axis to use it as the basis Y. */
552 if (fabsf(dot_v2v2(d_a_proj, y_axis_proj)) > fabsf(dot_v2v2(d_b_proj, y_axis_proj))) {
553 copy_v3_v3(cd->text_mat[0], d_b);
554 copy_v3_v3(cd->text_mat[1], d_a);
555
556 /* Flip the X and Y basis vectors to make sure they always point upwards and to the right. */
557 if (d_b_proj[0] < 0.0f) {
558 mul_v3_fl(cd->text_mat[0], -1.0f);
559 }
560 if (d_a_proj[1] < 0.0f) {
561 mul_v3_fl(cd->text_mat[1], -1.0f);
562 }
563 }
564 else {
565 copy_v3_v3(cd->text_mat[0], d_a);
566 copy_v3_v3(cd->text_mat[1], d_b);
567 if (d_a_proj[0] < 0.0f) {
568 mul_v3_fl(cd->text_mat[0], -1.0f);
569 }
570 if (d_b_proj[1] < 0.0f) {
571 mul_v3_fl(cd->text_mat[1], -1.0f);
572 }
573 }
574
575 /* Use the Bounding Box face normal as the basis Z. */
576 normal_tri_v3(cd->text_mat[2], cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[2]);
577
578 /* Invert object scale. */
579 float scale[3];
580 mat4_to_size(scale, active_object->object_to_world().ptr());
581 invert_v3(scale);
582 size_to_mat4(scale_mat, scale);
583
584 mul_m4_m4_pre(cd->text_mat, scale_mat);
585
586 /* Write the text position into the matrix. */
587 copy_v3_v3(cd->text_mat[3], text_pos);
588
589 /* Scale the text to constant viewport size. */
590 float text_pos_word_space[3];
591 mul_v3_m4v3(text_pos_word_space, active_object->object_to_world().ptr(), text_pos);
592 const float pixelsize = ED_view3d_pixel_size_no_ui_scale(rv3d, text_pos_word_space);
593 scale_m4_fl(scale_mat, pixelsize * 0.5f);
594 mul_m4_m4_post(cd->text_mat, scale_mat);
595
597
598 ED_region_tag_redraw(region);
599
601
603}
604
609
611{
612 /* identifiers */
613 ot->name = "Edit Voxel Size";
614 ot->description = "Modify the mesh voxel size interactively used in the voxel remesher";
615 ot->idname = "OBJECT_OT_voxel_size_edit";
616
617 /* API callbacks. */
618 ot->poll = voxel_size_edit_poll;
619 ot->invoke = voxel_size_edit_invoke;
620 ot->modal = voxel_size_edit_modal;
621 ot->cancel = voxel_size_edit_cancel;
622
624}
625
627
628/* -------------------------------------------------------------------- */
631
632#define QUADRIFLOW_MIRROR_BISECT_TOLERANCE 0.005f
633
634enum {
638};
639
646
648 SYMMETRY_AXES_X = (1 << 0),
649 SYMMETRY_AXES_Y = (1 << 1),
650 SYMMETRY_AXES_Z = (1 << 2),
651};
652
675
677{
678 /* In this check we count boundary edges as manifold. Additionally, we also
679 * check that the direction of the faces are consistent and doesn't suddenly
680 * flip
681 */
682 const Span<float3> positions = mesh->vert_positions();
683 const Span<int2> edges = mesh->edges();
684 const Span<int> corner_verts = mesh->corner_verts();
685 const Span<int> corner_edges = mesh->corner_edges();
686
687 bool is_manifold_consistent = true;
688 char *edge_faces = MEM_calloc_arrayN<char>(mesh->edges_num, "remesh_manifold_check");
689 int *edge_vert = MEM_malloc_arrayN<int>(mesh->edges_num, "remesh_consistent_check");
690
691 for (uint i = 0; i < mesh->edges_num; i++) {
692 edge_vert[i] = -1;
693 }
694
695 for (const int corner_i : corner_verts.index_range()) {
696 const int vert = corner_verts[corner_i];
697 const int edge = corner_edges[corner_i];
698 edge_faces[edge] += 1;
699 if (edge_faces[edge] > 2) {
700 is_manifold_consistent = false;
701 break;
702 }
703
704 if (edge_vert[edge] == -1) {
705 edge_vert[edge] = vert;
706 }
707 else if (edge_vert[edge] == vert) {
708 /* Mesh has flips in the surface so it is non consistent */
709 is_manifold_consistent = false;
710 break;
711 }
712 }
713
714 if (is_manifold_consistent) {
715 for (const int i : edges.index_range()) {
716 /* Check for wire edges. */
717 if (edge_faces[i] == 0) {
718 is_manifold_consistent = false;
719 break;
720 }
721 /* Check for zero length edges */
722 if (compare_v3v3(positions[edges[i][0]], positions[edges[i][1]], 1e-4f)) {
723 is_manifold_consistent = false;
724 break;
725 }
726 }
727 }
728
729 MEM_freeN(edge_faces);
730 MEM_freeN(edge_vert);
731
732 return is_manifold_consistent;
733}
734
735static void quadriflow_free_job(void *customdata)
736{
737 QuadriFlowJob *qj = static_cast<QuadriFlowJob *>(customdata);
738 MEM_freeN(qj);
739}
740
741/* called by quadriflowjob, only to check job 'stop' value */
742static int quadriflow_break_job(void *customdata)
743{
744 QuadriFlowJob *qj = (QuadriFlowJob *)customdata;
745 // return *(qj->stop);
746
747 /* this is not nice yet, need to make the jobs list template better
748 * for identifying/acting upon various different jobs canceled */
749 /* but for now we'll reuse the render break... */
750 bool should_break = false;
751
752 if (qj->is_nonblocking_job) {
753 bool should_break = (G.is_break);
754 if (should_break) {
756 }
757 }
758
759 return should_break;
760}
761
763static void quadriflow_update_job(void *customdata, float progress, int *cancel)
764{
765 QuadriFlowJob *qj = static_cast<QuadriFlowJob *>(customdata);
766
767 if (quadriflow_break_job(qj)) {
768 *cancel = 1;
769 }
770 else {
771 *cancel = 0;
772 }
773
774 qj->worker_status->do_update = true;
775 qj->worker_status->progress = progress;
776}
777
779{
780 MirrorModifierData mmd = {{nullptr}};
782
783 Mesh *mesh_bisect, *mesh_bisect_temp;
784 mesh_bisect = BKE_mesh_copy_for_eval(*mesh);
785
786 int axis;
787 float plane_co[3], plane_no[3];
788 zero_v3(plane_co);
789
790 for (char i = 0; i < 3; i++) {
791 eSymmetryAxes symm_it = (eSymmetryAxes)(1 << i);
792 if (symmetry_axes & symm_it) {
793 axis = i;
794 mmd.flag = 0;
795 mmd.flag &= MOD_MIR_BISECT_AXIS_X << i;
796 zero_v3(plane_no);
797 plane_no[axis] = -1.0f;
798 mesh_bisect_temp = mesh_bisect;
800 &mmd, mesh_bisect, axis, plane_co, plane_no);
801 if (mesh_bisect_temp != mesh_bisect) {
802 BKE_id_free(nullptr, mesh_bisect_temp);
803 }
804 }
805 }
806
807 BKE_id_free(nullptr, mesh);
808
809 return mesh_bisect;
810}
811
813{
814 MirrorModifierData mmd = {{nullptr}};
816 Mesh *mesh_mirror, *mesh_mirror_temp;
817
818 mesh_mirror = mesh;
819
820 int axis;
821
822 for (char i = 0; i < 3; i++) {
823 eSymmetryAxes symm_it = (eSymmetryAxes)(1 << i);
824 if (symmetry_axes & symm_it) {
825 axis = i;
826 mmd.flag = 0;
827 mmd.flag &= MOD_MIR_AXIS_X << i;
828 mesh_mirror_temp = mesh_mirror;
830 &mmd, ob, mesh_mirror, axis, true, nullptr, nullptr);
831 if (mesh_mirror_temp != mesh_mirror) {
832 BKE_id_free(nullptr, mesh_mirror_temp);
833 }
834 }
835 }
836
837 return mesh_mirror;
838}
839
840static void quadriflow_start_job(void *customdata, wmJobWorkerStatus *worker_status)
841{
842 QuadriFlowJob *qj = static_cast<QuadriFlowJob *>(customdata);
843
844 qj->worker_status = worker_status;
846
847 if (qj->is_nonblocking_job) {
848 G.is_break = false; /* XXX shared with render - replace with job 'stop' switch */
849 }
850
851 Object *ob = qj->owner;
852 Mesh *mesh = static_cast<Mesh *>(ob->data);
853 Scene &scene = *qj->scene;
854 Mesh *new_mesh;
855 Mesh *bisect_mesh;
856
857 /* Check if the mesh is manifold. Quadriflow requires manifold meshes */
860 return;
861 }
862
863 /* Run Quadriflow bisect operations on a copy of the mesh to keep the code readable without
864 * freeing the original ID */
865 bisect_mesh = BKE_mesh_copy_for_eval(*mesh);
866
867 /* Bisect the input mesh using the paint symmetry settings */
868 bisect_mesh = remesh_symmetry_bisect(bisect_mesh, qj->symmetry_axes);
869
870 new_mesh = BKE_mesh_remesh_quadriflow(bisect_mesh,
871 qj->target_faces,
872 qj->seed,
875#ifdef USE_MESH_CURVATURE
877#else
878 false,
879#endif
881 (void *)qj);
882
883 BKE_id_free(nullptr, bisect_mesh);
884
885 if (new_mesh == nullptr) {
886 worker_status->do_update = true;
887 worker_status->stop = false;
889 /* This is not a user cancellation event. */
891 }
892 return;
893 }
894
895 /* Mirror the Quadriflow result to build the final mesh */
896 new_mesh = remesh_symmetry_mirror(qj->owner, new_mesh, qj->symmetry_axes);
897
898 if (ob->mode == OB_MODE_SCULPT) {
899 sculpt_paint::undo::geometry_begin(scene, *ob, qj->op);
900 }
901
902 if (qj->preserve_attributes) {
904 }
905
906 BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob);
907
908 bke::mesh_smooth_set(*static_cast<Mesh *>(ob->data), qj->smooth_normals);
909
910 if (ob->mode == OB_MODE_SCULPT) {
913 }
914
916
917 worker_status->do_update = true;
918 worker_status->stop = false;
919}
920
921static void quadriflow_end_job(void *customdata)
922{
923 QuadriFlowJob *qj = (QuadriFlowJob *)customdata;
924
925 Object *ob = qj->owner;
926
927 if (qj->is_nonblocking_job) {
928 WM_set_locked_interface(static_cast<wmWindowManager *>(G_MAIN->wm.first), false);
929 }
930
931 ReportList *reports = qj->worker_status->reports;
932 switch (qj->status) {
935 BKE_reportf(reports, RPT_INFO, "QuadriFlow: Remeshing completed");
936 break;
938 BKE_reportf(reports, RPT_ERROR, "QuadriFlow: Remeshing failed");
939 break;
941 BKE_report(reports, RPT_WARNING, "QuadriFlow: Remeshing canceled");
942 break;
946 "QuadriFlow: The mesh needs to be manifold and have face normals that point in a "
947 "consistent direction");
948 break;
949 }
950}
951
953{
954 QuadriFlowJob *job = MEM_mallocN<QuadriFlowJob>("QuadriFlowJob");
955
956 job->op = op;
958 job->scene = CTX_data_scene(C);
959
960 job->target_faces = RNA_int_get(op->ptr, "target_faces");
961 job->seed = RNA_int_get(op->ptr, "seed");
962
963 job->use_mesh_symmetry = RNA_boolean_get(op->ptr, "use_mesh_symmetry");
964
965 job->use_preserve_sharp = RNA_boolean_get(op->ptr, "use_preserve_sharp");
966 job->use_preserve_boundary = RNA_boolean_get(op->ptr, "use_preserve_boundary");
967
968#ifdef USE_MESH_CURVATURE
969 job->use_mesh_curvature = RNA_boolean_get(op->ptr, "use_mesh_curvature");
970#endif
971
972 job->preserve_attributes = RNA_boolean_get(op->ptr, "preserve_attributes");
973 job->smooth_normals = RNA_boolean_get(op->ptr, "smooth_normals");
974
975 /* Update the target face count if symmetry is enabled */
977 if (ob && job->use_mesh_symmetry) {
979 job->symmetry_axes = (eSymmetryAxes)mesh->symmetry;
980 for (char i = 0; i < 3; i++) {
981 eSymmetryAxes symm_it = (eSymmetryAxes)(1 << i);
982 if (job->symmetry_axes & symm_it) {
983 job->target_faces = job->target_faces / 2;
984 }
985 }
986 }
987 else {
988 job->use_mesh_symmetry = false;
990 }
991
993 if ((op->flag & OP_IS_INVOKE) == 0) {
994 /* This is called directly from the exec operator, this operation is now blocking */
995 job->is_nonblocking_job = false;
996 wmJobWorkerStatus worker_status = {};
997 worker_status.reports = op->reports;
998 quadriflow_start_job(job, &worker_status);
999
1000 status = job->status;
1001 quadriflow_end_job(job);
1003 }
1004 else {
1005 /* Non blocking call. For when the operator has been called from the GUI. */
1006 job->is_nonblocking_job = true;
1007
1008 wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
1011 "QuadriFlow Remesh",
1014
1016 WM_jobs_timer(wm_job, 0.1, NC_GEOM | ND_DATA, NC_GEOM | ND_DATA);
1017 WM_jobs_callbacks(wm_job, quadriflow_start_job, nullptr, nullptr, quadriflow_end_job);
1018
1020
1021 WM_jobs_start(CTX_wm_manager(C), wm_job);
1022 }
1023
1024 if (status == QUADRIFLOW_STATUS_SUCCESS) {
1025 return OPERATOR_FINISHED;
1026 }
1027 /* Only ever runs with immediate execution. */
1028 return OPERATOR_CANCELLED;
1029}
1030
1032{
1033 int mode = RNA_enum_get(op->ptr, "mode");
1034
1035 if (mode == QUADRIFLOW_REMESH_EDGE_LENGTH) {
1036 float area = RNA_float_get(op->ptr, "mesh_area");
1037 if (area < 0.0f) {
1039 area = BKE_mesh_calc_area(static_cast<const Mesh *>(ob->data));
1040 RNA_float_set(op->ptr, "mesh_area", area);
1041 }
1042 int faces_num;
1043 float edge_len = RNA_float_get(op->ptr, "target_edge_length");
1044
1045 faces_num = area / (edge_len * edge_len);
1046 RNA_int_set(op->ptr, "target_faces", faces_num);
1047 }
1048 else if (mode == QUADRIFLOW_REMESH_RATIO) {
1050 Mesh *mesh = static_cast<Mesh *>(ob->data);
1051
1052 int faces_num;
1053 float ratio = RNA_float_get(op->ptr, "target_ratio");
1054
1055 faces_num = mesh->faces_num * ratio;
1056
1057 RNA_int_set(op->ptr, "target_faces", faces_num);
1058 }
1059
1060 return true;
1061}
1062
1063/* Hide the target variables if they are not active */
1064static bool quadriflow_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop)
1065{
1066 const char *prop_id = RNA_property_identifier(prop);
1067
1068 if (STRPREFIX(prop_id, "target")) {
1069 int mode = RNA_enum_get(op->ptr, "mode");
1070
1071 if (STREQ(prop_id, "target_edge_length") && mode != QUADRIFLOW_REMESH_EDGE_LENGTH) {
1072 return false;
1073 }
1074 if (STREQ(prop_id, "target_faces")) {
1075 if (mode != QUADRIFLOW_REMESH_FACES) {
1076 /* Make sure we can edit the target_faces value even if it doesn't start as EDITABLE */
1077 float area = RNA_float_get(op->ptr, "mesh_area");
1078 if (area < -0.8f) {
1079 area += 0.2f;
1080 /* Make sure we have up to date values from the start */
1082 quadriflow_check((bContext *)C, op);
1083 }
1084
1085 /* Only disable input */
1087 }
1088 else {
1090 }
1091 }
1092 else if (STREQ(prop_id, "target_ratio") && mode != QUADRIFLOW_REMESH_RATIO) {
1093 return false;
1094 }
1095 }
1096
1097 return true;
1098}
1099
1102 "RATIO",
1103 0,
1104 "Ratio",
1105 "Specify target number of faces relative to the current mesh"},
1107 "EDGE",
1108 0,
1109 "Edge Length",
1110 "Input target edge length in the new mesh"},
1111 {QUADRIFLOW_REMESH_FACES, "FACES", 0, "Faces", "Input target number of faces in the new mesh"},
1112 {0, nullptr, 0, nullptr, nullptr},
1113};
1114
1116{
1118 C, op, event, IFACE_("QuadriFlow Remesh the Selected Mesh"), IFACE_("Remesh"));
1119}
1120
1122{
1123 /* identifiers */
1124 ot->name = "QuadriFlow Remesh";
1125 ot->description =
1126 "Create a new quad based mesh using the surface data of the current mesh. All data "
1127 "layers will be lost";
1128 ot->idname = "OBJECT_OT_quadriflow_remesh";
1129
1130 /* API callbacks. */
1131 ot->poll = object_remesh_poll;
1132 ot->poll_property = quadriflow_poll_property;
1133 ot->check = quadriflow_check;
1134 ot->invoke = quadriflow_remesh_invoke;
1135 ot->exec = quadriflow_remesh_exec;
1136
1137 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1138
1139 PropertyRNA *prop;
1140
1141 /* properties */
1142 RNA_def_boolean(ot->srna,
1143 "use_mesh_symmetry",
1144 true,
1145 "Use Mesh Symmetry",
1146 "Generates a symmetrical mesh using the mesh symmetry configuration");
1147
1148 RNA_def_boolean(ot->srna,
1149 "use_preserve_sharp",
1150 false,
1151 "Preserve Sharp",
1152 "Try to preserve sharp features on the mesh");
1153
1154 RNA_def_boolean(ot->srna,
1155 "use_preserve_boundary",
1156 false,
1157 "Preserve Mesh Boundary",
1158 "Try to preserve mesh boundary on the mesh");
1159#ifdef USE_MESH_CURVATURE
1160 RNA_def_boolean(ot->srna,
1161 "use_mesh_curvature",
1162 false,
1163 "Use Mesh Curvature",
1164 "Take the mesh curvature into account when remeshing");
1165#endif
1166 RNA_def_boolean(ot->srna,
1167 "preserve_attributes",
1168 false,
1169 "Preserve Attributes",
1170 "Reproject attributes onto the new mesh");
1171
1172 RNA_def_boolean(ot->srna,
1173 "smooth_normals",
1174 false,
1175 "Smooth Normals",
1176 "Set the output mesh normals to smooth");
1177
1178 RNA_def_enum(ot->srna,
1179 "mode",
1182 "Mode",
1183 "How to specify the amount of detail for the new mesh");
1184
1185 prop = RNA_def_float(ot->srna,
1186 "target_ratio",
1187 1,
1188 0,
1189 FLT_MAX,
1190 "Ratio",
1191 "Relative number of faces compared to the current mesh",
1192 0.0f,
1193 1.0f);
1194
1195 prop = RNA_def_float(ot->srna,
1196 "target_edge_length",
1197 0.1f,
1198 0.0000001f,
1199 FLT_MAX,
1200 "Edge Length",
1201 "Target edge length in the new mesh",
1202 0.00001f,
1203 1.0f);
1204
1205 prop = RNA_def_int(ot->srna,
1206 "target_faces",
1207 4000,
1208 1,
1209 INT_MAX,
1210 "Number of Faces",
1211 "Approximate number of faces (quads) in the new mesh",
1212 1,
1213 INT_MAX);
1214
1215 prop = RNA_def_float(
1216 ot->srna,
1217 "mesh_area",
1218 -1.0f,
1219 -FLT_MAX,
1220 FLT_MAX,
1221 "Old Object Face Area",
1222 "This property is only used to cache the object area for later calculations",
1223 0.0f,
1224 FLT_MAX);
1226
1227 RNA_def_int(ot->srna,
1228 "seed",
1229 0,
1230 0,
1231 INT_MAX,
1232 "Seed",
1233 "Random seed to use with the solver. Different seeds will cause the remesher to "
1234 "come up with different quad layouts on the mesh",
1235 0,
1236 255);
1237}
1238
1240
1241} // namespace blender::ed::object
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define G_MAIN
void BKE_id_free(Main *bmain, void *idv)
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob)
Mesh * BKE_mesh_copy_for_eval(const Mesh &source)
@ BKE_MESH_BATCH_DIRTY_ALL
Definition BKE_mesh.h:38
float BKE_mesh_calc_area(const Mesh *mesh)
Mesh * BKE_mesh_from_object(Object *ob)
Mesh * BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, Object *ob, const Mesh *mesh, int axis, bool use_correct_order_on_merge, int **r_vert_merge_map, int *r_vert_merge_map_len)
Mesh * BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mmd, const Mesh *mesh, int axis, const float plane_co[3], float plane_no[3])
Mesh * BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh)
Mesh * BKE_mesh_remesh_voxel(const Mesh *mesh, float voxel_size, float adaptivity, float isovalue, const Object *object, ModifierData *modifier_data)
Mesh * BKE_mesh_remesh_quadriflow(const Mesh *mesh, int target_faces, int seed, bool preserve_sharp, bool preserve_boundary, bool adaptive_scale, void(*update_cb)(void *, float progress, int *cancel), void *update_cb_data)
bool BKE_modifiers_uses_multires(Object *ob)
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
void BKE_boundbox_init_from_minmax(BoundBox *bb, const float min[3], const float max[3])
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2146
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target)
size_t BKE_unit_value_as_string_scaled(char *str, int str_maxncpy, double value, int prec, int type, const UnitSettings &settings, bool pad)
Definition unit.cc:1889
@ B_UNIT_LENGTH
Definition BKE_unit.hh:124
void BLF_size(int fontid, float size)
Definition blf.cc:440
void BLF_width_and_height(int fontid, const char *str, size_t str_len, float *r_width, float *r_height) ATTR_NONNULL()
Definition blf.cc:792
void BLF_color3f(int fontid, float r, float g, float b)
Definition blf.cc:525
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:582
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:385
MINLINE float max_ff(float a, float b)
MINLINE float clamp_f(float value, float min, float max)
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
void mul_m3_v3(const float M[3][3], float r[3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void mul_m4_m4_pre(float R[4][4], const float A[4][4])
void size_to_mat4(float R[4][4], const float size[3])
void scale_m4_fl(float R[4][4], float scale)
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mat4_to_size(float size[3], const float M[4][4])
void mul_m4_m4_post(float R[4][4], const float B[4][4])
void unit_m4(float m[4][4])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void invert_v3(float r[3])
MINLINE bool compare_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2(float n[2])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE void zero_v3(float r[3])
MINLINE float normalize_v3(float n[3])
size_t BLI_strlen_utf8(const char *strc) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
unsigned int uint
#define STRPREFIX(a, b)
#define STREQ(a, b)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ ME_REMESH_REPROJECT_ATTRIBUTES
@ ME_REMESH_REPROJECT_VOLUME
@ ME_REMESH_FIX_POLES
@ MOD_MIR_BISECT_AXIS_X
@ MOD_MIR_AXIS_X
@ OB_MODE_SCULPT
Object is a sort of wrapper for general info.
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
bool ED_operator_object_active_editable_mesh(bContext *C)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
#define REGION_DRAW_POST_VIEW
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[3])
void ED_view3d_project_v2(const ARegion *region, const float world[3], float r_region_co[2])
void immEnd()
void immUnbindProgram()
void immUniformColor4f(float r, float g, float b, float a)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void GPU_matrix_push()
#define GPU_matrix_mul(x)
void GPU_matrix_pop()
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:166
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
@ PROP_EDITABLE
Definition RNA_types.hh:292
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
const uiStyle * UI_style_get()
@ WM_JOB_TYPE_QUADRIFLOW_REMESH
Definition WM_api.hh:1754
@ WM_JOB_PROGRESS
Definition WM_api.hh:1716
#define NC_GEOM
Definition WM_types.hh:390
#define ND_DATA
Definition WM_types.hh:506
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
ReportList * reports
Definition WM_types.hh:1025
float progress
Definition WM_types.hh:1019
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
void item_bool(std::string text, bool inverted, int icon1, int icon2=0)
Definition area.cc:995
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
#define fabsf(x)
#define str(s)
#define ID_IS_EDITABLE(_id)
#define ID_IS_OVERRIDE_LIBRARY(_id)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
void mesh_remesh_reproject_attributes(const Mesh &src, Mesh &dst)
void mesh_smooth_set(Mesh &mesh, bool use_smooth, bool keep_sharp_edges=false)
static bool object_remesh_poll(bContext *C)
static Mesh * remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes)
static void quadriflow_end_job(void *customdata)
static void quadriflow_update_job(void *customdata, float progress, int *cancel)
static wmOperatorStatus voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int quadriflow_break_job(void *customdata)
static void voxel_size_edit_cancel(bContext *C, wmOperator *op)
static bool mesh_is_manifold_consistent(Mesh *mesh)
static bool voxel_size_edit_poll(bContext *C)
static bool quadriflow_check(bContext *C, wmOperator *op)
void OBJECT_OT_voxel_size_edit(wmOperatorType *ot)
static const EnumPropertyItem mode_type_items[]
static void voxel_size_parallel_lines_draw(uint pos3d, const float initial_co[3], const float end_co[3], const float length_co[3], const float spacing)
static void voxel_size_edit_update_header(wmOperator *op, bContext *C)
static void voxel_size_edit_draw(const bContext *C, ARegion *, void *arg)
void OBJECT_OT_voxel_remesh(wmOperatorType *ot)
static wmOperatorStatus voxel_remesh_exec(bContext *C, wmOperator *op)
static wmOperatorStatus quadriflow_remesh_exec(bContext *C, wmOperator *op)
static Mesh * remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes)
static void quadriflow_free_job(void *customdata)
void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot)
static void quadriflow_start_job(void *customdata, wmJobWorkerStatus *worker_status)
static wmOperatorStatus voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
static bool quadriflow_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop)
static wmOperatorStatus quadriflow_remesh_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void geometry_begin(const Scene &scene, Object &ob, const wmOperator *op)
#define VOXEL_SIZE_EDIT_MAX_GRIDS_LINES
#define VOXEL_SIZE_EDIT_MAX_STR_LEN
#define QUADRIFLOW_MIRROR_BISECT_TOLERANCE
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
const char * RNA_property_identifier(const PropertyRNA *prop)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_clear_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#define FLT_MAX
Definition stdcycles.h:14
ARegionRuntimeHandle * runtime
float vec[8][3]
ObjectRuntimeHandle * runtime
struct SculptSession * sculpt
float viewinv[4][4]
struct UnitSettings unit
uiFontStyle widget
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int mval[2]
Definition WM_types.hh:760
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void WM_set_locked_interface(wmWindowManager *wm, bool lock)
@ RIGHTMOUSE
@ EVT_PADENTER
@ LEFTMOUSE
@ EVT_ESCKEY
@ EVT_LEFTSHIFTKEY
@ EVT_RETKEY
wmOperatorType * ot
Definition wm_files.cc:4226
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:353
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:456
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:190
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:365
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:337
wmOperatorStatus WM_operator_props_popup_confirm_ex(bContext *C, wmOperator *op, const wmEvent *, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default)