Blender V5.0
bmo_connect_nonplanar.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BLI_alloca.h"
12#include "BLI_linklist_stack.h"
13#include "BLI_math_geom.h"
14
15#include "bmesh.hh"
16
17#include "intern/bmesh_operators_private.hh" /* own include */
18
19#define EDGE_OUT (1 << 0)
20#define FACE_OUT (1 << 1)
21
25static float bm_face_subset_calc_planar(BMLoop *l_first, BMLoop *l_last, const float no[3])
26{
27 float axis_mat[3][3];
28 float z_prev;
29 float delta_z = 0.0f;
30
31 /* Newell's Method */
32 BMLoop *l_iter = l_first;
33 BMLoop *l_term = l_last->next;
34
35 axis_dominant_v3_to_m3(axis_mat, no);
36
37 z_prev = dot_m3_v3_row_z(axis_mat, l_last->v->co);
38 do {
39 const float z_curr = dot_m3_v3_row_z(axis_mat, l_iter->v->co);
40 delta_z += fabsf(z_curr - z_prev);
41 z_prev = z_curr;
42 } while ((l_iter = l_iter->next) != l_term);
43
44 return delta_z;
45}
46
47static bool bm_face_split_find(BMesh *bm, BMFace *f, BMLoop *l_pair[2], float *r_angle_cos)
48{
49 BMLoop *l_iter, *l_first;
50 BMLoop **l_arr = BLI_array_alloca(l_arr, f->len);
51 const uint f_len = f->len;
52 uint i_a, i_b;
53 bool found = false;
54
55 /* angle finding */
56 float err_best = FLT_MAX;
57 float angle_best_cos = -FLT_MAX;
58
59 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
60 i_a = 0;
61 do {
62 l_arr[i_a++] = l_iter;
63 } while ((l_iter = l_iter->next) != l_first);
64
65 /* now for the big search, O(N^2), however faces normally aren't so large */
66 for (i_a = 0; i_a < f_len; i_a++) {
67 BMLoop *l_a = l_arr[i_a];
68 for (i_b = i_a + 2; i_b < f_len; i_b++) {
69 BMLoop *l_b = l_arr[i_b];
70 /* check these are not touching
71 * (we could be smarter here) */
72 if (!BM_loop_is_adjacent(l_a, l_b)) {
73 /* first calculate normals */
74 float no_a[3], no_b[3];
75
76 if (BM_face_calc_normal_subset(l_a, l_b, no_a) != 0.0f &&
77 BM_face_calc_normal_subset(l_b, l_a, no_b) != 0.0f)
78 {
79 const float err_a = bm_face_subset_calc_planar(l_a, l_b, no_a);
80 const float err_b = bm_face_subset_calc_planar(l_b, l_a, no_b);
81 const float err_test = err_a + err_b;
82
83 if (err_test < err_best) {
84 /* check we're legal (we could batch this) */
85 BMLoop *l_split[2] = {l_a, l_b};
86 BM_face_splits_check_legal(bm, f, &l_split, 1);
87 if (l_split[0]) {
88 err_best = err_test;
89 l_pair[0] = l_a;
90 l_pair[1] = l_b;
91
92 angle_best_cos = dot_v3v3(no_a, no_b);
93 found = true;
94 }
95 }
96 }
97 }
98 }
99 }
100
101 *r_angle_cos = angle_best_cos;
102
103 return found;
104}
105
107 BMFace *f,
108 BMFace *r_f_pair[2],
109 const float angle_limit_cos)
110{
111 BMLoop *l_pair[2];
112 float angle_cos;
113
114 if (bm_face_split_find(bm, f, l_pair, &angle_cos) && (angle_cos < angle_limit_cos)) {
115 BMFace *f_new;
116 BMLoop *l_new;
117
118 f_new = BM_face_split(bm, f, l_pair[0], l_pair[1], &l_new, nullptr, false);
119 if (f_new) {
120 r_f_pair[0] = f;
121 r_f_pair[1] = f_new;
122
126 return true;
127 }
128 }
129
130 return false;
131}
132
134{
135 BMOIter siter;
136 BMFace *f;
137 bool changed = false;
138 BLI_LINKSTACK_DECLARE(fstack, BMFace *);
139
140 const float angle_limit_cos = cosf(BMO_slot_float_get(op->slots_in, "angle_limit"));
141
142 BLI_LINKSTACK_INIT(fstack);
143
144 BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) {
145 if (f->len > 3) {
146 BLI_LINKSTACK_PUSH(fstack, f);
147 }
148 }
149
150 while ((f = BLI_LINKSTACK_POP(fstack))) {
151 BMFace *f_pair[2];
152 if (bm_face_split_by_angle(bm, f, f_pair, angle_limit_cos)) {
153 int j;
154 for (j = 0; j < 2; j++) {
155 BM_face_normal_update(f_pair[j]);
156 if (f_pair[j]->len > 3) {
157 BLI_LINKSTACK_PUSH(fstack, f_pair[j]);
158 }
159 }
160 changed = true;
161 }
162 }
163
164 BLI_LINKSTACK_FREE(fstack);
165
166 if (changed) {
169 }
170}
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float dot_m3_v3_row_z(const float M[3][3], const float a[3]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
#define BM_FACE_FIRST_LOOP(p)
BMesh * bm
BMFace * BM_face_split(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, const bool no_double)
Face Split.
#define BM_FACE
#define BM_EDGE
#define BMO_edge_flag_enable(bm, e, oflag)
float BMO_slot_float_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
#define BMO_face_flag_enable(bm, e, oflag)
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
void BM_face_splits_check_legal(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len)
void BM_face_normal_update(BMFace *f)
float BM_face_calc_normal_subset(const BMLoop *l_first, const BMLoop *l_last, float r_no[3])
BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
#define FACE_OUT
Definition bmo_bridge.cc:28
#define EDGE_OUT
Definition bmo_bridge.cc:27
static bool bm_face_split_find(BMesh *bm, BMFace *f, BMLoop *l_pair[2], float *r_angle_cos)
void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op)
static float bm_face_subset_calc_planar(BMLoop *l_first, BMLoop *l_last, const float no[3])
static bool bm_face_split_by_angle(BMesh *bm, BMFace *f, BMFace *r_f_pair[2], const float angle_limit_cos)
#define fabsf
#define cosf
#define FLT_MAX
Definition stdcycles.h:14
struct BMVert * v
struct BMEdge * e
struct BMLoop * next
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
float co[3]
uint len