Blender V5.0
multires_unsubdivide.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12#include "MEM_guardedalloc.h"
13
14#include "DNA_mesh_types.h"
15#include "DNA_meshdata_types.h"
16#include "DNA_modifier_types.h"
17#include "DNA_object_types.h"
18
19#include "BLI_array_utils.hh"
20#include "BLI_gsqueue.h"
21#include "BLI_math_vector.h"
22
23#include "BKE_attribute.hh"
24#include "BKE_ccg.hh"
25#include "BKE_customdata.hh"
26#include "BKE_mesh.hh"
27#include "BKE_multires.hh"
28
29#include "bmesh.hh"
30
31#include "multires_reshape.hh"
33
34/* This is done in the following steps:
35 *
36 * - If there are already grids in the original mesh,
37 * convert them from tangent displacement to object space coordinates.
38 * - Assign data-layers to the original mesh to map vertices to a new base mesh.
39 * These data-layers store the indices of the elements in the original mesh.
40 * This way the original indices are
41 * preserved when doing mesh modifications (removing and dissolving vertices)
42 * when building the new base mesh.
43 * - Try to find a lower resolution base mesh. This is done by flood fill operation that tags the
44 * center vertices of the lower level grid.
45 * If the algorithm can tag all vertices correctly,
46 * the lower level base mesh is generated by dissolving the tagged vertices.
47 * - Use the data-layers to map vertices from the base mesh to the original mesh and original to
48 * base mesh.
49 * - Find two adjacent vertices on the base mesh to a given vertex to map that loop from base mesh
50 * to original mesh
51 * - Extract the grid from the original mesh from that loop. If there are no grids in the original
52 * mesh, build the new grid directly from the vertex coordinates by iterating in a grid pattern
53 * over them. If there are grids in the original mesh, iterate in a grid pattern over the faces,
54 * reorder all the coordinates of the grid in that face and copy those coordinates to the new
55 * base mesh grid.
56 * - Copy the new grid data over to a new allocated MDISP layer with the appropriate size to store
57 * the new levels.
58 * - Convert the grid data from object space to tangent displacement.
59 */
60
64static bool is_vertex_in_id(BMVert *v, const int *elem_id, int elem)
65{
66 const int v_index = BM_elem_index_get(v);
67 return elem_id[v_index] == elem;
68}
69
74
80
87static BMVert *unsubdivide_find_any_pole(BMesh *bm, int *elem_id, int elem)
88{
89 BMIter iter;
90 BMVert *v;
91 BMVert *pole = nullptr;
93 if (is_vertex_in_id(v, elem_id, elem) && is_vertex_pole_three(v)) {
94 return v;
95 }
96 if (is_vertex_in_id(v, elem_id, elem) && is_vertex_pole(v)) {
97 pole = v;
98 }
99 }
100 return pole;
101}
102
110{
111 BMIter iter;
112 BMFace *f;
113 BMVert *v;
114 if (bm->totface < 3) {
115 return false;
116 }
117
118 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
119 if (f->len != 4) {
120 return false;
121 }
122 }
123
124 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
125 if (BM_vert_is_wire(v) || (v->e == nullptr)) {
126 return false;
127 }
128 }
129
130 return true;
131}
132
136static bool is_vertex_diagonal(BMVert *from_v, BMVert *to_v)
137{
138 return !BM_edge_exists(from_v, to_v);
139}
140
150static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex)
151{
152 blender::BitVector<> visited_verts(bm->totvert);
153 GSQueue *queue;
154 queue = BLI_gsqueue_new(sizeof(BMVert *));
155
156 /* Add and tag the vertices connected by a diagonal to initial_vertex to the flood fill queue. If
157 * initial_vertex is a pole and there is a valid solution, those vertices should be the (0,0) of
158 * the grids for the loops of initial_vertex. */
159 BMIter iter;
160 BMIter iter_a;
161 BMFace *f;
162 BMVert *neighbor_v;
163 BM_ITER_ELEM (f, &iter, initial_vertex, BM_FACES_OF_VERT) {
164 BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) {
165 int neighbor_vertex_index = BM_elem_index_get(neighbor_v);
166 if (neighbor_v != initial_vertex && is_vertex_diagonal(neighbor_v, initial_vertex)) {
167 BLI_gsqueue_push(queue, &neighbor_v);
168 visited_verts[neighbor_vertex_index].set();
169 BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true);
170 }
171 }
172 }
173
174 /* Repeat a similar operation for all vertices in the queue. */
175 /* In this case, add to the queue the vertices connected by 2 steps using the diagonals in any
176 * direction. If a solution exists and `initial_vertex` was a pole, this is guaranteed that will
177 * tag all the (0,0) vertices of the grids, and nothing else. */
178 /* If it was not a pole, it may or may not find a solution, even if the solution exists. */
179 while (!BLI_gsqueue_is_empty(queue)) {
180 BMVert *from_v;
181 BLI_gsqueue_pop(queue, &from_v);
182
183 /* Get the diagonals (first connected step) */
184 GSQueue *diagonals;
185 diagonals = BLI_gsqueue_new(sizeof(BMVert *));
186 BM_ITER_ELEM (f, &iter, from_v, BM_FACES_OF_VERT) {
187 BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) {
188 if (neighbor_v != from_v && is_vertex_diagonal(neighbor_v, from_v)) {
189 BLI_gsqueue_push(diagonals, &neighbor_v);
190 }
191 }
192 }
193
194 /* Do the second connected step. This vertices are the ones that are added to the flood fill
195 * queue. */
196 while (!BLI_gsqueue_is_empty(diagonals)) {
197 BMVert *diagonal_v;
198 BLI_gsqueue_pop(diagonals, &diagonal_v);
199 BM_ITER_ELEM (f, &iter, diagonal_v, BM_FACES_OF_VERT) {
200 BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) {
201 int neighbor_vertex_index = BM_elem_index_get(neighbor_v);
202 if (!visited_verts[neighbor_vertex_index] && neighbor_v != diagonal_v &&
203 is_vertex_diagonal(neighbor_v, diagonal_v))
204 {
205 BLI_gsqueue_push(queue, &neighbor_v);
206 visited_verts[neighbor_vertex_index].set(true);
207 BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true);
208 }
209 }
210 }
211 }
212 BLI_gsqueue_free(diagonals);
213 }
214
215 BLI_gsqueue_free(queue);
216}
217
229static bool unsubdivide_is_center_vertex_tag_valid(BMesh *bm, int *elem_id, int elem)
230{
231 BMVert *v, *neighbor_v;
232 BMIter iter, iter_a, iter_b;
233 BMFace *f;
234
235 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
236 if (is_vertex_in_id(v, elem_id, elem)) {
238 /* Tagged vertex in boundary */
239 if (BM_vert_is_boundary(v)) {
240 return false;
241 }
242 /* Tagged vertex with connected tagged vertex. */
243 BM_ITER_ELEM (f, &iter_a, v, BM_FACES_OF_VERT) {
244 BM_ITER_ELEM (neighbor_v, &iter_b, f, BM_VERTS_OF_FACE) {
245 if (neighbor_v != v && BM_elem_flag_test(neighbor_v, BM_ELEM_TAG)) {
246 return false;
247 }
248 }
249 }
250 }
251 if (BM_vert_is_boundary(v)) {
252 /* Un-tagged vertex in boundary without connected tagged vertices. */
253 bool any_tagged = false;
254 BM_ITER_ELEM (f, &iter_a, v, BM_FACES_OF_VERT) {
255 BM_ITER_ELEM (neighbor_v, &iter_b, f, BM_VERTS_OF_FACE) {
256 if (neighbor_v != v && BM_elem_flag_test(neighbor_v, BM_ELEM_TAG)) {
257 any_tagged = true;
258 }
259 }
260 }
261 if (!any_tagged) {
262 return false;
263 }
264 }
265 }
266 }
267
268 return true;
269}
270
274static bool unsubdivide_tag_disconnected_mesh_element(BMesh *bm, int *elem_id, int elem)
275{
276 /* First, get vertex candidates to try to generate possible un-subdivide solution. */
277 /* Find a vertex pole. If there is a solution on an all quad base mesh, this vertex should be
278 * part of the base mesh. If it isn't, then there is no solution. */
279 GSQueue *initial_vertex = BLI_gsqueue_new(sizeof(BMVert *));
280 BMVert *initial_vertex_pole = unsubdivide_find_any_pole(bm, elem_id, elem);
281 if (initial_vertex_pole != nullptr) {
282 BLI_gsqueue_push(initial_vertex, &initial_vertex_pole);
283 }
284
285 /* Also try from the different 4 vertices of a quad in the current
286 * disconnected element ID. If a solution exists the search should return a valid solution from
287 * one of these vertices. */
288 BMFace *f, *init_face = nullptr;
289 BMVert *v;
290 BMIter iter_a, iter_b;
291 BM_ITER_MESH (f, &iter_a, bm, BM_FACES_OF_MESH) {
292 BM_ITER_ELEM (v, &iter_b, f, BM_VERTS_OF_FACE) {
293 if (is_vertex_in_id(v, elem_id, elem)) {
294 init_face = f;
295 break;
296 }
297 }
298 if (init_face != nullptr) {
299 break;
300 }
301 }
302
303 BM_ITER_ELEM (v, &iter_a, init_face, BM_VERTS_OF_FACE) {
304 BLI_gsqueue_push(initial_vertex, &v);
305 }
306
307 bool valid_tag_found = false;
308
309 /* Check all vertex candidates to a solution. */
310 while (!BLI_gsqueue_is_empty(initial_vertex)) {
311
312 BMVert *iv;
313 BLI_gsqueue_pop(initial_vertex, &iv);
314
315 /* Generate a possible solution. */
317
318 /* Check if the solution is valid. If it is, stop searching. */
319 if (unsubdivide_is_center_vertex_tag_valid(bm, elem_id, elem)) {
320 valid_tag_found = true;
321 break;
322 }
323
324 /* If the solution is not valid, reset the state of all tags in this disconnected element ID
325 * and try again. */
326 BMVert *v_reset;
327 BMIter iter;
328 BM_ITER_MESH (v_reset, &iter, bm, BM_VERTS_OF_MESH) {
329 if (is_vertex_in_id(v_reset, elem_id, elem)) {
330 BM_elem_flag_set(v_reset, BM_ELEM_TAG, false);
331 }
332 }
333 }
334 BLI_gsqueue_free(initial_vertex);
335 return valid_tag_found;
336}
337
341static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id)
342{
343 bool *visited_verts = MEM_calloc_arrayN<bool>(bm->totvert, "visited vertices");
344 int current_id = 0;
345 for (int i = 0; i < bm->totvert; i++) {
346 if (!visited_verts[i]) {
347 GSQueue *queue;
348 queue = BLI_gsqueue_new(sizeof(BMVert *));
349
350 visited_verts[i] = true;
351 elem_id[i] = current_id;
352 BMVert *iv = BM_vert_at_index(bm, i);
353 BLI_gsqueue_push(queue, &iv);
354
355 while (!BLI_gsqueue_is_empty(queue)) {
356 BMIter iter;
357 BMVert *current_v, *neighbor_v;
358 BMEdge *ed;
359 BLI_gsqueue_pop(queue, &current_v);
360 BM_ITER_ELEM (ed, &iter, current_v, BM_EDGES_OF_VERT) {
361 neighbor_v = BM_edge_other_vert(ed, current_v);
362 const int neighbor_index = BM_elem_index_get(neighbor_v);
363 if (!visited_verts[neighbor_index]) {
364 visited_verts[neighbor_index] = true;
365 elem_id[neighbor_index] = current_id;
366 BLI_gsqueue_push(queue, &neighbor_v);
367 }
368 }
369 }
370 current_id++;
371 BLI_gsqueue_free(queue);
372 }
373 }
374 MEM_freeN(visited_verts);
375 return current_id;
376}
377
383{
384 BMVert *v;
385 BMIter iter;
386
387 /* Stores the vertices which correspond to (1, 0) and (0, 1) of the grids in the select flag. */
389 BMVert *v_neighbor;
390 BMIter iter_a;
391 BMEdge *ed;
392 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
393 BM_ITER_ELEM (ed, &iter_a, v, BM_EDGES_OF_VERT) {
394 v_neighbor = BM_edge_other_vert(ed, v);
395 if (BM_elem_flag_test(v_neighbor, BM_ELEM_TAG)) {
397 }
398 }
399 }
400
401 /* Dissolves the (0,0) vertices of the grids. */
404 "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
406 false,
407 true);
408
410
411 /* Copy the select flag to the tag flag. */
412 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
415 }
416 }
417
418 /* Dissolves the (1,0) and (0,1) vertices of the grids. */
421 "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
423 false,
424 true);
425}
426
437{
438
439 /* Do a first check to make sure that it makes sense to search for un-subdivision in this mesh.
440 */
442 return false;
443 };
444
445 /* Initialize the vertex table. */
448
449 /* Build disconnected elements IDs. Each disconnected mesh element is evaluated separately. */
450 int *elem_id = MEM_calloc_arrayN<int>(bm->totvert, " ELEM ID");
451 const int tot_ids = unsubdivide_init_elem_ids(bm, elem_id);
452
453 bool valid_tag_found = true;
454
455 /* Reset the #BMesh flags as they are used to store data during the un-subdivide process.
456 * Un-hiding all faces is important so the entire mesh is handled, see: #126633. */
459
460 /* For each disconnected mesh element ID, search if an un-subdivide solution is possible. The
461 * whole un-subdivide process fails if a single disconnected mesh element fails. */
462 for (int id = 0; id < tot_ids; id++) {
463 /* Try to the #BMesh vertex flag tags corresponding to an un-subdivide solution. */
465 valid_tag_found = false;
466 break;
467 }
468 }
469
470 /* If a solution was found for all elements IDs, build the new base mesh using the solution
471 * stored in the BMVert tags. */
472 if (valid_tag_found) {
474 }
475
476 MEM_freeN(elem_id);
477 return valid_tag_found;
478}
479
483static BMEdge *edge_step(BMVert *v, BMEdge *edge, BMVert **r_next_vertex)
484{
485 BMIter iter;
486 BMEdge *test_edge;
487 if (edge == nullptr) {
488 (*r_next_vertex) = v;
489 return edge;
490 }
491 (*r_next_vertex) = BM_edge_other_vert(edge, v);
492 BM_ITER_ELEM (test_edge, &iter, (*r_next_vertex), BM_EDGES_OF_VERT) {
493 if (!BM_edge_share_quad_check(test_edge, edge)) {
494 return test_edge;
495 }
496 }
497 return nullptr;
498}
499
500static BMFace *face_step(BMEdge *edge, BMFace *f)
501{
502 BMIter iter;
503 BMFace *face_iter;
504
505 BM_ITER_ELEM (face_iter, &iter, edge, BM_FACES_OF_EDGE) {
506 if (BM_face_share_edge_check(face_iter, f)) {
507 return face_iter;
508 }
509 }
510 return f;
511}
512
517static BMEdge *get_initial_edge_y(BMFace *f, BMEdge *edge_x, BMVert *initial_vertex)
518{
519 BMIter iter;
520 BMEdge *test_edge;
521 BM_ITER_ELEM (test_edge, &iter, f, BM_EDGES_OF_FACE) {
522 if (edge_x != test_edge) {
523 if (test_edge->v1 != initial_vertex && test_edge->v2 == initial_vertex) {
524 return test_edge;
525 }
526 if (test_edge->v2 != initial_vertex && test_edge->v1 == initial_vertex) {
527 return test_edge;
528 }
529 }
530 }
531 return nullptr;
532}
533
538 float (*face_grid)[3], MDisps *mdisp, int face_grid_size, int orig_grid_size, int loop)
539{
540 int origin[2];
541 int step_x[2];
542 int step_y[2];
543
544 const int grid_offset = orig_grid_size - 1;
545 origin[0] = grid_offset;
546 origin[1] = grid_offset;
547
548 switch (loop) {
549 case 0:
550 step_x[0] = -1;
551 step_x[1] = 0;
552
553 step_y[0] = 0;
554 step_y[1] = -1;
555
556 break;
557 case 1:
558 step_x[0] = 0;
559 step_x[1] = 1;
560
561 step_y[0] = -1;
562 step_y[1] = -0;
563 break;
564 case 2:
565 step_x[0] = 1;
566 step_x[1] = 0;
567
568 step_y[0] = 0;
569 step_y[1] = 1;
570 break;
571 case 3:
572 step_x[0] = 0;
573 step_x[1] = -1;
574
575 step_y[0] = 1;
576 step_y[1] = 0;
577 break;
578 default:
579 BLI_assert_msg(0, "Should never happen");
580 break;
581 }
582
583 for (int y = 0; y < orig_grid_size; y++) {
584 for (int x = 0; x < orig_grid_size; x++) {
585 const int remap_x = origin[1] + (step_x[1] * x) + (step_y[1] * y);
586 const int remap_y = origin[0] + (step_x[0] * x) + (step_y[0] * y);
587
588 const int final_index = remap_x + remap_y * face_grid_size;
589 copy_v3_v3(face_grid[final_index], mdisp->disps[x + y * orig_grid_size]);
590 }
591 }
592}
593
599 float (*face_grid)[3],
600 int face_grid_size,
601 int gunsub_x,
602 int gunsub_y)
603{
604 const int grid_it = face_grid_size - 1;
605 for (int y = 0; y < face_grid_size; y++) {
606 for (int x = 0; x < face_grid_size; x++) {
607 const int remap_x = (grid_it * gunsub_x) + x;
608 const int remap_y = (grid_it * gunsub_y) + y;
609
610 const int remap_index_y = grid->grid_size - remap_x - 1;
611 const int remap_index_x = grid->grid_size - remap_y - 1;
612 const int grid_index = remap_index_x + (remap_index_y * grid->grid_size);
613 copy_v3_v3(grid->grid_co[grid_index], face_grid[x + y * face_grid_size]);
614 }
615 }
616}
617
626 BMVert *v,
627 BMFace *f,
628 int grid_x,
629 int grid_y)
630{
631 Mesh *original_mesh = context->original_mesh;
632 const blender::OffsetIndices faces = original_mesh->faces();
633 const blender::Span<int> corner_verts = original_mesh->corner_verts();
635
636 const int corner_vertex_index = BM_elem_index_get(v);
637
638 /* Calculates an offset to write the grids correctly oriented in the main
639 * #MultiresUnsubdivideGrid. */
640 int loop_offset = 0;
641 for (int i = 0; i < face.size(); i++) {
642 const int loop_index = face[i];
643 if (corner_verts[loop_index] == corner_vertex_index) {
644 loop_offset = i;
645 break;
646 }
647 }
648
649 /* Write the 4 grids of the current quad with the right orientation into the face_grid buffer. */
650 const int grid_size = CCG_grid_size(context->num_original_levels);
651 const int face_grid_size = CCG_grid_size(context->num_original_levels + 1);
652 const int face_grid_area = face_grid_size * face_grid_size;
653 float (*face_grid)[3] = MEM_calloc_arrayN<float[3]>(face_grid_area, "face_grid");
654
655 for (int i = 0; i < face.size(); i++) {
656 const int loop_index = face[i];
657 MDisps *mdisp = &context->original_mdisp[loop_index];
658 int quad_loop = i - loop_offset;
659 if (quad_loop < 0) {
660 quad_loop += 4;
661 }
662 if (quad_loop >= 4) {
663 quad_loop -= 4;
664 }
665 write_loop_in_face_grid(face_grid, mdisp, face_grid_size, grid_size, quad_loop);
666 }
667
668 /* Write the face_grid buffer in the correct position in the #MultiresUnsubdivideGrids that is
669 * being extracted. */
670 write_face_grid_in_unsubdivide_grid(grid, face_grid, face_grid_size, grid_x, grid_y);
671
672 MEM_freeN(face_grid);
673}
674
679static void store_vertex_data(MultiresUnsubdivideGrid *grid, BMVert *v, int grid_x, int grid_y)
680{
681 const int remap_index_y = grid->grid_size - 1 - grid_x;
682 const int remap_index_x = grid->grid_size - 1 - grid_y;
683
684 const int grid_index = remap_index_x + (remap_index_y * grid->grid_size);
685
686 copy_v3_v3(grid->grid_co[grid_index], v->co);
687}
688
694 BMFace *f1,
695 BMEdge *e1,
696 bool flip_grid,
698{
699 BMVert *initial_vertex;
700 BMEdge *initial_edge_x;
701 BMEdge *initial_edge_y;
702
703 const int grid_size = CCG_grid_size(context->num_new_levels);
704 const int unsubdiv_grid_size = grid->grid_size = CCG_grid_size(context->num_total_levels);
705 BLI_assert(grid->grid_co == nullptr);
706 grid->grid_size = unsubdiv_grid_size;
708 size_t(unsubdiv_grid_size) * size_t(unsubdiv_grid_size), "grids coordinates");
709
710 /* Get the vertex on the corner of the grid. This vertex was tagged previously as it also exist
711 * on the base mesh. */
712 initial_edge_x = e1;
713 if (BM_elem_flag_test(initial_edge_x->v1, BM_ELEM_TAG)) {
714 initial_vertex = initial_edge_x->v1;
715 }
716 else {
717 initial_vertex = initial_edge_x->v2;
718 }
719
720 /* From that vertex, get the edge that defines the grid Y axis for extraction. */
721 initial_edge_y = get_initial_edge_y(f1, initial_edge_x, initial_vertex);
722
723 if (flip_grid) {
724 BMEdge *edge_temp;
725 edge_temp = initial_edge_x;
726 initial_edge_x = initial_edge_y;
727 initial_edge_y = edge_temp;
728 }
729
730 int grid_x = 0;
731 int grid_y = 0;
732
733 BMVert *current_vertex_x = initial_vertex;
734 BMEdge *edge_x = initial_edge_x;
735
736 BMVert *current_vertex_y = initial_vertex;
737 BMEdge *edge_y = initial_edge_y;
738 BMEdge *prev_edge_y = initial_edge_y;
739
740 BMFace *current_face = f1;
741 BMFace *grid_face = f1;
742
743 /* If the data is going to be extracted from the already existing grids, there is no need to go
744 * to the last vertex of the iteration as that coordinate is also included in the grids
745 * corresponding to the loop of the face of the previous iteration. */
746 int grid_iteration_max_steps = grid_size;
747 if (context->num_original_levels > 0) {
748 grid_iteration_max_steps = grid_size - 1;
749 }
750
751 /* Iterate over the mesh vertices in a grid pattern using the axis defined by the two initial
752 * edges. */
753 while (grid_y < grid_iteration_max_steps) {
754
755 grid_face = current_face;
756
757 while (grid_x < grid_iteration_max_steps) {
758 if (context->num_original_levels == 0) {
759 /* If there were no grids on the original mesh, extract the data directly from the
760 * vertices. */
761 store_vertex_data(grid, current_vertex_x, grid_x, grid_y);
762 edge_x = edge_step(current_vertex_x, edge_x, &current_vertex_x);
763 }
764 else {
765 /* If there were grids in the original mesh, extract the data from the grids and iterate
766 * over the faces. */
767 store_grid_data(context, grid, current_vertex_x, grid_face, grid_x, grid_y);
768 edge_x = edge_step(current_vertex_x, edge_x, &current_vertex_x);
769 grid_face = face_step(edge_x, grid_face);
770 }
771
772 grid_x++;
773 }
774 grid_x = 0;
775
776 edge_y = edge_step(current_vertex_y, edge_y, &current_vertex_y);
777 current_vertex_x = current_vertex_y;
778
779 /* Get the next edge_x to extract the next row of the grid. This needs to be done because there
780 * may be two edges connected to current_vertex_x that belong to two different grids. */
781 BMIter iter;
782 BMEdge *ed;
783 BMFace *f;
784 BM_ITER_ELEM (ed, &iter, current_vertex_x, BM_EDGES_OF_VERT) {
785 if (ed != prev_edge_y && BM_edge_in_face(ed, current_face)) {
786 edge_x = ed;
787 break;
788 }
789 }
790 BM_ITER_ELEM (f, &iter, edge_x, BM_FACES_OF_EDGE) {
791 if (f != current_face) {
792 current_face = f;
793 break;
794 }
795 }
796
797 prev_edge_y = edge_y;
798 grid_y++;
799 }
800}
801
809 BMEdge *e1,
810 BMVert **r_corner_x,
811 BMVert **r_corner_y)
812{
813 BMVert *initial_vertex;
814 BMEdge *initial_edge_x;
815 BMEdge *initial_edge_y;
816
817 initial_edge_x = e1;
818 if (BM_elem_flag_test(initial_edge_x->v1, BM_ELEM_TAG)) {
819 initial_vertex = initial_edge_x->v1;
820 }
821 else {
822 initial_vertex = initial_edge_x->v2;
823 }
824
825 /* From that vertex, get the edge that defines the grid Y axis for extraction. */
826 initial_edge_y = get_initial_edge_y(f1, initial_edge_x, initial_vertex);
827
828 BMVert *current_vertex_x = initial_vertex;
829 BMEdge *edge_x = initial_edge_x;
830
831 BMVert *current_vertex_y = initial_vertex;
832 BMEdge *edge_y = initial_edge_y;
833
834 /* Do an edge step until it finds a tagged vertex, which is part of the base mesh. */
835 /* x axis */
836 edge_x = edge_step(current_vertex_x, edge_x, &current_vertex_x);
837 while (!BM_elem_flag_test(current_vertex_x, BM_ELEM_TAG)) {
838 edge_x = edge_step(current_vertex_x, edge_x, &current_vertex_x);
839 }
840 *r_corner_x = current_vertex_x;
841
842 /* Same for y axis */
843 edge_y = edge_step(current_vertex_y, edge_y, &current_vertex_y);
844 while (!BM_elem_flag_test(current_vertex_y, BM_ELEM_TAG)) {
845 edge_y = edge_step(current_vertex_y, edge_y, &current_vertex_y);
846 }
847 *r_corner_y = current_vertex_y;
848}
849
851{
852 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
853
854 BMeshCreateParams bm_create_params{};
855 bm_create_params.use_toolflags = true;
856 BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params);
857
858 BMeshFromMeshParams bm_from_me_params{};
859 bm_from_me_params.calc_face_normal = true;
860 bm_from_me_params.calc_vert_normal = true;
861 BM_mesh_bm_from_me(bm, mesh, &bm_from_me_params);
862
863 return bm;
864}
865
866/* Data-layer names to store the original indices of the elements before modifying the mesh. */
867static const char lname[] = "l_remap_index";
868static const char vname[] = "v_remap_index";
869
871{
872 blender::bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
873 attributes.remove(lname);
874 attributes.remove(vname);
875}
876
882{
883 using namespace blender;
885 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
886
890 l_index.finish();
891
895 v_index.finish();
896}
897
900{
901 Mesh *original_mesh = context->original_mesh;
902
903 Mesh *base_mesh = context->base_mesh;
904
905 BMesh *bm_original_mesh = context->bm_original_mesh = get_bmesh_from_mesh(original_mesh);
906
907 /* Initialize the elem tables. */
908 BM_mesh_elem_table_ensure(bm_original_mesh, BM_VERT | BM_EDGE | BM_FACE);
909
910 /* Disable all flags. */
911 BM_mesh_elem_hflag_disable_all(bm_original_mesh,
914 false);
915
916 /* Get the mapping data-layer. */
917 blender::bke::AttributeAccessor attributes = base_mesh->attributes();
918 context->base_to_orig_vmap = *attributes.lookup<int>(vname);
919
920 /* Tag the base mesh vertices in the original mesh. */
921 for (int i = 0; i < base_mesh->verts_num; i++) {
922 int vert_basemesh_index = context->base_to_orig_vmap[i];
923 BMVert *v = BM_vert_at_index(bm_original_mesh, vert_basemesh_index);
925 }
926
927 context->loop_to_face_map = original_mesh->corner_to_face_map();
928}
929
935 const blender::Span<int> corner_verts,
936 int face_index,
937 int loop,
938 int v_x)
939{
940 const blender::IndexRange face = faces[face_index];
941
942 const int v_first = corner_verts[face.start()];
943 if ((loop == (face.start() + (face.size() - 1))) && v_first == v_x) {
944 return true;
945 }
946
947 int next_l_index = loop + 1;
948 if (next_l_index < face.start() + face.size()) {
949 const int v_next = corner_verts[next_l_index];
950 if (v_next == v_x) {
951 return true;
952 }
953 }
954
955 return false;
956}
957
959{
960 using namespace blender;
961 Mesh *original_mesh = context->original_mesh;
962 Mesh *base_mesh = context->base_mesh;
963
964 BMesh *bm_original_mesh = context->bm_original_mesh;
965
966 context->num_grids = base_mesh->corners_num;
967 context->base_mesh_grids = MEM_calloc_arrayN<MultiresUnsubdivideGrid>(
968 size_t(base_mesh->corners_num), "grids");
969
970 /* Based on the existing indices in the data-layers, generate two vertex indices maps. */
971 /* From vertex index in original to vertex index in base and from vertex index in base to vertex
972 * index in original. */
973 int *orig_to_base_vmap = MEM_calloc_arrayN<int>(bm_original_mesh->totvert, "orig vmap");
974 int *base_to_orig_vmap = MEM_calloc_arrayN<int>(base_mesh->verts_num, "base vmap");
975
976 const bke::AttributeAccessor attributes = base_mesh->attributes();
977 context->base_to_orig_vmap = *attributes.lookup<int>(vname);
978 for (int i = 0; i < base_mesh->verts_num; i++) {
979 base_to_orig_vmap[i] = context->base_to_orig_vmap[i];
980 }
981
982 /* If an index in original does not exist in base (it was dissolved when creating the new base
983 * mesh, return -1. */
984 for (int i = 0; i < original_mesh->verts_num; i++) {
985 orig_to_base_vmap[i] = -1;
986 }
987
988 for (int i = 0; i < base_mesh->verts_num; i++) {
989 const int orig_vertex_index = context->base_to_orig_vmap[i];
990 orig_to_base_vmap[orig_vertex_index] = i;
991 }
992
993 /* Add the original data-layers to the base mesh to have the loop indices stored in a data-layer,
994 * so they can be used from #BMesh. */
996
997 BMesh *bm_base_mesh = get_bmesh_from_mesh(base_mesh);
998 BMIter iter, iter_a, iter_b;
999 BMVert *v;
1000 BMLoop *l, *lb;
1001
1002 BM_mesh_elem_table_ensure(bm_base_mesh, BM_VERT | BM_FACE);
1003
1004 /* Get the data-layer that contains the loops indices. */
1005 const int base_l_offset = CustomData_get_offset_named(
1006 &bm_base_mesh->ldata, CD_PROP_INT32, lname);
1007
1008 const blender::OffsetIndices faces = base_mesh->faces();
1009 const blender::Span<int> corner_verts = base_mesh->corner_verts();
1010
1011 /* Main loop for extracting the grids. Iterates over the base mesh vertices. */
1012 BM_ITER_MESH (v, &iter, bm_base_mesh, BM_VERTS_OF_MESH) {
1013
1014 /* For each base mesh vertex, get the corresponding #BMVert of the original mesh using the
1015 * vertex map. */
1016 const int orig_vertex_index = base_to_orig_vmap[BM_elem_index_get(v)];
1017 BMVert *vert_original = BM_vert_at_index(bm_original_mesh, orig_vertex_index);
1018
1019 /* Iterate over the loops of that vertex in the original mesh. */
1020 BM_ITER_ELEM (l, &iter_a, vert_original, BM_LOOPS_OF_VERT) {
1021 /* For each loop, get the two vertices that should map to the l+1 and l-1 vertices in the
1022 * base mesh of the face of grid that is going to be extracted. */
1023 BMVert *corner_x, *corner_y;
1024 multires_unsubdivide_get_grid_corners_on_base_mesh(l->f, l->e, &corner_x, &corner_y);
1025
1026 /* Map the two obtained vertices to the base mesh. */
1027 const int corner_x_index = orig_to_base_vmap[BM_elem_index_get(corner_x)];
1028 const int corner_y_index = orig_to_base_vmap[BM_elem_index_get(corner_y)];
1029 if (corner_x_index < 0 || corner_y_index < 0) {
1030 continue;
1031 }
1032
1033 /* Iterate over the loops of the same vertex in the base mesh. With the previously obtained
1034 * vertices and the current vertex it is possible to get the index of the loop in the base
1035 * mesh the grid that is going to be extracted belongs to. */
1036 BM_ITER_ELEM (lb, &iter_b, v, BM_LOOPS_OF_VERT) {
1037 BMFace *base_face = lb->f;
1038 BMVert *base_corner_x = BM_vert_at_index(bm_base_mesh, corner_x_index);
1039 BMVert *base_corner_y = BM_vert_at_index(bm_base_mesh, corner_y_index);
1040 /* If this is the correct loop in the base mesh, the original vertex and the two corners
1041 * should be in the loop's face. */
1042 if (BM_vert_in_face(base_corner_x, base_face) && BM_vert_in_face(base_corner_y, base_face))
1043 {
1044 /* Get the index of the loop. */
1045 const int base_mesh_loop_index = BM_ELEM_CD_GET_INT(lb, base_l_offset);
1046 const int base_mesh_face_index = BM_elem_index_get(base_face);
1047
1048 /* Check the orientation of the loops in case that is needed to flip the x and y axis
1049 * when extracting the grid. */
1050 const bool flip_grid = multires_unsubdivide_flip_grid_x_axis(
1051 faces, corner_verts, base_mesh_face_index, base_mesh_loop_index, corner_x_index);
1052
1053 /* Extract the grid for that loop. */
1054 MultiresUnsubdivideGrid *grid = &context->base_mesh_grids[base_mesh_loop_index];
1055 if (UNLIKELY(grid->grid_co != nullptr)) {
1056 /* It's possible this grid has already been initialized which occurs when quads
1057 * share two edge, while not so common it happens with "Suzanne's" nose,
1058 * see: #126633 & run un-subdivide.
1059 *
1060 * Continue here instead of breaking as logically: quads sharing 2 edges
1061 * will share 3 vertices and those 3 vertices may be attached to any number of quads.
1062 * So in this case, continue scanning instead of breaking out of the loop
1063 * because the `lb` to extract a grid from has not yet been encountered. */
1064 continue;
1065 }
1066
1067 grid->grid_index = base_mesh_loop_index;
1069 context, l->f, l->e, !flip_grid, grid);
1070
1071 break;
1072 }
1073 }
1074 }
1075 }
1076
1077 MEM_freeN(orig_to_base_vmap);
1078 MEM_freeN(base_to_orig_vmap);
1079
1080 BM_mesh_free(bm_base_mesh);
1082}
1083
1085{
1086 if (context->bm_original_mesh != nullptr) {
1087 BM_mesh_free(context->bm_original_mesh);
1088 }
1089}
1090
1092 Mesh *original_mesh,
1094{
1095 context->original_mesh = original_mesh;
1096 context->num_new_levels = 0;
1097 context->num_total_levels = 0;
1098 context->num_original_levels = mmd->totlvl;
1099}
1100
1102{
1103 Mesh *original_mesh = context->original_mesh;
1104
1105 /* Prepare the data-layers to map base to original. */
1107 BMesh *bm_base_mesh = get_bmesh_from_mesh(original_mesh);
1108
1109 /* Un-subdivide as many iterations as possible. */
1110 context->num_new_levels = 0;
1111 int num_levels_left = context->max_new_levels;
1112 while (num_levels_left > 0 && multires_unsubdivide_single_level(bm_base_mesh)) {
1113 context->num_new_levels++;
1114 num_levels_left--;
1115 }
1116
1117 /* If no un-subdivide steps were possible, free the bmesh, the map data-layers and stop. */
1118 if (context->num_new_levels == 0) {
1120 BM_mesh_free(bm_base_mesh);
1121 return false;
1122 }
1123
1124 /* Calculate the final levels for the new grids over base mesh. */
1125 context->num_total_levels = context->num_new_levels + context->num_original_levels;
1126
1127 /* Store the new base-mesh as a mesh in context, free bmesh.
1128 * NOTE(@ideasman42): passing the original mesh in the template is important so mesh settings &
1129 * vertex groups are kept, see: #93911. */
1130 context->base_mesh = BKE_mesh_new_nomain_from_template(original_mesh, 0, 0, 0, 0);
1131
1132 /* De-select all.
1133 * The user-selection has been overwritten and this selection has not been flushed. */
1135
1136 BMeshToMeshParams bm_to_me_params{};
1137 bm_to_me_params.calc_object_remap = true;
1138 BM_mesh_bm_to_me(nullptr, bm_base_mesh, context->base_mesh, &bm_to_me_params);
1139 BM_mesh_free(bm_base_mesh);
1140
1141 /* Initialize bmesh and maps for the original mesh and extract the grids. */
1142
1145
1146 return true;
1147}
1148
1150{
1152 for (int i = 0; i < context->num_grids; i++) {
1153 if (context->base_mesh_grids[i].grid_size > 0) {
1154 MEM_SAFE_FREE(context->base_mesh_grids[i].grid_co);
1155 }
1156 }
1157 MEM_SAFE_FREE(context->base_mesh_grids);
1158}
1159
1165 Mesh *base_mesh)
1166{
1167 /* Free the current MDISPS and create a new ones. */
1168 if (CustomData_has_layer(&base_mesh->corner_data, CD_MDISPS)) {
1170 }
1171 MDisps *mdisps = static_cast<MDisps *>(CustomData_add_layer(
1172 &base_mesh->corner_data, CD_MDISPS, CD_SET_DEFAULT, base_mesh->corners_num));
1173
1174 const int totdisp = pow_i(CCG_grid_size(context->num_total_levels), 2);
1175 const int totloop = base_mesh->corners_num;
1176
1177 BLI_assert(base_mesh->corners_num == context->num_grids);
1178
1179 /* Allocate the MDISPS grids and copy the extracted data from context. */
1180 for (int i = 0; i < totloop; i++) {
1181 float (*disps)[3] = MEM_calloc_arrayN<float[3]>(totdisp, __func__);
1182
1183 if (mdisps[i].disps) {
1184 MEM_freeN(mdisps[i].disps);
1185 }
1186
1187 for (int j = 0; j < totdisp; j++) {
1188 if (context->base_mesh_grids[i].grid_co) {
1189 copy_v3_v3(disps[j], context->base_mesh_grids[i].grid_co[j]);
1190 }
1191 }
1192
1193 mdisps[i].disps = disps;
1194 mdisps[i].totdisp = totdisp;
1195 mdisps[i].level = context->num_total_levels;
1196 }
1197}
1198
1200 Object *object,
1202 int rebuild_limit,
1203 bool switch_view_to_lower_level)
1204{
1205 Mesh *mesh = static_cast<Mesh *>(object->data);
1206
1208
1209 MultiresUnsubdivideContext unsubdiv_context{};
1210 MultiresReshapeContext reshape_context{};
1211
1212 multires_unsubdivide_context_init(&unsubdiv_context, mesh, mmd);
1213
1214 /* Convert and store the existing grids in object space if available. */
1215 if (mmd->totlvl != 0) {
1216 if (!multires_reshape_context_create_from_object(&reshape_context, depsgraph, object, mmd)) {
1217 return 0;
1218 }
1219
1220 multires_reshape_store_original_grids(&reshape_context);
1222 unsubdiv_context.original_mdisp = reshape_context.mdisps;
1223 }
1224
1225 /* Set the limit for the levels that should be rebuild. */
1226 unsubdiv_context.max_new_levels = rebuild_limit;
1227
1228 /* Un-subdivide and create the data for the new grids. */
1229 if (multires_unsubdivide_to_basemesh(&unsubdiv_context) == 0) {
1230 /* If there was no possible to rebuild any level, free the data and return. */
1231 if (mmd->totlvl != 0) {
1233 multires_unsubdivide_context_free(&unsubdiv_context);
1234 }
1235 multires_reshape_context_free(&reshape_context);
1236 return 0;
1237 }
1238
1239 /* Free the reshape context used to convert the data from the original grids to object space. */
1240 if (mmd->totlvl != 0) {
1241 multires_reshape_context_free(&reshape_context);
1242 }
1243
1244 /* Copy the new base mesh to the original mesh. */
1245 Mesh *base_mesh = static_cast<Mesh *>(object->data);
1246 BKE_mesh_nomain_to_mesh(unsubdiv_context.base_mesh, base_mesh, object);
1247 multires_create_grids_in_unsubdivided_base_mesh(&unsubdiv_context, base_mesh);
1248
1249 /* Update the levels in the modifier. Force always to display at level 0 as it contains the new
1250 * created level. */
1251 mmd->totlvl = char(unsubdiv_context.num_total_levels);
1252
1253 if (switch_view_to_lower_level) {
1254 mmd->sculptlvl = 0;
1255 mmd->lvl = 0;
1256 }
1257 else {
1258 mmd->sculptlvl = char(mmd->sculptlvl + unsubdiv_context.num_new_levels);
1259 mmd->lvl = char(mmd->lvl + unsubdiv_context.num_new_levels);
1260 }
1261
1262 mmd->renderlvl = char(mmd->renderlvl + unsubdiv_context.num_new_levels);
1263
1264 /* Create a reshape context to convert the MDISPS data to tangent displacement. It can be the
1265 * same as the previous one as a new Subdivision needs to be created for the new base mesh. */
1266 if (!multires_reshape_context_create_from_base_mesh(&reshape_context, depsgraph, object, mmd)) {
1267 return 0;
1268 }
1270 multires_reshape_context_free(&reshape_context);
1271
1272 /* Free the un-subdivide context and return the total number of levels that were rebuild. */
1273 const int rebuild_subdvis = unsubdiv_context.num_new_levels;
1274 multires_unsubdivide_context_free(&unsubdiv_context);
1275
1276 return rebuild_subdvis;
1277}
int CCG_grid_size(const int level)
Definition BKE_ccg.hh:104
CustomData interface, see also DNA_customdata_types.h.
@ CD_SET_DEFAULT
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
void CustomData_free_layers(CustomData *data, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
void * CustomData_add_layer(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem)
Mesh * BKE_mesh_new_nomain_from_template(const Mesh *me_src, int verts_num, int edges_num, int faces_num, int corners_num)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob, bool process_shape_keys=true)
void multires_force_sculpt_rebuild(Object *object)
Definition multires.cc:327
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
void BLI_gsqueue_free(GSQueue *queue)
Definition gsqueue.cc:93
void BLI_gsqueue_push(GSQueue *queue, const void *item)
Definition gsqueue.cc:100
void BLI_gsqueue_pop(GSQueue *queue, void *r_item)
Definition gsqueue.cc:135
GSQueue * BLI_gsqueue_new(size_t elem_size)
Definition gsqueue.cc:72
struct _GSQueue GSQueue
Definition BLI_gsqueue.h:13
bool BLI_gsqueue_is_empty(const GSQueue *queue)
Definition gsqueue.cc:163
int pow_i(int base, int exp)
Definition math_base.cc:13
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define UNLIKELY(x)
@ CD_PROP_INT32
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define BM_ELEM_CD_GET_INT(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_elem_index_get(ele)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_VERTS_OF_MESH
@ BM_VERTS_OF_FACE
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_EDGES_OF_VERT
@ BM_EDGES_OF_FACE
BMesh * bm
void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_table_init(BMesh *bm, const char htype)
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
#define BMALLOC_TEMPLATE_FROM_ME(...)
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
bool BMO_op_callf(BMesh *bm, int flag, const char *fmt,...)
#define BMO_FLAG_DEFAULTS
@ BMO_FLAG_RESPECT_HIDE
bool BM_vert_is_wire(const BMVert *v)
bool BM_edge_share_quad_check(BMEdge *e1, BMEdge *e2)
bool BM_edge_in_face(const BMEdge *e, const BMFace *f)
bool BM_face_share_edge_check(BMFace *f_a, BMFace *f_b)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
bool BM_vert_in_face(BMVert *v, BMFace *f)
bool BM_vert_is_boundary(const BMVert *v)
#define BM_vert_edge_count_is_equal(v, n)
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define BM_vert_edge_count_is_over(v, n)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
BPy_StructRNA * depsgraph
AttributeSet attributes
constexpr int64_t size() const
constexpr int64_t start() const
GAttributeReader lookup(const StringRef attribute_id) const
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
nullptr float
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
void multires_reshape_assign_final_coords_from_mdisps(const MultiresReshapeContext *reshape_context)
bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context, Depsgraph *depsgraph, Object *object, MultiresModifierData *mmd)
void multires_reshape_context_free(MultiresReshapeContext *reshape_context)
void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_context)
void multires_reshape_object_grids_to_tangent_displacement(const MultiresReshapeContext *reshape_context)
bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *reshape_context, Depsgraph *depsgraph, Object *object, MultiresModifierData *mmd)
static BMFace * face_step(BMEdge *edge, BMFace *f)
static bool unsubdivide_tag_disconnected_mesh_element(BMesh *bm, int *elem_id, int elem)
static void store_vertex_data(MultiresUnsubdivideGrid *grid, BMVert *v, int grid_x, int grid_y)
static void write_loop_in_face_grid(float(*face_grid)[3], MDisps *mdisp, int face_grid_size, int orig_grid_size, int loop)
static bool is_vertex_in_id(BMVert *v, const int *elem_id, int elem)
static void multires_create_grids_in_unsubdivided_base_mesh(MultiresUnsubdivideContext *context, Mesh *base_mesh)
static bool multires_unsubdivide_single_level(BMesh *bm)
static void multires_unsubdivide_prepare_original_bmesh_for_extract(MultiresUnsubdivideContext *context)
static BMEdge * edge_step(BMVert *v, BMEdge *edge, BMVert **r_next_vertex)
static void multires_unsubdivide_private_extract_data_free(MultiresUnsubdivideContext *context)
static BMVert * unsubdivide_find_any_pole(BMesh *bm, int *elem_id, int elem)
static bool is_vertex_pole_three(BMVert *v)
static void multires_unsubdivide_add_original_index_datalayers(Mesh *mesh)
bool multires_unsubdivide_to_basemesh(MultiresUnsubdivideContext *context)
static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex)
static bool is_vertex_pole(BMVert *v)
static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id)
static void store_grid_data(MultiresUnsubdivideContext *context, MultiresUnsubdivideGrid *grid, BMVert *v, BMFace *f, int grid_x, int grid_y)
static const char lname[]
static bool unsubdivide_is_center_vertex_tag_valid(BMesh *bm, int *elem_id, int elem)
static bool unsubdivide_is_all_quads(BMesh *bm)
void multires_unsubdivide_context_init(MultiresUnsubdivideContext *context, Mesh *original_mesh, MultiresModifierData *mmd)
static bool multires_unsubdivide_flip_grid_x_axis(const blender::OffsetIndices< int > faces, const blender::Span< int > corner_verts, int face_index, int loop, int v_x)
static void multires_unsubdivide_get_grid_corners_on_base_mesh(BMFace *f1, BMEdge *e1, BMVert **r_corner_x, BMVert **r_corner_y)
static void unsubdivide_build_base_mesh_from_tags(BMesh *bm)
static void multires_unsubdivide_free_original_datalayers(Mesh *mesh)
static bool is_vertex_diagonal(BMVert *from_v, BMVert *to_v)
static BMEdge * get_initial_edge_y(BMFace *f, BMEdge *edge_x, BMVert *initial_vertex)
static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *context)
static void multires_unsubdivide_extract_single_grid_from_face_edge(MultiresUnsubdivideContext *context, BMFace *f1, BMEdge *e1, bool flip_grid, MultiresUnsubdivideGrid *grid)
static const char vname[]
int multiresModifier_rebuild_subdiv(Depsgraph *depsgraph, Object *object, MultiresModifierData *mmd, int rebuild_limit, bool switch_view_to_lower_level)
static void write_face_grid_in_unsubdivide_grid(MultiresUnsubdivideGrid *grid, float(*face_grid)[3], int face_grid_size, int gunsub_x, int gunsub_y)
void multires_unsubdivide_context_free(MultiresUnsubdivideContext *context)
static BMesh * get_bmesh_from_mesh(Mesh *mesh)
void fill_index_range(MutableSpan< T > span, const T start=0)
BMVert * v1
BMVert * v2
struct BMFace * f
int totvert
CustomData ldata
float(* disps)[3]
int corners_num
CustomData corner_data
int verts_num
i
Definition text_draw.cc:230