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