Welcome to mirror list, hosted at ThFree Co, Russian Federation.

bmo_join_triangles.c « operators « bmesh « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: c3e96a9b08e33689a5e5bf577dabdb42bed794b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/** \file
 * \ingroup bmesh
 *
 * Convert triangle to quads.
 *
 * TODO
 * - convert triangles to any sided faces, not just quads.
 */

#include "MEM_guardedalloc.h"

#include "DNA_meshdata_types.h"

#include "BLI_math.h"
#include "BLI_sort_utils.h"

#include "BKE_customdata.h"

#include "bmesh.h"

#include "intern/bmesh_operators_private.h" /* own include */

/* assumes edges are validated before reaching this poin */
static float quad_calc_error(const float v1[3],
                             const float v2[3],
                             const float v3[3],
                             const float v4[3])
{
  /* Gives a 'weight' to a pair of triangles that join an edge
   * to decide how good a join they would make. */
  /* Note: this is more complicated than it needs to be and should be cleaned up. */
  float error = 0.0f;

  /* Normal difference */
  {
    float n1[3], n2[3];
    float angle_a, angle_b;
    float diff;

    normal_tri_v3(n1, v1, v2, v3);
    normal_tri_v3(n2, v1, v3, v4);
    angle_a = (compare_v3v3(n1, n2, FLT_EPSILON)) ? 0.0f : angle_normalized_v3v3(n1, n2);

    normal_tri_v3(n1, v2, v3, v4);
    normal_tri_v3(n2, v4, v1, v2);
    angle_b = (compare_v3v3(n1, n2, FLT_EPSILON)) ? 0.0f : angle_normalized_v3v3(n1, n2);

    diff = (angle_a + angle_b) / (float)(M_PI * 2);

    error += diff;
  }

  /* Co-linearity */
  {
    float edge_vecs[4][3];
    float diff;

    sub_v3_v3v3(edge_vecs[0], v1, v2);
    sub_v3_v3v3(edge_vecs[1], v2, v3);
    sub_v3_v3v3(edge_vecs[2], v3, v4);
    sub_v3_v3v3(edge_vecs[3], v4, v1);

    normalize_v3(edge_vecs[0]);
    normalize_v3(edge_vecs[1]);
    normalize_v3(edge_vecs[2]);
    normalize_v3(edge_vecs[3]);

    /* a completely skinny face is 'pi' after halving */
    diff = (fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - (float)M_PI_2) +
            fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - (float)M_PI_2) +
            fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - (float)M_PI_2) +
            fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - (float)M_PI_2)) /
           (float)(M_PI * 2);

    error += diff;
  }

  /* Concavity */
  {
    float area_min, area_max, area_a, area_b;
    float diff;

    area_a = area_tri_v3(v1, v2, v3) + area_tri_v3(v1, v3, v4);
    area_b = area_tri_v3(v2, v3, v4) + area_tri_v3(v4, v1, v2);

    area_min = min_ff(area_a, area_b);
    area_max = max_ff(area_a, area_b);

    diff = area_max ? (1.0f - (area_min / area_max)) : 1.0f;

    error += diff;
  }

  return error;
}

static void bm_edge_to_quad_verts(const BMEdge *e, const BMVert *r_v_quad[4])
{
  BLI_assert(e->l->f->len == 3 && e->l->radial_next->f->len == 3);
  BLI_assert(BM_edge_is_manifold(e));
  r_v_quad[0] = e->l->v;
  r_v_quad[1] = e->l->prev->v;
  r_v_quad[2] = e->l->next->v;
  r_v_quad[3] = e->l->radial_next->prev->v;
}

/* cache customdata delimiters */
struct DelimitData_CD {
  int cd_type;
  int cd_size;
  int cd_offset;
  int cd_offset_end;
};

struct DelimitData {
  uint do_seam : 1;
  uint do_sharp : 1;
  uint do_mat : 1;
  uint do_angle_face : 1;
  uint do_angle_shape : 1;

  float angle_face;
  float angle_face__cos;

  float angle_shape;

  struct DelimitData_CD cdata[4];
  int cdata_len;
};

static bool bm_edge_is_contiguous_loop_cd_all(const BMEdge *e,
                                              const struct DelimitData_CD *delimit_data)
{
  int cd_offset;
  for (cd_offset = delimit_data->cd_offset; cd_offset < delimit_data->cd_offset_end;
       cd_offset += delimit_data->cd_size) {
    if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_type, cd_offset) == false) {
      return false;
    }
  }

  return true;
}

static bool bm_edge_delimit_cdata(CustomData *ldata,
                                  CustomDataType type,
                                  struct DelimitData_CD *r_delim_cd)
{
  const int layer_len = CustomData_number_of_layers(ldata, type);
  r_delim_cd->cd_type = type;
  r_delim_cd->cd_size = CustomData_sizeof(r_delim_cd->cd_type);
  r_delim_cd->cd_offset = CustomData_get_n_offset(ldata, type, 0);
  r_delim_cd->cd_offset_end = r_delim_cd->cd_offset + (r_delim_cd->cd_size * layer_len);
  return (r_delim_cd->cd_offset != -1);
}

static float bm_edge_is_delimit(const BMEdge *e, const struct DelimitData *delimit_data)
{
  BMFace *f_a = e->l->f, *f_b = e->l->radial_next->f;
#if 0
  const bool is_contig = BM_edge_is_contiguous(e);
  float angle;
#endif

  if ((delimit_data->do_seam) && (BM_elem_flag_test(e, BM_ELEM_SEAM))) {
    goto fail;
  }

  if ((delimit_data->do_sharp) && (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0)) {
    goto fail;
  }

  if ((delimit_data->do_mat) && (f_a->mat_nr != f_b->mat_nr)) {
    goto fail;
  }

  if (delimit_data->do_angle_face) {
    if (dot_v3v3(f_a->no, f_b->no) < delimit_data->angle_face__cos) {
      goto fail;
    }
  }

  if (delimit_data->do_angle_shape) {
    const BMVert *verts[4];
    bm_edge_to_quad_verts(e, verts);

    /* if we're checking the shape at all, a flipped face is out of the question */
    if (is_quad_flip_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) {
      goto fail;
    }
    else {
      float edge_vecs[4][3];

      sub_v3_v3v3(edge_vecs[0], verts[0]->co, verts[1]->co);
      sub_v3_v3v3(edge_vecs[1], verts[1]->co, verts[2]->co);
      sub_v3_v3v3(edge_vecs[2], verts[2]->co, verts[3]->co);
      sub_v3_v3v3(edge_vecs[3], verts[3]->co, verts[0]->co);

      normalize_v3(edge_vecs[0]);
      normalize_v3(edge_vecs[1]);
      normalize_v3(edge_vecs[2]);
      normalize_v3(edge_vecs[3]);

      if ((fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - (float)M_PI_2) >
           delimit_data->angle_shape) ||
          (fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - (float)M_PI_2) >
           delimit_data->angle_shape) ||
          (fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - (float)M_PI_2) >
           delimit_data->angle_shape) ||
          (fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - (float)M_PI_2) >
           delimit_data->angle_shape)) {
        goto fail;
      }
    }
  }

  if (delimit_data->cdata_len) {
    int i;
    for (i = 0; i < delimit_data->cdata_len; i++) {
      if (!bm_edge_is_contiguous_loop_cd_all(e, &delimit_data->cdata[i])) {
        goto fail;
      }
    }
  }

  return false;

fail:
  return true;
}

#define EDGE_MARK (1 << 0)

#define FACE_OUT (1 << 0)
#define FACE_INPUT (1 << 2)

void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
{
  float angle_face, angle_shape;

  BMIter iter;
  BMOIter siter;
  BMFace *f;
  BMEdge *e;
  /* data: edge-to-join, sort_value: error weight */
  struct SortPtrByFloat *jedges;
  uint i, totedge;
  uint totedge_tag = 0;

  struct DelimitData delimit_data = {0};

  delimit_data.do_seam = BMO_slot_bool_get(op->slots_in, "cmp_seam");
  delimit_data.do_sharp = BMO_slot_bool_get(op->slots_in, "cmp_sharp");
  delimit_data.do_mat = BMO_slot_bool_get(op->slots_in, "cmp_materials");

  angle_face = BMO_slot_float_get(op->slots_in, "angle_face_threshold");
  if (angle_face < DEG2RADF(180.0f)) {
    delimit_data.angle_face = angle_face;
    delimit_data.angle_face__cos = cosf(angle_face);
    delimit_data.do_angle_face = true;
  }
  else {
    delimit_data.do_angle_face = false;
  }

  angle_shape = BMO_slot_float_get(op->slots_in, "angle_shape_threshold");
  if (angle_shape < DEG2RADF(180.0f)) {
    delimit_data.angle_shape = angle_shape;
    delimit_data.do_angle_shape = true;
  }
  else {
    delimit_data.do_angle_shape = false;
  }

  if (BMO_slot_bool_get(op->slots_in, "cmp_uvs") &&
      bm_edge_delimit_cdata(&bm->ldata, CD_MLOOPUV, &delimit_data.cdata[delimit_data.cdata_len])) {
    delimit_data.cdata_len += 1;
  }

  delimit_data.cdata[delimit_data.cdata_len].cd_offset = -1;
  if (BMO_slot_bool_get(op->slots_in, "cmp_vcols") &&
      bm_edge_delimit_cdata(
          &bm->ldata, CD_MLOOPCOL, &delimit_data.cdata[delimit_data.cdata_len])) {
    delimit_data.cdata_len += 1;
  }

  /* flag all edges of all input face */
  BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) {
    if (f->len == 3) {
      BMO_face_flag_enable(bm, f, FACE_INPUT);
    }
  }

  /* flag edges surrounded by 2 flagged triangles */
  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
    BMFace *f_a, *f_b;
    if (BM_edge_face_pair(e, &f_a, &f_b) &&
        (BMO_face_flag_test(bm, f_a, FACE_INPUT) && BMO_face_flag_test(bm, f_b, FACE_INPUT))) {
      if (!bm_edge_is_delimit(e, &delimit_data)) {
        BMO_edge_flag_enable(bm, e, EDGE_MARK);
        totedge_tag++;
      }
    }
  }

  if (totedge_tag == 0) {
    return;
  }

  /* over alloc, some of the edges will be delimited */
  jedges = MEM_mallocN(sizeof(*jedges) * totedge_tag, __func__);

  i = 0;
  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
    const BMVert *verts[4];
    float error;

    if (!BMO_edge_flag_test(bm, e, EDGE_MARK)) {
      continue;
    }

    bm_edge_to_quad_verts(e, verts);

    error = quad_calc_error(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co);

    jedges[i].data = e;
    jedges[i].sort_value = error;
    i++;
  }

  totedge = i;
  qsort(jedges, totedge, sizeof(*jedges), BLI_sortutil_cmp_float);

  for (i = 0; i < totedge; i++) {
    BMLoop *l_a, *l_b;

    e = jedges[i].data;
    l_a = e->l;
    l_b = e->l->radial_next;

    /* check if another edge already claimed this face */
    if ((l_a->f->len == 3) && (l_b->f->len == 3)) {
      BMFace *f_new;
      f_new = BM_faces_join_pair(bm, l_a, l_b, true);
      if (f_new) {
        BMO_face_flag_enable(bm, f_new, FACE_OUT);
      }
    }
  }

  MEM_freeN(jedges);

  BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT);
}