Blender V4.3
bmo_normals.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
11#include "MEM_guardedalloc.h"
12
13#include "BLI_linklist_stack.h"
14#include "BLI_math_vector.h"
15
16#include "bmesh.hh"
17
18#include "intern/bmesh_operators_private.hh" /* own include */
19
20/********* Right-hand faces implementation ****** */
21
22#define FACE_FLAG (1 << 0)
23#define FACE_FLIP (1 << 1)
24#define FACE_TEMP (1 << 2)
25
26static bool bmo_recalc_normal_loop_filter_cb(const BMLoop *l, void * /*user_data*/)
27{
28 return BM_edge_is_manifold(l->e);
29}
30
63 BMFace **faces,
64 const int faces_len,
65 bool *r_is_flip)
66{
67 const float eps = FLT_EPSILON;
68 float cent_area_accum = 0.0f;
69 float cent[3];
70 const float cent_fac = 1.0f / float(faces_len);
71
72 bool is_flip = false;
73 int f_start_index;
74 int i;
75
77 struct {
83 float dist_sq;
88 float edge_dot;
93 float loop_dot;
94 } best, test;
95
97
98 zero_v3(cent);
99
100 /* first calculate the center */
101 for (i = 0; i < faces_len; i++) {
102 float f_cent[3];
103 const float f_area = BM_face_calc_area(faces[i]);
104 BM_face_calc_center_median_weighted(faces[i], f_cent);
105 madd_v3_v3fl(cent, f_cent, cent_fac * f_area);
106 cent_area_accum += f_area;
107
108 BLI_assert(BMO_face_flag_test(bm, faces[i], FACE_TEMP) == 0);
110 }
111
112 if (cent_area_accum != 0.0f) {
113 mul_v3_fl(cent, 1.0f / cent_area_accum);
114 }
115
116 /* Distances must start above zero,
117 * or we can't do meaningful calculations based on the direction to the center */
118 best.dist_sq = eps;
119 best.edge_dot = best.loop_dot = -FLT_MAX;
120
121 /* used in degenerate cases only */
122 f_start_index = 0;
123
132 for (i = 0; i < faces_len; i++) {
133 BMLoop *l_iter, *l_first;
134
135 l_iter = l_first = BM_FACE_FIRST_LOOP(faces[i]);
136 do {
137 bool is_best_dist_sq;
138 float dir[3];
139 sub_v3_v3v3(dir, l_iter->v->co, cent);
140 test.dist_sq = len_squared_v3(dir);
141 is_best_dist_sq = (test.dist_sq > best.dist_sq);
142 if (is_best_dist_sq || (test.dist_sq == best.dist_sq)) {
143 float edge_dir_pair[2][3];
144 mul_v3_fl(dir, 1.0f / sqrtf(test.dist_sq));
145
146 sub_v3_v3v3(edge_dir_pair[0], l_iter->next->v->co, l_iter->v->co);
147 sub_v3_v3v3(edge_dir_pair[1], l_iter->prev->v->co, l_iter->v->co);
148
149 if ((normalize_v3(edge_dir_pair[0]) > eps) && (normalize_v3(edge_dir_pair[1]) > eps)) {
150 bool is_best_edge_dot;
151 test.edge_dot = max_ff(dot_v3v3(dir, edge_dir_pair[0]), dot_v3v3(dir, edge_dir_pair[1]));
152 is_best_edge_dot = (test.edge_dot > best.edge_dot);
153 if (is_best_dist_sq || is_best_edge_dot || (test.edge_dot == best.edge_dot)) {
154 float loop_dir[3];
155 cross_v3_v3v3(loop_dir, edge_dir_pair[0], edge_dir_pair[1]);
156 if (normalize_v3(loop_dir) > eps) {
157 float loop_dir_dot;
158 /* Highly unlikely the furthest loop is also the concave part of an ngon,
159 * but it can be contrived with _very_ non-planar faces - so better check. */
160 if (UNLIKELY(dot_v3v3(loop_dir, l_iter->f->no) < 0.0f)) {
161 negate_v3(loop_dir);
162 }
163 loop_dir_dot = dot_v3v3(dir, loop_dir);
164 test.loop_dot = fabsf(loop_dir_dot);
165 if (is_best_dist_sq || is_best_edge_dot || (test.loop_dot > best.loop_dot)) {
166 best = test;
167 f_start_index = i;
168 is_flip = (loop_dir_dot < 0.0f);
169 }
170 }
171 }
172 }
173 }
174 } while ((l_iter = l_iter->next) != l_first);
175 }
176
177 *r_is_flip = is_flip;
178 return f_start_index;
179}
180
191 BMFace **faces,
192 const int faces_len,
193 const short oflag)
194{
195 int i, f_start_index;
196 const short oflag_flip = oflag | FACE_FLIP;
197 bool is_flip;
198
199 BMFace *f;
200
201 BLI_LINKSTACK_DECLARE(fstack, BMFace *);
202
203 f_start_index = recalc_face_normals_find_index(bm, faces, faces_len, &is_flip);
204
205 if (is_flip) {
206 BMO_face_flag_enable(bm, faces[f_start_index], FACE_FLIP);
207 }
208
209 /* now that we've found our starting face, make all connected faces
210 * have the same winding. this is done recursively, using a manual
211 * stack (if we use simple function recursion, we'd end up overloading
212 * the stack on large meshes). */
213 BLI_LINKSTACK_INIT(fstack);
214
215 BLI_LINKSTACK_PUSH(fstack, faces[f_start_index]);
216 BMO_face_flag_enable(bm, faces[f_start_index], FACE_TEMP);
217
218 while ((f = BLI_LINKSTACK_POP(fstack))) {
219 const bool flip_state = BMO_face_flag_test_bool(bm, f, FACE_FLIP);
220 BMLoop *l_iter, *l_first;
221
222 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
223 do {
224 BMLoop *l_other = l_iter->radial_next;
225
226 if ((l_other != l_iter) && bmo_recalc_normal_loop_filter_cb(l_iter, nullptr)) {
227 if (!BMO_face_flag_test(bm, l_other->f, FACE_TEMP)) {
229 BMO_face_flag_set(bm, l_other->f, FACE_FLIP, (l_other->v == l_iter->v) != flip_state);
230 BLI_LINKSTACK_PUSH(fstack, l_other->f);
231 }
232 }
233 } while ((l_iter = l_iter->next) != l_first);
234 }
235
236 BLI_LINKSTACK_FREE(fstack);
237
238 /* apply flipping to oflag'd faces */
239 for (i = 0; i < faces_len; i++) {
240 if (BMO_face_flag_test(bm, faces[i], oflag_flip) == oflag_flip) {
241 BM_face_normal_flip(bm, faces[i]);
242 }
244 }
245}
246
257{
258 int *groups_array = static_cast<int *>(
259 MEM_mallocN(sizeof(*groups_array) * bm->totface, __func__));
260 BMFace **faces_grp = static_cast<BMFace **>(
261 MEM_mallocN(sizeof(*faces_grp) * bm->totface, __func__));
262
263 int(*group_index)[2];
264 const int group_tot = BM_mesh_calc_face_groups(bm,
265 groups_array,
266 &group_index,
268 nullptr,
269 nullptr,
270 0,
271 BM_EDGE);
272 int i;
273
275
277
278 for (i = 0; i < group_tot; i++) {
279 const int fg_sta = group_index[i][0];
280 const int fg_len = group_index[i][1];
281 int j;
282 bool is_calc = false;
283
284 for (j = 0; j < fg_len; j++) {
285 faces_grp[j] = BM_face_at_index(bm, groups_array[fg_sta + j]);
286
287 if (is_calc == false) {
288 is_calc = BMO_face_flag_test_bool(bm, faces_grp[j], FACE_FLAG);
289 }
290 }
291
292 if (is_calc) {
293 bmo_recalc_face_normals_array(bm, faces_grp, fg_len, FACE_FLAG);
294 }
295 }
296
297 MEM_freeN(faces_grp);
298
299 MEM_freeN(groups_array);
300 MEM_freeN(group_index);
301}
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE float max_ff(float a, float b)
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 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 float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void negate_v3(float r[3])
MINLINE void zero_v3(float r[3])
MINLINE float normalize_v3(float n[3])
#define UNUSED_VARS_NDEBUG(...)
#define UNLIKELY(x)
Read Guarded memory(de)allocation.
#define BM_FACE_FIRST_LOOP(p)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
#define BM_FACE
#define BM_EDGE
void BMO_slot_buffer_flag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
BMO_FLAG_BUFFER.
#define BMO_face_flag_test_bool(bm, e, oflag)
#define BMO_face_flag_enable(bm, e, oflag)
#define BMO_face_flag_set(bm, e, oflag, val)
#define BMO_face_flag_disable(bm, e, oflag)
#define BMO_face_flag_test(bm, e, oflag)
ATTR_WARN_UNUSED_RESULT const BMFlagLayer const short oflag
void BM_face_normal_flip(BMesh *bm, BMFace *f)
float BM_face_calc_area(const BMFace *f)
void BM_face_calc_center_median_weighted(const BMFace *f, float r_cent[3])
int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int(**r_group_index)[2], BMLoopFilterFunc filter_fn, BMLoopPairFilterFunc filter_pair_fn, void *user_data, const char hflag_test, const char htype_step)
bool BM_face_is_normal_valid(const BMFace *f)
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
static bool bmo_recalc_normal_loop_filter_cb(const BMLoop *l, void *)
#define FACE_FLAG
#define FACE_TEMP
static int recalc_face_normals_find_index(BMesh *bm, BMFace **faces, const int faces_len, bool *r_is_flip)
static void bmo_recalc_face_normals_array(BMesh *bm, BMFace **faces, const int faces_len, const short oflag)
void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op)
#define FACE_FLIP
#define fabsf(x)
#define sqrtf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
const btScalar eps
Definition poly34.cpp:11
#define FLT_MAX
Definition stdcycles.h:14
float no[3]
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
float co[3]
int totface