Blender  V3.3
MOD_solidify_nonmanifold.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "BLI_utildefines.h"
8 
9 #include "BLI_math.h"
10 
11 #include "DNA_mesh_types.h"
12 #include "DNA_meshdata_types.h"
13 #include "DNA_object_types.h"
14 
15 #include "MEM_guardedalloc.h"
16 
17 #include "BKE_deform.h"
18 #include "BKE_mesh.h"
19 #include "BKE_particle.h"
20 
21 #include "MOD_modifiertypes.h"
22 #include "MOD_solidify_util.h" /* Own include. */
23 #include "MOD_util.h"
24 
25 #ifdef __GNUC__
26 # pragma GCC diagnostic error "-Wsign-conversion"
27 #endif
28 
29 /* -------------------------------------------------------------------- */
36 static float project_v3_v3(float r[3], const float a[3])
37 {
38  float d = dot_v3v3(r, a);
39  r[0] -= a[0] * d;
40  r[1] -= a[1] * d;
41  r[2] -= a[2] * d;
42  return d;
43 }
44 
45 static float angle_signed_on_axis_normalized_v3v3_v3(const float n[3],
46  const float ref_n[3],
47  const float axis[3])
48 {
49  float d = dot_v3v3(n, ref_n);
50  CLAMP(d, -1, 1);
51  float angle = acosf(d);
52  float cross[3];
53  cross_v3_v3v3(cross, n, ref_n);
54  if (dot_v3v3(cross, axis) >= 0) {
55  angle = 2 * M_PI - angle;
56  }
57  return angle;
58 }
59 
60 static float clamp_nonzero(const float value, const float epsilon)
61 {
62  BLI_assert(!(epsilon < 0.0f));
63  /* Return closest value with `abs(value) >= epsilon`. */
64  if (value < 0.0f) {
65  return min_ff(value, -epsilon);
66  }
67  return max_ff(value, epsilon);
68 }
69 
72 /* -------------------------------------------------------------------- */
76 /* Data structures for manifold solidify. */
77 
78 typedef struct NewFaceRef {
81  bool reversed;
84 
85 typedef struct OldEdgeFaceRef {
91 
92 typedef struct OldVertEdgeRef {
96 
97 typedef struct NewEdgeRef {
101  float angle;
104 
105 typedef struct EdgeGroup {
106  bool valid;
115  float co[3];
116  float no[3];
119 
120 typedef struct FaceKeyPair {
121  float angle;
124 
125 static int comp_float_int_pair(const void *a, const void *b)
126 {
127  FaceKeyPair *x = (FaceKeyPair *)a;
128  FaceKeyPair *y = (FaceKeyPair *)b;
129  return (int)(x->angle > y->angle) - (int)(x->angle < y->angle);
130 }
131 
132 /* NOLINTNEXTLINE: readability-function-size */
134  const ModifierEvalContext *ctx,
135  Mesh *mesh)
136 {
137  Mesh *result;
138  const SolidifyModifierData *smd = (SolidifyModifierData *)md;
139 
140  MVert *mv, *mvert, *orig_mvert;
141  MEdge *ed, *medge, *orig_medge;
142  MLoop *ml, *mloop, *orig_mloop;
143  MPoly *mp, *mpoly, *orig_mpoly;
144  const uint verts_num = (uint)mesh->totvert;
145  const uint edges_num = (uint)mesh->totedge;
146  const uint polys_num = (uint)mesh->totpoly;
147 
148  if (polys_num == 0 && verts_num != 0) {
149  return mesh;
150  }
151 
152  /* Only use material offsets if we have 2 or more materials. */
153  const short mat_nrs = ctx->object->totcol > 1 ? ctx->object->totcol : 1;
154  const short mat_nr_max = mat_nrs - 1;
155  const short mat_ofs = mat_nrs > 1 ? smd->mat_ofs : 0;
156  const short mat_ofs_rim = mat_nrs > 1 ? smd->mat_ofs_rim : 0;
157 
158  /* #ofs_front and #ofs_back are the offset from the original
159  * surface along the normal, where #oft_front is along the positive
160  * and #oft_back is along the negative normal. */
161  const float ofs_front = (smd->offset_fac + 1.0f) * 0.5f * smd->offset;
162  const float ofs_back = ofs_front - smd->offset * smd->offset_fac;
163  /* #ofs_front_clamped and #ofs_back_clamped are the same as
164  * #ofs_front and #ofs_back, but never zero. */
165  const float ofs_front_clamped = clamp_nonzero(ofs_front, 1e-5f);
166  const float ofs_back_clamped = clamp_nonzero(ofs_back, 1e-5f);
167  const float offset_fac_vg = smd->offset_fac_vg;
168  const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg;
169  const float offset = fabsf(smd->offset) * smd->offset_clamp;
170  const bool do_angle_clamp = smd->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP;
171  /* #do_flip, flips the normals of the result. This is inverted if negative thickness
172  * is used, since simple solidify with negative thickness keeps the faces facing outside. */
173  const bool do_flip = ((smd->flag & MOD_SOLIDIFY_FLIP) != 0) == (smd->offset > 0);
174  const bool do_rim = smd->flag & MOD_SOLIDIFY_RIM;
175  const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) ==
176  0;
177  const bool do_clamp = (smd->offset_clamp != 0.0f);
178 
179  const float bevel_convex = smd->bevel_convex;
180 
181  MDeformVert *dvert;
182  const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0;
183  int defgrp_index;
184  const int shell_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->shell_defgrp_name);
185  const int rim_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->rim_defgrp_name);
186 
187  MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index);
188 
189  const bool do_flat_faces = dvert && (smd->flag & MOD_SOLIDIFY_NONMANIFOLD_FLAT_FACES);
190 
191  orig_mvert = mesh->mvert;
192  orig_medge = mesh->medge;
193  orig_mloop = mesh->mloop;
194  orig_mpoly = mesh->mpoly;
195 
196  uint new_verts_num = 0;
197  uint new_edges_num = 0;
198  uint new_loops_num = 0;
199  uint new_polys_num = 0;
200 
201 #define MOD_SOLIDIFY_EMPTY_TAG ((uint)-1)
202 
203  /* Calculate only face normals. Copied because they are modified directly below. */
204  float(*poly_nors)[3] = MEM_malloc_arrayN(polys_num, sizeof(float[3]), __func__);
205  memcpy(poly_nors, BKE_mesh_poly_normals_ensure(mesh), sizeof(float[3]) * polys_num);
206 
207  NewFaceRef *face_sides_arr = MEM_malloc_arrayN(
208  polys_num * 2, sizeof(*face_sides_arr), "face_sides_arr in solidify");
209  bool *null_faces =
211  MEM_calloc_arrayN(polys_num, sizeof(*null_faces), "null_faces in solidify") :
212  NULL;
213  uint largest_ngon = 3;
214  /* Calculate face to #NewFaceRef map. */
215  {
216  mp = orig_mpoly;
217  for (uint i = 0; i < polys_num; i++, mp++) {
218  /* Make normals for faces without area (should really be avoided though). */
219  if (len_squared_v3(poly_nors[i]) < 0.5f) {
220  MEdge *e = orig_medge + orig_mloop[mp->loopstart].e;
221  float edgedir[3];
222  sub_v3_v3v3(edgedir, orig_mvert[e->v2].co, orig_mvert[e->v1].co);
223  if (fabsf(edgedir[2]) < fabsf(edgedir[1])) {
224  poly_nors[i][2] = 1.0f;
225  }
226  else {
227  poly_nors[i][1] = 1.0f;
228  }
229  if (null_faces) {
230  null_faces[i] = true;
231  }
232  }
233 
234  NewEdgeRef **link_edges = MEM_calloc_arrayN(
235  (uint)mp->totloop, sizeof(*link_edges), "NewFaceRef::link_edges in solidify");
236  face_sides_arr[i * 2] = (NewFaceRef){
237  .face = mp, .index = i, .reversed = false, .link_edges = link_edges};
238  link_edges = MEM_calloc_arrayN(
239  (uint)mp->totloop, sizeof(*link_edges), "NewFaceRef::link_edges in solidify");
240  face_sides_arr[i * 2 + 1] = (NewFaceRef){
241  .face = mp, .index = i, .reversed = true, .link_edges = link_edges};
242  if (mp->totloop > largest_ngon) {
243  largest_ngon = (uint)mp->totloop;
244  }
245  /* add to final mesh face count */
246  if (do_shell) {
247  new_polys_num += 2;
248  new_loops_num += (uint)mp->totloop * 2;
249  }
250  }
251  }
252 
253  uint *edge_adj_faces_len = MEM_calloc_arrayN(
254  edges_num, sizeof(*edge_adj_faces_len), "edge_adj_faces_len in solidify");
255  /* Count for each edge how many faces it has adjacent. */
256  {
257  mp = orig_mpoly;
258  for (uint i = 0; i < polys_num; i++, mp++) {
259  ml = orig_mloop + mp->loopstart;
260  for (uint j = 0; j < mp->totloop; j++, ml++) {
261  edge_adj_faces_len[ml->e]++;
262  }
263  }
264  }
265 
266  /* Original edge to #NewEdgeRef map. */
267  NewEdgeRef ***orig_edge_data_arr = MEM_calloc_arrayN(
268  edges_num, sizeof(*orig_edge_data_arr), "orig_edge_data_arr in solidify");
269  /* Original edge length cache. */
270  float *orig_edge_lengths = MEM_calloc_arrayN(
271  edges_num, sizeof(*orig_edge_lengths), "orig_edge_lengths in solidify");
272  /* Edge groups for every original vert. */
273  EdgeGroup **orig_vert_groups_arr = MEM_calloc_arrayN(
274  verts_num, sizeof(*orig_vert_groups_arr), "orig_vert_groups_arr in solidify");
275  /* vertex map used to map duplicates. */
276  uint *vm = MEM_malloc_arrayN(verts_num, sizeof(*vm), "orig_vert_map in solidify");
277  for (uint i = 0; i < verts_num; i++) {
278  vm[i] = i;
279  }
280 
281  uint edge_index = 0;
282  uint loop_index = 0;
283  uint poly_index = 0;
284 
285  bool has_singularities = false;
286 
287  /* Vert edge adjacent map. */
288  OldVertEdgeRef **vert_adj_edges = MEM_calloc_arrayN(
289  verts_num, sizeof(*vert_adj_edges), "vert_adj_edges in solidify");
290  /* Original vertex positions (changed for degenerated geometry). */
291  float(*orig_mvert_co)[3] = MEM_malloc_arrayN(
292  verts_num, sizeof(*orig_mvert_co), "orig_mvert_co in solidify");
293  /* Fill in the original vertex positions. */
294  for (uint i = 0; i < verts_num; i++) {
295  orig_mvert_co[i][0] = orig_mvert[i].co[0];
296  orig_mvert_co[i][1] = orig_mvert[i].co[1];
297  orig_mvert_co[i][2] = orig_mvert[i].co[2];
298  }
299 
300  /* Create edge to #NewEdgeRef map. */
301  {
302  OldEdgeFaceRef **edge_adj_faces = MEM_calloc_arrayN(
303  edges_num, sizeof(*edge_adj_faces), "edge_adj_faces in solidify");
304 
305  /* Create link_faces for edges. */
306  {
307  mp = orig_mpoly;
308  for (uint i = 0; i < polys_num; i++, mp++) {
309  ml = orig_mloop + mp->loopstart;
310  for (uint j = 0; j < mp->totloop; j++, ml++) {
311  const uint edge = ml->e;
312  const bool reversed = orig_medge[edge].v2 != ml->v;
313  OldEdgeFaceRef *old_face_edge_ref = edge_adj_faces[edge];
314  if (old_face_edge_ref == NULL) {
315  const uint len = edge_adj_faces_len[edge];
316  BLI_assert(len > 0);
317  uint *adj_faces = MEM_malloc_arrayN(
318  len, sizeof(*adj_faces), "OldEdgeFaceRef::faces in solidify");
319  bool *adj_faces_reversed = MEM_malloc_arrayN(
320  len, sizeof(*adj_faces_reversed), "OldEdgeFaceRef::reversed in solidify");
321  adj_faces[0] = i;
322  for (uint k = 1; k < len; k++) {
323  adj_faces[k] = MOD_SOLIDIFY_EMPTY_TAG;
324  }
325  adj_faces_reversed[0] = reversed;
326  OldEdgeFaceRef *ref = MEM_mallocN(sizeof(*ref), "OldEdgeFaceRef in solidify");
327  *ref = (OldEdgeFaceRef){adj_faces, len, adj_faces_reversed, 1};
328  edge_adj_faces[edge] = ref;
329  }
330  else {
331  for (uint k = 1; k < old_face_edge_ref->faces_len; k++) {
332  if (old_face_edge_ref->faces[k] == MOD_SOLIDIFY_EMPTY_TAG) {
333  old_face_edge_ref->faces[k] = i;
334  old_face_edge_ref->faces_reversed[k] = reversed;
335  break;
336  }
337  }
338  }
339  }
340  }
341  }
342 
343  float edgedir[3] = {0, 0, 0};
344  uint *vert_adj_edges_len = MEM_calloc_arrayN(
345  verts_num, sizeof(*vert_adj_edges_len), "vert_adj_edges_len in solidify");
346 
347  /* Calculate edge lengths and len vert_adj edges. */
348  {
349  bool *face_singularity = MEM_calloc_arrayN(
350  polys_num, sizeof(*face_singularity), "face_sides_arr in solidify");
351 
352  const float merge_tolerance_sqr = smd->merge_tolerance * smd->merge_tolerance;
353  uint *combined_verts = MEM_calloc_arrayN(
354  verts_num, sizeof(*combined_verts), "combined_verts in solidify");
355 
356  ed = orig_medge;
357  for (uint i = 0; i < edges_num; i++, ed++) {
358  if (edge_adj_faces_len[i] > 0) {
359  uint v1 = vm[ed->v1];
360  uint v2 = vm[ed->v2];
361  if (v1 == v2) {
362  continue;
363  }
364 
365  if (v2 < v1) {
366  SWAP(uint, v1, v2);
367  }
368  sub_v3_v3v3(edgedir, orig_mvert_co[v2], orig_mvert_co[v1]);
369  orig_edge_lengths[i] = len_squared_v3(edgedir);
370 
371  if (orig_edge_lengths[i] <= merge_tolerance_sqr) {
372  /* Merge verts. But first check if that would create a higher poly count. */
373  /* This check is very slow. It would need the vertex edge links to get
374  * accelerated that are not yet available at this point. */
375  bool can_merge = true;
376  for (uint k = 0; k < edges_num && can_merge; k++) {
377  if (k != i && edge_adj_faces_len[k] > 0 &&
378  (ELEM(vm[orig_medge[k].v1], v1, v2) != ELEM(vm[orig_medge[k].v2], v1, v2))) {
379  for (uint j = 0; j < edge_adj_faces[k]->faces_len && can_merge; j++) {
380  mp = orig_mpoly + edge_adj_faces[k]->faces[j];
381  uint changes = 0;
382  int cur = mp->totloop - 1;
383  for (int next = 0; next < mp->totloop && changes <= 2; next++) {
384  uint cur_v = vm[orig_mloop[mp->loopstart + cur].v];
385  uint next_v = vm[orig_mloop[mp->loopstart + next].v];
386  changes += (ELEM(cur_v, v1, v2) != ELEM(next_v, v1, v2));
387  cur = next;
388  }
389  can_merge = can_merge && changes <= 2;
390  }
391  }
392  }
393 
394  if (!can_merge) {
395  orig_edge_lengths[i] = 0.0f;
396  vert_adj_edges_len[v1]++;
397  vert_adj_edges_len[v2]++;
398  continue;
399  }
400 
401  mul_v3_fl(edgedir,
402  (combined_verts[v2] + 1) /
403  (float)(combined_verts[v1] + combined_verts[v2] + 2));
404  add_v3_v3(orig_mvert_co[v1], edgedir);
405  for (uint j = v2; j < verts_num; j++) {
406  if (vm[j] == v2) {
407  vm[j] = v1;
408  }
409  }
410  vert_adj_edges_len[v1] += vert_adj_edges_len[v2];
411  vert_adj_edges_len[v2] = 0;
412  combined_verts[v1] += combined_verts[v2] + 1;
413 
414  if (do_shell) {
415  new_loops_num -= edge_adj_faces_len[i] * 2;
416  }
417 
418  edge_adj_faces_len[i] = 0;
419  MEM_freeN(edge_adj_faces[i]->faces);
420  MEM_freeN(edge_adj_faces[i]->faces_reversed);
421  MEM_freeN(edge_adj_faces[i]);
422  edge_adj_faces[i] = NULL;
423  }
424  else {
425  orig_edge_lengths[i] = sqrtf(orig_edge_lengths[i]);
426  vert_adj_edges_len[v1]++;
427  vert_adj_edges_len[v2]++;
428  }
429  }
430  }
431  /* remove zero faces in a second pass */
432  ed = orig_medge;
433  for (uint i = 0; i < edges_num; i++, ed++) {
434  const uint v1 = vm[ed->v1];
435  const uint v2 = vm[ed->v2];
436  if (v1 == v2 && edge_adj_faces[i]) {
437  /* Remove polys. */
438  for (uint j = 0; j < edge_adj_faces[i]->faces_len; j++) {
439  const uint face = edge_adj_faces[i]->faces[j];
440  if (!face_singularity[face]) {
441  bool is_singularity = true;
442  for (uint k = 0; k < orig_mpoly[face].totloop; k++) {
443  if (vm[orig_mloop[((uint)orig_mpoly[face].loopstart) + k].v] != v1) {
444  is_singularity = false;
445  break;
446  }
447  }
448  if (is_singularity) {
449  face_singularity[face] = true;
450  /* remove from final mesh poly count */
451  if (do_shell) {
452  new_polys_num -= 2;
453  }
454  }
455  }
456  }
457 
458  if (do_shell) {
459  new_loops_num -= edge_adj_faces_len[i] * 2;
460  }
461 
462  edge_adj_faces_len[i] = 0;
463  MEM_freeN(edge_adj_faces[i]->faces);
464  MEM_freeN(edge_adj_faces[i]->faces_reversed);
465  MEM_freeN(edge_adj_faces[i]);
466  edge_adj_faces[i] = NULL;
467  }
468  }
469 
470  MEM_freeN(face_singularity);
471  MEM_freeN(combined_verts);
472  }
473 
474  /* Create vert_adj_edges for verts. */
475  {
476  ed = orig_medge;
477  for (uint i = 0; i < edges_num; i++, ed++) {
478  if (edge_adj_faces_len[i] > 0) {
479  const uint vs[2] = {vm[ed->v1], vm[ed->v2]};
480  uint invalid_edge_index = 0;
481  bool invalid_edge_reversed = false;
482  for (uint j = 0; j < 2; j++) {
483  const uint vert = vs[j];
484  const uint len = vert_adj_edges_len[vert];
485  if (len > 0) {
486  OldVertEdgeRef *old_edge_vert_ref = vert_adj_edges[vert];
487  if (old_edge_vert_ref == NULL) {
488  uint *adj_edges = MEM_calloc_arrayN(
489  len, sizeof(*adj_edges), "OldVertEdgeRef::edges in solidify");
490  adj_edges[0] = i;
491  for (uint k = 1; k < len; k++) {
492  adj_edges[k] = MOD_SOLIDIFY_EMPTY_TAG;
493  }
494  OldVertEdgeRef *ref = MEM_mallocN(sizeof(*ref), "OldVertEdgeRef in solidify");
495  *ref = (OldVertEdgeRef){adj_edges, 1};
496  vert_adj_edges[vert] = ref;
497  }
498  else {
499  const uint *f = old_edge_vert_ref->edges;
500  for (uint k = 0; k < len && k <= old_edge_vert_ref->edges_len; k++, f++) {
501  const uint edge = old_edge_vert_ref->edges[k];
502  if (edge == MOD_SOLIDIFY_EMPTY_TAG || k == old_edge_vert_ref->edges_len) {
503  old_edge_vert_ref->edges[k] = i;
504  old_edge_vert_ref->edges_len++;
505  break;
506  }
507  if (vm[orig_medge[edge].v1] == vs[1 - j]) {
508  invalid_edge_index = edge + 1;
509  invalid_edge_reversed = (j == 0);
510  break;
511  }
512  if (vm[orig_medge[edge].v2] == vs[1 - j]) {
513  invalid_edge_index = edge + 1;
514  invalid_edge_reversed = (j == 1);
515  break;
516  }
517  }
518  if (invalid_edge_index) {
519  if (j == 1) {
520  /* Should never actually be executed. */
521  vert_adj_edges[vs[0]]->edges_len--;
522  }
523  break;
524  }
525  }
526  }
527  }
528  /* Remove zero faces that are in shape of an edge. */
529  if (invalid_edge_index) {
530  const uint tmp = invalid_edge_index - 1;
531  invalid_edge_index = i;
532  i = tmp;
533  OldEdgeFaceRef *i_adj_faces = edge_adj_faces[i];
534  OldEdgeFaceRef *invalid_adj_faces = edge_adj_faces[invalid_edge_index];
535  uint j = 0;
536  for (uint k = 0; k < i_adj_faces->faces_len; k++) {
537  for (uint l = 0; l < invalid_adj_faces->faces_len; l++) {
538  if (i_adj_faces->faces[k] == invalid_adj_faces->faces[l] &&
539  i_adj_faces->faces[k] != MOD_SOLIDIFY_EMPTY_TAG) {
540  i_adj_faces->faces[k] = MOD_SOLIDIFY_EMPTY_TAG;
541  invalid_adj_faces->faces[l] = MOD_SOLIDIFY_EMPTY_TAG;
542  j++;
543  }
544  }
545  }
546  /* remove from final face count */
547  if (do_shell) {
548  new_polys_num -= 2 * j;
549  new_loops_num -= 4 * j;
550  }
551  const uint len = i_adj_faces->faces_len + invalid_adj_faces->faces_len - 2 * j;
552  uint *adj_faces = MEM_malloc_arrayN(
553  len, sizeof(*adj_faces), "OldEdgeFaceRef::faces in solidify");
554  bool *adj_faces_loops_reversed = MEM_malloc_arrayN(
555  len, sizeof(*adj_faces_loops_reversed), "OldEdgeFaceRef::reversed in solidify");
556  /* Clean merge of adj_faces. */
557  j = 0;
558  for (uint k = 0; k < i_adj_faces->faces_len; k++) {
559  if (i_adj_faces->faces[k] != MOD_SOLIDIFY_EMPTY_TAG) {
560  adj_faces[j] = i_adj_faces->faces[k];
561  adj_faces_loops_reversed[j++] = i_adj_faces->faces_reversed[k];
562  }
563  }
564  for (uint k = 0; k < invalid_adj_faces->faces_len; k++) {
565  if (invalid_adj_faces->faces[k] != MOD_SOLIDIFY_EMPTY_TAG) {
566  adj_faces[j] = invalid_adj_faces->faces[k];
567  adj_faces_loops_reversed[j++] = (invalid_edge_reversed !=
568  invalid_adj_faces->faces_reversed[k]);
569  }
570  }
571  BLI_assert(j == len);
572  edge_adj_faces_len[invalid_edge_index] = 0;
573  edge_adj_faces_len[i] = len;
574  MEM_freeN(i_adj_faces->faces);
575  MEM_freeN(i_adj_faces->faces_reversed);
576  i_adj_faces->faces_len = len;
577  i_adj_faces->faces = adj_faces;
578  i_adj_faces->faces_reversed = adj_faces_loops_reversed;
579  i_adj_faces->used += invalid_adj_faces->used;
580  MEM_freeN(invalid_adj_faces->faces);
581  MEM_freeN(invalid_adj_faces->faces_reversed);
582  MEM_freeN(invalid_adj_faces);
583  edge_adj_faces[invalid_edge_index] = i_adj_faces;
584  /* Reset counter to continue. */
585  i = invalid_edge_index;
586  }
587  }
588  }
589  }
590 
591  MEM_freeN(vert_adj_edges_len);
592 
593  /* Filter duplicate polys. */
594  {
595  ed = orig_medge;
596  /* Iterate over edges and only check the faces around an edge for duplicates
597  * (performance optimization). */
598  for (uint i = 0; i < edges_num; i++, ed++) {
599  if (edge_adj_faces_len[i] > 0) {
600  const OldEdgeFaceRef *adj_faces = edge_adj_faces[i];
601  uint adj_len = adj_faces->faces_len;
602  /* Not that #adj_len doesn't need to equal edge_adj_faces_len anymore
603  * because #adj_len is shared when a face got collapsed to an edge. */
604  if (adj_len > 1) {
605  /* For each face pair check if they have equal verts. */
606  for (uint j = 0; j < adj_len; j++) {
607  const uint face = adj_faces->faces[j];
608  const int j_loopstart = orig_mpoly[face].loopstart;
609  const int totloop = orig_mpoly[face].totloop;
610  const uint j_first_v = vm[orig_mloop[j_loopstart].v];
611  for (uint k = j + 1; k < adj_len; k++) {
612  if (orig_mpoly[adj_faces->faces[k]].totloop != totloop) {
613  continue;
614  }
615  /* Find first face first loop vert in second face loops. */
616  const int k_loopstart = orig_mpoly[adj_faces->faces[k]].loopstart;
617  int l;
618  ml = orig_mloop + k_loopstart;
619  for (l = 0; l < totloop && vm[ml->v] != j_first_v; l++, ml++) {
620  /* Pass. */
621  }
622  if (l == totloop) {
623  continue;
624  }
625  /* Check if all following loops have equal verts. */
626  const bool reversed = adj_faces->faces_reversed[j] != adj_faces->faces_reversed[k];
627  const int count_dir = reversed ? -1 : 1;
628  bool has_diff = false;
629  ml = orig_mloop + j_loopstart;
630  for (int m = 0, n = l + totloop; m < totloop && !has_diff;
631  m++, n += count_dir, ml++) {
632  has_diff = has_diff || vm[ml->v] != vm[orig_mloop[k_loopstart + n % totloop].v];
633  }
634  /* If the faces are equal, discard one (j). */
635  if (!has_diff) {
636  ml = orig_mloop + j_loopstart;
637  uint del_loops = 0;
638  for (uint m = 0; m < totloop; m++, ml++) {
639  const uint e = ml->e;
640  OldEdgeFaceRef *e_adj_faces = edge_adj_faces[e];
641  if (e_adj_faces) {
642  uint face_index = j;
643  uint *e_adj_faces_faces = e_adj_faces->faces;
644  bool *e_adj_faces_reversed = e_adj_faces->faces_reversed;
645  const uint faces_len = e_adj_faces->faces_len;
646  if (e_adj_faces_faces != adj_faces->faces) {
647  /* Find index of e in #adj_faces. */
648  for (face_index = 0;
649  face_index < faces_len && e_adj_faces_faces[face_index] != face;
650  face_index++) {
651  /* Pass. */
652  }
653  /* If not found. */
654  if (face_index == faces_len) {
655  continue;
656  }
657  }
658  else {
659  /* If we shrink #edge_adj_faces[i] we need to update this field. */
660  adj_len--;
661  }
662  memmove(e_adj_faces_faces + face_index,
663  e_adj_faces_faces + face_index + 1,
664  (faces_len - face_index - 1) * sizeof(*e_adj_faces_faces));
665  memmove(e_adj_faces_reversed + face_index,
666  e_adj_faces_reversed + face_index + 1,
667  (faces_len - face_index - 1) * sizeof(*e_adj_faces_reversed));
668  e_adj_faces->faces_len--;
669  if (edge_adj_faces_len[e] > 0) {
670  edge_adj_faces_len[e]--;
671  if (edge_adj_faces_len[e] == 0) {
672  e_adj_faces->used--;
673  edge_adj_faces[e] = NULL;
674  }
675  }
676  else if (e_adj_faces->used > 1) {
677  for (uint n = 0; n < edges_num; n++) {
678  if (edge_adj_faces[n] == e_adj_faces && edge_adj_faces_len[n] > 0) {
679  edge_adj_faces_len[n]--;
680  if (edge_adj_faces_len[n] == 0) {
681  edge_adj_faces[n]->used--;
682  edge_adj_faces[n] = NULL;
683  }
684  break;
685  }
686  }
687  }
688  del_loops++;
689  }
690  }
691  if (do_shell) {
692  new_polys_num -= 2;
693  new_loops_num -= 2 * (uint)del_loops;
694  }
695  break;
696  }
697  }
698  }
699  }
700  }
701  }
702  }
703 
704  /* Create #NewEdgeRef array. */
705  {
706  ed = orig_medge;
707  for (uint i = 0; i < edges_num; i++, ed++) {
708  const uint v1 = vm[ed->v1];
709  const uint v2 = vm[ed->v2];
710  if (edge_adj_faces_len[i] > 0) {
711  if (LIKELY(orig_edge_lengths[i] > FLT_EPSILON)) {
712  sub_v3_v3v3(edgedir, orig_mvert_co[v2], orig_mvert_co[v1]);
713  mul_v3_fl(edgedir, 1.0f / orig_edge_lengths[i]);
714  }
715  else {
716  /* Smart fallback. */
717  /* This makes merging non essential, but correct
718  * merging will still give way better results. */
719  float pos[3];
720  copy_v3_v3(pos, orig_mvert_co[v2]);
721 
722  OldVertEdgeRef *link1 = vert_adj_edges[v1];
723  float v1_dir[3];
724  zero_v3(v1_dir);
725  for (int j = 0; j < link1->edges_len; j++) {
726  uint e = link1->edges[j];
727  if (edge_adj_faces_len[e] > 0 && e != i) {
728  uint other_v =
729  vm[vm[orig_medge[e].v1] == v1 ? orig_medge[e].v2 : orig_medge[e].v1];
730  sub_v3_v3v3(edgedir, orig_mvert_co[other_v], pos);
731  add_v3_v3(v1_dir, edgedir);
732  }
733  }
734  OldVertEdgeRef *link2 = vert_adj_edges[v2];
735  float v2_dir[3];
736  zero_v3(v2_dir);
737  for (int j = 0; j < link2->edges_len; j++) {
738  uint e = link2->edges[j];
739  if (edge_adj_faces_len[e] > 0 && e != i) {
740  uint other_v =
741  vm[vm[orig_medge[e].v1] == v2 ? orig_medge[e].v2 : orig_medge[e].v1];
742  sub_v3_v3v3(edgedir, orig_mvert_co[other_v], pos);
743  add_v3_v3(v2_dir, edgedir);
744  }
745  }
746  sub_v3_v3v3(edgedir, v2_dir, v1_dir);
747  float len = normalize_v3(edgedir);
748  if (len == 0.0f) {
749  edgedir[0] = 0.0f;
750  edgedir[1] = 0.0f;
751  edgedir[2] = 1.0f;
752  }
753  }
754 
755  OldEdgeFaceRef *adj_faces = edge_adj_faces[i];
756  const uint adj_len = adj_faces->faces_len;
757  const uint *adj_faces_faces = adj_faces->faces;
758  const bool *adj_faces_reversed = adj_faces->faces_reversed;
759  uint new_edges_len = 0;
760  FaceKeyPair *sorted_faces = MEM_malloc_arrayN(
761  adj_len, sizeof(*sorted_faces), "sorted_faces in solidify");
762  if (adj_len > 1) {
763  new_edges_len = adj_len;
764  /* Get keys for sorting. */
765  float ref_nor[3] = {0, 0, 0};
766  float nor[3];
767  for (uint j = 0; j < adj_len; j++) {
768  const bool reverse = adj_faces_reversed[j];
769  const uint face_i = adj_faces_faces[j];
770  if (reverse) {
771  negate_v3_v3(nor, poly_nors[face_i]);
772  }
773  else {
774  copy_v3_v3(nor, poly_nors[face_i]);
775  }
776  float d = 1;
777  if (orig_mpoly[face_i].totloop > 3) {
778  d = project_v3_v3(nor, edgedir);
779  if (LIKELY(d != 0)) {
780  d = normalize_v3(nor);
781  }
782  else {
783  d = 1;
784  }
785  }
786  if (UNLIKELY(d == 0.0f)) {
787  sorted_faces[j].angle = 0.0f;
788  }
789  else if (j == 0) {
790  copy_v3_v3(ref_nor, nor);
791  sorted_faces[j].angle = 0.0f;
792  }
793  else {
794  float angle = angle_signed_on_axis_normalized_v3v3_v3(nor, ref_nor, edgedir);
795  sorted_faces[j].angle = -angle;
796  }
797  sorted_faces[j].face = face_sides_arr + adj_faces_faces[j] * 2 +
798  (adj_faces_reversed[j] ? 1 : 0);
799  }
800  /* Sort faces by order around the edge (keep order in faces,
801  * reversed and face_angles the same). */
802  qsort(sorted_faces, adj_len, sizeof(*sorted_faces), comp_float_int_pair);
803  }
804  else {
805  new_edges_len = 2;
806  sorted_faces[0].face = face_sides_arr + adj_faces_faces[0] * 2 +
807  (adj_faces_reversed[0] ? 1 : 0);
808  if (do_rim) {
809  /* Only add the loops parallel to the edge for now. */
810  new_loops_num += 2;
811  new_polys_num++;
812  }
813  }
814 
815  /* Create a list of new edges and fill it. */
816  NewEdgeRef **new_edges = MEM_malloc_arrayN(
817  new_edges_len + 1, sizeof(*new_edges), "new_edges in solidify");
818  new_edges[new_edges_len] = NULL;
819  NewFaceRef *faces[2];
820  for (uint j = 0; j < new_edges_len; j++) {
821  float angle;
822  if (adj_len > 1) {
823  const uint next_j = j + 1 == adj_len ? 0 : j + 1;
824  faces[0] = sorted_faces[j].face;
825  faces[1] = sorted_faces[next_j].face->reversed ? sorted_faces[next_j].face - 1 :
826  sorted_faces[next_j].face + 1;
827  angle = sorted_faces[next_j].angle - sorted_faces[j].angle;
828  if (angle < 0) {
829  angle += 2 * M_PI;
830  }
831  }
832  else {
833  faces[0] = sorted_faces[0].face->reversed ? sorted_faces[0].face - j :
834  sorted_faces[0].face + j;
835  faces[1] = NULL;
836  angle = 0;
837  }
838  NewEdgeRef *edge_data = MEM_mallocN(sizeof(*edge_data), "edge_data in solidify");
839  uint edge_data_edge_index = MOD_SOLIDIFY_EMPTY_TAG;
840  if (do_shell || (adj_len == 1 && do_rim)) {
841  edge_data_edge_index = 0;
842  }
843  *edge_data = (NewEdgeRef){.old_edge = i,
844  .faces = {faces[0], faces[1]},
845  .link_edge_groups = {NULL, NULL},
846  .angle = angle,
847  .new_edge = edge_data_edge_index};
848  new_edges[j] = edge_data;
849  for (uint k = 0; k < 2; k++) {
850  if (faces[k] != NULL) {
851  ml = orig_mloop + faces[k]->face->loopstart;
852  for (int l = 0; l < faces[k]->face->totloop; l++, ml++) {
853  if (edge_adj_faces[ml->e] == edge_adj_faces[i]) {
854  if (ml->e != i && orig_edge_data_arr[ml->e] == NULL) {
855  orig_edge_data_arr[ml->e] = new_edges;
856  }
857  faces[k]->link_edges[l] = edge_data;
858  break;
859  }
860  }
861  }
862  }
863  }
864  MEM_freeN(sorted_faces);
865  orig_edge_data_arr[i] = new_edges;
866  if (do_shell || (adj_len == 1 && do_rim)) {
867  new_edges_num += new_edges_len;
868  }
869  }
870  }
871  }
872 
873  for (uint i = 0; i < edges_num; i++) {
874  if (edge_adj_faces[i]) {
875  if (edge_adj_faces[i]->used > 1) {
876  edge_adj_faces[i]->used--;
877  }
878  else {
879  MEM_freeN(edge_adj_faces[i]->faces);
880  MEM_freeN(edge_adj_faces[i]->faces_reversed);
881  MEM_freeN(edge_adj_faces[i]);
882  }
883  }
884  }
885  MEM_freeN(edge_adj_faces);
886  }
887 
888  /* Create sorted edge groups for every vert. */
889  {
890  OldVertEdgeRef **adj_edges_ptr = vert_adj_edges;
891  for (uint i = 0; i < verts_num; i++, adj_edges_ptr++) {
892  if (*adj_edges_ptr != NULL && (*adj_edges_ptr)->edges_len >= 2) {
893  EdgeGroup *edge_groups;
894 
895  int eg_index = -1;
896  bool contains_long_groups = false;
897  uint topo_groups = 0;
898 
899  /* Initial sorted creation. */
900  {
901  const uint *adj_edges = (*adj_edges_ptr)->edges;
902  const uint tot_adj_edges = (*adj_edges_ptr)->edges_len;
903 
904  uint unassigned_edges_len = 0;
905  for (uint j = 0; j < tot_adj_edges; j++) {
906  NewEdgeRef **new_edges = orig_edge_data_arr[adj_edges[j]];
907  /* TODO: check where the null pointer come from,
908  * because there should not be any... */
909  if (new_edges) {
910  /* count the number of new edges around the original vert */
911  while (*new_edges) {
912  unassigned_edges_len++;
913  new_edges++;
914  }
915  }
916  }
917  NewEdgeRef **unassigned_edges = MEM_malloc_arrayN(
918  unassigned_edges_len, sizeof(*unassigned_edges), "unassigned_edges in solidify");
919  for (uint j = 0, k = 0; j < tot_adj_edges; j++) {
920  NewEdgeRef **new_edges = orig_edge_data_arr[adj_edges[j]];
921  if (new_edges) {
922  while (*new_edges) {
923  unassigned_edges[k++] = *new_edges;
924  new_edges++;
925  }
926  }
927  }
928 
929  /* An edge group will always contain min 2 edges
930  * so max edge group count can be calculated. */
931  uint edge_groups_len = unassigned_edges_len / 2;
932  edge_groups = MEM_calloc_arrayN(
933  edge_groups_len + 1, sizeof(*edge_groups), "edge_groups in solidify");
934 
935  uint assigned_edges_len = 0;
936  NewEdgeRef *found_edge = NULL;
937  uint found_edge_index = 0;
938  bool insert_at_start = false;
939  uint eg_capacity = 5;
940  NewFaceRef *eg_track_faces[2] = {NULL, NULL};
941  NewFaceRef *last_open_edge_track = NULL;
942 
943  while (assigned_edges_len < unassigned_edges_len) {
944  found_edge = NULL;
945  insert_at_start = false;
946  if (eg_index >= 0 && edge_groups[eg_index].edges_len == 0) {
947  /* Called every time a new group was started in the last iteration. */
948  /* Find an unused edge to start the next group
949  * and setup variables to start creating it. */
950  uint j = 0;
951  NewEdgeRef *edge = NULL;
952  while (!edge && j < unassigned_edges_len) {
953  edge = unassigned_edges[j++];
954  if (edge && last_open_edge_track &&
955  (edge->faces[0] != last_open_edge_track || edge->faces[1] != NULL)) {
956  edge = NULL;
957  }
958  }
959  if (!edge && last_open_edge_track) {
960  topo_groups++;
961  last_open_edge_track = NULL;
962  edge_groups[eg_index].topo_group++;
963  j = 0;
964  while (!edge && j < unassigned_edges_len) {
965  edge = unassigned_edges[j++];
966  }
967  }
968  else if (!last_open_edge_track && eg_index > 0) {
969  topo_groups++;
970  edge_groups[eg_index].topo_group++;
971  }
972  BLI_assert(edge != NULL);
973  found_edge_index = j - 1;
974  found_edge = edge;
975  if (!last_open_edge_track && vm[orig_medge[edge->old_edge].v1] == i) {
976  eg_track_faces[0] = edge->faces[0];
977  eg_track_faces[1] = edge->faces[1];
978  if (edge->faces[1] == NULL) {
979  last_open_edge_track = edge->faces[0]->reversed ? edge->faces[0] - 1 :
980  edge->faces[0] + 1;
981  }
982  }
983  else {
984  eg_track_faces[0] = edge->faces[1];
985  eg_track_faces[1] = edge->faces[0];
986  }
987  }
988  else if (eg_index >= 0) {
989  NewEdgeRef **edge_ptr = unassigned_edges;
990  for (found_edge_index = 0; found_edge_index < unassigned_edges_len;
991  found_edge_index++, edge_ptr++) {
992  if (*edge_ptr) {
993  NewEdgeRef *edge = *edge_ptr;
994  if (edge->faces[0] == eg_track_faces[1]) {
995  insert_at_start = false;
996  eg_track_faces[1] = edge->faces[1];
997  found_edge = edge;
998  if (edge->faces[1] == NULL) {
999  edge_groups[eg_index].is_orig_closed = false;
1000  last_open_edge_track = edge->faces[0]->reversed ? edge->faces[0] - 1 :
1001  edge->faces[0] + 1;
1002  }
1003  break;
1004  }
1005  if (edge->faces[0] == eg_track_faces[0]) {
1006  insert_at_start = true;
1007  eg_track_faces[0] = edge->faces[1];
1008  found_edge = edge;
1009  if (edge->faces[1] == NULL) {
1010  edge_groups[eg_index].is_orig_closed = false;
1011  }
1012  break;
1013  }
1014  if (edge->faces[1] != NULL) {
1015  if (edge->faces[1] == eg_track_faces[1]) {
1016  insert_at_start = false;
1017  eg_track_faces[1] = edge->faces[0];
1018  found_edge = edge;
1019  break;
1020  }
1021  if (edge->faces[1] == eg_track_faces[0]) {
1022  insert_at_start = true;
1023  eg_track_faces[0] = edge->faces[0];
1024  found_edge = edge;
1025  break;
1026  }
1027  }
1028  }
1029  }
1030  }
1031  if (found_edge) {
1032  unassigned_edges[found_edge_index] = NULL;
1033  assigned_edges_len++;
1034  const uint needed_capacity = edge_groups[eg_index].edges_len + 1;
1035  if (needed_capacity > eg_capacity) {
1036  eg_capacity = needed_capacity + 1;
1037  NewEdgeRef **new_eg = MEM_calloc_arrayN(
1038  eg_capacity, sizeof(*new_eg), "edge_group realloc in solidify");
1039  if (insert_at_start) {
1040  memcpy(new_eg + 1,
1041  edge_groups[eg_index].edges,
1042  edge_groups[eg_index].edges_len * sizeof(*new_eg));
1043  }
1044  else {
1045  memcpy(new_eg,
1046  edge_groups[eg_index].edges,
1047  edge_groups[eg_index].edges_len * sizeof(*new_eg));
1048  }
1049  MEM_freeN(edge_groups[eg_index].edges);
1050  edge_groups[eg_index].edges = new_eg;
1051  }
1052  else if (insert_at_start) {
1053  memmove(edge_groups[eg_index].edges + 1,
1054  edge_groups[eg_index].edges,
1055  edge_groups[eg_index].edges_len * sizeof(*edge_groups[eg_index].edges));
1056  }
1057  edge_groups[eg_index].edges[insert_at_start ? 0 : edge_groups[eg_index].edges_len] =
1058  found_edge;
1059  edge_groups[eg_index].edges_len++;
1060  if (edge_groups[eg_index].edges[edge_groups[eg_index].edges_len - 1]->faces[1] !=
1061  NULL) {
1062  last_open_edge_track = NULL;
1063  }
1064  if (edge_groups[eg_index].edges_len > 3) {
1065  contains_long_groups = true;
1066  }
1067  }
1068  else {
1069  /* called on first iteration to clean up the eg_index = -1 and start the first group,
1070  * or when the current group is found to be complete (no new found_edge) */
1071  eg_index++;
1072  BLI_assert(eg_index < edge_groups_len);
1073  eg_capacity = 5;
1074  NewEdgeRef **edges = MEM_calloc_arrayN(
1075  eg_capacity, sizeof(*edges), "edge_group in solidify");
1076  edge_groups[eg_index] = (EdgeGroup){
1077  .valid = true,
1078  .edges = edges,
1079  .edges_len = 0,
1080  .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG,
1081  .is_orig_closed = true,
1082  .is_even_split = false,
1083  .split = 0,
1084  .is_singularity = false,
1085  .topo_group = topo_groups,
1086  .co = {0.0f, 0.0f, 0.0f},
1087  .no = {0.0f, 0.0f, 0.0f},
1088  .new_vert = MOD_SOLIDIFY_EMPTY_TAG,
1089  };
1090  eg_track_faces[0] = NULL;
1091  eg_track_faces[1] = NULL;
1092  }
1093  }
1094  /* #eg_index is the number of groups from here on. */
1095  eg_index++;
1096  /* #topo_groups is the number of topo groups from here on. */
1097  topo_groups++;
1098 
1099  MEM_freeN(unassigned_edges);
1100 
1101  /* TODO: reshape the edge_groups array to its actual size
1102  * after writing is finished to save on memory. */
1103  }
1104 
1105  /* Split of long self intersection groups */
1106  {
1107  uint splits = 0;
1108  if (contains_long_groups) {
1109  uint add_index = 0;
1110  for (uint j = 0; j < eg_index; j++) {
1111  const uint edges_len = edge_groups[j + add_index].edges_len;
1112  if (edges_len > 3) {
1113  bool has_doubles = false;
1114  bool *doubles = MEM_calloc_arrayN(
1115  edges_len, sizeof(*doubles), "doubles in solidify");
1116  EdgeGroup g = edge_groups[j + add_index];
1117  for (uint k = 0; k < edges_len; k++) {
1118  for (uint l = k + 1; l < edges_len; l++) {
1119  if (g.edges[k]->old_edge == g.edges[l]->old_edge) {
1120  doubles[k] = true;
1121  doubles[l] = true;
1122  has_doubles = true;
1123  }
1124  }
1125  }
1126  if (has_doubles) {
1127  const uint prior_splits = splits;
1128  const uint prior_index = add_index;
1129  int unique_start = -1;
1130  int first_unique_end = -1;
1131  int last_split = -1;
1132  int first_split = -1;
1133  bool first_even_split = false;
1134  uint real_k = 0;
1135  while (real_k < edges_len ||
1136  (g.is_orig_closed &&
1137  (real_k <=
1138  (first_unique_end == -1 ? 0 : first_unique_end) + (int)edges_len ||
1139  first_split != last_split))) {
1140  const uint k = real_k % edges_len;
1141  if (!doubles[k]) {
1142  if (first_unique_end != -1 && unique_start == -1) {
1143  unique_start = (int)real_k;
1144  }
1145  }
1146  else if (first_unique_end == -1) {
1147  first_unique_end = (int)k;
1148  }
1149  else if (unique_start != -1) {
1150  const uint split = (((uint)unique_start + real_k + 1) / 2) % edges_len;
1151  const bool is_even_split = (((uint)unique_start + real_k) & 1);
1152  if (last_split != -1) {
1153  /* Override g on first split (no insert). */
1154  if (prior_splits != splits) {
1155  memmove(edge_groups + j + add_index + 1,
1156  edge_groups + j + add_index,
1157  ((uint)eg_index - j) * sizeof(*edge_groups));
1158  add_index++;
1159  }
1160  if (last_split > split) {
1161  const uint edges_len_group = (split + edges_len) - (uint)last_split;
1162  NewEdgeRef **edges = MEM_malloc_arrayN(
1163  edges_len_group, sizeof(*edges), "edge_group split in solidify");
1164  memcpy(edges,
1165  g.edges + last_split,
1166  (edges_len - (uint)last_split) * sizeof(*edges));
1167  memcpy(edges + (edges_len - (uint)last_split),
1168  g.edges,
1169  split * sizeof(*edges));
1170  edge_groups[j + add_index] = (EdgeGroup){
1171  .valid = true,
1172  .edges = edges,
1173  .edges_len = edges_len_group,
1174  .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG,
1175  .is_orig_closed = g.is_orig_closed,
1176  .is_even_split = is_even_split,
1177  .split = add_index - prior_index + 1 + (uint)!g.is_orig_closed,
1178  .is_singularity = false,
1179  .topo_group = g.topo_group,
1180  .co = {0.0f, 0.0f, 0.0f},
1181  .no = {0.0f, 0.0f, 0.0f},
1182  .new_vert = MOD_SOLIDIFY_EMPTY_TAG,
1183  };
1184  }
1185  else {
1186  const uint edges_len_group = split - (uint)last_split;
1187  NewEdgeRef **edges = MEM_malloc_arrayN(
1188  edges_len_group, sizeof(*edges), "edge_group split in solidify");
1189  memcpy(edges, g.edges + last_split, edges_len_group * sizeof(*edges));
1190  edge_groups[j + add_index] = (EdgeGroup){
1191  .valid = true,
1192  .edges = edges,
1193  .edges_len = edges_len_group,
1194  .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG,
1195  .is_orig_closed = g.is_orig_closed,
1196  .is_even_split = is_even_split,
1197  .split = add_index - prior_index + 1 + (uint)!g.is_orig_closed,
1198  .is_singularity = false,
1199  .topo_group = g.topo_group,
1200  .co = {0.0f, 0.0f, 0.0f},
1201  .no = {0.0f, 0.0f, 0.0f},
1202  .new_vert = MOD_SOLIDIFY_EMPTY_TAG,
1203  };
1204  }
1205  splits++;
1206  }
1207  last_split = (int)split;
1208  if (first_split == -1) {
1209  first_split = (int)split;
1210  first_even_split = is_even_split;
1211  }
1212  unique_start = -1;
1213  }
1214  real_k++;
1215  }
1216  if (first_split != -1) {
1217  if (!g.is_orig_closed) {
1218  if (prior_splits != splits) {
1219  memmove(edge_groups + (j + prior_index + 1),
1220  edge_groups + (j + prior_index),
1221  ((uint)eg_index + add_index - (j + prior_index)) *
1222  sizeof(*edge_groups));
1223  memmove(edge_groups + (j + add_index + 2),
1224  edge_groups + (j + add_index + 1),
1225  ((uint)eg_index - j) * sizeof(*edge_groups));
1226  add_index++;
1227  }
1228  else {
1229  memmove(edge_groups + (j + add_index + 2),
1230  edge_groups + (j + add_index + 1),
1231  ((uint)eg_index - j - 1) * sizeof(*edge_groups));
1232  }
1233  NewEdgeRef **edges = MEM_malloc_arrayN(
1234  (uint)first_split, sizeof(*edges), "edge_group split in solidify");
1235  memcpy(edges, g.edges, (uint)first_split * sizeof(*edges));
1236  edge_groups[j + prior_index] = (EdgeGroup){
1237  .valid = true,
1238  .edges = edges,
1239  .edges_len = (uint)first_split,
1240  .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG,
1241  .is_orig_closed = g.is_orig_closed,
1242  .is_even_split = first_even_split,
1243  .split = 1,
1244  .is_singularity = false,
1245  .topo_group = g.topo_group,
1246  .co = {0.0f, 0.0f, 0.0f},
1247  .no = {0.0f, 0.0f, 0.0f},
1248  .new_vert = MOD_SOLIDIFY_EMPTY_TAG,
1249  };
1250  add_index++;
1251  splits++;
1252  edges = MEM_malloc_arrayN(edges_len - (uint)last_split,
1253  sizeof(*edges),
1254  "edge_group split in solidify");
1255  memcpy(edges,
1256  g.edges + last_split,
1257  (edges_len - (uint)last_split) * sizeof(*edges));
1258  edge_groups[j + add_index] = (EdgeGroup){
1259  .valid = true,
1260  .edges = edges,
1261  .edges_len = (edges_len - (uint)last_split),
1262  .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG,
1263  .is_orig_closed = g.is_orig_closed,
1264  .is_even_split = false,
1265  .split = add_index - prior_index + 1,
1266  .is_singularity = false,
1267  .topo_group = g.topo_group,
1268  .co = {0.0f, 0.0f, 0.0f},
1269  .no = {0.0f, 0.0f, 0.0f},
1270  .new_vert = MOD_SOLIDIFY_EMPTY_TAG,
1271  };
1272  }
1273  if (prior_splits != splits) {
1274  MEM_freeN(g.edges);
1275  }
1276  }
1277  if (first_unique_end != -1 && prior_splits == splits) {
1278  has_singularities = true;
1279  edge_groups[j + add_index].is_singularity = true;
1280  }
1281  }
1282  MEM_freeN(doubles);
1283  }
1284  }
1285  }
1286  }
1287 
1288  orig_vert_groups_arr[i] = edge_groups;
1289  /* Count new edges, loops, polys and add to link_edge_groups. */
1290  {
1291  uint new_verts = 0;
1292  bool contains_open_splits = false;
1293  uint open_edges = 0;
1294  uint contains_splits = 0;
1295  uint last_added = 0;
1296  uint first_added = 0;
1297  bool first_set = false;
1298  for (EdgeGroup *g = edge_groups; g->valid; g++) {
1299  NewEdgeRef **e = g->edges;
1300  for (uint j = 0; j < g->edges_len; j++, e++) {
1301  const uint flip = (uint)(vm[orig_medge[(*e)->old_edge].v2] == i);
1302  BLI_assert(flip || vm[orig_medge[(*e)->old_edge].v1] == i);
1303  (*e)->link_edge_groups[flip] = g;
1304  }
1305  uint added = 0;
1306  if (do_shell || (do_rim && !g->is_orig_closed)) {
1307  BLI_assert(g->new_vert == MOD_SOLIDIFY_EMPTY_TAG);
1308  g->new_vert = new_verts_num++;
1309  if (do_rim || (do_shell && g->split)) {
1310  new_verts++;
1311  contains_splits += (g->split != 0);
1312  contains_open_splits |= g->split && !g->is_orig_closed;
1313  added = g->split;
1314  }
1315  }
1316  open_edges += (uint)(added < last_added);
1317  if (!first_set) {
1318  first_set = true;
1319  first_added = added;
1320  }
1321  last_added = added;
1322  if (!(g + 1)->valid || g->topo_group != (g + 1)->topo_group) {
1323  if (new_verts > 2) {
1324  new_polys_num++;
1325  new_edges_num += new_verts;
1326  open_edges += (uint)(first_added < last_added);
1327  open_edges -= (uint)(open_edges && !contains_open_splits);
1328  if (do_shell && do_rim) {
1329  new_loops_num += new_verts * 2;
1330  }
1331  else if (do_shell) {
1332  new_loops_num += new_verts * 2 - open_edges;
1333  }
1334  else { // do_rim
1335  new_loops_num += new_verts * 2 + open_edges - contains_splits;
1336  }
1337  }
1338  else if (new_verts == 2) {
1339  new_edges_num++;
1340  new_loops_num += 2u - (uint)(!(do_rim && do_shell) && contains_open_splits);
1341  }
1342  new_verts = 0;
1343  contains_open_splits = false;
1344  contains_splits = 0;
1345  open_edges = 0;
1346  last_added = 0;
1347  first_added = 0;
1348  first_set = false;
1349  }
1350  }
1351  }
1352  }
1353  }
1354  }
1355 
1356  /* Free vert_adj_edges memory. */
1357  {
1358  uint i = 0;
1359  for (OldVertEdgeRef **p = vert_adj_edges; i < verts_num; i++, p++) {
1360  if (*p) {
1361  MEM_freeN((*p)->edges);
1362  MEM_freeN(*p);
1363  }
1364  }
1365  MEM_freeN(vert_adj_edges);
1366  }
1367 
1368  /* TODO: create_regions if fix_intersections. */
1369 
1370  /* General use pointer for #EdgeGroup iteration. */
1371  EdgeGroup **gs_ptr;
1372 
1373  /* Calculate EdgeGroup vertex coordinates. */
1374  {
1375  float *face_weight = NULL;
1376 
1377  if (do_flat_faces) {
1378  face_weight = MEM_malloc_arrayN(polys_num, sizeof(*face_weight), "face_weight in solidify");
1379 
1380  mp = orig_mpoly;
1381  for (uint i = 0; i < polys_num; i++, mp++) {
1382  float scalar_vgroup = 1.0f;
1383  int loopend = mp->loopstart + mp->totloop;
1384  ml = orig_mloop + mp->loopstart;
1385  for (int j = mp->loopstart; j < loopend; j++, ml++) {
1386  MDeformVert *dv = &dvert[ml->v];
1387  if (defgrp_invert) {
1388  scalar_vgroup = min_ff(1.0f - BKE_defvert_find_weight(dv, defgrp_index),
1389  scalar_vgroup);
1390  }
1391  else {
1392  scalar_vgroup = min_ff(BKE_defvert_find_weight(dv, defgrp_index), scalar_vgroup);
1393  }
1394  }
1395  scalar_vgroup = offset_fac_vg + (scalar_vgroup * offset_fac_vg_inv);
1396  face_weight[i] = scalar_vgroup;
1397  }
1398  }
1399 
1400  mv = orig_mvert;
1401  gs_ptr = orig_vert_groups_arr;
1402  for (uint i = 0; i < verts_num; i++, mv++, gs_ptr++) {
1403  if (*gs_ptr) {
1404  EdgeGroup *g = *gs_ptr;
1405  for (uint j = 0; g->valid; j++, g++) {
1406  if (!g->is_singularity) {
1407  float *nor = g->no;
1408  /* During vertex position calculation, the algorithm decides if it wants to disable the
1409  * boundary fix to maintain correct thickness. If the used algorithm does not produce a
1410  * free move direction (move_nor), it can use approximate_free_direction to decide on
1411  * a movement direction based on the connected edges. */
1412  float move_nor[3] = {0, 0, 0};
1413  bool disable_boundary_fix = (smd->nonmanifold_boundary_mode ==
1415  (g->is_orig_closed || g->split));
1416  bool approximate_free_direction = false;
1417  /* Constraints Method. */
1418  if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) {
1419  NewEdgeRef *first_edge = NULL;
1420  NewEdgeRef **edge_ptr = g->edges;
1421  /* Contains normal and offset [nx, ny, nz, ofs]. */
1422  float(*planes_queue)[4] = MEM_malloc_arrayN(
1423  g->edges_len + 1, sizeof(*planes_queue), "planes_queue in solidify");
1424  uint queue_index = 0;
1425 
1426  float fallback_nor[3];
1427  float fallback_ofs = 0.0f;
1428 
1429  const bool cycle = (g->is_orig_closed && !g->split) || g->is_even_split;
1430  for (uint k = 0; k < g->edges_len; k++, edge_ptr++) {
1431  if (!(k & 1) || (!cycle && k == g->edges_len - 1)) {
1432  NewEdgeRef *edge = *edge_ptr;
1433  for (uint l = 0; l < 2; l++) {
1434  NewFaceRef *face = edge->faces[l];
1435  if (face && (first_edge == NULL ||
1436  (first_edge->faces[0] != face && first_edge->faces[1] != face))) {
1437  float ofs = face->reversed ? ofs_back_clamped : ofs_front_clamped;
1438  /* Use face_weight here to make faces thinner. */
1439  if (do_flat_faces) {
1440  ofs *= face_weight[face->index];
1441  }
1442 
1443  if (!null_faces[face->index]) {
1444  /* And plane to the queue. */
1445  mul_v3_v3fl(planes_queue[queue_index],
1446  poly_nors[face->index],
1447  face->reversed ? -1 : 1);
1448  planes_queue[queue_index++][3] = ofs;
1449  }
1450  else {
1451  /* Just use this approximate normal of the null face if there is no other
1452  * normal to use. */
1453  mul_v3_v3fl(fallback_nor, poly_nors[face->index], face->reversed ? -1 : 1);
1454  fallback_ofs = ofs;
1455  }
1456  }
1457  }
1458  if ((cycle && k == 0) || (!cycle && k + 3 >= g->edges_len)) {
1459  first_edge = edge;
1460  }
1461  }
1462  }
1463  if (queue_index > 2) {
1464  /* Find the two most different normals. */
1465  float min_p = 2.0f;
1466  uint min_n0 = 0;
1467  uint min_n1 = 0;
1468  for (uint k = 0; k < queue_index; k++) {
1469  for (uint m = k + 1; m < queue_index; m++) {
1470  float p = dot_v3v3(planes_queue[k], planes_queue[m]);
1471  if (p < min_p) {
1472  min_p = p;
1473  min_n0 = k;
1474  min_n1 = m;
1475  }
1476  }
1477  }
1478  /* Put the two found normals, first in the array queue. */
1479  if (min_n1 != 0) {
1480  swap_v4_v4(planes_queue[min_n0], planes_queue[0]);
1481  swap_v4_v4(planes_queue[min_n1], planes_queue[1]);
1482  }
1483  else {
1484  swap_v4_v4(planes_queue[min_n0], planes_queue[1]);
1485  }
1486  /* Find the third most important/different normal. */
1487  min_p = 1.0f;
1488  min_n1 = 2;
1489  float max_p = -1.0f;
1490  for (uint k = 2; k < queue_index; k++) {
1491  max_p = max_ff(dot_v3v3(planes_queue[0], planes_queue[k]),
1492  dot_v3v3(planes_queue[1], planes_queue[k]));
1493  if (max_p <= min_p) {
1494  min_p = max_p;
1495  min_n1 = k;
1496  }
1497  }
1498  swap_v4_v4(planes_queue[min_n1], planes_queue[2]);
1499  }
1500  /* Remove/average duplicate normals in planes_queue. */
1501  while (queue_index > 2) {
1502  uint best_n0 = 0;
1503  uint best_n1 = 0;
1504  float best_p = -1.0f;
1505  float best_ofs_diff = 0.0f;
1506  for (uint k = 0; k < queue_index; k++) {
1507  for (uint m = k + 1; m < queue_index; m++) {
1508  float p = dot_v3v3(planes_queue[m], planes_queue[k]);
1509  float ofs_diff = fabsf(planes_queue[m][3] - planes_queue[k][3]);
1510  if (p > best_p + FLT_EPSILON || (p >= best_p && ofs_diff < best_ofs_diff)) {
1511  best_p = p;
1512  best_ofs_diff = ofs_diff;
1513  best_n0 = k;
1514  best_n1 = m;
1515  }
1516  }
1517  }
1518  /* Make sure there are no equal planes. This threshold is crucial for the
1519  * methods below to work without numerical issues. */
1520  if (best_p < 0.98f) {
1521  break;
1522  }
1523  add_v3_v3(planes_queue[best_n0], planes_queue[best_n1]);
1524  normalize_v3(planes_queue[best_n0]);
1525  planes_queue[best_n0][3] = (planes_queue[best_n0][3] + planes_queue[best_n1][3]) *
1526  0.5f;
1527  queue_index--;
1528  memmove(planes_queue + best_n1,
1529  planes_queue + best_n1 + 1,
1530  (queue_index - best_n1) * sizeof(*planes_queue));
1531  }
1532  const uint size = queue_index;
1533  /* If there is more than 2 planes at this vertex, the boundary fix should be disabled
1534  * to stay at the correct thickness for all the faces. This is not very good in
1535  * practice though, since that will almost always disable the boundary fix. Instead
1536  * introduce a threshold which decides whether the boundary fix can be used without
1537  * major thickness changes. If the following constant is 1.0, it would always
1538  * prioritize correct thickness. At 0.7 the thickness is allowed to change a bit if
1539  * necessary for the fix (~10%). Note this only applies if a boundary fix is used. */
1540  const float boundary_fix_threshold = 0.7f;
1541  if (size > 3) {
1542  /* Use the most general least squares method to find the best position. */
1543  float mat[3][3];
1544  zero_m3(mat);
1545  for (int k = 0; k < 3; k++) {
1546  for (int m = 0; m < size; m++) {
1547  madd_v3_v3fl(mat[k], planes_queue[m], planes_queue[m][k]);
1548  }
1549  /* Add a small epsilon to ensure the invert is going to work.
1550  * This addition makes the inverse more stable and the results
1551  * seem to get more precise. */
1552  mat[k][k] += 5e-5f;
1553  }
1554  /* NOTE: this matrix invert fails if there is less than 3 different normals. */
1555  invert_m3(mat);
1556  zero_v3(nor);
1557  for (int k = 0; k < size; k++) {
1558  madd_v3_v3fl(nor, planes_queue[k], planes_queue[k][3]);
1559  }
1560  mul_v3_m3v3(nor, mat, nor);
1561 
1562  if (!disable_boundary_fix) {
1563  /* Figure out if the approximate boundary fix can get use here. */
1564  float greatest_angle_cos = 1.0f;
1565  for (uint k = 0; k < 2; k++) {
1566  for (uint m = 2; m < size; m++) {
1567  float p = dot_v3v3(planes_queue[m], planes_queue[k]);
1568  if (p < greatest_angle_cos) {
1569  greatest_angle_cos = p;
1570  }
1571  }
1572  }
1573  if (greatest_angle_cos > boundary_fix_threshold) {
1574  approximate_free_direction = true;
1575  }
1576  else {
1577  disable_boundary_fix = true;
1578  }
1579  }
1580  }
1581  else if (size > 1) {
1582  /* When up to 3 constraint normals are found, there is a simple solution. */
1583  const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f;
1584  const float q = dot_v3v3(planes_queue[0], planes_queue[1]);
1585  float d = 1.0f - q * q;
1586  cross_v3_v3v3(move_nor, planes_queue[0], planes_queue[1]);
1587  normalize_v3(move_nor);
1588  if (d > FLT_EPSILON * 10 && q < stop_explosion) {
1589  d = 1.0f / d;
1590  mul_v3_fl(planes_queue[0], (planes_queue[0][3] - planes_queue[1][3] * q) * d);
1591  mul_v3_fl(planes_queue[1], (planes_queue[1][3] - planes_queue[0][3] * q) * d);
1592  }
1593  else {
1594  d = 1.0f / (fabsf(q) + 1.0f);
1595  mul_v3_fl(planes_queue[0], planes_queue[0][3] * d);
1596  mul_v3_fl(planes_queue[1], planes_queue[1][3] * d);
1597  }
1598  add_v3_v3v3(nor, planes_queue[0], planes_queue[1]);
1599  if (size == 3) {
1600  d = dot_v3v3(planes_queue[2], move_nor);
1601  /* The following threshold ignores the third plane if it is almost orthogonal to
1602  * the still free direction. */
1603  if (fabsf(d) > 0.02f) {
1604  float tmp[3];
1605  madd_v3_v3v3fl(tmp, nor, planes_queue[2], -planes_queue[2][3]);
1606  mul_v3_v3fl(tmp, move_nor, dot_v3v3(planes_queue[2], tmp) / d);
1607  sub_v3_v3(nor, tmp);
1608  /* Disable boundary fix if the constraints would be majorly unsatisfied. */
1609  if (fabsf(d) > 1.0f - boundary_fix_threshold) {
1610  disable_boundary_fix = true;
1611  }
1612  }
1613  }
1614  approximate_free_direction = false;
1615  }
1616  else if (size == 1) {
1617  /* Face corner case. */
1618  mul_v3_v3fl(nor, planes_queue[0], planes_queue[0][3]);
1619  if (g->edges_len > 2) {
1620  disable_boundary_fix = true;
1621  approximate_free_direction = true;
1622  }
1623  }
1624  else {
1625  /* Fallback case for null faces. */
1626  mul_v3_v3fl(nor, fallback_nor, fallback_ofs);
1627  disable_boundary_fix = true;
1628  }
1629  MEM_freeN(planes_queue);
1630  }
1631  /* Fixed/Even Method. */
1632  else {
1633  float total_angle = 0;
1634  float total_angle_back = 0;
1635  NewEdgeRef *first_edge = NULL;
1636  NewEdgeRef **edge_ptr = g->edges;
1637  float face_nor[3];
1638  float nor_back[3] = {0, 0, 0};
1639  bool has_back = false;
1640  bool has_front = false;
1641  bool cycle = (g->is_orig_closed && !g->split) || g->is_even_split;
1642  for (uint k = 0; k < g->edges_len; k++, edge_ptr++) {
1643  if (!(k & 1) || (!cycle && k == g->edges_len - 1)) {
1644  NewEdgeRef *edge = *edge_ptr;
1645  for (uint l = 0; l < 2; l++) {
1646  NewFaceRef *face = edge->faces[l];
1647  if (face && (first_edge == NULL ||
1648  (first_edge->faces[0] != face && first_edge->faces[1] != face))) {
1649  float angle = 1.0f;
1650  float ofs = face->reversed ? -ofs_back_clamped : ofs_front_clamped;
1651  /* Use face_weight here to make faces thinner. */
1652  if (do_flat_faces) {
1653  ofs *= face_weight[face->index];
1654  }
1655 
1656  if (smd->nonmanifold_offset_mode ==
1658  MLoop *ml_next = orig_mloop + face->face->loopstart;
1659  ml = ml_next + (face->face->totloop - 1);
1660  MLoop *ml_prev = ml - 1;
1661  for (int m = 0; m < face->face->totloop && vm[ml->v] != i;
1662  m++, ml_next++) {
1663  ml_prev = ml;
1664  ml = ml_next;
1665  }
1666  angle = angle_v3v3v3(orig_mvert_co[vm[ml_prev->v]],
1667  orig_mvert_co[i],
1668  orig_mvert_co[vm[ml_next->v]]);
1669  if (face->reversed) {
1670  total_angle_back += angle * ofs * ofs;
1671  }
1672  else {
1673  total_angle += angle * ofs * ofs;
1674  }
1675  }
1676  else {
1677  if (face->reversed) {
1678  total_angle_back++;
1679  }
1680  else {
1681  total_angle++;
1682  }
1683  }
1684  mul_v3_v3fl(face_nor, poly_nors[face->index], angle * ofs);
1685  if (face->reversed) {
1686  add_v3_v3(nor_back, face_nor);
1687  has_back = true;
1688  }
1689  else {
1690  add_v3_v3(nor, face_nor);
1691  has_front = true;
1692  }
1693  }
1694  }
1695  if ((cycle && k == 0) || (!cycle && k + 3 >= g->edges_len)) {
1696  first_edge = edge;
1697  }
1698  }
1699  }
1700 
1701  /* Set normal length with selected method. */
1702  if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) {
1703  if (has_front) {
1704  float length_sq = len_squared_v3(nor);
1705  if (LIKELY(length_sq > FLT_EPSILON)) {
1706  mul_v3_fl(nor, total_angle / length_sq);
1707  }
1708  }
1709  if (has_back) {
1710  float length_sq = len_squared_v3(nor_back);
1711  if (LIKELY(length_sq > FLT_EPSILON)) {
1712  mul_v3_fl(nor_back, total_angle_back / length_sq);
1713  }
1714  if (!has_front) {
1715  copy_v3_v3(nor, nor_back);
1716  }
1717  }
1718  if (has_front && has_back) {
1719  float nor_length = len_v3(nor);
1720  float nor_back_length = len_v3(nor_back);
1721  float q = dot_v3v3(nor, nor_back);
1722  if (LIKELY(fabsf(q) > FLT_EPSILON)) {
1723  q /= nor_length * nor_back_length;
1724  }
1725  float d = 1.0f - q * q;
1726  if (LIKELY(d > FLT_EPSILON)) {
1727  d = 1.0f / d;
1728  if (LIKELY(nor_length > FLT_EPSILON)) {
1729  mul_v3_fl(nor, (1 - nor_back_length * q / nor_length) * d);
1730  }
1731  if (LIKELY(nor_back_length > FLT_EPSILON)) {
1732  mul_v3_fl(nor_back, (1 - nor_length * q / nor_back_length) * d);
1733  }
1734  add_v3_v3(nor, nor_back);
1735  }
1736  else {
1737  mul_v3_fl(nor, 0.5f);
1738  mul_v3_fl(nor_back, 0.5f);
1739  add_v3_v3(nor, nor_back);
1740  }
1741  }
1742  }
1743  else {
1744  if (has_front && total_angle > FLT_EPSILON) {
1745  mul_v3_fl(nor, 1.0f / total_angle);
1746  }
1747  if (has_back && total_angle_back > FLT_EPSILON) {
1748  mul_v3_fl(nor_back, 1.0f / total_angle_back);
1749  add_v3_v3(nor, nor_back);
1750  if (has_front && total_angle > FLT_EPSILON) {
1751  mul_v3_fl(nor, 0.5f);
1752  }
1753  }
1754  }
1755  /* Set move_nor for boundary fix. */
1756  if (!disable_boundary_fix && g->edges_len > 2) {
1757  approximate_free_direction = true;
1758  }
1759  else {
1760  disable_boundary_fix = true;
1761  }
1762  }
1763  if (approximate_free_direction) {
1764  /* Set move_nor for boundary fix. */
1765  NewEdgeRef **edge_ptr = g->edges + 1;
1766  float tmp[3];
1767  int k;
1768  for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) {
1769  MEdge *e = orig_medge + (*edge_ptr)->old_edge;
1770  sub_v3_v3v3(tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]);
1771  add_v3_v3(move_nor, tmp);
1772  }
1773  if (k == 1) {
1774  disable_boundary_fix = true;
1775  }
1776  else {
1777  disable_boundary_fix = normalize_v3(move_nor) == 0.0f;
1778  }
1779  }
1780  /* Fix boundary verts. */
1781  if (!disable_boundary_fix) {
1782  /* Constraint normal, nor * constr_nor == 0 after this fix. */
1783  float constr_nor[3];
1784  MEdge *e0_edge = orig_medge + g->edges[0]->old_edge;
1785  MEdge *e1_edge = orig_medge + g->edges[g->edges_len - 1]->old_edge;
1786  float e0[3];
1787  float e1[3];
1788  sub_v3_v3v3(e0,
1789  orig_mvert_co[vm[e0_edge->v1] == i ? e0_edge->v2 : e0_edge->v1],
1790  orig_mvert_co[i]);
1791  sub_v3_v3v3(e1,
1792  orig_mvert_co[vm[e1_edge->v1] == i ? e1_edge->v2 : e1_edge->v1],
1793  orig_mvert_co[i]);
1794  if (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) {
1795  cross_v3_v3v3(constr_nor, e0, e1);
1796  normalize_v3(constr_nor);
1797  }
1798  else {
1799  BLI_assert(smd->nonmanifold_boundary_mode ==
1801  float f0[3];
1802  float f1[3];
1803  if (g->edges[0]->faces[0]->reversed) {
1804  negate_v3_v3(f0, poly_nors[g->edges[0]->faces[0]->index]);
1805  }
1806  else {
1807  copy_v3_v3(f0, poly_nors[g->edges[0]->faces[0]->index]);
1808  }
1809  if (g->edges[g->edges_len - 1]->faces[0]->reversed) {
1810  negate_v3_v3(f1, poly_nors[g->edges[g->edges_len - 1]->faces[0]->index]);
1811  }
1812  else {
1813  copy_v3_v3(f1, poly_nors[g->edges[g->edges_len - 1]->faces[0]->index]);
1814  }
1815  float n0[3];
1816  float n1[3];
1817  cross_v3_v3v3(n0, e0, f0);
1818  cross_v3_v3v3(n1, f1, e1);
1819  normalize_v3(n0);
1820  normalize_v3(n1);
1821  add_v3_v3v3(constr_nor, n0, n1);
1822  normalize_v3(constr_nor);
1823  }
1824  float d = dot_v3v3(constr_nor, move_nor);
1825  /* Only allow the thickness to increase about 10 times. */
1826  if (fabsf(d) > 0.1f) {
1827  mul_v3_fl(move_nor, dot_v3v3(constr_nor, nor) / d);
1828  sub_v3_v3(nor, move_nor);
1829  }
1830  }
1831  float scalar_vgroup = 1;
1832  /* Use vertex group. */
1833  if (dvert && !do_flat_faces) {
1834  MDeformVert *dv = &dvert[i];
1835  if (defgrp_invert) {
1836  scalar_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
1837  }
1838  else {
1839  scalar_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
1840  }
1841  scalar_vgroup = offset_fac_vg + (scalar_vgroup * offset_fac_vg_inv);
1842  }
1843  /* Do clamping. */
1844  if (do_clamp) {
1845  if (do_angle_clamp) {
1846  if (g->edges_len > 2) {
1847  float min_length = 0;
1848  float angle = 0.5f * M_PI;
1849  uint k = 0;
1850  for (NewEdgeRef **p = g->edges; k < g->edges_len; k++, p++) {
1851  float length = orig_edge_lengths[(*p)->old_edge];
1852  float e_ang = (*p)->angle;
1853  if (e_ang > angle) {
1854  angle = e_ang;
1855  }
1856  if (length < min_length || k == 0) {
1857  min_length = length;
1858  }
1859  }
1860  float cos_ang = cosf(angle * 0.5f);
1861  if (cos_ang > 0) {
1862  float max_off = min_length * 0.5f / cos_ang;
1863  if (max_off < offset * 0.5f) {
1864  scalar_vgroup *= max_off / offset * 2;
1865  }
1866  }
1867  }
1868  }
1869  else {
1870  float min_length = 0;
1871  uint k = 0;
1872  for (NewEdgeRef **p = g->edges; k < g->edges_len; k++, p++) {
1873  float length = orig_edge_lengths[(*p)->old_edge];
1874  if (length < min_length || k == 0) {
1875  min_length = length;
1876  }
1877  }
1878  if (min_length < offset) {
1879  scalar_vgroup *= min_length / offset;
1880  }
1881  }
1882  }
1883  mul_v3_fl(nor, scalar_vgroup);
1884  add_v3_v3v3(g->co, nor, orig_mvert_co[i]);
1885  }
1886  else {
1887  copy_v3_v3(g->co, orig_mvert_co[i]);
1888  }
1889  }
1890  }
1891  }
1892 
1893  if (do_flat_faces) {
1894  MEM_freeN(face_weight);
1895  }
1896  }
1897 
1898  MEM_freeN(orig_mvert_co);
1899  if (null_faces) {
1900  MEM_freeN(null_faces);
1901  }
1902 
1903  /* TODO: create vertdata for intersection fixes (intersection fixing per topology region). */
1904 
1905  /* Correction for adjacent one sided groups around a vert to
1906  * prevent edge duplicates and null polys. */
1907  uint(*singularity_edges)[2] = NULL;
1908  uint totsingularity = 0;
1909  if (has_singularities) {
1910  has_singularities = false;
1911  uint i = 0;
1912  uint singularity_edges_len = 1;
1913  singularity_edges = MEM_malloc_arrayN(
1914  singularity_edges_len, sizeof(*singularity_edges), "singularity_edges in solidify");
1915  for (NewEdgeRef ***new_edges = orig_edge_data_arr; i < edges_num; i++, new_edges++) {
1916  if (*new_edges && (do_shell || edge_adj_faces_len[i] == 1) && (**new_edges)->old_edge == i) {
1917  for (NewEdgeRef **l = *new_edges; *l; l++) {
1918  if ((*l)->link_edge_groups[0]->is_singularity &&
1919  (*l)->link_edge_groups[1]->is_singularity) {
1920  const uint v1 = (*l)->link_edge_groups[0]->new_vert;
1921  const uint v2 = (*l)->link_edge_groups[1]->new_vert;
1922  bool exists_already = false;
1923  uint j = 0;
1924  for (uint(*p)[2] = singularity_edges; j < totsingularity; p++, j++) {
1925  if (((*p)[0] == v1 && (*p)[1] == v2) || ((*p)[0] == v2 && (*p)[1] == v1)) {
1926  exists_already = true;
1927  break;
1928  }
1929  }
1930  if (!exists_already) {
1931  has_singularities = true;
1932  if (singularity_edges_len <= totsingularity) {
1933  singularity_edges_len = totsingularity + 1;
1934  singularity_edges = MEM_reallocN_id(singularity_edges,
1935  singularity_edges_len *
1936  sizeof(*singularity_edges),
1937  "singularity_edges in solidify");
1938  }
1939  singularity_edges[totsingularity][0] = v1;
1940  singularity_edges[totsingularity][1] = v2;
1941  totsingularity++;
1942  if (edge_adj_faces_len[i] == 1 && do_rim) {
1943  new_loops_num -= 2;
1944  new_polys_num--;
1945  }
1946  }
1947  else {
1948  new_edges_num--;
1949  }
1950  }
1951  }
1952  }
1953  }
1954  }
1955 
1956  /* Create Mesh *result with proper capacity. */
1958  (int)(new_verts_num),
1959  (int)(new_edges_num),
1960  0,
1961  (int)(new_loops_num),
1962  (int)(new_polys_num));
1963 
1964  mpoly = result->mpoly;
1965  mloop = result->mloop;
1966  medge = result->medge;
1967  mvert = result->mvert;
1968 
1969  int *origindex_edge = CustomData_get_layer(&result->edata, CD_ORIGINDEX);
1970  int *origindex_poly = CustomData_get_layer(&result->pdata, CD_ORIGINDEX);
1971 
1972  if (bevel_convex != 0.0f || (result->cd_flag & ME_CDFLAG_VERT_BWEIGHT) != 0) {
1973  /* make sure bweight is enabled */
1974  result->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT;
1975  }
1976 
1977  /* Checks that result has dvert data. */
1978  if (shell_defgrp_index != -1 || rim_defgrp_index != -1) {
1980  /* If no vertices were ever added to an object's vgroup, dvert might be NULL. */
1981  if (dvert == NULL) {
1982  /* Add a valid data layer! */
1983  dvert = CustomData_add_layer(
1984  &result->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, result->totvert);
1985  }
1986  result->dvert = dvert;
1987  }
1988 
1989  /* Get vertex crease layer and ensure edge creases are active if vertex creases are found, since
1990  * they will introduce edge creases in the used custom interpolation method. */
1991  const float *vertex_crease = CustomData_get_layer(&mesh->vdata, CD_CREASE);
1992  if (vertex_crease) {
1993  result->cd_flag |= ME_CDFLAG_EDGE_CREASE;
1994  /* delete all vertex creases in the result if a rim is used. */
1995  if (do_rim) {
1996  CustomData_free_layers(&result->vdata, CD_CREASE, result->totvert);
1997  result->cd_flag &= (char)(~ME_CDFLAG_VERT_CREASE);
1998  }
1999  }
2000 
2001  /* Make_new_verts. */
2002  {
2003  gs_ptr = orig_vert_groups_arr;
2004  for (uint i = 0; i < verts_num; i++, gs_ptr++) {
2005  EdgeGroup *gs = *gs_ptr;
2006  if (gs) {
2007  EdgeGroup *g = gs;
2008  for (uint j = 0; g->valid; j++, g++) {
2009  if (g->new_vert != MOD_SOLIDIFY_EMPTY_TAG) {
2010  CustomData_copy_data(&mesh->vdata, &result->vdata, (int)i, (int)g->new_vert, 1);
2011  copy_v3_v3(mvert[g->new_vert].co, g->co);
2012  mvert[g->new_vert].flag = orig_mvert[i].flag;
2013  }
2014  }
2015  }
2016  }
2017  }
2018 
2019  /* Make edges. */
2020  {
2021  uint i = 0;
2022  edge_index += totsingularity;
2023  for (NewEdgeRef ***new_edges = orig_edge_data_arr; i < edges_num; i++, new_edges++) {
2024  if (*new_edges && (do_shell || edge_adj_faces_len[i] == 1) && (**new_edges)->old_edge == i) {
2025  for (NewEdgeRef **l = *new_edges; *l; l++) {
2026  if ((*l)->new_edge != MOD_SOLIDIFY_EMPTY_TAG) {
2027  const uint v1 = (*l)->link_edge_groups[0]->new_vert;
2028  const uint v2 = (*l)->link_edge_groups[1]->new_vert;
2029  uint insert = edge_index;
2030  if (has_singularities && ((*l)->link_edge_groups[0]->is_singularity &&
2031  (*l)->link_edge_groups[1]->is_singularity)) {
2032  uint j = 0;
2033  for (uint(*p)[2] = singularity_edges; j < totsingularity; p++, j++) {
2034  if (((*p)[0] == v1 && (*p)[1] == v2) || ((*p)[0] == v2 && (*p)[1] == v1)) {
2035  insert = j;
2036  break;
2037  }
2038  }
2039  BLI_assert(insert == j);
2040  }
2041  else {
2042  edge_index++;
2043  }
2044  CustomData_copy_data(&mesh->edata, &result->edata, (int)i, (int)insert, 1);
2047  medge[insert].v1 = v1;
2048  medge[insert].v2 = v2;
2049  medge[insert].flag = orig_medge[(*l)->old_edge].flag | ME_EDGEDRAW | ME_EDGERENDER;
2050  medge[insert].crease = orig_medge[(*l)->old_edge].crease;
2051  medge[insert].bweight = orig_medge[(*l)->old_edge].bweight;
2052  if (bevel_convex != 0.0f && (*l)->faces[1] != NULL) {
2053  medge[insert].bweight = (char)clamp_i(
2054  (int)medge[insert].bweight + (int)(((*l)->angle > M_PI + FLT_EPSILON ?
2055  clamp_f(bevel_convex, 0.0f, 1.0f) :
2056  ((*l)->angle < M_PI - FLT_EPSILON ?
2057  clamp_f(bevel_convex, -1.0f, 0.0f) :
2058  0)) *
2059  255),
2060  0,
2061  255);
2062  }
2063  (*l)->new_edge = insert;
2064  }
2065  }
2066  }
2067  }
2068  }
2069  if (singularity_edges) {
2070  MEM_freeN(singularity_edges);
2071  }
2072 
2073  /* DEBUG CODE FOR BUG-FIXING (can not be removed because every bug-fix needs this badly!). */
2074 #if 0
2075  {
2076  /* this code will output the content of orig_vert_groups_arr.
2077  * in orig_vert_groups_arr these conditions must be met for every vertex:
2078  * - new_edge value should have no duplicates
2079  * - every old_edge value should appear twice
2080  * - every group should have at least two members (edges)
2081  * NOTE: that there can be vertices that only have one group. They are called singularities.
2082  * These vertices will only have one side (there is no way of telling apart front
2083  * from back like on a mobius strip)
2084  */
2085 
2086  /* Debug output format:
2087  * <original vertex id>:
2088  * {
2089  * { <old edge id>/<new edge id>, } \
2090  * (tg:<topology group id>)(s:<is split group>,c:<is closed group (before splitting)>)
2091  * }
2092  */
2093  gs_ptr = orig_vert_groups_arr;
2094  for (uint i = 0; i < verts_num; i++, gs_ptr++) {
2095  EdgeGroup *gs = *gs_ptr;
2096  /* check if the vertex is present (may be dissolved because of proximity) */
2097  if (gs) {
2098  printf("%d:\n", i);
2099  for (EdgeGroup *g = gs; g->valid; g++) {
2100  NewEdgeRef **e = g->edges;
2101  for (uint j = 0; j < g->edges_len; j++, e++) {
2102  printf("%u/%d, ", (*e)->old_edge, (int)(*e)->new_edge);
2103  }
2104  printf("(tg:%u)(s:%u,c:%d)\n", g->topo_group, g->split, g->is_orig_closed);
2105  }
2106  }
2107  }
2108  }
2109 #endif
2110 
2111  /* Make boundary edges/faces. */
2112  {
2113  gs_ptr = orig_vert_groups_arr;
2114  mv = orig_mvert;
2115  for (uint i = 0; i < verts_num; i++, gs_ptr++, mv++) {
2116  EdgeGroup *gs = *gs_ptr;
2117  if (gs) {
2118  EdgeGroup *g = gs;
2119  EdgeGroup *g2 = gs;
2120  EdgeGroup *last_g = NULL;
2121  EdgeGroup *first_g = NULL;
2122  char mv_crease = vertex_crease ? (char)(vertex_crease[i] * 255.0f) : 0;
2123  /* Data calculation cache. */
2124  char max_crease;
2125  char last_max_crease = 0;
2126  char first_max_crease = 0;
2127  char max_bweight;
2128  char last_max_bweight = 0;
2129  char first_max_bweight = 0;
2130  short flag;
2131  short last_flag = 0;
2132  short first_flag = 0;
2133  for (uint j = 0; g->valid; g++) {
2134  if ((do_rim && !g->is_orig_closed) || (do_shell && g->split)) {
2135  max_crease = 0;
2136  max_bweight = 0;
2137  flag = 0;
2138 
2139  BLI_assert(g->edges_len >= 2);
2140 
2141  if (g->edges_len == 2) {
2142  max_crease = min_cc(orig_medge[g->edges[0]->old_edge].crease,
2143  orig_medge[g->edges[1]->old_edge].crease);
2144  }
2145  else {
2146  for (uint k = 1; k < g->edges_len - 1; k++) {
2147  ed = orig_medge + g->edges[k]->old_edge;
2148  if (ed->crease > max_crease) {
2149  max_crease = ed->crease;
2150  }
2151  if (g->edges[k]->new_edge != MOD_SOLIDIFY_EMPTY_TAG) {
2152  char bweight = medge[g->edges[k]->new_edge].bweight;
2153  if (bweight > max_bweight) {
2154  max_bweight = bweight;
2155  }
2156  }
2157  flag |= ed->flag;
2158  }
2159  }
2160 
2161  const char bweight_open_edge = min_cc(
2162  orig_medge[g->edges[0]->old_edge].bweight,
2163  orig_medge[g->edges[g->edges_len - 1]->old_edge].bweight);
2164  if (bweight_open_edge > 0) {
2165  max_bweight = min_cc(bweight_open_edge, max_bweight);
2166  }
2167  else {
2168  if (bevel_convex < 0.0f) {
2169  max_bweight = 0;
2170  }
2171  }
2172  if (!first_g) {
2173  first_g = g;
2174  first_max_crease = max_crease;
2175  first_max_bweight = max_bweight;
2176  first_flag = flag;
2177  }
2178  else {
2179  last_g->open_face_edge = edge_index;
2181  &result->edata,
2182  (int)last_g->edges[0]->old_edge,
2183  (int)edge_index,
2184  1);
2185  if (origindex_edge) {
2186  origindex_edge[edge_index] = ORIGINDEX_NONE;
2187  }
2188  medge[edge_index].v1 = last_g->new_vert;
2189  medge[edge_index].v2 = g->new_vert;
2190  medge[edge_index].flag = ME_EDGEDRAW | ME_EDGERENDER |
2191  ((last_flag | flag) & (ME_SEAM | ME_SHARP));
2192  medge[edge_index].crease = max_cc(mv_crease, min_cc(last_max_crease, max_crease));
2193  medge[edge_index++].bweight = max_cc(mv->bweight,
2194  min_cc(last_max_bweight, max_bweight));
2195  }
2196  last_g = g;
2197  last_max_crease = max_crease;
2198  last_max_bweight = max_bweight;
2199  last_flag = flag;
2200  j++;
2201  }
2202  if (!(g + 1)->valid || g->topo_group != (g + 1)->topo_group) {
2203  if (j == 2) {
2204  last_g->open_face_edge = edge_index - 1;
2205  }
2206  if (j > 2) {
2208  &result->edata,
2209  (int)last_g->edges[0]->old_edge,
2210  (int)edge_index,
2211  1);
2212  if (origindex_edge) {
2213  origindex_edge[edge_index] = ORIGINDEX_NONE;
2214  }
2215  last_g->open_face_edge = edge_index;
2216  medge[edge_index].v1 = last_g->new_vert;
2217  medge[edge_index].v2 = first_g->new_vert;
2218  medge[edge_index].flag = ME_EDGEDRAW | ME_EDGERENDER |
2219  ((last_flag | first_flag) & (ME_SEAM | ME_SHARP));
2220  medge[edge_index].crease = max_cc(mv_crease,
2221  min_cc(last_max_crease, first_max_crease));
2222  medge[edge_index++].bweight = max_cc(mv->bweight,
2223  min_cc(last_max_bweight, first_max_bweight));
2224 
2225  /* Loop data. */
2226  int *loops = MEM_malloc_arrayN(j, sizeof(*loops), "loops in solidify");
2227  /* The #mat_nr is from consensus. */
2228  short most_mat_nr = 0;
2229  uint most_mat_nr_face = 0;
2230  uint most_mat_nr_count = 0;
2231  for (short l = 0; l < mat_nrs; l++) {
2232  uint count = 0;
2233  uint face = 0;
2234  uint k = 0;
2235  for (EdgeGroup *g3 = g2; g3->valid && k < j; g3++) {
2236  if ((do_rim && !g3->is_orig_closed) || (do_shell && g3->split)) {
2237  /* Check both far ends in terms of faces of an edge group. */
2238  if (g3->edges[0]->faces[0]->face->mat_nr == l) {
2239  face = g3->edges[0]->faces[0]->index;
2240  count++;
2241  }
2242  NewEdgeRef *le = g3->edges[g3->edges_len - 1];
2243  if (le->faces[1] && le->faces[1]->face->mat_nr == l) {
2244  face = le->faces[1]->index;
2245  count++;
2246  }
2247  else if (!le->faces[1] && le->faces[0]->face->mat_nr == l) {
2248  face = le->faces[0]->index;
2249  count++;
2250  }
2251  k++;
2252  }
2253  }
2254  if (count > most_mat_nr_count) {
2255  most_mat_nr = l;
2256  most_mat_nr_face = face;
2257  most_mat_nr_count = count;
2258  }
2259  }
2261  &mesh->pdata, &result->pdata, (int)most_mat_nr_face, (int)poly_index, 1);
2262  if (origindex_poly) {
2263  origindex_poly[poly_index] = ORIGINDEX_NONE;
2264  }
2265  mpoly[poly_index].loopstart = (int)loop_index;
2266  mpoly[poly_index].totloop = (int)j;
2267  mpoly[poly_index].mat_nr = most_mat_nr +
2268  (g->is_orig_closed || !do_rim ? 0 : mat_ofs_rim);
2269  CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max);
2270  mpoly[poly_index].flag = orig_mpoly[most_mat_nr_face].flag;
2271  poly_index++;
2272 
2273  for (uint k = 0; g2->valid && k < j; g2++) {
2274  if ((do_rim && !g2->is_orig_closed) || (do_shell && g2->split)) {
2275  MPoly *face = g2->edges[0]->faces[0]->face;
2276  ml = orig_mloop + face->loopstart;
2277  for (int l = 0; l < face->totloop; l++, ml++) {
2278  if (vm[ml->v] == i) {
2279  loops[k] = face->loopstart + l;
2280  break;
2281  }
2282  }
2283  k++;
2284  }
2285  }
2286 
2287  if (!do_flip) {
2288  for (uint k = 0; k < j; k++) {
2289  CustomData_copy_data(&mesh->ldata, &result->ldata, loops[k], (int)loop_index, 1);
2290  mloop[loop_index].v = medge[edge_index - j + k].v1;
2291  mloop[loop_index++].e = edge_index - j + k;
2292  }
2293  }
2294  else {
2295  for (uint k = 1; k <= j; k++) {
2297  &mesh->ldata, &result->ldata, loops[j - k], (int)loop_index, 1);
2298  mloop[loop_index].v = medge[edge_index - k].v2;
2299  mloop[loop_index++].e = edge_index - k;
2300  }
2301  }
2302  MEM_freeN(loops);
2303  }
2304  /* Reset everything for the next poly. */
2305  j = 0;
2306  last_g = NULL;
2307  first_g = NULL;
2308  last_max_crease = 0;
2309  first_max_crease = 0;
2310  last_max_bweight = 0;
2311  first_max_bweight = 0;
2312  last_flag = 0;
2313  first_flag = 0;
2314  }
2315  }
2316  }
2317  }
2318  }
2319 
2320  /* Make boundary faces. */
2321  if (do_rim) {
2322  for (uint i = 0; i < edges_num; i++) {
2323  if (edge_adj_faces_len[i] == 1 && orig_edge_data_arr[i] &&
2324  (*orig_edge_data_arr[i])->old_edge == i) {
2325  NewEdgeRef **new_edges = orig_edge_data_arr[i];
2326 
2327  NewEdgeRef *edge1 = new_edges[0];
2328  NewEdgeRef *edge2 = new_edges[1];
2329  const bool v1_singularity = edge1->link_edge_groups[0]->is_singularity &&
2330  edge2->link_edge_groups[0]->is_singularity;
2331  const bool v2_singularity = edge1->link_edge_groups[1]->is_singularity &&
2332  edge2->link_edge_groups[1]->is_singularity;
2333  if (v1_singularity && v2_singularity) {
2334  continue;
2335  }
2336 
2337  MPoly *face = (*new_edges)->faces[0]->face;
2339  &mesh->pdata, &result->pdata, (int)(*new_edges)->faces[0]->index, (int)poly_index, 1);
2340  mpoly[poly_index].loopstart = (int)loop_index;
2341  mpoly[poly_index].totloop = 4 - (int)(v1_singularity || v2_singularity);
2342  mpoly[poly_index].mat_nr = face->mat_nr + mat_ofs_rim;
2343  CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max);
2344  mpoly[poly_index].flag = face->flag;
2345  poly_index++;
2346 
2347  int loop1 = -1;
2348  int loop2 = -1;
2349  ml = orig_mloop + face->loopstart;
2350  const uint old_v1 = vm[orig_medge[edge1->old_edge].v1];
2351  const uint old_v2 = vm[orig_medge[edge1->old_edge].v2];
2352  for (uint j = 0; j < face->totloop; j++, ml++) {
2353  if (vm[ml->v] == old_v1) {
2354  loop1 = face->loopstart + (int)j;
2355  }
2356  else if (vm[ml->v] == old_v2) {
2357  loop2 = face->loopstart + (int)j;
2358  }
2359  }
2360  BLI_assert(loop1 != -1 && loop2 != -1);
2361  MEdge *open_face_edge;
2362  uint open_face_edge_index;
2363  if (!do_flip) {
2364  if (rim_defgrp_index != -1) {
2365  BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v1], rim_defgrp_index)
2366  ->weight = 1.0f;
2367  }
2368  CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1);
2369  mloop[loop_index].v = medge[edge1->new_edge].v1;
2370  mloop[loop_index++].e = edge1->new_edge;
2371 
2372  if (!v2_singularity) {
2373  open_face_edge_index = edge1->link_edge_groups[1]->open_face_edge;
2374  if (rim_defgrp_index != -1) {
2375  BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v2], rim_defgrp_index)
2376  ->weight = 1.0f;
2377  }
2378  CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1);
2379  mloop[loop_index].v = medge[edge1->new_edge].v2;
2380  open_face_edge = medge + open_face_edge_index;
2381  if (ELEM(medge[edge2->new_edge].v2, open_face_edge->v1, open_face_edge->v2)) {
2382  mloop[loop_index++].e = open_face_edge_index;
2383  }
2384  else {
2385  mloop[loop_index++].e = edge2->link_edge_groups[1]->open_face_edge;
2386  }
2387  }
2388 
2389  if (rim_defgrp_index != -1) {
2390  BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v2], rim_defgrp_index)
2391  ->weight = 1.0f;
2392  }
2393  CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1);
2394  mloop[loop_index].v = medge[edge2->new_edge].v2;
2395  mloop[loop_index++].e = edge2->new_edge;
2396 
2397  if (!v1_singularity) {
2398  open_face_edge_index = edge2->link_edge_groups[0]->open_face_edge;
2399  if (rim_defgrp_index != -1) {
2400  BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v1], rim_defgrp_index)
2401  ->weight = 1.0f;
2402  }
2403  CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1);
2404  mloop[loop_index].v = medge[edge2->new_edge].v1;
2405  open_face_edge = medge + open_face_edge_index;
2406  if (ELEM(medge[edge1->new_edge].v1, open_face_edge->v1, open_face_edge->v2)) {
2407  mloop[loop_index++].e = open_face_edge_index;
2408  }
2409  else {
2410  mloop[loop_index++].e = edge1->link_edge_groups[0]->open_face_edge;
2411  }
2412  }
2413  }
2414  else {
2415  if (!v1_singularity) {
2416  open_face_edge_index = edge1->link_edge_groups[0]->open_face_edge;
2417  if (rim_defgrp_index != -1) {
2418  BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v1], rim_defgrp_index)
2419  ->weight = 1.0f;
2420  }
2421  CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1);
2422  mloop[loop_index].v = medge[edge1->new_edge].v1;
2423  open_face_edge = medge + open_face_edge_index;
2424  if (ELEM(medge[edge2->new_edge].v1, open_face_edge->v1, open_face_edge->v2)) {
2425  mloop[loop_index++].e = open_face_edge_index;
2426  }
2427  else {
2428  mloop[loop_index++].e = edge2->link_edge_groups[0]->open_face_edge;
2429  }
2430  }
2431 
2432  if (rim_defgrp_index != -1) {
2433  BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v1], rim_defgrp_index)
2434  ->weight = 1.0f;
2435  }
2436  CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1);
2437  mloop[loop_index].v = medge[edge2->new_edge].v1;
2438  mloop[loop_index++].e = edge2->new_edge;
2439 
2440  if (!v2_singularity) {
2441  open_face_edge_index = edge2->link_edge_groups[1]->open_face_edge;
2442  if (rim_defgrp_index != -1) {
2443  BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v2], rim_defgrp_index)
2444  ->weight = 1.0f;
2445  }
2446  CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1);
2447  mloop[loop_index].v = medge[edge2->new_edge].v2;
2448  open_face_edge = medge + open_face_edge_index;
2449  if (ELEM(medge[edge1->new_edge].v2, open_face_edge->v1, open_face_edge->v2)) {
2450  mloop[loop_index++].e = open_face_edge_index;
2451  }
2452  else {
2453  mloop[loop_index++].e = edge1->link_edge_groups[1]->open_face_edge;
2454  }
2455  }
2456 
2457  if (rim_defgrp_index != -1) {
2458  BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v2], rim_defgrp_index)
2459  ->weight = 1.0f;
2460  }
2461  CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1);
2462  mloop[loop_index].v = medge[edge1->new_edge].v2;
2463  mloop[loop_index++].e = edge1->new_edge;
2464  }
2465  }
2466  }
2467  }
2468 
2469  /* Make faces. */
2470  if (do_shell) {
2471  NewFaceRef *fr = face_sides_arr;
2472  uint *face_loops = MEM_malloc_arrayN(
2473  largest_ngon * 2, sizeof(*face_loops), "face_loops in solidify");
2474  uint *face_verts = MEM_malloc_arrayN(
2475  largest_ngon * 2, sizeof(*face_verts), "face_verts in solidify");
2476  uint *face_edges = MEM_malloc_arrayN(
2477  largest_ngon * 2, sizeof(*face_edges), "face_edges in solidify");
2478  for (uint i = 0; i < polys_num * 2; i++, fr++) {
2479  const uint loopstart = (uint)fr->face->loopstart;
2480  uint totloop = (uint)fr->face->totloop;
2481  uint valid_edges = 0;
2482  uint k = 0;
2483  while (totloop > 0 && (!fr->link_edges[totloop - 1] ||
2484  fr->link_edges[totloop - 1]->new_edge == MOD_SOLIDIFY_EMPTY_TAG)) {
2485  totloop--;
2486  }
2487  if (totloop > 0) {
2488  NewEdgeRef *prior_edge = fr->link_edges[totloop - 1];
2489  uint prior_flip = (uint)(vm[orig_medge[prior_edge->old_edge].v1] ==
2490  vm[orig_mloop[loopstart + (totloop - 1)].v]);
2491  for (uint j = 0; j < totloop; j++) {
2492  NewEdgeRef *new_edge = fr->link_edges[j];
2493  if (new_edge && new_edge->new_edge != MOD_SOLIDIFY_EMPTY_TAG) {
2494  valid_edges++;
2495  const uint flip = (uint)(vm[orig_medge[new_edge->old_edge].v2] ==
2496  vm[orig_mloop[loopstart + j].v]);
2497  BLI_assert(flip ||
2498  vm[orig_medge[new_edge->old_edge].v1] == vm[orig_mloop[loopstart + j].v]);
2499  /* The vert that's in the current loop. */
2500  const uint new_v1 = new_edge->link_edge_groups[flip]->new_vert;
2501  /* The vert that's in the next loop. */
2502  const uint new_v2 = new_edge->link_edge_groups[1 - flip]->new_vert;
2503  if (k == 0 || face_verts[k - 1] != new_v1) {
2504  face_loops[k] = loopstart + j;
2505  if (fr->reversed) {
2506  face_edges[k] = prior_edge->link_edge_groups[prior_flip]->open_face_edge;
2507  }
2508  else {
2509  face_edges[k] = new_edge->link_edge_groups[flip]->open_face_edge;
2510  }
2511  BLI_assert(k == 0 || medge[face_edges[k]].v2 == face_verts[k - 1] ||
2512  medge[face_edges[k]].v1 == face_verts[k - 1]);
2513  BLI_assert(face_edges[k] == MOD_SOLIDIFY_EMPTY_TAG ||
2514  medge[face_edges[k]].v2 == new_v1 || medge[face_edges[k]].v1 == new_v1);
2515  face_verts[k++] = new_v1;
2516  }
2517  prior_edge = new_edge;
2518  prior_flip = 1 - flip;
2519  if (j < totloop - 1 || face_verts[0] != new_v2) {
2520  face_loops[k] = loopstart + (j + 1) % totloop;
2521  face_edges[k] = new_edge->new_edge;
2522  face_verts[k++] = new_v2;
2523  }
2524  else {
2525  face_edges[0] = new_edge->new_edge;
2526  }
2527  }
2528  }
2529  if (k > 2 && valid_edges > 2) {
2530  CustomData_copy_data(&mesh->pdata, &result->pdata, (int)(i / 2), (int)poly_index, 1);
2531  mpoly[poly_index].loopstart = (int)loop_index;
2532  mpoly[poly_index].totloop = (int)k;
2533  mpoly[poly_index].mat_nr = fr->face->mat_nr + (fr->reversed != do_flip ? mat_ofs : 0);
2534  CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max);
2535  mpoly[poly_index].flag = fr->face->flag;
2536  if (fr->reversed != do_flip) {
2537  for (int l = (int)k - 1; l >= 0; l--) {
2538  if (shell_defgrp_index != -1) {
2539  BKE_defvert_ensure_index(&result->dvert[face_verts[l]], shell_defgrp_index)
2540  ->weight = 1.0f;
2541  }
2543  &mesh->ldata, &result->ldata, (int)face_loops[l], (int)loop_index, 1);
2544  mloop[loop_index].v = face_verts[l];
2545  mloop[loop_index++].e = face_edges[l];
2546  }
2547  }
2548  else {
2549  uint l = k - 1;
2550  for (uint next_l = 0; next_l < k; next_l++) {
2552  &mesh->ldata, &result->ldata, (int)face_loops[l], (int)loop_index, 1);
2553  mloop[loop_index].v = face_verts[l];
2554  mloop[loop_index++].e = face_edges[next_l];
2555  l = next_l;
2556  }
2557  }
2558  poly_index++;
2559  }
2560  }
2561  }
2562  MEM_freeN(face_loops);
2563  MEM_freeN(face_verts);
2564  MEM_freeN(face_edges);
2565  }
2566  if (edge_index != new_edges_num) {
2567  BKE_modifier_set_error(ctx->object,
2568  md,
2569  "Internal Error: edges array wrong size: %u instead of %u",
2570  new_edges_num,
2571  edge_index);
2572  }
2573  if (poly_index != new_polys_num) {
2574  BKE_modifier_set_error(ctx->object,
2575  md,
2576  "Internal Error: polys array wrong size: %u instead of %u",
2577  new_polys_num,
2578  poly_index);
2579  }
2580  if (loop_index != new_loops_num) {
2581  BKE_modifier_set_error(ctx->object,
2582  md,
2583  "Internal Error: loops array wrong size: %u instead of %u",
2584  new_loops_num,
2585  loop_index);
2586  }
2587  BLI_assert(edge_index == new_edges_num);
2588  BLI_assert(poly_index == new_polys_num);
2589  BLI_assert(loop_index == new_loops_num);
2590 
2591  /* Free remaining memory */
2592  {
2593  MEM_freeN(vm);
2594  MEM_freeN(edge_adj_faces_len);
2595  uint i = 0;
2596  for (EdgeGroup **p = orig_vert_groups_arr; i < verts_num; i++, p++) {
2597  if (*p) {
2598  for (EdgeGroup *eg = *p; eg->valid; eg++) {
2599  MEM_freeN(eg->edges);
2600  }
2601  MEM_freeN(*p);
2602  }
2603  }
2604  MEM_freeN(orig_vert_groups_arr);
2605  i = edges_num;
2606  for (NewEdgeRef ***p = orig_edge_data_arr + (edges_num - 1); i > 0; i--, p--) {
2607  if (*p && (**p)->old_edge == i - 1) {
2608  for (NewEdgeRef **l = *p; *l; l++) {
2609  MEM_freeN(*l);
2610  }
2611  MEM_freeN(*p);
2612  }
2613  }
2614  MEM_freeN(orig_edge_data_arr);
2615  MEM_freeN(orig_edge_lengths);
2616  i = 0;
2617  for (NewFaceRef *p = face_sides_arr; i < polys_num * 2; i++, p++) {
2618  MEM_freeN(p->link_edges);
2619  }
2620  MEM_freeN(face_sides_arr);
2621  MEM_freeN(poly_nors);
2622  }
2623 
2624 #undef MOD_SOLIDIFY_EMPTY_TAG
2625 
2626  return result;
2627 }
2628 
typedef float(TangentPoint)[2]
@ CD_CALLOC
void CustomData_free_layers(struct CustomData *data, int type, int totelem)
Definition: customdata.cc:2904
#define ORIGINDEX_NONE
void * CustomData_get_layer(const struct CustomData *data, int type)
void * CustomData_add_layer(struct CustomData *data, int type, eCDAllocType alloctype, void *layer, int totelem)
Definition: customdata.cc:2776
void * CustomData_duplicate_referenced_layer(struct CustomData *data, int type, int totelem)
Definition: customdata.cc:2976
void CustomData_copy_data(const struct CustomData *source, struct CustomData *dest, int source_index, int dest_index, int count)
support for deformation groups and hooks.
struct MDeformWeight * BKE_defvert_ensure_index(struct MDeformVert *dv, int defgroup)
Definition: deform.c:748
float BKE_defvert_find_weight(const struct MDeformVert *dvert, int defgroup)
Definition: deform.c:704
int BKE_id_defgroup_name_index(const struct ID *id, const char *name)
const float(* BKE_mesh_poly_normals_ensure(const struct Mesh *mesh))[3]
struct Mesh * BKE_mesh_new_nomain_from_template(const struct Mesh *me_src, int verts_len, int edges_len, int tessface_len, int loops_len, int polys_len)
void BKE_modifier_set_error(const struct Object *ob, struct ModifierData *md, const char *format,...) ATTR_PRINTF_FORMAT(3
#define BLI_assert(a)
Definition: BLI_assert.h:46
MINLINE float max_ff(float a, float b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
MINLINE char min_cc(char a, char b)
MINLINE int clamp_i(int value, int min, int max)
#define M_PI
Definition: BLI_math_base.h:20
MINLINE char max_cc(char a, char b)
void zero_m3(float m[3][3])
Definition: math_matrix.c:23
bool invert_m3(float R[3][3])
Definition: math_matrix.c:1171
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
Definition: math_matrix.c:897
MINLINE void swap_v4_v4(float a[4], float b[4])
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float normalize_v3(float r[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_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 cross_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])
float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:361
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
Definition: BLI_sys_types.h:67
#define SWAP(type, a, b)
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
@ CD_MDEFORMVERT
@ CD_ORIGINDEX
@ ME_CDFLAG_VERT_CREASE
@ ME_CDFLAG_EDGE_CREASE
@ ME_CDFLAG_VERT_BWEIGHT
@ ME_CDFLAG_EDGE_BWEIGHT
@ ME_EDGEDRAW
@ ME_SEAM
@ ME_EDGERENDER
@ ME_SHARP
@ MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT
@ MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_ROUND
@ MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE
@ MOD_SOLIDIFY_RIM
@ MOD_SOLIDIFY_FLIP
@ MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP
@ MOD_SOLIDIFY_NONMANIFOLD_FLAT_FACES
@ MOD_SOLIDIFY_VGROUP_INV
@ MOD_SOLIDIFY_NOSHELL
@ MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN
@ MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS
Object is a sort of wrapper for general info.
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
Read Guarded memory(de)allocation.
struct OldEdgeFaceRef OldEdgeFaceRef
Mesh * MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
struct OldVertEdgeRef OldVertEdgeRef
static int comp_float_int_pair(const void *a, const void *b)
static float angle_signed_on_axis_normalized_v3v3_v3(const float n[3], const float ref_n[3], const float axis[3])
struct FaceKeyPair FaceKeyPair
static float clamp_nonzero(const float value, const float epsilon)
#define MOD_SOLIDIFY_EMPTY_TAG
struct NewEdgeRef NewEdgeRef
struct NewFaceRef NewFaceRef
struct EdgeGroup EdgeGroup
static float project_v3_v3(float r[3], const float a[3])
void MOD_get_vgroup(Object *ob, struct Mesh *mesh, const char *name, MDeformVert **dvert, int *defgrp_index)
Definition: MOD_util.c:235
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
#define cosf(x)
Definition: cuda/compat.h:101
int len
Definition: draw_manager.c:108
uint pos
uint nor
int count
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:34
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_calloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:32
void *(* MEM_reallocN_id)(void *vmemh, size_t len, const char *str)
Definition: mallocn.c:29
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
static ulong * next
static char faces[256]
#define acosf(x)
Definition: metal/compat.h:222
#define fabsf(x)
Definition: metal/compat.h:219
#define sqrtf(x)
Definition: metal/compat.h:243
static unsigned a[3]
Definition: RandGen.cpp:78
Insertion insert(const float3 &point_prev, const float3 &handle_prev, const float3 &handle_next, const float3 &point_next, float parameter)
Definition: curve_bezier.cc:61
void split(const std::string &s, const char delim, std::vector< std::string > &tokens)
Definition: abc_util.cc:92
vec_base< T, 3 > cross(const vec_base< T, 3 > &a, const vec_base< T, 3 > &b)
T length(const vec_base< T, Size > &a)
static MEdge new_edge(const int v1, const int v2)
static double epsilon
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
static const pxr::TfToken g("g", pxr::TfToken::Immortal)
NewEdgeRef ** edges
unsigned int v1
unsigned int v2
unsigned int e
unsigned int v
short mat_nr
float co[3]
struct MEdge * medge
CustomData vdata
struct MVert * mvert
int totedge
int totvert
struct MLoop * mloop
CustomData pdata
int totpoly
CustomData edata
struct MPoly * mpoly
CustomData ldata
struct Object * object
Definition: BKE_modifier.h:141
NewFaceRef * faces[2]
struct EdgeGroup * link_edge_groups[2]
struct NewEdgeRef ** link_edges