diff options
Diffstat (limited to 'source/blender/modifiers/intern/MOD_weighted_normal.cc')
-rw-r--r-- | source/blender/modifiers/intern/MOD_weighted_normal.cc | 774 |
1 files changed, 774 insertions, 0 deletions
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.cc b/source/blender/modifiers/intern/MOD_weighted_normal.cc new file mode 100644 index 00000000000..cb81781479a --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weighted_normal.cc @@ -0,0 +1,774 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap.h" +#include "BLI_linklist.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_defaults.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "MOD_modifiertypes.h" +#include "MOD_ui_common.h" +#include "MOD_util.h" + +#include "bmesh.h" + +#define CLNORS_VALID_VEC_LEN (1e-6f) + +struct ModePair { + float val; /* Contains mode based value (face area / corner angle). */ + int index; /* Index value per poly or per loop. */ +}; + +/* Sorting function used in modifier, sorts in decreasing order. */ +static int modepair_cmp_by_val_inverse(const void *p1, const void *p2) +{ + ModePair *r1 = (ModePair *)p1; + ModePair *r2 = (ModePair *)p2; + + return (r1->val < r2->val) ? 1 : ((r1->val > r2->val) ? -1 : 0); +} + +/* There will be one of those per vertex + * (simple case, computing one normal per vertex), or per smooth fan. */ +struct WeightedNormalDataAggregateItem { + float normal[3]; + + int loops_num; /* Count number of loops using this item so far. */ + float curr_val; /* Current max val for this item. */ + int curr_strength; /* Current max strength encountered for this item. */ +}; + +#define NUM_CACHED_INVERSE_POWERS_OF_WEIGHT 128 + +struct WeightedNormalData { + int verts_num; + int edges_num; + int loops_num; + int polys_num; + + const MVert *mvert; + const float (*vert_normals)[3]; + MEdge *medge; + + const MLoop *mloop; + short (*clnors)[2]; + bool has_clnors; /* True if clnors already existed, false if we had to create them. */ + float split_angle; + + const MPoly *mpoly; + const float (*polynors)[3]; + const int *poly_strength; + + const MDeformVert *dvert; + int defgrp_index; + bool use_invert_vgroup; + + float weight; + short mode; + + /* Lower-level, internal processing data. */ + float cached_inverse_powers_of_weight[NUM_CACHED_INVERSE_POWERS_OF_WEIGHT]; + + WeightedNormalDataAggregateItem *items_data; + + ModePair *mode_pair; + + int *loop_to_poly; +}; + +/** + * Check strength of given poly compared to those found so far for that given item + * (vertex or smooth fan), and reset matching item_data in case we get a stronger new strength. + */ +static bool check_item_poly_strength(WeightedNormalData *wn_data, + WeightedNormalDataAggregateItem *item_data, + const int mp_index) +{ + BLI_assert(wn_data->poly_strength != nullptr); + + const int mp_strength = wn_data->poly_strength[mp_index]; + + if (mp_strength > item_data->curr_strength) { + item_data->curr_strength = mp_strength; + item_data->curr_val = 0.0f; + item_data->loops_num = 0; + zero_v3(item_data->normal); + } + + return mp_strength == item_data->curr_strength; +} + +static void aggregate_item_normal(WeightedNormalModifierData *wnmd, + WeightedNormalData *wn_data, + WeightedNormalDataAggregateItem *item_data, + const int mv_index, + const int mp_index, + const float curr_val, + const bool use_face_influence) +{ + const float(*polynors)[3] = wn_data->polynors; + + const MDeformVert *dvert = wn_data->dvert; + const int defgrp_index = wn_data->defgrp_index; + const bool use_invert_vgroup = wn_data->use_invert_vgroup; + + const float weight = wn_data->weight; + + float *cached_inverse_powers_of_weight = wn_data->cached_inverse_powers_of_weight; + + const bool has_vgroup = dvert != nullptr; + const bool vert_of_group = has_vgroup && + BKE_defvert_find_index(&dvert[mv_index], defgrp_index) != nullptr; + + if (has_vgroup && + ((vert_of_group && use_invert_vgroup) || (!vert_of_group && !use_invert_vgroup))) { + return; + } + + if (use_face_influence && !check_item_poly_strength(wn_data, item_data, mp_index)) { + return; + } + + /* If item's curr_val is 0 init it to present value. */ + if (item_data->curr_val == 0.0f) { + item_data->curr_val = curr_val; + } + if (!compare_ff(item_data->curr_val, curr_val, wnmd->thresh)) { + /* item's curr_val and present value differ more than threshold, update. */ + item_data->loops_num++; + item_data->curr_val = curr_val; + } + + /* Exponentially divided weight for each normal + * (since a few values will be used by most cases, we cache those). */ + const int loops_num = item_data->loops_num; + if (loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT && + cached_inverse_powers_of_weight[loops_num] == 0.0f) { + cached_inverse_powers_of_weight[loops_num] = 1.0f / powf(weight, loops_num); + } + const float inverted_n_weight = loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT ? + cached_inverse_powers_of_weight[loops_num] : + 1.0f / powf(weight, loops_num); + + madd_v3_v3fl(item_data->normal, polynors[mp_index], curr_val * inverted_n_weight); +} + +static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, + WeightedNormalData *wn_data) +{ + const int verts_num = wn_data->verts_num; + const int edges_num = wn_data->edges_num; + const int loops_num = wn_data->loops_num; + const int polys_num = wn_data->polys_num; + + const MVert *mvert = wn_data->mvert; + MEdge *medge = wn_data->medge; + + const MLoop *mloop = wn_data->mloop; + short(*clnors)[2] = wn_data->clnors; + int *loop_to_poly = wn_data->loop_to_poly; + + const MPoly *mpoly = wn_data->mpoly; + const float(*polynors)[3] = wn_data->polynors; + const int *poly_strength = wn_data->poly_strength; + + const MDeformVert *dvert = wn_data->dvert; + + const short mode = wn_data->mode; + ModePair *mode_pair = wn_data->mode_pair; + + const bool has_clnors = wn_data->has_clnors; + const float split_angle = wn_data->split_angle; + MLoopNorSpaceArray lnors_spacearr = {nullptr}; + + const bool keep_sharp = (wnmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0; + const bool use_face_influence = (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) != 0 && + poly_strength != nullptr; + const bool has_vgroup = dvert != nullptr; + + float(*loop_normals)[3] = nullptr; + + WeightedNormalDataAggregateItem *items_data = nullptr; + int items_num = 0; + if (keep_sharp) { + BLI_bitmap *done_loops = BLI_BITMAP_NEW(loops_num, __func__); + + /* This will give us loop normal spaces, + * we do not actually care about computed loop_normals for now... */ + loop_normals = static_cast<float(*)[3]>( + MEM_calloc_arrayN((size_t)loops_num, sizeof(*loop_normals), __func__)); + BKE_mesh_normals_loop_split(mvert, + wn_data->vert_normals, + verts_num, + medge, + edges_num, + mloop, + loop_normals, + loops_num, + mpoly, + polynors, + polys_num, + true, + split_angle, + &lnors_spacearr, + has_clnors ? clnors : nullptr, + loop_to_poly); + + items_num = lnors_spacearr.spaces_num; + items_data = static_cast<WeightedNormalDataAggregateItem *>( + MEM_calloc_arrayN((size_t)items_num, sizeof(*items_data), __func__)); + + /* In this first loop, we assign each WeightedNormalDataAggregateItem + * to its smooth fan of loops (aka lnor space). */ + const MPoly *mp; + int mp_index; + int item_index; + for (mp = mpoly, mp_index = 0, item_index = 0; mp_index < polys_num; mp++, mp_index++) { + int ml_index = mp->loopstart; + const int ml_end_index = ml_index + mp->totloop; + + for (; ml_index < ml_end_index; ml_index++) { + if (BLI_BITMAP_TEST(done_loops, ml_index)) { + /* Smooth fan of this loop has already been processed, skip it. */ + continue; + } + BLI_assert(item_index < items_num); + + WeightedNormalDataAggregateItem *itdt = &items_data[item_index]; + itdt->curr_strength = FACE_STRENGTH_WEAK; + + MLoopNorSpace *lnor_space = lnors_spacearr.lspacearr[ml_index]; + lnor_space->user_data = itdt; + + if (!(lnor_space->flags & MLNOR_SPACE_IS_SINGLE)) { + for (LinkNode *lnode = lnor_space->loops; lnode; lnode = lnode->next) { + const int ml_fan_index = POINTER_AS_INT(lnode->link); + BLI_BITMAP_ENABLE(done_loops, ml_fan_index); + } + } + else { + BLI_BITMAP_ENABLE(done_loops, ml_index); + } + + item_index++; + } + } + + MEM_freeN(done_loops); + } + else { + items_num = verts_num; + items_data = static_cast<WeightedNormalDataAggregateItem *>( + MEM_calloc_arrayN((size_t)items_num, sizeof(*items_data), __func__)); + if (use_face_influence) { + for (int item_index = 0; item_index < items_num; item_index++) { + items_data[item_index].curr_strength = FACE_STRENGTH_WEAK; + } + } + } + wn_data->items_data = items_data; + + switch (mode) { + case MOD_WEIGHTEDNORMAL_MODE_FACE: + for (int i = 0; i < polys_num; i++) { + const int mp_index = mode_pair[i].index; + const float mp_val = mode_pair[i].val; + + int ml_index = mpoly[mp_index].loopstart; + const int ml_index_end = ml_index + mpoly[mp_index].totloop; + for (; ml_index < ml_index_end; ml_index++) { + const int mv_index = mloop[ml_index].v; + WeightedNormalDataAggregateItem *item_data = + keep_sharp ? static_cast<WeightedNormalDataAggregateItem *>( + lnors_spacearr.lspacearr[ml_index]->user_data) : + &items_data[mv_index]; + + aggregate_item_normal( + wnmd, wn_data, item_data, mv_index, mp_index, mp_val, use_face_influence); + } + } + break; + case MOD_WEIGHTEDNORMAL_MODE_ANGLE: + case MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE: + BLI_assert(loop_to_poly != nullptr); + + for (int i = 0; i < loops_num; i++) { + const int ml_index = mode_pair[i].index; + const float ml_val = mode_pair[i].val; + + const int mp_index = loop_to_poly[ml_index]; + const int mv_index = mloop[ml_index].v; + WeightedNormalDataAggregateItem *item_data = + keep_sharp ? static_cast<WeightedNormalDataAggregateItem *>( + lnors_spacearr.lspacearr[ml_index]->user_data) : + &items_data[mv_index]; + + aggregate_item_normal( + wnmd, wn_data, item_data, mv_index, mp_index, ml_val, use_face_influence); + } + break; + default: + BLI_assert_unreachable(); + } + + /* Validate computed weighted normals. */ + for (int item_index = 0; item_index < items_num; item_index++) { + if (normalize_v3(items_data[item_index].normal) < CLNORS_VALID_VEC_LEN) { + zero_v3(items_data[item_index].normal); + } + } + + if (keep_sharp) { + /* Set loop normals for normal computed for each lnor space (smooth fan). + * Note that loop_normals is already populated with clnors + * (before this modifier is applied, at start of this function), + * so no need to recompute them here. */ + for (int ml_index = 0; ml_index < loops_num; ml_index++) { + WeightedNormalDataAggregateItem *item_data = static_cast<WeightedNormalDataAggregateItem *>( + lnors_spacearr.lspacearr[ml_index]->user_data); + if (!is_zero_v3(item_data->normal)) { + copy_v3_v3(loop_normals[ml_index], item_data->normal); + } + } + + BKE_mesh_normals_loop_custom_set(mvert, + wn_data->vert_normals, + verts_num, + medge, + edges_num, + mloop, + loop_normals, + loops_num, + mpoly, + polynors, + polys_num, + clnors); + } + else { + /* TODO: Ideally, we could add an option to `BKE_mesh_normals_loop_custom_[from_verts_]set()` + * to keep current clnors instead of resetting them to default auto-computed ones, + * when given new custom normal is zero-vec. + * But this is not exactly trivial change, better to keep this optimization for later... + */ + if (!has_vgroup) { + /* NOTE: in theory, we could avoid this extra allocation & copying... + * But think we can live with it for now, + * and it makes code simpler & cleaner. */ + float(*vert_normals)[3] = static_cast<float(*)[3]>( + MEM_calloc_arrayN((size_t)verts_num, sizeof(*loop_normals), __func__)); + + for (int ml_index = 0; ml_index < loops_num; ml_index++) { + const int mv_index = mloop[ml_index].v; + copy_v3_v3(vert_normals[mv_index], items_data[mv_index].normal); + } + + BKE_mesh_normals_loop_custom_from_verts_set(mvert, + wn_data->vert_normals, + vert_normals, + verts_num, + medge, + edges_num, + mloop, + loops_num, + mpoly, + polynors, + polys_num, + clnors); + + MEM_freeN(vert_normals); + } + else { + loop_normals = static_cast<float(*)[3]>( + MEM_calloc_arrayN((size_t)loops_num, sizeof(*loop_normals), __func__)); + + BKE_mesh_normals_loop_split(mvert, + wn_data->vert_normals, + verts_num, + medge, + edges_num, + mloop, + loop_normals, + loops_num, + mpoly, + polynors, + polys_num, + true, + split_angle, + nullptr, + has_clnors ? clnors : nullptr, + loop_to_poly); + + for (int ml_index = 0; ml_index < loops_num; ml_index++) { + const int item_index = mloop[ml_index].v; + if (!is_zero_v3(items_data[item_index].normal)) { + copy_v3_v3(loop_normals[ml_index], items_data[item_index].normal); + } + } + + BKE_mesh_normals_loop_custom_set(mvert, + wn_data->vert_normals, + verts_num, + medge, + edges_num, + mloop, + loop_normals, + loops_num, + mpoly, + polynors, + polys_num, + clnors); + } + } + + if (keep_sharp) { + BKE_lnor_spacearr_free(&lnors_spacearr); + } + MEM_SAFE_FREE(loop_normals); +} + +static void wn_face_area(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data) +{ + const int polys_num = wn_data->polys_num; + + const MVert *mvert = wn_data->mvert; + const MLoop *mloop = wn_data->mloop; + const MPoly *mpoly = wn_data->mpoly; + + const MPoly *mp; + int mp_index; + + ModePair *face_area = static_cast<ModePair *>( + MEM_malloc_arrayN((size_t)polys_num, sizeof(*face_area), __func__)); + + ModePair *f_area = face_area; + for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++, f_area++) { + f_area->val = BKE_mesh_calc_poly_area(mp, &mloop[mp->loopstart], mvert); + f_area->index = mp_index; + } + + qsort(face_area, polys_num, sizeof(*face_area), modepair_cmp_by_val_inverse); + + wn_data->mode_pair = face_area; + apply_weights_vertex_normal(wnmd, wn_data); +} + +static void wn_corner_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data) +{ + const int loops_num = wn_data->loops_num; + const int polys_num = wn_data->polys_num; + + const MVert *mvert = wn_data->mvert; + const MLoop *mloop = wn_data->mloop; + const MPoly *mpoly = wn_data->mpoly; + + const MPoly *mp; + int mp_index; + + int *loop_to_poly = static_cast<int *>( + MEM_malloc_arrayN((size_t)loops_num, sizeof(*loop_to_poly), __func__)); + + ModePair *corner_angle = static_cast<ModePair *>( + MEM_malloc_arrayN((size_t)loops_num, sizeof(*corner_angle), __func__)); + + for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++) { + const MLoop *ml_start = &mloop[mp->loopstart]; + + float *index_angle = static_cast<float *>( + MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__)); + BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle); + + ModePair *c_angl = &corner_angle[mp->loopstart]; + float *angl = index_angle; + for (int ml_index = mp->loopstart; ml_index < mp->loopstart + mp->totloop; + ml_index++, c_angl++, angl++) { + c_angl->val = (float)M_PI - *angl; + c_angl->index = ml_index; + + loop_to_poly[ml_index] = mp_index; + } + MEM_freeN(index_angle); + } + + qsort(corner_angle, loops_num, sizeof(*corner_angle), modepair_cmp_by_val_inverse); + + wn_data->loop_to_poly = loop_to_poly; + wn_data->mode_pair = corner_angle; + apply_weights_vertex_normal(wnmd, wn_data); +} + +static void wn_face_with_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data) +{ + const int loops_num = wn_data->loops_num; + const int polys_num = wn_data->polys_num; + + const MVert *mvert = wn_data->mvert; + const MLoop *mloop = wn_data->mloop; + const MPoly *mpoly = wn_data->mpoly; + + const MPoly *mp; + int mp_index; + + int *loop_to_poly = static_cast<int *>( + MEM_malloc_arrayN((size_t)loops_num, sizeof(*loop_to_poly), __func__)); + + ModePair *combined = static_cast<ModePair *>( + MEM_malloc_arrayN((size_t)loops_num, sizeof(*combined), __func__)); + + for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++) { + const MLoop *ml_start = &mloop[mp->loopstart]; + + float face_area = BKE_mesh_calc_poly_area(mp, ml_start, mvert); + float *index_angle = static_cast<float *>( + MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__)); + BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle); + + ModePair *cmbnd = &combined[mp->loopstart]; + float *angl = index_angle; + for (int ml_index = mp->loopstart; ml_index < mp->loopstart + mp->totloop; + ml_index++, cmbnd++, angl++) { + /* In this case val is product of corner angle and face area. */ + cmbnd->val = ((float)M_PI - *angl) * face_area; + cmbnd->index = ml_index; + + loop_to_poly[ml_index] = mp_index; + } + MEM_freeN(index_angle); + } + + qsort(combined, loops_num, sizeof(*combined), modepair_cmp_by_val_inverse); + + wn_data->loop_to_poly = loop_to_poly; + wn_data->mode_pair = combined; + apply_weights_vertex_normal(wnmd, wn_data); +} + +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +{ + WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; + Object *ob = ctx->object; + + /* XXX TODO(Rohan Rathi): + * Once we fully switch to Mesh evaluation of modifiers, + * we can expect to get that flag from the COW copy. + * But for now, it is lost in the DM intermediate step, + * so we need to directly check orig object's data. */ +#if 0 + if (!(mesh->flag & ME_AUTOSMOOTH)) +#else + if (!(((Mesh *)ob->data)->flag & ME_AUTOSMOOTH)) +#endif + { + BKE_modifier_set_error( + ctx->object, (ModifierData *)wnmd, "Enable 'Auto Smooth' in Object Data Properties"); + return mesh; + } + + Mesh *result; + result = (Mesh *)BKE_id_copy_ex(nullptr, &mesh->id, nullptr, LIB_ID_COPY_LOCALIZE); + + const int verts_num = result->totvert; + const int edges_num = result->totedge; + const int loops_num = result->totloop; + const int polys_num = result->totpoly; + const MVert *mvert = BKE_mesh_verts(result); + MEdge *medge = BKE_mesh_edges_for_write(result); + const MPoly *mpoly = BKE_mesh_polys(result); + const MLoop *mloop = BKE_mesh_loops(result); + + /* Right now: + * If weight = 50 then all faces are given equal weight. + * If weight > 50 then more weight given to faces with larger vals (face area / corner angle). + * If weight < 50 then more weight given to faces with lesser vals. However current calculation + * does not converge to min/max. + */ + float weight = ((float)wnmd->weight) / 50.0f; + if (wnmd->weight == 100) { + weight = (float)SHRT_MAX; + } + else if (wnmd->weight == 1) { + weight = 1 / (float)SHRT_MAX; + } + else if ((weight - 1) * 25 > 1) { + weight = (weight - 1) * 25; + } + + const float split_angle = mesh->smoothresh; + short(*clnors)[2] = static_cast<short(*)[2]>( + CustomData_get_layer(&result->ldata, CD_CUSTOMLOOPNORMAL)); + + /* Keep info whether we had clnors, + * it helps when generating clnor spaces and default normals. */ + const bool has_clnors = clnors != nullptr; + if (!clnors) { + clnors = static_cast<short(*)[2]>(CustomData_add_layer( + &result->ldata, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, nullptr, loops_num)); + } + + const MDeformVert *dvert; + int defgrp_index; + MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index); + + WeightedNormalData wn_data{}; + wn_data.verts_num = verts_num; + wn_data.edges_num = edges_num; + wn_data.loops_num = loops_num; + wn_data.polys_num = polys_num; + + wn_data.mvert = mvert; + wn_data.vert_normals = BKE_mesh_vertex_normals_ensure(result); + wn_data.medge = medge; + + wn_data.mloop = mloop; + wn_data.clnors = clnors; + wn_data.has_clnors = has_clnors; + wn_data.split_angle = split_angle; + + wn_data.mpoly = mpoly; + wn_data.polynors = BKE_mesh_poly_normals_ensure(mesh); + wn_data.poly_strength = static_cast<const int *>(CustomData_get_layer_named( + &result->pdata, CD_PROP_INT32, MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID)); + + wn_data.dvert = dvert; + wn_data.defgrp_index = defgrp_index; + wn_data.use_invert_vgroup = (wnmd->flag & MOD_WEIGHTEDNORMAL_INVERT_VGROUP) != 0; + + wn_data.weight = weight; + wn_data.mode = wnmd->mode; + + switch (wnmd->mode) { + case MOD_WEIGHTEDNORMAL_MODE_FACE: + wn_face_area(wnmd, &wn_data); + break; + case MOD_WEIGHTEDNORMAL_MODE_ANGLE: + wn_corner_angle(wnmd, &wn_data); + break; + case MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE: + wn_face_with_angle(wnmd, &wn_data); + break; + } + + MEM_SAFE_FREE(wn_data.loop_to_poly); + MEM_SAFE_FREE(wn_data.mode_pair); + MEM_SAFE_FREE(wn_data.items_data); + + result->runtime.is_original_bmesh = false; + + return result; +} + +static void initData(ModifierData *md) +{ + WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wnmd, modifier)); + + MEMCPY_STRUCT_AFTER(wnmd, DNA_struct_default_get(WeightedNormalModifierData), modifier); +} + +static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) +{ + WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; + + r_cddata_masks->lmask = CD_MASK_CUSTOMLOOPNORMAL; + + if (wnmd->defgrp_name[0] != '\0') { + r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; + } + + if (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) { + r_cddata_masks->pmask |= CD_MASK_PROP_INT32; + } +} + +static bool dependsOnNormals(ModifierData *UNUSED(md)) +{ + return true; +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "mode", 0, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "weight", 0, IFACE_("Weight"), ICON_NONE); + uiItemR(layout, ptr, "thresh", 0, IFACE_("Threshold"), ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "keep_sharp", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_face_influence", 0, nullptr, ICON_NONE); + + modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr); + + modifier_panel_end(layout, ptr); +} + +static void panelRegister(ARegionType *region_type) +{ + modifier_panel_register(region_type, eModifierType_WeightedNormal, panel_draw); +} + +ModifierTypeInfo modifierType_WeightedNormal = { + /* name */ N_("WeightedNormal"), + /* structName */ "WeightedNormalModifierData", + /* structSize */ sizeof(WeightedNormalModifierData), + /* srna */ &RNA_WeightedNormalModifier, + /* type */ eModifierTypeType_Constructive, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | + eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode, + /* icon */ ICON_MOD_VERTEX_WEIGHT, + + /* copyData */ BKE_modifier_copydata_generic, + + /* deformVerts */ nullptr, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ modifyMesh, + /* modifyGeometrySet */ nullptr, + + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ nullptr, + /* isDisabled */ nullptr, + /* updateDepsgraph */ nullptr, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ dependsOnNormals, + /* foreachIDLink */ nullptr, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, + /* panelRegister */ panelRegister, + /* blendWrite */ nullptr, + /* blendRead */ nullptr, +}; |