Blender V4.5
MOD_weighted_normal.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
8
9#include "MEM_guardedalloc.h"
10
11#include "BLI_array_utils.hh"
12#include "BLI_math_vector.h"
13
14#include "BLT_translation.hh"
15
16#include "DNA_defaults.h"
17#include "DNA_mesh_types.h"
18#include "DNA_meshdata_types.h"
19#include "DNA_screen_types.h"
20
21#include "BKE_attribute.hh"
22#include "BKE_context.hh"
23#include "BKE_deform.hh"
24#include "BKE_lib_id.hh"
25#include "BKE_mesh.hh"
26#include "BKE_screen.hh"
27
28#include "UI_interface.hh"
29#include "UI_resources.hh"
30
31#include "RNA_access.hh"
32#include "RNA_prototypes.hh"
33
34#include "MOD_modifiertypes.hh"
35#include "MOD_ui_common.hh"
36#include "MOD_util.hh"
37
38#include "bmesh.hh"
39
40#define CLNORS_VALID_VEC_LEN (1e-6f)
41
42struct ModePair {
43 float val; /* Contains mode based value (face area / corner angle). */
44 int index; /* Index value per face or per loop. */
45};
46
47/* Sorting function used in modifier, sorts in decreasing order. */
48static int modepair_cmp_by_val_inverse(const void *p1, const void *p2)
49{
50 ModePair *r1 = (ModePair *)p1;
51 ModePair *r2 = (ModePair *)p2;
52
53 return (r1->val < r2->val) ? 1 : ((r1->val > r2->val) ? -1 : 0);
54}
55
56/* There will be one of those per vertex
57 * (simple case, computing one normal per vertex), or per smooth fan. */
59 float normal[3];
60
61 int loops_num; /* Count number of loops using this item so far. */
62 float curr_val; /* Current max val for this item. */
63 int curr_strength; /* Current max strength encountered for this item. */
64};
65
66#define NUM_CACHED_INVERSE_POWERS_OF_WEIGHT 128
67
100
107 const int face_index)
108{
109 BLI_assert(wn_data->face_strength != nullptr);
110
111 const int mp_strength = wn_data->face_strength[face_index];
112
113 if (mp_strength > item_data->curr_strength) {
114 item_data->curr_strength = mp_strength;
115 item_data->curr_val = 0.0f;
116 item_data->loops_num = 0;
117 zero_v3(item_data->normal);
118 }
119
120 return mp_strength == item_data->curr_strength;
121}
122
124 WeightedNormalData *wn_data,
126 const int mv_index,
127 const int face_index,
128 const float curr_val,
129 const bool use_face_influence)
130{
131 const blender::Span<blender::float3> face_normals = wn_data->face_normals;
132
133 const MDeformVert *dvert = wn_data->dvert;
134 const int defgrp_index = wn_data->defgrp_index;
135 const bool use_invert_vgroup = wn_data->use_invert_vgroup;
136
137 const float weight = wn_data->weight;
138
139 float *cached_inverse_powers_of_weight = wn_data->cached_inverse_powers_of_weight;
140
141 const bool has_vgroup = dvert != nullptr;
142 const bool vert_of_group = has_vgroup &&
143 BKE_defvert_find_index(&dvert[mv_index], defgrp_index) != nullptr;
144
145 if (has_vgroup &&
146 ((vert_of_group && use_invert_vgroup) || (!vert_of_group && !use_invert_vgroup)))
147 {
148 return;
149 }
150
151 if (use_face_influence && !check_item_face_strength(wn_data, item_data, face_index)) {
152 return;
153 }
154
155 /* If item's curr_val is 0 init it to present value. */
156 if (item_data->curr_val == 0.0f) {
157 item_data->curr_val = curr_val;
158 }
159 if (!compare_ff(item_data->curr_val, curr_val, wnmd->thresh)) {
160 /* item's curr_val and present value differ more than threshold, update. */
161 item_data->loops_num++;
162 item_data->curr_val = curr_val;
163 }
164
165 /* Exponentially divided weight for each normal
166 * (since a few values will be used by most cases, we cache those). */
167 const int loops_num = item_data->loops_num;
168 if (loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT &&
169 cached_inverse_powers_of_weight[loops_num] == 0.0f)
170 {
171 cached_inverse_powers_of_weight[loops_num] = 1.0f / powf(weight, loops_num);
172 }
173 const float inverted_n_weight = loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT ?
174 cached_inverse_powers_of_weight[loops_num] :
175 1.0f / powf(weight, loops_num);
176
177 madd_v3_v3fl(item_data->normal, face_normals[face_index], curr_val * inverted_n_weight);
178}
179
181 WeightedNormalData *wn_data)
182{
183 using namespace blender;
184 const int verts_num = wn_data->verts_num;
185
186 const blender::Span<blender::float3> positions = wn_data->vert_positions;
187 const blender::OffsetIndices faces = wn_data->faces;
188 const blender::Span<int> corner_verts = wn_data->corner_verts;
189 const blender::Span<int> corner_edges = wn_data->corner_edges;
190
191 MutableSpan<short2> clnors = wn_data->clnors;
192 const blender::Span<int> loop_to_face = wn_data->loop_to_face;
193
194 const blender::Span<blender::float3> face_normals = wn_data->face_normals;
195 const int *face_strength = wn_data->face_strength;
196
197 const MDeformVert *dvert = wn_data->dvert;
198
199 const short mode = wn_data->mode;
200 ModePair *mode_pair = wn_data->mode_pair;
201
203
204 const bool keep_sharp = (wnmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0;
205 const bool use_face_influence = (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) != 0 &&
206 face_strength != nullptr;
207 const bool has_vgroup = dvert != nullptr;
208
209 blender::Array<blender::float3> corner_normals;
210
212 if (keep_sharp) {
213 /* This will give us loop normal spaces,
214 * we do not actually care about computed corner_normals for now... */
215 corner_normals.reinitialize(corner_verts.size());
217 faces,
218 corner_verts,
219 corner_edges,
220 wn_data->vert_to_face_map,
221 wn_data->face_normals,
222 wn_data->sharp_edges,
223 wn_data->sharp_faces,
224 clnors,
225 &lnors_spacearr,
226 corner_normals);
227
230 items_data = Array<WeightedNormalDataAggregateItem>(lnors_spacearr.spaces.size(), start_item);
231 }
232 else {
235 items_data = Array<WeightedNormalDataAggregateItem>(verts_num, start_item);
236 lnors_spacearr.corner_space_indices.reinitialize(corner_verts.size());
238 }
239 wn_data->items_data = items_data;
240
241 switch (mode) {
243 for (const int i : faces.index_range()) {
244 const int face_index = mode_pair[i].index;
245 const float mp_val = mode_pair[i].val;
246
247 for (const int corner : faces[face_index]) {
248 const int mv_index = corner_verts[corner];
249 const int space_index = lnors_spacearr.corner_space_indices[corner];
250
251 WeightedNormalDataAggregateItem *item_data = keep_sharp ? &items_data[space_index] :
252 &items_data[mv_index];
253
255 wnmd, wn_data, item_data, mv_index, face_index, mp_val, use_face_influence);
256 }
257 }
258 break;
261 for (int i = 0; i < corner_verts.size(); i++) {
262 const int corner = mode_pair[i].index;
263 const float ml_val = mode_pair[i].val;
264 const int space_index = lnors_spacearr.corner_space_indices[corner];
265
266 const int face_index = loop_to_face[corner];
267 const int mv_index = corner_verts[corner];
268 WeightedNormalDataAggregateItem *item_data = keep_sharp ? &items_data[space_index] :
269 &items_data[mv_index];
270
272 wnmd, wn_data, item_data, mv_index, face_index, ml_val, use_face_influence);
273 }
274 break;
275 default:
277 }
278
279 /* Validate computed weighted normals. */
280 for (int item_index : items_data.index_range()) {
281 if (normalize_v3(items_data[item_index].normal) < CLNORS_VALID_VEC_LEN) {
282 zero_v3(items_data[item_index].normal);
283 }
284 }
285
286 if (keep_sharp) {
287 /* Set loop normals for normal computed for each lnor space (smooth fan).
288 * Note that corner_normals is already populated with clnors
289 * (before this modifier is applied, at start of this function),
290 * so no need to recompute them here. */
291 for (int corner = 0; corner < corner_verts.size(); corner++) {
292 const int space_index = lnors_spacearr.corner_space_indices[corner];
293 WeightedNormalDataAggregateItem *item_data = &items_data[space_index];
294 if (!is_zero_v3(item_data->normal)) {
295 copy_v3_v3(corner_normals[corner], item_data->normal);
296 }
297 }
298
300 faces,
301 corner_verts,
302 corner_edges,
303 wn_data->vert_to_face_map,
304 wn_data->vert_normals,
305 face_normals,
306 wn_data->sharp_faces,
307 wn_data->sharp_edges,
308 corner_normals,
309 clnors);
310 }
311 else {
312 /* TODO: Ideally, we could add an option to `BKE_mesh_normals_loop_custom_[from_verts_]set()`
313 * to keep current clnors instead of resetting them to default auto-computed ones,
314 * when given new custom normal is zero-vec.
315 * But this is not exactly trivial change, better to keep this optimization for later...
316 */
317 if (!has_vgroup) {
318 /* NOTE: in theory, we could avoid this extra allocation & copying...
319 * But think we can live with it for now,
320 * and it makes code simpler & cleaner. */
321 blender::Array<blender::float3> vert_normals(verts_num, float3(0.0f));
322
323 for (int corner = 0; corner < corner_verts.size(); corner++) {
324 const int mv_index = corner_verts[corner];
325 copy_v3_v3(vert_normals[mv_index], items_data[mv_index].normal);
326 }
327
329 faces,
330 corner_verts,
331 corner_edges,
332 wn_data->vert_to_face_map,
333 wn_data->vert_normals,
334 face_normals,
335 wn_data->sharp_faces,
336 wn_data->sharp_edges,
337 vert_normals,
338 clnors);
339 }
340 else {
341 corner_normals.reinitialize(corner_verts.size());
343 faces,
344 corner_verts,
345 corner_edges,
346 wn_data->vert_to_face_map,
347 face_normals,
348 wn_data->sharp_edges,
349 wn_data->sharp_faces,
350 clnors,
351 nullptr,
352 corner_normals);
353
354 for (int corner = 0; corner < corner_verts.size(); corner++) {
355 const int item_index = corner_verts[corner];
356 if (!is_zero_v3(items_data[item_index].normal)) {
357 copy_v3_v3(corner_normals[corner], items_data[item_index].normal);
358 }
359 }
361 faces,
362 corner_verts,
363 corner_edges,
364 wn_data->vert_to_face_map,
365 wn_data->vert_normals,
366 face_normals,
367 wn_data->sharp_faces,
368 wn_data->sharp_edges,
369 corner_normals,
370 clnors);
371 }
372 }
373}
374
376{
377 const blender::Span<blender::float3> positions = wn_data->vert_positions;
378 const blender::OffsetIndices faces = wn_data->faces;
379 const blender::Span<int> corner_verts = wn_data->corner_verts;
380
381 ModePair *face_area = MEM_malloc_arrayN<ModePair>(size_t(faces.size()), __func__);
382
383 ModePair *f_area = face_area;
384 for (const int i : faces.index_range()) {
385 f_area[i].val = blender::bke::mesh::face_area_calc(positions, corner_verts.slice(faces[i]));
386 f_area[i].index = i;
387 }
388
389 qsort(face_area, faces.size(), sizeof(*face_area), modepair_cmp_by_val_inverse);
390
391 wn_data->mode_pair = face_area;
392 apply_weights_vertex_normal(wnmd, wn_data);
393}
394
396{
397 const blender::Span<blender::float3> positions = wn_data->vert_positions;
398 const blender::OffsetIndices faces = wn_data->faces;
399 const blender::Span<int> corner_verts = wn_data->corner_verts;
400
401 ModePair *corner_angle = MEM_malloc_arrayN<ModePair>(size_t(corner_verts.size()), __func__);
402
403 for (const int i : faces.index_range()) {
404 const blender::IndexRange face = faces[i];
405 float *index_angle = MEM_malloc_arrayN<float>(size_t(face.size()), __func__);
407 positions, corner_verts.slice(face), {index_angle, face.size()});
408
409 ModePair *c_angl = &corner_angle[face.start()];
410 float *angl = index_angle;
411 for (int corner = face.start(); corner < face.start() + face.size();
412 corner++, c_angl++, angl++)
413 {
414 c_angl->val = float(M_PI) - *angl;
415 c_angl->index = corner;
416 }
417 MEM_freeN(index_angle);
418 }
419
420 qsort(corner_angle, corner_verts.size(), sizeof(*corner_angle), modepair_cmp_by_val_inverse);
421
422 wn_data->mode_pair = corner_angle;
423 apply_weights_vertex_normal(wnmd, wn_data);
424}
425
427{
428 const blender::Span<blender::float3> positions = wn_data->vert_positions;
429 const blender::OffsetIndices faces = wn_data->faces;
430 const blender::Span<int> corner_verts = wn_data->corner_verts;
431
432 ModePair *combined = MEM_malloc_arrayN<ModePair>(size_t(corner_verts.size()), __func__);
433
434 for (const int i : faces.index_range()) {
435 const blender::IndexRange face = faces[i];
436 const blender::Span<int> face_verts = corner_verts.slice(face);
437 const float face_area = blender::bke::mesh::face_area_calc(positions, face_verts);
438 float *index_angle = MEM_malloc_arrayN<float>(size_t(face.size()), __func__);
439 blender::bke::mesh::face_angles_calc(positions, face_verts, {index_angle, face.size()});
440
441 ModePair *cmbnd = &combined[face.start()];
442 float *angl = index_angle;
443 for (int corner = face.start(); corner < face.start() + face.size(); corner++, cmbnd++, angl++)
444 {
445 /* In this case val is product of corner angle and face area. */
446 cmbnd->val = (float(M_PI) - *angl) * face_area;
447 cmbnd->index = corner;
448 }
449 MEM_freeN(index_angle);
450 }
451
452 qsort(combined, corner_verts.size(), sizeof(*combined), modepair_cmp_by_val_inverse);
453
454 wn_data->mode_pair = combined;
455 apply_weights_vertex_normal(wnmd, wn_data);
456}
457
458static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
459{
460 using namespace blender;
462
463 Mesh *result;
464 result = (Mesh *)BKE_id_copy_ex(nullptr, &mesh->id, nullptr, LIB_ID_COPY_LOCALIZE);
465
466 const int verts_num = result->verts_num;
467 const blender::Span<blender::float3> positions = mesh->vert_positions();
468 const OffsetIndices faces = result->faces();
469 const blender::Span<int> corner_verts = mesh->corner_verts();
470 const blender::Span<int> corner_edges = mesh->corner_edges();
471
472 /* Right now:
473 * If weight = 50 then all faces are given equal weight.
474 * If weight > 50 then more weight given to faces with larger values (face area / corner angle).
475 * If weight < 50 then more weight given to faces with lesser values. However current calculation
476 * does not converge to min/max.
477 */
478 float weight = float(wnmd->weight) / 50.0f;
479 if (wnmd->weight == 100) {
480 weight = float(SHRT_MAX);
481 }
482 else if (wnmd->weight == 1) {
483 weight = 1 / float(SHRT_MAX);
484 }
485 else if ((weight - 1) * 25 > 1) {
486 weight = (weight - 1) * 25;
487 }
488
489 const MDeformVert *dvert;
490 int defgrp_index;
491 MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index);
492
493 const Span<int> loop_to_face_map = result->corner_to_face_map();
494
495 bke::MutableAttributeAccessor attributes = result->attributes_for_write();
497 "sharp_edge", bke::AttrDomain::Edge);
499 "custom_normal", bke::AttrDomain::Corner);
500 if (!clnors) {
501 return result;
502 }
503
504 WeightedNormalData wn_data{};
505 wn_data.verts_num = verts_num;
506
507 wn_data.vert_positions = positions;
508 wn_data.vert_normals = result->vert_normals();
509 wn_data.sharp_edges = sharp_edges.span;
510
511 wn_data.corner_verts = corner_verts;
512 wn_data.corner_edges = corner_edges;
513 wn_data.vert_to_face_map = result->vert_to_face_map();
514 wn_data.loop_to_face = loop_to_face_map;
515 wn_data.clnors = clnors.span;
516
517 wn_data.faces = faces;
518 wn_data.face_normals = mesh->face_normals_true();
519 wn_data.sharp_faces = *attributes.lookup<bool>("sharp_face", bke::AttrDomain::Face);
520 wn_data.face_strength = static_cast<const int *>(CustomData_get_layer_named(
522
523 wn_data.dvert = dvert;
524 wn_data.defgrp_index = defgrp_index;
526
527 wn_data.weight = weight;
528 wn_data.mode = wnmd->mode;
529
530 switch (wnmd->mode) {
532 wn_face_area(wnmd, &wn_data);
533 break;
535 wn_corner_angle(wnmd, &wn_data);
536 break;
538 wn_face_with_angle(wnmd, &wn_data);
539 break;
540 }
541
542 MEM_SAFE_FREE(wn_data.mode_pair);
543
544 result->runtime->is_original_bmesh = false;
545
546 sharp_edges.finish();
547 clnors.finish();
548
549 return result;
550}
551
560
561static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
562{
564
565 if (wnmd->defgrp_name[0] != '\0') {
566 r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
567 }
568
570 r_cddata_masks->pmask |= CD_MASK_PROP_INT32;
571 }
572}
573
574static void panel_draw(const bContext * /*C*/, Panel *panel)
575{
576 uiLayout *col;
577 uiLayout *layout = panel->layout;
578
579 PointerRNA ob_ptr;
581
582 uiLayoutSetPropSep(layout, true);
583
584 layout->prop(ptr, "mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
585
586 layout->prop(ptr, "weight", UI_ITEM_NONE, IFACE_("Weight"), ICON_NONE);
587 layout->prop(ptr, "thresh", UI_ITEM_NONE, IFACE_("Threshold"), ICON_NONE);
588
589 col = &layout->column(false);
590 col->prop(ptr, "keep_sharp", UI_ITEM_NONE, std::nullopt, ICON_NONE);
591 col->prop(ptr, "use_face_influence", UI_ITEM_NONE, std::nullopt, ICON_NONE);
592
593 modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", std::nullopt);
594
596}
597
598static void panel_register(ARegionType *region_type)
599{
601}
602
604 /*idname*/ "WeightedNormal",
605 /*name*/ N_("WeightedNormal"),
606 /*struct_name*/ "WeightedNormalModifierData",
607 /*struct_size*/ sizeof(WeightedNormalModifierData),
608 /*srna*/ &RNA_WeightedNormalModifier,
612 /*icon*/ ICON_MOD_NORMALEDIT,
613
614 /*copy_data*/ BKE_modifier_copydata_generic,
615
616 /*deform_verts*/ nullptr,
617 /*deform_matrices*/ nullptr,
618 /*deform_verts_EM*/ nullptr,
619 /*deform_matrices_EM*/ nullptr,
620 /*modify_mesh*/ modify_mesh,
621 /*modify_geometry_set*/ nullptr,
622
623 /*init_data*/ init_data,
624 /*required_data_mask*/ required_data_mask,
625 /*free_data*/ nullptr,
626 /*is_disabled*/ nullptr,
627 /*update_depsgraph*/ nullptr,
628 /*depends_on_time*/ nullptr,
629 /*depends_on_normals*/ nullptr,
630 /*foreach_ID_link*/ nullptr,
631 /*foreach_tex_link*/ nullptr,
632 /*free_runtime_data*/ nullptr,
633 /*panel_register*/ panel_register,
634 /*blend_write*/ nullptr,
635 /*blend_read*/ nullptr,
636 /*foreach_cache*/ nullptr,
637};
const void * CustomData_get_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
support for deformation groups and hooks.
MDeformWeight * BKE_defvert_find_index(const MDeformVert *dv, int defgroup)
Definition deform.cc:795
@ LIB_ID_COPY_LOCALIZE
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:767
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_SupportsMapping
@ eModifierTypeFlag_EnableInEditmode
@ eModifierTypeFlag_SupportsEditmode
@ eModifierTypeFlag_AcceptsMesh
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE int compare_ff(float a, float b, float max_diff)
#define M_PI
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE float normalize_v3(float n[3])
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define IFACE_(msgid)
@ CD_PROP_INT32
#define DNA_struct_default_get(struct_name)
@ eModifierType_WeightedNormal
#define MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID
@ MOD_WEIGHTEDNORMAL_KEEP_SHARP
@ MOD_WEIGHTEDNORMAL_FACE_INFLUENCE
@ MOD_WEIGHTEDNORMAL_INVERT_VGROUP
@ MOD_WEIGHTEDNORMAL_MODE_FACE
@ MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE
@ MOD_WEIGHTEDNORMAL_MODE_ANGLE
Read Guarded memory(de)allocation.
static void init_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void required_data_mask(ModifierData *, CustomData_MeshMasks *r_cddata_masks)
static void panel_draw(const bContext *, Panel *panel)
static Mesh * modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
Definition MOD_array.cc:862
void modifier_vgroup_ui(uiLayout *layout, PointerRNA *ptr, PointerRNA *ob_ptr, const StringRefNull vgroup_prop, const std::optional< StringRefNull > invert_vgroup_prop, const std::optional< StringRefNull > text)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
void MOD_get_vgroup(const Object *ob, const Mesh *mesh, const char *name, const MDeformVert **dvert, int *defgrp_index)
Definition MOD_util.cc:156
static void init_data(ModifierData *md)
static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data)
static void aggregate_item_normal(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data, WeightedNormalDataAggregateItem *item_data, const int mv_index, const int face_index, const float curr_val, const bool use_face_influence)
static void panel_register(ARegionType *region_type)
static void wn_corner_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data)
static void wn_face_area(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data)
#define NUM_CACHED_INVERSE_POWERS_OF_WEIGHT
static Mesh * modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
ModifierTypeInfo modifierType_WeightedNormal
static int modepair_cmp_by_val_inverse(const void *p1, const void *p2)
static bool check_item_face_strength(WeightedNormalData *wn_data, WeightedNormalDataAggregateItem *item_data, const int face_index)
static void panel_draw(const bContext *, Panel *panel)
static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
static void wn_face_with_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
@ FACE_STRENGTH_WEAK
IndexRange index_range() const
Definition BLI_array.hh:349
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:398
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:398
constexpr int64_t size() const
constexpr int64_t start() const
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr int64_t size() const
Definition BLI_span.hh:252
GAttributeReader lookup(const StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
#define powf(x, y)
#define CLNORS_VALID_VEC_LEN
uint col
VecBase< short, 2 > short2
VecBase< float, 3 > float3
#define CD_MASK_MDEFORMVERT
#define MEM_SAFE_FREE(v)
#define CD_MASK_PROP_INT32
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
void fill_index_range(MutableSpan< T > span, const T start=0)
void face_angles_calc(Span< float3 > vert_positions, Span< int > face_verts, MutableSpan< float > angles)
float face_area_calc(Span< float3 > vert_positions, Span< int > face_verts)
void normals_calc_corners(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, Span< int > corner_edges, GroupedSpan< int > vert_to_face_map, Span< float3 > face_normals, Span< bool > sharp_edges, Span< bool > sharp_faces, Span< short2 > custom_normals, CornerNormalSpaceArray *r_fan_spaces, MutableSpan< float3 > r_corner_normals)
void normals_corner_custom_set_from_verts(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, Span< int > corner_edges, GroupedSpan< int > vert_to_face_map, Span< float3 > vert_normals, Span< float3 > face_normals, Span< bool > sharp_faces, MutableSpan< bool > sharp_edges, MutableSpan< float3 > r_custom_vert_normals, MutableSpan< short2 > r_clnors_data)
void normals_corner_custom_set(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, Span< int > corner_edges, GroupedSpan< int > vert_to_face_map, Span< float3 > vert_normals, Span< float3 > face_normals, Span< bool > sharp_faces, MutableSpan< bool > sharp_edges, MutableSpan< float3 > r_custom_corner_normals, MutableSpan< short2 > r_clnors_data)
struct uiLayout * layout
blender::MutableSpan< blender::short2 > clnors
blender::Span< int > corner_edges
blender::Span< int > loop_to_face
blender::VArraySpan< bool > sharp_faces
blender::Span< WeightedNormalDataAggregateItem > items_data
blender::Span< int > corner_verts
blender::Span< blender::float3 > vert_positions
blender::Span< blender::float3 > face_normals
const MDeformVert * dvert
blender::MutableSpan< bool > sharp_edges
blender::OffsetIndices< int > faces
blender::GroupedSpan< int > vert_to_face_map
blender::Span< blender::float3 > vert_normals
float cached_inverse_powers_of_weight[NUM_CACHED_INVERSE_POWERS_OF_WEIGHT]
Vector< CornerNormalSpace > spaces
Definition BKE_mesh.hh:166
uiLayout & column(bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4226