Blender V5.0
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
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 const int mp_strength = wn_data->face_strength[face_index];
110
111 if (mp_strength > item_data->curr_strength) {
112 item_data->curr_strength = mp_strength;
113 item_data->curr_val = 0.0f;
114 item_data->loops_num = 0;
115 zero_v3(item_data->normal);
116 }
117
118 return mp_strength == item_data->curr_strength;
119}
120
122 WeightedNormalData *wn_data,
124 const int mv_index,
125 const int face_index,
126 const float curr_val,
127 const bool use_face_influence)
128{
129 const blender::Span<blender::float3> face_normals = wn_data->face_normals;
130
131 const MDeformVert *dvert = wn_data->dvert;
132 const int defgrp_index = wn_data->defgrp_index;
133 const bool use_invert_vgroup = wn_data->use_invert_vgroup;
134
135 const float weight = wn_data->weight;
136
137 float *cached_inverse_powers_of_weight = wn_data->cached_inverse_powers_of_weight;
138
139 const bool has_vgroup = dvert != nullptr;
140 const bool vert_of_group = has_vgroup &&
141 BKE_defvert_find_index(&dvert[mv_index], defgrp_index) != nullptr;
142
143 if (has_vgroup &&
144 ((vert_of_group && use_invert_vgroup) || (!vert_of_group && !use_invert_vgroup)))
145 {
146 return;
147 }
148
149 if (use_face_influence && !check_item_face_strength(wn_data, item_data, face_index)) {
150 return;
151 }
152
153 /* If item's curr_val is 0 init it to present value. */
154 if (item_data->curr_val == 0.0f) {
155 item_data->curr_val = curr_val;
156 }
157 if (!compare_ff(item_data->curr_val, curr_val, wnmd->thresh)) {
158 /* item's curr_val and present value differ more than threshold, update. */
159 item_data->loops_num++;
160 item_data->curr_val = curr_val;
161 }
162
163 /* Exponentially divided weight for each normal
164 * (since a few values will be used by most cases, we cache those). */
165 const int loops_num = item_data->loops_num;
166 if (loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT &&
167 cached_inverse_powers_of_weight[loops_num] == 0.0f)
168 {
169 cached_inverse_powers_of_weight[loops_num] = 1.0f / powf(weight, loops_num);
170 }
171 const float inverted_n_weight = loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT ?
172 cached_inverse_powers_of_weight[loops_num] :
173 1.0f / powf(weight, loops_num);
174
175 madd_v3_v3fl(item_data->normal, face_normals[face_index], curr_val * inverted_n_weight);
176}
177
179 WeightedNormalData *wn_data)
180{
181 using namespace blender;
182 const int verts_num = wn_data->verts_num;
183
184 const blender::Span<blender::float3> positions = wn_data->vert_positions;
185 const blender::OffsetIndices faces = wn_data->faces;
186 const blender::Span<int> corner_verts = wn_data->corner_verts;
187 const blender::Span<int> corner_edges = wn_data->corner_edges;
188
189 MutableSpan<short2> clnors = wn_data->clnors;
190 const blender::Span<int> loop_to_face = wn_data->loop_to_face;
191
192 const blender::Span<blender::float3> face_normals = wn_data->face_normals;
193 const VArray<int> face_strength = wn_data->face_strength;
194
195 const MDeformVert *dvert = wn_data->dvert;
196
197 const short mode = wn_data->mode;
198 ModePair *mode_pair = wn_data->mode_pair;
199
201
202 const bool keep_sharp = (wnmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0;
203 const bool use_face_influence = (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) != 0 &&
204 bool(face_strength);
205 const bool has_vgroup = dvert != nullptr;
206
207 blender::Array<blender::float3> corner_normals;
208
210 if (keep_sharp) {
211 /* This will give us loop normal spaces,
212 * we do not actually care about computed corner_normals for now... */
213 corner_normals.reinitialize(corner_verts.size());
215 faces,
216 corner_verts,
217 corner_edges,
218 wn_data->vert_to_face_map,
219 wn_data->face_normals,
220 wn_data->sharp_edges,
221 wn_data->sharp_faces,
222 clnors,
223 &lnors_spacearr,
224 corner_normals);
225
228 items_data = Array<WeightedNormalDataAggregateItem>(lnors_spacearr.spaces.size(), start_item);
229 }
230 else {
233 items_data = Array<WeightedNormalDataAggregateItem>(verts_num, start_item);
234 lnors_spacearr.corner_space_indices.reinitialize(corner_verts.size());
236 }
237 wn_data->items_data = items_data;
238
239 switch (mode) {
241 for (const int i : faces.index_range()) {
242 const int face_index = mode_pair[i].index;
243 const float mp_val = mode_pair[i].val;
244
245 for (const int corner : faces[face_index]) {
246 const int mv_index = corner_verts[corner];
247 const int space_index = lnors_spacearr.corner_space_indices[corner];
248
249 WeightedNormalDataAggregateItem *item_data = keep_sharp ? &items_data[space_index] :
250 &items_data[mv_index];
251
253 wnmd, wn_data, item_data, mv_index, face_index, mp_val, use_face_influence);
254 }
255 }
256 break;
259 for (int i = 0; i < corner_verts.size(); i++) {
260 const int corner = mode_pair[i].index;
261 const float ml_val = mode_pair[i].val;
262 const int space_index = lnors_spacearr.corner_space_indices[corner];
263
264 const int face_index = loop_to_face[corner];
265 const int mv_index = corner_verts[corner];
266 WeightedNormalDataAggregateItem *item_data = keep_sharp ? &items_data[space_index] :
267 &items_data[mv_index];
268
270 wnmd, wn_data, item_data, mv_index, face_index, ml_val, use_face_influence);
271 }
272 break;
273 default:
275 }
276
277 /* Validate computed weighted normals. */
278 for (int item_index : items_data.index_range()) {
279 if (normalize_v3(items_data[item_index].normal) < CLNORS_VALID_VEC_LEN) {
280 zero_v3(items_data[item_index].normal);
281 }
282 }
283
284 if (keep_sharp) {
285 /* Set loop normals for normal computed for each lnor space (smooth fan).
286 * Note that corner_normals is already populated with clnors
287 * (before this modifier is applied, at start of this function),
288 * so no need to recompute them here. */
289 for (int corner = 0; corner < corner_verts.size(); corner++) {
290 const int space_index = lnors_spacearr.corner_space_indices[corner];
291 WeightedNormalDataAggregateItem *item_data = &items_data[space_index];
292 if (!is_zero_v3(item_data->normal)) {
293 copy_v3_v3(corner_normals[corner], item_data->normal);
294 }
295 }
296
298 faces,
299 corner_verts,
300 corner_edges,
301 wn_data->vert_to_face_map,
302 wn_data->vert_normals,
303 face_normals,
304 wn_data->sharp_faces,
305 wn_data->sharp_edges,
306 corner_normals,
307 clnors);
308 }
309 else {
310 /* TODO: Ideally, we could add an option to `BKE_mesh_normals_loop_custom_[from_verts_]set()`
311 * to keep current clnors instead of resetting them to default auto-computed ones,
312 * when given new custom normal is zero-vec.
313 * But this is not exactly trivial change, better to keep this optimization for later...
314 */
315 if (!has_vgroup) {
316 /* NOTE: in theory, we could avoid this extra allocation & copying...
317 * But think we can live with it for now,
318 * and it makes code simpler & cleaner. */
319 blender::Array<blender::float3> vert_normals(verts_num, float3(0.0f));
320
321 for (int corner = 0; corner < corner_verts.size(); corner++) {
322 const int mv_index = corner_verts[corner];
323 copy_v3_v3(vert_normals[mv_index], items_data[mv_index].normal);
324 }
325
327 faces,
328 corner_verts,
329 corner_edges,
330 wn_data->vert_to_face_map,
331 wn_data->vert_normals,
332 face_normals,
333 wn_data->sharp_faces,
334 wn_data->sharp_edges,
335 vert_normals,
336 clnors);
337 }
338 else {
339 corner_normals.reinitialize(corner_verts.size());
341 faces,
342 corner_verts,
343 corner_edges,
344 wn_data->vert_to_face_map,
345 face_normals,
346 wn_data->sharp_edges,
347 wn_data->sharp_faces,
348 clnors,
349 nullptr,
350 corner_normals);
351
352 for (int corner = 0; corner < corner_verts.size(); corner++) {
353 const int item_index = corner_verts[corner];
354 if (!is_zero_v3(items_data[item_index].normal)) {
355 copy_v3_v3(corner_normals[corner], items_data[item_index].normal);
356 }
357 }
359 faces,
360 corner_verts,
361 corner_edges,
362 wn_data->vert_to_face_map,
363 wn_data->vert_normals,
364 face_normals,
365 wn_data->sharp_faces,
366 wn_data->sharp_edges,
367 corner_normals,
368 clnors);
369 }
370 }
371}
372
374{
375 const blender::Span<blender::float3> positions = wn_data->vert_positions;
376 const blender::OffsetIndices faces = wn_data->faces;
377 const blender::Span<int> corner_verts = wn_data->corner_verts;
378
379 ModePair *face_area = MEM_malloc_arrayN<ModePair>(size_t(faces.size()), __func__);
380
381 ModePair *f_area = face_area;
382 for (const int i : faces.index_range()) {
383 f_area[i].val = blender::bke::mesh::face_area_calc(positions, corner_verts.slice(faces[i]));
384 f_area[i].index = i;
385 }
386
387 qsort(face_area, faces.size(), sizeof(*face_area), modepair_cmp_by_val_inverse);
388
389 wn_data->mode_pair = face_area;
390 apply_weights_vertex_normal(wnmd, wn_data);
391}
392
394{
395 const blender::Span<blender::float3> positions = wn_data->vert_positions;
396 const blender::OffsetIndices faces = wn_data->faces;
397 const blender::Span<int> corner_verts = wn_data->corner_verts;
398
399 ModePair *corner_angle = MEM_malloc_arrayN<ModePair>(size_t(corner_verts.size()), __func__);
400
401 for (const int i : faces.index_range()) {
402 const blender::IndexRange face = faces[i];
403 float *index_angle = MEM_malloc_arrayN<float>(size_t(face.size()), __func__);
405 positions, corner_verts.slice(face), {index_angle, face.size()});
406
407 ModePair *c_angl = &corner_angle[face.start()];
408 float *angl = index_angle;
409 for (int corner = face.start(); corner < face.start() + face.size();
410 corner++, c_angl++, angl++)
411 {
412 c_angl->val = float(M_PI) - *angl;
413 c_angl->index = corner;
414 }
415 MEM_freeN(index_angle);
416 }
417
418 qsort(corner_angle, corner_verts.size(), sizeof(*corner_angle), modepair_cmp_by_val_inverse);
419
420 wn_data->mode_pair = corner_angle;
421 apply_weights_vertex_normal(wnmd, wn_data);
422}
423
425{
426 const blender::Span<blender::float3> positions = wn_data->vert_positions;
427 const blender::OffsetIndices faces = wn_data->faces;
428 const blender::Span<int> corner_verts = wn_data->corner_verts;
429
430 ModePair *combined = MEM_malloc_arrayN<ModePair>(size_t(corner_verts.size()), __func__);
431
432 for (const int i : faces.index_range()) {
433 const blender::IndexRange face = faces[i];
434 const blender::Span<int> face_verts = corner_verts.slice(face);
435 const float face_area = blender::bke::mesh::face_area_calc(positions, face_verts);
436 float *index_angle = MEM_malloc_arrayN<float>(size_t(face.size()), __func__);
437 blender::bke::mesh::face_angles_calc(positions, face_verts, {index_angle, face.size()});
438
439 ModePair *cmbnd = &combined[face.start()];
440 float *angl = index_angle;
441 for (int corner = face.start(); corner < face.start() + face.size(); corner++, cmbnd++, angl++)
442 {
443 /* In this case val is product of corner angle and face area. */
444 cmbnd->val = (float(M_PI) - *angl) * face_area;
445 cmbnd->index = corner;
446 }
447 MEM_freeN(index_angle);
448 }
449
450 qsort(combined, corner_verts.size(), sizeof(*combined), modepair_cmp_by_val_inverse);
451
452 wn_data->mode_pair = combined;
453 apply_weights_vertex_normal(wnmd, wn_data);
454}
455
456static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
457{
458 using namespace blender;
460
461 Mesh *result;
462 result = (Mesh *)BKE_id_copy_ex(nullptr, &mesh->id, nullptr, LIB_ID_COPY_LOCALIZE);
463
464 const int verts_num = result->verts_num;
465 const blender::Span<blender::float3> positions = mesh->vert_positions();
466 const OffsetIndices faces = result->faces();
467 const blender::Span<int> corner_verts = mesh->corner_verts();
468 const blender::Span<int> corner_edges = mesh->corner_edges();
469
470 /* Right now:
471 * If weight = 50 then all faces are given equal weight.
472 * If weight > 50 then more weight given to faces with larger values (face area / corner angle).
473 * If weight < 50 then more weight given to faces with lesser values. However current calculation
474 * does not converge to min/max.
475 */
476 float weight = float(wnmd->weight) / 50.0f;
477 if (wnmd->weight == 100) {
478 weight = float(SHRT_MAX);
479 }
480 else if (wnmd->weight == 1) {
481 weight = 1 / float(SHRT_MAX);
482 }
483 else if ((weight - 1) * 25 > 1) {
484 weight = (weight - 1) * 25;
485 }
486
487 const MDeformVert *dvert;
488 int defgrp_index;
489 MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index);
490
491 const Span<int> loop_to_face_map = result->corner_to_face_map();
492
493 bke::MutableAttributeAccessor attributes = result->attributes_for_write();
495 "sharp_edge", bke::AttrDomain::Edge);
497 "custom_normal", bke::AttrDomain::Corner);
498 if (!clnors) {
499 return result;
500 }
501
502 WeightedNormalData wn_data{};
503 wn_data.verts_num = verts_num;
504
505 wn_data.vert_positions = positions;
506 wn_data.vert_normals = result->vert_normals();
507 wn_data.sharp_edges = sharp_edges.span;
508
509 wn_data.corner_verts = corner_verts;
510 wn_data.corner_edges = corner_edges;
511 wn_data.vert_to_face_map = result->vert_to_face_map();
512 wn_data.loop_to_face = loop_to_face_map;
513 wn_data.clnors = clnors.span;
514
515 wn_data.faces = faces;
516 wn_data.face_normals = mesh->face_normals_true();
517 wn_data.sharp_faces = *attributes.lookup<bool>("sharp_face", bke::AttrDomain::Face);
520
521 wn_data.dvert = dvert;
522 wn_data.defgrp_index = defgrp_index;
524
525 wn_data.weight = weight;
526 wn_data.mode = wnmd->mode;
527
528 switch (wnmd->mode) {
530 wn_face_area(wnmd, &wn_data);
531 break;
533 wn_corner_angle(wnmd, &wn_data);
534 break;
536 wn_face_with_angle(wnmd, &wn_data);
537 break;
538 }
539
540 MEM_SAFE_FREE(wn_data.mode_pair);
541
542 result->runtime->is_original_bmesh = false;
543
544 sharp_edges.finish();
545 clnors.finish();
546
547 return result;
548}
549
558
559static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
560{
562
563 if (wnmd->defgrp_name[0] != '\0') {
564 r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
565 }
566
568 r_cddata_masks->pmask |= CD_MASK_PROP_INT32;
569 }
570}
571
572static void panel_draw(const bContext * /*C*/, Panel *panel)
573{
574 uiLayout *col;
575 uiLayout *layout = panel->layout;
576
577 PointerRNA ob_ptr;
579
580 layout->use_property_split_set(true);
581
582 layout->prop(ptr, "mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
583
584 layout->prop(ptr, "weight", UI_ITEM_NONE, IFACE_("Weight"), ICON_NONE);
585 layout->prop(ptr, "thresh", UI_ITEM_NONE, IFACE_("Threshold"), ICON_NONE);
586
587 col = &layout->column(false);
588 col->prop(ptr, "keep_sharp", UI_ITEM_NONE, std::nullopt, ICON_NONE);
589 col->prop(ptr, "use_face_influence", UI_ITEM_NONE, std::nullopt, ICON_NONE);
590
591 modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", std::nullopt);
592
594}
595
596static void panel_register(ARegionType *region_type)
597{
599}
600
602 /*idname*/ "WeightedNormal",
603 /*name*/ N_("WeightedNormal"),
604 /*struct_name*/ "WeightedNormalModifierData",
605 /*struct_size*/ sizeof(WeightedNormalModifierData),
606 /*srna*/ &RNA_WeightedNormalModifier,
610 /*icon*/ ICON_MOD_NORMALEDIT,
611
612 /*copy_data*/ BKE_modifier_copydata_generic,
613
614 /*deform_verts*/ nullptr,
615 /*deform_matrices*/ nullptr,
616 /*deform_verts_EM*/ nullptr,
617 /*deform_matrices_EM*/ nullptr,
618 /*modify_mesh*/ modify_mesh,
619 /*modify_geometry_set*/ nullptr,
620
621 /*init_data*/ init_data,
622 /*required_data_mask*/ required_data_mask,
623 /*free_data*/ nullptr,
624 /*is_disabled*/ nullptr,
625 /*update_depsgraph*/ nullptr,
626 /*depends_on_time*/ nullptr,
627 /*depends_on_normals*/ nullptr,
628 /*foreach_ID_link*/ nullptr,
629 /*foreach_tex_link*/ nullptr,
630 /*free_runtime_data*/ nullptr,
631 /*panel_register*/ panel_register,
632 /*blend_write*/ nullptr,
633 /*blend_read*/ nullptr,
634 /*foreach_cache*/ nullptr,
635 /*foreach_working_space_color*/ nullptr,
636};
support for deformation groups and hooks.
MDeformWeight * BKE_defvert_find_index(const MDeformVert *dv, int defgroup)
Definition deform.cc:806
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:777
@ LIB_ID_COPY_LOCALIZE
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)
#define CD_MASK_MDEFORMVERT
#define CD_MASK_PROP_INT32
#define DNA_struct_default_get(struct_name)
@ MOD_WEIGHTEDNORMAL_MODE_FACE
@ MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE
@ MOD_WEIGHTEDNORMAL_MODE_ANGLE
@ eModifierType_WeightedNormal
#define MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID
@ MOD_WEIGHTEDNORMAL_KEEP_SHARP
@ MOD_WEIGHTEDNORMAL_FACE_INFLUENCE
@ MOD_WEIGHTEDNORMAL_INVERT_VGROUP
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
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)
#define UI_ITEM_NONE
@ FACE_STRENGTH_WEAK
IndexRange index_range() const
Definition BLI_array.hh:360
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
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, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
nullptr float
#define powf(x, y)
#define CLNORS_VALID_VEC_LEN
uint col
VecBase< short, 2 > short2
VecBase< float, 3 > float3
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::VArray< int > face_strength
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:165
uiLayout & column(bool align)
void use_property_split_set(bool value)
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:4238