Blender V4.3
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
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_matrix.h"
18#include "BLI_string_utf8.h"
19#include "BLI_utildefines.h"
20
21#include "DNA_object_types.h"
22#include "DNA_userdef_types.h"
23
24#include "BLT_translation.hh"
25
26#include "BKE_attribute.hh"
27#include "BKE_context.hh"
28#include "BKE_global.hh"
29#include "BKE_lib_id.hh"
30#include "BKE_main.hh"
31#include "BKE_mesh.hh"
32#include "BKE_mesh_mirror.hh"
34#include "BKE_modifier.hh"
35#include "BKE_object.hh"
36#include "BKE_object_types.hh"
37#include "BKE_paint.hh"
38#include "BKE_report.hh"
39#include "BKE_shrinkwrap.hh"
40#include "BKE_unit.hh"
41
42#include "DEG_depsgraph.hh"
43
44#include "ED_screen.hh"
45#include "ED_sculpt.hh"
46#include "ED_space_api.hh"
47#include "ED_view3d.hh"
48
49#include "RNA_access.hh"
50#include "RNA_define.hh"
51
52#include "GPU_immediate.hh"
53#include "GPU_matrix.hh"
54#include "GPU_state.hh"
55
56#include "WM_api.hh"
57#include "WM_types.hh"
58
59#include "UI_interface.hh"
60
61#include "BLF_api.hh"
62
63#include "object_intern.hh" /* own include */
64
65namespace blender::ed::object {
66
67/* TODO(sebpa): unstable, can lead to unrecoverable errors. */
68// #define USE_MESH_CURVATURE
69
70/* -------------------------------------------------------------------- */
75{
77
78 if (ob == nullptr || ob->data == nullptr) {
79 return false;
80 }
81
83 CTX_wm_operator_poll_msg_set(C, "The remesher cannot work on linked or override data");
84 return false;
85 }
86
88 CTX_wm_operator_poll_msg_set(C, "The remesher cannot run from edit mode");
89 return false;
90 }
91
92 if (ob->mode == OB_MODE_SCULPT && ob->sculpt->bm) {
93 CTX_wm_operator_poll_msg_set(C, "The remesher cannot run with dyntopo activated");
94 return false;
95 }
96
99 C, "The remesher cannot run with a Multires modifier in the modifier stack");
100 return false;
101 }
102
104}
105
107{
108 const Scene &scene = *CTX_data_scene(C);
110
111 Mesh *mesh = static_cast<Mesh *>(ob->data);
112
113 if (mesh->remesh_voxel_size <= 0.0f) {
114 BKE_report(op->reports, RPT_ERROR, "Voxel remesher cannot run with a voxel size of 0.0");
115 return OPERATOR_CANCELLED;
116 }
117
118 if (mesh->faces_num == 0) {
119 return OPERATOR_CANCELLED;
120 }
121
122 float isovalue = 0.0f;
123 if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) {
124 isovalue = mesh->remesh_voxel_size * 0.3f;
125 }
126
127 Mesh *new_mesh = BKE_mesh_remesh_voxel(
128 mesh, mesh->remesh_voxel_size, mesh->remesh_voxel_adaptivity, isovalue);
129
130 if (!new_mesh) {
131 BKE_report(op->reports, RPT_ERROR, "Voxel remesher failed to create mesh");
132 return OPERATOR_CANCELLED;
133 }
134
135 if (ob->mode == OB_MODE_SCULPT) {
139 }
140
141 if (mesh->flag & ME_REMESH_FIX_POLES && mesh->remesh_voxel_adaptivity <= 0.0f) {
142 Mesh *mesh_fixed_poles = BKE_mesh_remesh_voxel_fix_poles(new_mesh);
143 BKE_id_free(nullptr, new_mesh);
144 new_mesh = mesh_fixed_poles;
145 }
146
147 if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) {
148 BKE_shrinkwrap_remesh_target_project(new_mesh, mesh, ob);
149 }
150
151 if (mesh->flag & ME_REMESH_REPROJECT_ATTRIBUTES) {
153 }
154 else {
155 const VArray<bool> sharp_face = *mesh->attributes().lookup_or_default<bool>(
156 "sharp_face", bke::AttrDomain::Face, false);
157 bke::mesh_smooth_set(*new_mesh, !sharp_face[0]);
158 }
159
160 BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob);
161
162 if (ob->mode == OB_MODE_SCULPT) {
165 }
166
170
171 return OPERATOR_FINISHED;
172}
173
175{
176 /* identifiers */
177 ot->name = "Voxel Remesh";
178 ot->description =
179 "Calculates a new manifold mesh based on the volume of the current mesh. All data layers "
180 "will be lost";
181 ot->idname = "OBJECT_OT_voxel_remesh";
182
183 /* api callbacks */
186
188}
189
192/* -------------------------------------------------------------------- */
196#define VOXEL_SIZE_EDIT_MAX_GRIDS_LINES 500
197#define VOXEL_SIZE_EDIT_MAX_STR_LEN 20
198
218
220 const float initial_co[3],
221 const float end_co[3],
222 const float length_co[3],
223 const float spacing)
224{
225 const float total_len = len_v3v3(initial_co, end_co);
226 const int tot_lines = int(total_len / spacing);
227 const int tot_lines_half = (tot_lines / 2) + 1;
228 float spacing_dir[3], lines_start[3];
229 float line_dir[3];
230 sub_v3_v3v3(spacing_dir, end_co, initial_co);
231 normalize_v3(spacing_dir);
232
233 sub_v3_v3v3(line_dir, length_co, initial_co);
234
235 if (tot_lines > VOXEL_SIZE_EDIT_MAX_GRIDS_LINES || tot_lines <= 1) {
236 return;
237 }
238
239 mid_v3_v3v3(lines_start, initial_co, end_co);
240
241 immBegin(GPU_PRIM_LINES, uint(tot_lines_half) * 2);
242 for (int i = 0; i < tot_lines_half; i++) {
243 float line_start[3];
244 float line_end[3];
245 madd_v3_v3v3fl(line_start, lines_start, spacing_dir, spacing * i);
246 add_v3_v3v3(line_end, line_start, line_dir);
247 immVertex3fv(pos3d, line_start);
248 immVertex3fv(pos3d, line_end);
249 }
250 immEnd();
251
252 mul_v3_fl(spacing_dir, -1.0f);
253
254 immBegin(GPU_PRIM_LINES, uint(tot_lines_half - 1) * 2);
255 for (int i = 1; i < tot_lines_half; i++) {
256 float line_start[3];
257 float line_end[3];
258 madd_v3_v3v3fl(line_start, lines_start, spacing_dir, spacing * i);
259 add_v3_v3v3(line_end, line_start, line_dir);
260 immVertex3fv(pos3d, line_start);
261 immVertex3fv(pos3d, line_end);
262 }
263 immEnd();
264}
265
266static void voxel_size_edit_draw(const bContext *C, ARegion * /*region*/, void *arg)
267{
268 VoxelSizeEditCustomData *cd = static_cast<VoxelSizeEditCustomData *>(arg);
269
271 GPU_line_smooth(true);
272
276 GPU_matrix_mul(cd->active_object->object_to_world().ptr());
277
278 /* Draw Rect */
279 immUniformColor4f(0.9f, 0.9f, 0.9f, 0.8f);
280 GPU_line_width(3.0f);
281
283 immVertex3fv(pos3d, cd->preview_plane[0]);
284 immVertex3fv(pos3d, cd->preview_plane[1]);
285
286 immVertex3fv(pos3d, cd->preview_plane[1]);
287 immVertex3fv(pos3d, cd->preview_plane[2]);
288
289 immVertex3fv(pos3d, cd->preview_plane[2]);
290 immVertex3fv(pos3d, cd->preview_plane[3]);
291
292 immVertex3fv(pos3d, cd->preview_plane[3]);
293 immVertex3fv(pos3d, cd->preview_plane[0]);
294 immEnd();
295
296 /* Draw Grid */
297 GPU_line_width(1.0f);
298
299 const float total_len = len_v3v3(cd->preview_plane[0], cd->preview_plane[1]);
300 const int tot_lines = int(total_len / cd->voxel_size);
301
302 /* Smooth-step to reduce the alpha of the grid as the line number increases. */
303 const float a = VOXEL_SIZE_EDIT_MAX_GRIDS_LINES * 0.1f;
305 const float x = clamp_f((tot_lines - a) / (b - a), 0.0f, 1.0);
306 const float alpha_factor = 1.0f - (x * x * (3.0f - 2.0f * x));
307
308 immUniformColor4f(0.9f, 0.9f, 0.9f, 0.75f * alpha_factor);
310 pos3d, cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[3], cd->voxel_size);
312 pos3d, cd->preview_plane[1], cd->preview_plane[2], cd->preview_plane[0], cd->voxel_size);
313
314 /* Draw text */
315 const uiStyle *style = UI_style_get();
316 const uiFontStyle *fstyle = &style->widget;
317 const int fontid = fstyle->uifont_id;
318 float strwidth, strheight;
319 short fstyle_points = fstyle->points;
321 short strdrawlen = 0;
322 Scene *scene = CTX_data_scene(C);
323 const UnitSettings *unit = &scene->unit;
326 double(cd->voxel_size * unit->scale_length),
327 -3,
329 unit,
330 true);
331 strdrawlen = BLI_strlen_utf8(str);
332
334
337 BLF_size(fontid, 10.0f * fstyle_points * UI_SCALE_FAC);
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->type, cd->draw_handle);
356
358
359 ED_workspace_status_text(C, nullptr);
360}
361
363{
365 WorkspaceStatus status(C);
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
372static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
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->type, cd->draw_handle);
394 mesh->remesh_voxel_size = cd->voxel_size;
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
438static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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
444 VoxelSizeEditCustomData *cd = MEM_cnew<VoxelSizeEditCustomData>(
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->init_voxel_size = mesh->remesh_voxel_size;
454 cd->voxel_size = mesh->remesh_voxel_size;
455 cd->slow_mode = false;
456 op->customdata = cd;
457
458 /* Select the front facing face of the mesh bounding box. */
459 const Bounds<float3> bounds = *mesh->bounds_min_max();
460 BoundBox bb;
461 BKE_boundbox_init_from_minmax(&bb, bounds.min, bounds.max);
462
463 /* Indices of the Bounding Box faces. */
464 const int BB_faces[6][4] = {
465 {3, 0, 4, 7},
466 {1, 2, 6, 5},
467 {3, 2, 1, 0},
468 {4, 5, 6, 7},
469 {0, 1, 5, 4},
470 {2, 3, 7, 6},
471 };
472
473 copy_v3_v3(cd->preview_plane[0], bb.vec[BB_faces[0][0]]);
474 copy_v3_v3(cd->preview_plane[1], bb.vec[BB_faces[0][1]]);
475 copy_v3_v3(cd->preview_plane[2], bb.vec[BB_faces[0][2]]);
476 copy_v3_v3(cd->preview_plane[3], bb.vec[BB_faces[0][3]]);
477
479
480 float mat[3][3];
481 float current_normal[3];
482 float view_normal[3] = {0.0f, 0.0f, 1.0f};
483
484 /* Calculate the view normal. */
485 invert_m4_m4(active_object->runtime->world_to_object.ptr(),
486 active_object->object_to_world().ptr());
487 copy_m3_m4(mat, rv3d->viewinv);
488 mul_m3_v3(mat, view_normal);
489 copy_m3_m4(mat, active_object->world_to_object().ptr());
490 mul_m3_v3(mat, view_normal);
491 normalize_v3(view_normal);
492
493 normal_tri_v3(current_normal, cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[2]);
494
495 float min_dot = dot_v3v3(current_normal, view_normal);
496 float current_dot = 1;
497
498 /* Check if there is a face that is more aligned towards the view. */
499 for (int i = 0; i < 6; i++) {
501 current_normal, bb.vec[BB_faces[i][0]], bb.vec[BB_faces[i][1]], bb.vec[BB_faces[i][2]]);
502 current_dot = dot_v3v3(current_normal, view_normal);
503
504 if (current_dot < min_dot) {
505 min_dot = current_dot;
506 copy_v3_v3(cd->preview_plane[0], bb.vec[BB_faces[i][0]]);
507 copy_v3_v3(cd->preview_plane[1], bb.vec[BB_faces[i][1]]);
508 copy_v3_v3(cd->preview_plane[2], bb.vec[BB_faces[i][2]]);
509 copy_v3_v3(cd->preview_plane[3], bb.vec[BB_faces[i][3]]);
510 }
511 }
512
513 /* Cap the max/min voxel size based on the point where we cant visually display any more info
514 * with grid lines. */
516 len_v3v3(cd->preview_plane[3], cd->preview_plane[0])) *
517 0.5f;
519
520 /* Matrix calculation to position the text in 3D space. */
521 float text_pos[3];
522 float scale_mat[4][4];
523
524 float d_a[3], d_b[3];
525 float d_a_proj[2], d_b_proj[2];
526 float preview_plane_proj[4][2];
527 const float y_axis_proj[2] = {0.0f, 1.0f};
528
529 mid_v3_v3v3(text_pos, cd->preview_plane[0], cd->preview_plane[2]);
530
531 /* Project the selected face in the previous step of the Bounding Box. */
532 for (int i = 0; i < 4; i++) {
533 float preview_plane_world_space[3];
535 preview_plane_world_space, active_object->object_to_world().ptr(), cd->preview_plane[i]);
536 ED_view3d_project_v2(region, preview_plane_world_space, preview_plane_proj[i]);
537 }
538
539 /* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */
540 sub_v3_v3v3(d_a, cd->preview_plane[1], cd->preview_plane[0]);
541 sub_v3_v3v3(d_b, cd->preview_plane[3], cd->preview_plane[0]);
542 normalize_v3(d_a);
543 normalize_v3(d_b);
544
545 /* Project the X and Y axis. */
546 sub_v2_v2v2(d_a_proj, preview_plane_proj[1], preview_plane_proj[0]);
547 sub_v2_v2v2(d_b_proj, preview_plane_proj[3], preview_plane_proj[0]);
548 normalize_v2(d_a_proj);
549 normalize_v2(d_b_proj);
550
551 unit_m4(cd->text_mat);
552
553 /* Select the axis that is aligned with the view Y axis to use it as the basis Y. */
554 if (fabsf(dot_v2v2(d_a_proj, y_axis_proj)) > fabsf(dot_v2v2(d_b_proj, y_axis_proj))) {
555 copy_v3_v3(cd->text_mat[0], d_b);
556 copy_v3_v3(cd->text_mat[1], d_a);
557
558 /* Flip the X and Y basis vectors to make sure they always point upwards and to the right. */
559 if (d_b_proj[0] < 0.0f) {
560 mul_v3_fl(cd->text_mat[0], -1.0f);
561 }
562 if (d_a_proj[1] < 0.0f) {
563 mul_v3_fl(cd->text_mat[1], -1.0f);
564 }
565 }
566 else {
567 copy_v3_v3(cd->text_mat[0], d_a);
568 copy_v3_v3(cd->text_mat[1], d_b);
569 if (d_a_proj[0] < 0.0f) {
570 mul_v3_fl(cd->text_mat[0], -1.0f);
571 }
572 if (d_b_proj[1] < 0.0f) {
573 mul_v3_fl(cd->text_mat[1], -1.0f);
574 }
575 }
576
577 /* Use the Bounding Box face normal as the basis Z. */
578 normal_tri_v3(cd->text_mat[2], cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[2]);
579
580 /* Invert object scale. */
581 float scale[3];
582 mat4_to_size(scale, active_object->object_to_world().ptr());
583 invert_v3(scale);
584 size_to_mat4(scale_mat, scale);
585
586 mul_m4_m4_pre(cd->text_mat, scale_mat);
587
588 /* Write the text position into the matrix. */
589 copy_v3_v3(cd->text_mat[3], text_pos);
590
591 /* Scale the text to constant viewport size. */
592 float text_pos_word_space[3];
593 mul_v3_m4v3(text_pos_word_space, active_object->object_to_world().ptr(), text_pos);
594 const float pixelsize = ED_view3d_pixel_size(rv3d, text_pos_word_space);
595 scale_m4_fl(scale_mat, pixelsize * 0.5f);
596 mul_m4_m4_post(cd->text_mat, scale_mat);
597
599
600 ED_region_tag_redraw(region);
601
603
605}
606
608{
610}
611
613{
614 /* identifiers */
615 ot->name = "Edit Voxel Size";
616 ot->description = "Modify the mesh voxel size interactively used in the voxel remesher";
617 ot->idname = "OBJECT_OT_voxel_size_edit";
618
619 /* api callbacks */
624
626}
627
630/* -------------------------------------------------------------------- */
634#define QUADRIFLOW_MIRROR_BISECT_TOLERANCE 0.005f
635
636enum {
640};
641
643 SYMMETRY_AXES_X = (1 << 0),
644 SYMMETRY_AXES_Y = (1 << 1),
645 SYMMETRY_AXES_Z = (1 << 2),
646};
647
671
673{
674 /* In this check we count boundary edges as manifold. Additionally, we also
675 * check that the direction of the faces are consistent and doesn't suddenly
676 * flip
677 */
678 const Span<float3> positions = mesh->vert_positions();
679 const Span<int2> edges = mesh->edges();
680 const Span<int> corner_verts = mesh->corner_verts();
681 const Span<int> corner_edges = mesh->corner_edges();
682
683 bool is_manifold_consistent = true;
684 char *edge_faces = (char *)MEM_callocN(mesh->edges_num * sizeof(char), "remesh_manifold_check");
685 int *edge_vert = (int *)MEM_malloc_arrayN(
686 mesh->edges_num, sizeof(uint), "remesh_consistent_check");
687
688 for (uint i = 0; i < mesh->edges_num; i++) {
689 edge_vert[i] = -1;
690 }
691
692 for (const int corner_i : corner_verts.index_range()) {
693 const int vert = corner_verts[corner_i];
694 const int edge = corner_edges[corner_i];
695 edge_faces[edge] += 1;
696 if (edge_faces[edge] > 2) {
697 is_manifold_consistent = false;
698 break;
699 }
700
701 if (edge_vert[edge] == -1) {
702 edge_vert[edge] = vert;
703 }
704 else if (edge_vert[edge] == vert) {
705 /* Mesh has flips in the surface so it is non consistent */
706 is_manifold_consistent = false;
707 break;
708 }
709 }
710
711 if (is_manifold_consistent) {
712 for (const int i : edges.index_range()) {
713 /* Check for wire edges. */
714 if (edge_faces[i] == 0) {
715 is_manifold_consistent = false;
716 break;
717 }
718 /* Check for zero length edges */
719 if (compare_v3v3(positions[edges[i][0]], positions[edges[i][1]], 1e-4f)) {
720 is_manifold_consistent = false;
721 break;
722 }
723 }
724 }
725
726 MEM_freeN(edge_faces);
727 MEM_freeN(edge_vert);
728
729 return is_manifold_consistent;
730}
731
732static void quadriflow_free_job(void *customdata)
733{
734 QuadriFlowJob *qj = static_cast<QuadriFlowJob *>(customdata);
735 MEM_freeN(qj);
736}
737
738/* called by quadriflowjob, only to check job 'stop' value */
739static int quadriflow_break_job(void *customdata)
740{
741 QuadriFlowJob *qj = (QuadriFlowJob *)customdata;
742 // return *(qj->stop);
743
744 /* this is not nice yet, need to make the jobs list template better
745 * for identifying/acting upon various different jobs */
746 /* but for now we'll reuse the render break... */
747 bool should_break = (G.is_break);
748
749 if (should_break) {
750 qj->success = -1;
751 }
752
753 return should_break;
754}
755
757static void quadriflow_update_job(void *customdata, float progress, int *cancel)
758{
759 QuadriFlowJob *qj = static_cast<QuadriFlowJob *>(customdata);
760
761 if (quadriflow_break_job(qj)) {
762 *cancel = 1;
763 }
764 else {
765 *cancel = 0;
766 }
767
768 *(qj->do_update) = true;
769 *(qj->progress) = progress;
770}
771
772static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes)
773{
774 MirrorModifierData mmd = {{nullptr}};
776
777 Mesh *mesh_bisect, *mesh_bisect_temp;
778 mesh_bisect = BKE_mesh_copy_for_eval(*mesh);
779
780 int axis;
781 float plane_co[3], plane_no[3];
782 zero_v3(plane_co);
783
784 for (char i = 0; i < 3; i++) {
785 eSymmetryAxes symm_it = (eSymmetryAxes)(1 << i);
786 if (symmetry_axes & symm_it) {
787 axis = i;
788 mmd.flag = 0;
789 mmd.flag &= MOD_MIR_BISECT_AXIS_X << i;
790 zero_v3(plane_no);
791 plane_no[axis] = -1.0f;
792 mesh_bisect_temp = mesh_bisect;
794 &mmd, mesh_bisect, axis, plane_co, plane_no);
795 if (mesh_bisect_temp != mesh_bisect) {
796 BKE_id_free(nullptr, mesh_bisect_temp);
797 }
798 }
799 }
800
801 BKE_id_free(nullptr, mesh);
802
803 return mesh_bisect;
804}
805
806static Mesh *remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes)
807{
808 MirrorModifierData mmd = {{nullptr}};
810 Mesh *mesh_mirror, *mesh_mirror_temp;
811
812 mesh_mirror = mesh;
813
814 int axis;
815
816 for (char i = 0; i < 3; i++) {
817 eSymmetryAxes symm_it = (eSymmetryAxes)(1 << i);
818 if (symmetry_axes & symm_it) {
819 axis = i;
820 mmd.flag = 0;
821 mmd.flag &= MOD_MIR_AXIS_X << i;
822 mesh_mirror_temp = mesh_mirror;
824 &mmd, ob, mesh_mirror, axis, true, nullptr, nullptr);
825 if (mesh_mirror_temp != mesh_mirror) {
826 BKE_id_free(nullptr, mesh_mirror_temp);
827 }
828 }
829 }
830
831 return mesh_mirror;
832}
833
834static void quadriflow_start_job(void *customdata, wmJobWorkerStatus *worker_status)
835{
836 QuadriFlowJob *qj = static_cast<QuadriFlowJob *>(customdata);
837
838 qj->stop = &worker_status->stop;
839 qj->do_update = &worker_status->do_update;
840 qj->progress = &worker_status->progress;
841 qj->success = 1;
842
843 if (qj->is_nonblocking_job) {
844 G.is_break = false; /* XXX shared with render - replace with job 'stop' switch */
845 }
846
847 Object *ob = qj->owner;
848 Mesh *mesh = static_cast<Mesh *>(ob->data);
849 Scene &scene = *qj->scene;
850 Mesh *new_mesh;
851 Mesh *bisect_mesh;
852
853 /* Check if the mesh is manifold. Quadriflow requires manifold meshes */
854 if (!mesh_is_manifold_consistent(mesh)) {
855 qj->success = -2;
856 return;
857 }
858
859 /* Run Quadriflow bisect operations on a copy of the mesh to keep the code readable without
860 * freeing the original ID */
861 bisect_mesh = BKE_mesh_copy_for_eval(*mesh);
862
863 /* Bisect the input mesh using the paint symmetry settings */
864 bisect_mesh = remesh_symmetry_bisect(bisect_mesh, qj->symmetry_axes);
865
866 new_mesh = BKE_mesh_remesh_quadriflow(bisect_mesh,
867 qj->target_faces,
868 qj->seed,
871#ifdef USE_MESH_CURVATURE
873#else
874 false,
875#endif
877 (void *)qj);
878
879 BKE_id_free(nullptr, bisect_mesh);
880
881 if (new_mesh == nullptr) {
882 worker_status->do_update = true;
883 worker_status->stop = false;
884 if (qj->success == 1) {
885 /* This is not a user cancellation event. */
886 qj->success = 0;
887 }
888 return;
889 }
890
891 /* Mirror the Quadriflow result to build the final mesh */
892 new_mesh = remesh_symmetry_mirror(qj->owner, new_mesh, qj->symmetry_axes);
893
894 if (ob->mode == OB_MODE_SCULPT) {
895 sculpt_paint::undo::geometry_begin(scene, *ob, qj->op);
896 }
897
898 if (qj->preserve_attributes) {
900 }
901
902 BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob);
903
904 bke::mesh_smooth_set(*static_cast<Mesh *>(ob->data), qj->smooth_normals);
905
906 if (ob->mode == OB_MODE_SCULPT) {
909 }
910
912
913 worker_status->do_update = true;
914 worker_status->stop = false;
915}
916
917static void quadriflow_end_job(void *customdata)
918{
919 QuadriFlowJob *qj = (QuadriFlowJob *)customdata;
920
921 Object *ob = qj->owner;
922
923 if (qj->is_nonblocking_job) {
924 WM_set_locked_interface(static_cast<wmWindowManager *>(G_MAIN->wm.first), false);
925 }
926
927 switch (qj->success) {
928 case 1:
930 WM_reportf(RPT_INFO, "QuadriFlow: Remeshing completed");
931 break;
932 case 0:
933 WM_reportf(RPT_ERROR, "QuadriFlow: Remeshing failed");
934 break;
935 case -1:
936 WM_report(RPT_WARNING, "QuadriFlow: Remeshing canceled");
937 break;
938 case -2:
940 "QuadriFlow: The mesh needs to be manifold and have face normals that point in a "
941 "consistent direction");
942 break;
943 }
944}
945
947{
948 QuadriFlowJob *job = (QuadriFlowJob *)MEM_mallocN(sizeof(QuadriFlowJob), "QuadriFlowJob");
949
950 job->op = op;
952 job->scene = CTX_data_scene(C);
953
954 job->target_faces = RNA_int_get(op->ptr, "target_faces");
955 job->seed = RNA_int_get(op->ptr, "seed");
956
957 job->use_mesh_symmetry = RNA_boolean_get(op->ptr, "use_mesh_symmetry");
958
959 job->use_preserve_sharp = RNA_boolean_get(op->ptr, "use_preserve_sharp");
960 job->use_preserve_boundary = RNA_boolean_get(op->ptr, "use_preserve_boundary");
961
962#ifdef USE_MESH_CURVATURE
963 job->use_mesh_curvature = RNA_boolean_get(op->ptr, "use_mesh_curvature");
964#endif
965
966 job->preserve_attributes = RNA_boolean_get(op->ptr, "preserve_attributes");
967 job->smooth_normals = RNA_boolean_get(op->ptr, "smooth_normals");
968
969 /* Update the target face count if symmetry is enabled */
971 if (ob && job->use_mesh_symmetry) {
972 Mesh *mesh = BKE_mesh_from_object(ob);
973 job->symmetry_axes = (eSymmetryAxes)mesh->symmetry;
974 for (char i = 0; i < 3; i++) {
975 eSymmetryAxes symm_it = (eSymmetryAxes)(1 << i);
976 if (job->symmetry_axes & symm_it) {
977 job->target_faces = job->target_faces / 2;
978 }
979 }
980 }
981 else {
982 job->use_mesh_symmetry = false;
984 }
985
986 if (op->flag == 0) {
987 /* This is called directly from the exec operator, this operation is now blocking */
988 job->is_nonblocking_job = false;
989 wmJobWorkerStatus worker_status = {};
990 quadriflow_start_job(job, &worker_status);
993 }
994 else {
995 /* Non blocking call. For when the operator has been called from the GUI. */
996 job->is_nonblocking_job = true;
997
998 wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
999 CTX_wm_window(C),
1000 CTX_data_scene(C),
1001 "QuadriFlow Remesh",
1004
1006 WM_jobs_timer(wm_job, 0.1, NC_GEOM | ND_DATA, NC_GEOM | ND_DATA);
1007 WM_jobs_callbacks(wm_job, quadriflow_start_job, nullptr, nullptr, quadriflow_end_job);
1008
1010
1011 WM_jobs_start(CTX_wm_manager(C), wm_job);
1012 }
1013 return OPERATOR_FINISHED;
1014}
1015
1017{
1018 int mode = RNA_enum_get(op->ptr, "mode");
1019
1020 if (mode == QUADRIFLOW_REMESH_EDGE_LENGTH) {
1021 float area = RNA_float_get(op->ptr, "mesh_area");
1022 if (area < 0.0f) {
1024 area = BKE_mesh_calc_area(static_cast<const Mesh *>(ob->data));
1025 RNA_float_set(op->ptr, "mesh_area", area);
1026 }
1027 int faces_num;
1028 float edge_len = RNA_float_get(op->ptr, "target_edge_length");
1029
1030 faces_num = area / (edge_len * edge_len);
1031 RNA_int_set(op->ptr, "target_faces", faces_num);
1032 }
1033 else if (mode == QUADRIFLOW_REMESH_RATIO) {
1035 Mesh *mesh = static_cast<Mesh *>(ob->data);
1036
1037 int faces_num;
1038 float ratio = RNA_float_get(op->ptr, "target_ratio");
1039
1040 faces_num = mesh->faces_num * ratio;
1041
1042 RNA_int_set(op->ptr, "target_faces", faces_num);
1043 }
1044
1045 return true;
1046}
1047
1048/* Hide the target variables if they are not active */
1049static bool quadriflow_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop)
1050{
1051 const char *prop_id = RNA_property_identifier(prop);
1052
1053 if (STRPREFIX(prop_id, "target")) {
1054 int mode = RNA_enum_get(op->ptr, "mode");
1055
1056 if (STREQ(prop_id, "target_edge_length") && mode != QUADRIFLOW_REMESH_EDGE_LENGTH) {
1057 return false;
1058 }
1059 if (STREQ(prop_id, "target_faces")) {
1060 if (mode != QUADRIFLOW_REMESH_FACES) {
1061 /* Make sure we can edit the target_faces value even if it doesn't start as EDITABLE */
1062 float area = RNA_float_get(op->ptr, "mesh_area");
1063 if (area < -0.8f) {
1064 area += 0.2f;
1065 /* Make sure we have up to date values from the start */
1067 quadriflow_check((bContext *)C, op);
1068 }
1069
1070 /* Only disable input */
1072 }
1073 else {
1075 }
1076 }
1077 else if (STREQ(prop_id, "target_ratio") && mode != QUADRIFLOW_REMESH_RATIO) {
1078 return false;
1079 }
1080 }
1081
1082 return true;
1083}
1084
1087 "RATIO",
1088 0,
1089 "Ratio",
1090 "Specify target number of faces relative to the current mesh"},
1092 "EDGE",
1093 0,
1094 "Edge Length",
1095 "Input target edge length in the new mesh"},
1096 {QUADRIFLOW_REMESH_FACES, "FACES", 0, "Faces", "Input target number of faces in the new mesh"},
1097 {0, nullptr, 0, nullptr, nullptr},
1098};
1099
1100static int quadriflow_remesh_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1101{
1103 C, op, event, IFACE_("QuadriFlow Remesh the Selected Mesh"), IFACE_("Remesh"));
1104}
1105
1107{
1108 /* identifiers */
1109 ot->name = "QuadriFlow Remesh";
1110 ot->description =
1111 "Create a new quad based mesh using the surface data of the current mesh. All data "
1112 "layers will be lost";
1113 ot->idname = "OBJECT_OT_quadriflow_remesh";
1114
1115 /* api callbacks */
1121
1123
1124 PropertyRNA *prop;
1125
1126 /* properties */
1128 "use_mesh_symmetry",
1129 true,
1130 "Use Mesh Symmetry",
1131 "Generates a symmetrical mesh using the mesh symmetry configuration");
1132
1134 "use_preserve_sharp",
1135 false,
1136 "Preserve Sharp",
1137 "Try to preserve sharp features on the mesh");
1138
1140 "use_preserve_boundary",
1141 false,
1142 "Preserve Mesh Boundary",
1143 "Try to preserve mesh boundary on the mesh");
1144#ifdef USE_MESH_CURVATURE
1146 "use_mesh_curvature",
1147 false,
1148 "Use Mesh Curvature",
1149 "Take the mesh curvature into account when remeshing");
1150#endif
1152 "preserve_attributes",
1153 false,
1154 "Preserve Attributes",
1155 "Reproject attributes onto the new mesh");
1156
1158 "smooth_normals",
1159 false,
1160 "Smooth Normals",
1161 "Set the output mesh normals to smooth");
1162
1164 "mode",
1167 "Mode",
1168 "How to specify the amount of detail for the new mesh");
1169
1170 prop = RNA_def_float(ot->srna,
1171 "target_ratio",
1172 1,
1173 0,
1174 FLT_MAX,
1175 "Ratio",
1176 "Relative number of faces compared to the current mesh",
1177 0.0f,
1178 1.0f);
1179
1180 prop = RNA_def_float(ot->srna,
1181 "target_edge_length",
1182 0.1f,
1183 0.0000001f,
1184 FLT_MAX,
1185 "Edge Length",
1186 "Target edge length in the new mesh",
1187 0.00001f,
1188 1.0f);
1189
1190 prop = RNA_def_int(ot->srna,
1191 "target_faces",
1192 4000,
1193 1,
1194 INT_MAX,
1195 "Number of Faces",
1196 "Approximate number of faces (quads) in the new mesh",
1197 1,
1198 INT_MAX);
1199
1200 prop = RNA_def_float(
1201 ot->srna,
1202 "mesh_area",
1203 -1.0f,
1204 -FLT_MAX,
1205 FLT_MAX,
1206 "Old Object Face Area",
1207 "This property is only used to cache the object area for later calculations",
1208 0.0f,
1209 FLT_MAX);
1211
1213 "seed",
1214 0,
1215 0,
1216 INT_MAX,
1217 "Seed",
1218 "Random seed to use with the solver. Different seeds will cause the remesher to "
1219 "come up with different quad layouts on the mesh",
1220 0,
1221 255);
1222}
1223
1226} // namespace blender::ed::object
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
wmWindow * CTX_wm_window(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(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)
float BKE_mesh_calc_area(const Mesh *mesh)
Mesh * BKE_mesh_from_object(Object *ob)
@ BKE_MESH_BATCH_DIRTY_ALL
Definition BKE_mesh.h:38
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)
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_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2601
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2099
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target)
@ B_UNIT_LENGTH
Definition BKE_unit.hh:107
size_t BKE_unit_value_as_string(char *str, int str_maxncpy, double value, int prec, int type, const UnitSettings *settings, bool pad)
Definition unit.cc:1876
void BLF_size(int fontid, float size)
Definition blf.cc:426
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:778
void BLF_color3f(int fontid, float r, float g, float b)
Definition blf.cc:511
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:568
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:371
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:39
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 unit_m4(float m[4][4])
Definition rct.c:1127
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])
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:1041
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ 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.
#define UI_SCALE_FAC
@ 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:966
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
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(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:161
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
PropertyFlag
Definition RNA_types.hh:201
@ PROP_EDITABLE
Definition RNA_types.hh:207
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
const uiStyle * UI_style_get()
@ WM_JOB_TYPE_QUADRIFLOW_REMESH
Definition WM_api.hh:1604
@ WM_JOB_PROGRESS
Definition WM_api.hh:1566
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
void item_bool(std::string text, bool inverted, int icon1, int icon2=0)
Definition area.cc:924
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:908
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
local_group_size(16, 16) .push_constant(Type b
const Depsgraph * depsgraph
#define fabsf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
#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 int quadriflow_break_job(void *customdata)
static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
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 int quadriflow_remesh_exec(bContext *C, wmOperator *op)
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 int quadriflow_remesh_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static Mesh * remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes)
static void quadriflow_free_job(void *customdata)
static int voxel_remesh_exec(bContext *C, wmOperator *op)
void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot)
static void quadriflow_start_job(void *customdata, wmJobWorkerStatus *worker_status)
static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool quadriflow_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop)
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
float vec[8][3]
ObjectRuntimeHandle * runtime
struct SculptSession * sculpt
float viewinv[4][4]
uiFontStyle widget
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
short type
Definition WM_types.hh:722
const char * name
Definition WM_types.hh:990
bool(* poll_property)(const bContext *C, wmOperator *op, const PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1048
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
bool(* check)(bContext *C, wmOperator *op)
Definition WM_types.hh:1014
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct PointerRNA * ptr
void WM_report(eReportType type, const char *message)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_reportf(eReportType type, const char *format,...)
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:4125
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:352
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:455
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:189
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:364
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:336
int 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)