Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/modifiers/intern/MOD_weighted_normal.c')
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c1082
1 files changed, 572 insertions, 510 deletions
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index 98b2117e5a3..2162f223b7a 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -38,616 +38,678 @@
#define CLNORS_VALID_VEC_LEN (1e-6f)
typedef struct ModePair {
- float val; /* Contains mode based value (face area / corner angle). */
- int index; /* Index value per poly or per loop. */
+ float val; /* Contains mode based value (face area / corner angle). */
+ int index; /* Index value per poly or per loop. */
} ModePair;
/* 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;
+ ModePair *r1 = (ModePair *)p1;
+ ModePair *r2 = (ModePair *)p2;
- return (r1->val < r2->val) ? 1 : ((r1->val > r2->val) ? -1 : 0);
+ 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. */
typedef struct WeightedNormalDataAggregateItem {
- float normal[3];
+ float normal[3];
- int num_loops; /* 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. */
+ int num_loops; /* 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. */
} WeightedNormalDataAggregateItem;
#define NUM_CACHED_INVERSE_POWERS_OF_WEIGHT 128
typedef struct WeightedNormalData {
- const int numVerts;
- const int numEdges;
- const int numLoops;
- const int numPolys;
+ const int numVerts;
+ const int numEdges;
+ const int numLoops;
+ const int numPolys;
- MVert *mvert;
- MEdge *medge;
+ MVert *mvert;
+ MEdge *medge;
- MLoop *mloop;
- short (*clnors)[2];
- const bool has_clnors; /* True if clnors already existed, false if we had to create them. */
- const float split_angle;
+ MLoop *mloop;
+ short (*clnors)[2];
+ const bool has_clnors; /* True if clnors already existed, false if we had to create them. */
+ const float split_angle;
- MPoly *mpoly;
- float (*polynors)[3];
- int *poly_strength;
+ MPoly *mpoly;
+ float (*polynors)[3];
+ int *poly_strength;
- MDeformVert *dvert;
- const int defgrp_index;
- const bool use_invert_vgroup;
+ MDeformVert *dvert;
+ const int defgrp_index;
+ const bool use_invert_vgroup;
- const float weight;
- const short mode;
+ const float weight;
+ const short mode;
- /* Lower-level, internal processing data. */
- float cached_inverse_powers_of_weight[NUM_CACHED_INVERSE_POWERS_OF_WEIGHT];
+ /* Lower-level, internal processing data. */
+ float cached_inverse_powers_of_weight[NUM_CACHED_INVERSE_POWERS_OF_WEIGHT];
- WeightedNormalDataAggregateItem *items_data;
+ WeightedNormalDataAggregateItem *items_data;
- ModePair *mode_pair;
+ ModePair *mode_pair;
- int *loop_to_poly;
+ int *loop_to_poly;
} WeightedNormalData;
/* 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)
+static bool check_item_poly_strength(WeightedNormalData *wn_data,
+ WeightedNormalDataAggregateItem *item_data,
+ const int mp_index)
{
- BLI_assert (wn_data->poly_strength != NULL);
+ BLI_assert(wn_data->poly_strength != NULL);
- const int mp_strength = wn_data->poly_strength[mp_index];
+ 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->num_loops = 0;
- zero_v3(item_data->normal);
- }
+ if (mp_strength > item_data->curr_strength) {
+ item_data->curr_strength = mp_strength;
+ item_data->curr_val = 0.0f;
+ item_data->num_loops = 0;
+ zero_v3(item_data->normal);
+ }
- return mp_strength == item_data->curr_strength;
+ 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)
+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)
{
- float (*polynors)[3] = wn_data->polynors;
-
- 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 != NULL;
- const bool vert_of_group = has_vgroup && defvert_find_index(&dvert[mv_index], defgrp_index) != NULL;
-
- 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->num_loops++;
- 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 num_loops = item_data->num_loops;
- if (num_loops < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT && cached_inverse_powers_of_weight[num_loops] == 0.0f) {
- cached_inverse_powers_of_weight[num_loops] = 1.0f / powf(weight, num_loops);
- }
- const float inverted_n_weight = num_loops < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT ?
- cached_inverse_powers_of_weight[num_loops] : 1.0f / powf(weight, num_loops);
-
- madd_v3_v3fl(item_data->normal, polynors[mp_index], curr_val * inverted_n_weight);
+ float(*polynors)[3] = wn_data->polynors;
+
+ 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 != NULL;
+ const bool vert_of_group = has_vgroup &&
+ defvert_find_index(&dvert[mv_index], defgrp_index) != NULL;
+
+ 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->num_loops++;
+ 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 num_loops = item_data->num_loops;
+ if (num_loops < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT &&
+ cached_inverse_powers_of_weight[num_loops] == 0.0f) {
+ cached_inverse_powers_of_weight[num_loops] = 1.0f / powf(weight, num_loops);
+ }
+ const float inverted_n_weight = num_loops < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT ?
+ cached_inverse_powers_of_weight[num_loops] :
+ 1.0f / powf(weight, num_loops);
+
+ 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)
+static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
+ WeightedNormalData *wn_data)
{
- const int numVerts = wn_data->numVerts;
- const int numEdges = wn_data->numEdges;
- const int numLoops = wn_data->numLoops;
- const int numPolys = wn_data->numPolys;
-
- MVert *mvert = wn_data->mvert;
- MEdge *medge = wn_data->medge;
-
- MLoop *mloop = wn_data->mloop;
- short (*clnors)[2] = wn_data->clnors;
- int *loop_to_poly = wn_data->loop_to_poly;
-
- MPoly *mpoly = wn_data->mpoly;
- float (*polynors)[3] = wn_data->polynors;
- int *poly_strength = wn_data->poly_strength;
-
- 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 = {NULL};
-
- 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 != NULL;
- const bool has_vgroup = dvert != NULL;
-
- float (*loop_normals)[3] = NULL;
-
- WeightedNormalDataAggregateItem *items_data = NULL;
- int num_items = 0;
- if (keep_sharp) {
- BLI_bitmap *done_loops = BLI_BITMAP_NEW(numLoops, __func__);
-
- /* This will give us loop normal spaces, we do not actually care about computed loop_normals for now... */
- loop_normals = MEM_calloc_arrayN((size_t)numLoops, sizeof(*loop_normals), __func__);
- BKE_mesh_normals_loop_split(mvert, numVerts, medge, numEdges,
- mloop, loop_normals, numLoops, mpoly, polynors, numPolys,
- true, split_angle, &lnors_spacearr, has_clnors ? clnors : NULL, loop_to_poly);
-
- num_items = lnors_spacearr.num_spaces;
- items_data = MEM_calloc_arrayN((size_t)num_items, sizeof(*items_data), __func__);
-
- /* In this first loop, we assign each WeightedNormalDataAggregateItem
- * to its smooth fan of loops (aka lnor space). */
- MPoly *mp;
- int mp_index;
- int item_index;
- for (mp = mpoly, mp_index = 0, item_index = 0; mp_index < numPolys; 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 < num_items);
-
- 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 {
- num_items = numVerts;
- items_data = MEM_calloc_arrayN((size_t)num_items, sizeof(*items_data), __func__);
- if (use_face_influence) {
- for (int item_index = 0; item_index < num_items; 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 < numPolys; 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 ?
- 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 != NULL);
-
- for (int i = 0; i < numLoops; 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 ?
- 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(0);
- }
-
- /* Validate computed weighted normals. */
- for (int item_index = 0; item_index < num_items; 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 < numLoops; ml_index++) {
- WeightedNormalDataAggregateItem *item_data = 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, numVerts, medge, numEdges,
- mloop, loop_normals, numLoops, mpoly, polynors, numPolys, clnors);
- }
- else {
- /* TODO: Ideally, we could add an option to BKE_mesh_normals_loop_custom_[from_vertices_]set() to keep current
- * clnors instead of resetting them to default autocomputed 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] = MEM_calloc_arrayN((size_t)numVerts, sizeof(*loop_normals), __func__);
-
- for (int ml_index = 0; ml_index < numLoops; 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_vertices_set(mvert, vert_normals, numVerts, medge, numEdges,
- mloop, numLoops, mpoly, polynors, numPolys, clnors);
-
- MEM_freeN(vert_normals);
- }
- else {
- loop_normals = MEM_calloc_arrayN((size_t)numLoops, sizeof(*loop_normals), __func__);
-
- BKE_mesh_normals_loop_split(mvert, numVerts, medge, numEdges,
- mloop, loop_normals, numLoops, mpoly, polynors, numPolys,
- true, split_angle, NULL, has_clnors ? clnors : NULL, loop_to_poly);
-
- for (int ml_index = 0; ml_index < numLoops; 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, numVerts, medge, numEdges,
- mloop, loop_normals, numLoops, mpoly, polynors, numPolys, clnors);
- }
- }
-
- if (keep_sharp) {
- BKE_lnor_spacearr_free(&lnors_spacearr);
- }
- MEM_SAFE_FREE(loop_normals);
+ const int numVerts = wn_data->numVerts;
+ const int numEdges = wn_data->numEdges;
+ const int numLoops = wn_data->numLoops;
+ const int numPolys = wn_data->numPolys;
+
+ MVert *mvert = wn_data->mvert;
+ MEdge *medge = wn_data->medge;
+
+ MLoop *mloop = wn_data->mloop;
+ short(*clnors)[2] = wn_data->clnors;
+ int *loop_to_poly = wn_data->loop_to_poly;
+
+ MPoly *mpoly = wn_data->mpoly;
+ float(*polynors)[3] = wn_data->polynors;
+ int *poly_strength = wn_data->poly_strength;
+
+ 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 = {NULL};
+
+ 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 != NULL;
+ const bool has_vgroup = dvert != NULL;
+
+ float(*loop_normals)[3] = NULL;
+
+ WeightedNormalDataAggregateItem *items_data = NULL;
+ int num_items = 0;
+ if (keep_sharp) {
+ BLI_bitmap *done_loops = BLI_BITMAP_NEW(numLoops, __func__);
+
+ /* This will give us loop normal spaces, we do not actually care about computed loop_normals for now... */
+ loop_normals = MEM_calloc_arrayN((size_t)numLoops, sizeof(*loop_normals), __func__);
+ BKE_mesh_normals_loop_split(mvert,
+ numVerts,
+ medge,
+ numEdges,
+ mloop,
+ loop_normals,
+ numLoops,
+ mpoly,
+ polynors,
+ numPolys,
+ true,
+ split_angle,
+ &lnors_spacearr,
+ has_clnors ? clnors : NULL,
+ loop_to_poly);
+
+ num_items = lnors_spacearr.num_spaces;
+ items_data = MEM_calloc_arrayN((size_t)num_items, sizeof(*items_data), __func__);
+
+ /* In this first loop, we assign each WeightedNormalDataAggregateItem
+ * to its smooth fan of loops (aka lnor space). */
+ MPoly *mp;
+ int mp_index;
+ int item_index;
+ for (mp = mpoly, mp_index = 0, item_index = 0; mp_index < numPolys; 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 < num_items);
+
+ 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 {
+ num_items = numVerts;
+ items_data = MEM_calloc_arrayN((size_t)num_items, sizeof(*items_data), __func__);
+ if (use_face_influence) {
+ for (int item_index = 0; item_index < num_items; 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 < numPolys; 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 ? 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 != NULL);
+
+ for (int i = 0; i < numLoops; 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 ? 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(0);
+ }
+
+ /* Validate computed weighted normals. */
+ for (int item_index = 0; item_index < num_items; 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 < numLoops; ml_index++) {
+ WeightedNormalDataAggregateItem *item_data = 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,
+ numVerts,
+ medge,
+ numEdges,
+ mloop,
+ loop_normals,
+ numLoops,
+ mpoly,
+ polynors,
+ numPolys,
+ clnors);
+ }
+ else {
+ /* TODO: Ideally, we could add an option to BKE_mesh_normals_loop_custom_[from_vertices_]set() to keep current
+ * clnors instead of resetting them to default autocomputed 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] = MEM_calloc_arrayN(
+ (size_t)numVerts, sizeof(*loop_normals), __func__);
+
+ for (int ml_index = 0; ml_index < numLoops; 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_vertices_set(mvert,
+ vert_normals,
+ numVerts,
+ medge,
+ numEdges,
+ mloop,
+ numLoops,
+ mpoly,
+ polynors,
+ numPolys,
+ clnors);
+
+ MEM_freeN(vert_normals);
+ }
+ else {
+ loop_normals = MEM_calloc_arrayN((size_t)numLoops, sizeof(*loop_normals), __func__);
+
+ BKE_mesh_normals_loop_split(mvert,
+ numVerts,
+ medge,
+ numEdges,
+ mloop,
+ loop_normals,
+ numLoops,
+ mpoly,
+ polynors,
+ numPolys,
+ true,
+ split_angle,
+ NULL,
+ has_clnors ? clnors : NULL,
+ loop_to_poly);
+
+ for (int ml_index = 0; ml_index < numLoops; 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,
+ numVerts,
+ medge,
+ numEdges,
+ mloop,
+ loop_normals,
+ numLoops,
+ mpoly,
+ polynors,
+ numPolys,
+ 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 numPolys = wn_data->numPolys;
+ const int numPolys = wn_data->numPolys;
- MVert *mvert = wn_data->mvert;
- MLoop *mloop = wn_data->mloop;
- MPoly *mpoly = wn_data->mpoly;
+ MVert *mvert = wn_data->mvert;
+ MLoop *mloop = wn_data->mloop;
+ MPoly *mpoly = wn_data->mpoly;
- MPoly *mp;
- int mp_index;
+ MPoly *mp;
+ int mp_index;
- ModePair *face_area = MEM_malloc_arrayN((size_t)numPolys, sizeof(*face_area), __func__);
+ ModePair *face_area = MEM_malloc_arrayN((size_t)numPolys, sizeof(*face_area), __func__);
- ModePair *f_area = face_area;
- for (mp_index = 0, mp = mpoly; mp_index < numPolys; mp_index++, mp++, f_area++) {
- f_area->val = BKE_mesh_calc_poly_area(mp, &mloop[mp->loopstart], mvert);
- f_area->index = mp_index;
- }
+ ModePair *f_area = face_area;
+ for (mp_index = 0, mp = mpoly; mp_index < numPolys; 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, numPolys, sizeof(*face_area), modepair_cmp_by_val_inverse);
+ qsort(face_area, numPolys, sizeof(*face_area), modepair_cmp_by_val_inverse);
- wn_data->mode_pair = face_area;
- apply_weights_vertex_normal(wnmd, wn_data);
+ 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 numLoops = wn_data->numLoops;
- const int numPolys = wn_data->numPolys;
+ const int numLoops = wn_data->numLoops;
+ const int numPolys = wn_data->numPolys;
- MVert *mvert = wn_data->mvert;
- MLoop *mloop = wn_data->mloop;
- MPoly *mpoly = wn_data->mpoly;
+ MVert *mvert = wn_data->mvert;
+ MLoop *mloop = wn_data->mloop;
+ MPoly *mpoly = wn_data->mpoly;
- MPoly *mp;
- int mp_index;
+ MPoly *mp;
+ int mp_index;
- int *loop_to_poly = MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__);
+ int *loop_to_poly = MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__);
- ModePair *corner_angle = MEM_malloc_arrayN((size_t)numLoops, sizeof(*corner_angle), __func__);
+ ModePair *corner_angle = MEM_malloc_arrayN((size_t)numLoops, sizeof(*corner_angle), __func__);
- for (mp_index = 0, mp = mpoly; mp_index < numPolys; mp_index++, mp++) {
- MLoop *ml_start = &mloop[mp->loopstart];
+ for (mp_index = 0, mp = mpoly; mp_index < numPolys; mp_index++, mp++) {
+ MLoop *ml_start = &mloop[mp->loopstart];
- float *index_angle = MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__);
- BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle);
+ float *index_angle = 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;
+ 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);
- }
+ loop_to_poly[ml_index] = mp_index;
+ }
+ MEM_freeN(index_angle);
+ }
- qsort(corner_angle, numLoops, sizeof(*corner_angle), modepair_cmp_by_val_inverse);
+ qsort(corner_angle, numLoops, 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);
+ 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 numLoops = wn_data->numLoops;
- const int numPolys = wn_data->numPolys;
+ const int numLoops = wn_data->numLoops;
+ const int numPolys = wn_data->numPolys;
- MVert *mvert = wn_data->mvert;
- MLoop *mloop = wn_data->mloop;
- MPoly *mpoly = wn_data->mpoly;
+ MVert *mvert = wn_data->mvert;
+ MLoop *mloop = wn_data->mloop;
+ MPoly *mpoly = wn_data->mpoly;
- MPoly *mp;
- int mp_index;
+ MPoly *mp;
+ int mp_index;
- int *loop_to_poly = MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__);
+ int *loop_to_poly = MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__);
- ModePair *combined = MEM_malloc_arrayN((size_t)numLoops, sizeof(*combined), __func__);
+ ModePair *combined = MEM_malloc_arrayN((size_t)numLoops, sizeof(*combined), __func__);
- for (mp_index = 0, mp = mpoly; mp_index < numPolys; mp_index++, mp++) {
- MLoop *ml_start = &mloop[mp->loopstart];
+ for (mp_index = 0, mp = mpoly; mp_index < numPolys; mp_index++, mp++) {
+ MLoop *ml_start = &mloop[mp->loopstart];
- float face_area = BKE_mesh_calc_poly_area(mp, ml_start, mvert);
- float *index_angle = MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__);
- BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle);
+ float face_area = BKE_mesh_calc_poly_area(mp, ml_start, mvert);
+ float *index_angle = 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;
+ 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);
- }
+ loop_to_poly[ml_index] = mp_index;
+ }
+ MEM_freeN(index_angle);
+ }
- qsort(combined, numLoops, sizeof(*combined), modepair_cmp_by_val_inverse);
+ qsort(combined, numLoops, 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);
+ wn_data->loop_to_poly = loop_to_poly;
+ wn_data->mode_pair = combined;
+ apply_weights_vertex_normal(wnmd, wn_data);
}
static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
- WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
- Object *ob = ctx->object;
+ WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
+ Object *ob = ctx->object;
- /* XXX TODO ARG GRRR XYQWNMPRXTYY
- * 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. */
+ /* XXX TODO ARG GRRR XYQWNMPRXTYY
+ * 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)) {
+ if (!(mesh->flag & ME_AUTOSMOOTH)) {
#else
- if (!(((Mesh *)ob->data)->flag & ME_AUTOSMOOTH)) {
+ if (!(((Mesh *)ob->data)->flag & ME_AUTOSMOOTH)) {
#endif
- modifier_setError((ModifierData *)wnmd, "Enable 'Auto Smooth' option in mesh settings");
- return mesh;
- }
-
- Mesh *result;
- BKE_id_copy_ex(NULL, &mesh->id, (ID **)&result, LIB_ID_COPY_LOCALIZE);
-
- const int numVerts = result->totvert;
- const int numEdges = result->totedge;
- const int numLoops = result->totloop;
- const int numPolys = result->totpoly;
-
- MEdge *medge = result->medge;
- MPoly *mpoly = result->mpoly;
- MVert *mvert = result->mvert;
- MLoop *mloop = result->mloop;
-
- /* 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;
- }
-
- CustomData *pdata = &result->pdata;
- float (*polynors)[3] = CustomData_get_layer(pdata, CD_NORMAL);
- if (!polynors) {
- polynors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, numPolys);
- CustomData_set_layer_flag(pdata, CD_NORMAL, CD_FLAG_TEMPORARY);
- }
- BKE_mesh_calc_normals_poly(mvert, NULL, numVerts, mloop, mpoly, numLoops, numPolys, polynors, false);
-
-
- const float split_angle = mesh->smoothresh;
- short (*clnors)[2];
- CustomData *ldata = &result->ldata;
- clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL);
-
- /* Keep info whether we had clnors, it helps when generating clnor spaces and default normals. */
- const bool has_clnors = clnors != NULL;
- if (!clnors) {
- clnors = CustomData_add_layer(ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, numLoops);
- }
-
- MDeformVert *dvert;
- int defgrp_index;
- MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index);
-
- WeightedNormalData wn_data = {
- .numVerts = numVerts,
- .numEdges = numEdges,
- .numLoops = numLoops,
- .numPolys = numPolys,
-
- .mvert = mvert,
- .medge = medge,
-
- .mloop = mloop,
- .clnors = clnors,
- .has_clnors = has_clnors,
- .split_angle = split_angle,
-
- .mpoly = mpoly,
- .polynors = polynors,
- .poly_strength = CustomData_get_layer_named(
- &result->pdata, CD_PROP_INT,
- MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID),
-
- .dvert = dvert,
- .defgrp_index = defgrp_index,
- .use_invert_vgroup = (wnmd->flag & MOD_WEIGHTEDNORMAL_INVERT_VGROUP) != 0,
-
- .weight = weight,
- .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);
-
- /* Currently Modifier stack assumes there is no poly normal data passed around... */
- CustomData_free_layers(pdata, CD_NORMAL, numPolys);
- return result;
+ modifier_setError((ModifierData *)wnmd, "Enable 'Auto Smooth' option in mesh settings");
+ return mesh;
+}
+
+Mesh *result;
+BKE_id_copy_ex(NULL, &mesh->id, (ID **)&result, LIB_ID_COPY_LOCALIZE);
+
+const int numVerts = result->totvert;
+const int numEdges = result->totedge;
+const int numLoops = result->totloop;
+const int numPolys = result->totpoly;
+
+MEdge *medge = result->medge;
+MPoly *mpoly = result->mpoly;
+MVert *mvert = result->mvert;
+MLoop *mloop = result->mloop;
+
+/* 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;
+}
+
+CustomData *pdata = &result->pdata;
+float (*polynors)[3] = CustomData_get_layer(pdata, CD_NORMAL);
+if (!polynors) {
+ polynors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, numPolys);
+ CustomData_set_layer_flag(pdata, CD_NORMAL, CD_FLAG_TEMPORARY);
+}
+BKE_mesh_calc_normals_poly(
+ mvert, NULL, numVerts, mloop, mpoly, numLoops, numPolys, polynors, false);
+
+const float split_angle = mesh->smoothresh;
+short (*clnors)[2];
+CustomData *ldata = &result->ldata;
+clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL);
+
+/* Keep info whether we had clnors, it helps when generating clnor spaces and default normals. */
+const bool has_clnors = clnors != NULL;
+if (!clnors) {
+ clnors = CustomData_add_layer(ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, numLoops);
+}
+
+MDeformVert *dvert;
+int defgrp_index;
+MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index);
+
+WeightedNormalData wn_data = {
+ .numVerts = numVerts,
+ .numEdges = numEdges,
+ .numLoops = numLoops,
+ .numPolys = numPolys,
+
+ .mvert = mvert,
+ .medge = medge,
+
+ .mloop = mloop,
+ .clnors = clnors,
+ .has_clnors = has_clnors,
+ .split_angle = split_angle,
+
+ .mpoly = mpoly,
+ .polynors = polynors,
+ .poly_strength = CustomData_get_layer_named(
+ &result->pdata, CD_PROP_INT, MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID),
+
+ .dvert = dvert,
+ .defgrp_index = defgrp_index,
+ .use_invert_vgroup = (wnmd->flag & MOD_WEIGHTEDNORMAL_INVERT_VGROUP) != 0,
+
+ .weight = weight,
+ .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);
+
+/* Currently Modifier stack assumes there is no poly normal data passed around... */
+CustomData_free_layers(pdata, CD_NORMAL, numPolys);
+return result;
}
static void initData(ModifierData *md)
{
- WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
- wnmd->mode = MOD_WEIGHTEDNORMAL_MODE_FACE;
- wnmd->weight = 50;
- wnmd->thresh = 1e-2f;
- wnmd->flag = 0;
+ WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
+ wnmd->mode = MOD_WEIGHTEDNORMAL_MODE_FACE;
+ wnmd->weight = 50;
+ wnmd->thresh = 1e-2f;
+ wnmd->flag = 0;
}
-static void requiredDataMask(Object *UNUSED(ob), ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(Object *UNUSED(ob),
+ ModifierData *md,
+ CustomData_MeshMasks *r_cddata_masks)
{
- WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
+ WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
- r_cddata_masks->lmask = CD_MASK_CUSTOMLOOPNORMAL;
+ r_cddata_masks->lmask = CD_MASK_CUSTOMLOOPNORMAL;
- if (wnmd->defgrp_name[0] != '\0') {
- r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
- }
+ 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_INT;
- }
+ if (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) {
+ r_cddata_masks->pmask |= CD_MASK_PROP_INT;
+ }
}
static bool dependsOnNormals(ModifierData *UNUSED(md))
{
- return true;
+ return true;
}
ModifierTypeInfo modifierType_WeightedNormal = {
- /* name */ "Weighted Normal",
- /* structName */ "WeightedNormalModifierData",
- /* structSize */ sizeof(WeightedNormalModifierData),
- /* type */ eModifierTypeType_Constructive,
- /* flags */ eModifierTypeFlag_AcceptsMesh |
- eModifierTypeFlag_SupportsMapping |
- eModifierTypeFlag_SupportsEditmode |
- eModifierTypeFlag_EnableInEditmode,
-
- /* copyData */ modifier_copyData_generic,
-
- /* deformVerts */ NULL,
- /* deformMatrices */ NULL,
- /* deformVertsEM */ NULL,
- /* deformMatricesEM */ NULL,
- /* applyModifier */ applyModifier,
-
- /* initData */ initData,
- /* requiredDataMask */ requiredDataMask,
- /* freeData */ NULL,
- /* isDisabled */ NULL,
- /* updateDepsgraph */ NULL,
- /* dependsOnTime */ NULL,
- /* dependsOnNormals */ dependsOnNormals,
- /* foreachObjectLink */ NULL,
- /* foreachIDLink */ NULL,
- /* foreachTexLink */ NULL,
- /* freeRuntimeData */ NULL,
+ /* name */ "Weighted Normal",
+ /* structName */ "WeightedNormalModifierData",
+ /* structSize */ sizeof(WeightedNormalModifierData),
+ /* type */ eModifierTypeType_Constructive,
+ /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping |
+ eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode,
+
+ /* copyData */ modifier_copyData_generic,
+
+ /* deformVerts */ NULL,
+ /* deformMatrices */ NULL,
+ /* deformVertsEM */ NULL,
+ /* deformMatricesEM */ NULL,
+ /* applyModifier */ applyModifier,
+
+ /* initData */ initData,
+ /* requiredDataMask */ requiredDataMask,
+ /* freeData */ NULL,
+ /* isDisabled */ NULL,
+ /* updateDepsgraph */ NULL,
+ /* dependsOnTime */ NULL,
+ /* dependsOnNormals */ dependsOnNormals,
+ /* foreachObjectLink */ NULL,
+ /* foreachIDLink */ NULL,
+ /* foreachTexLink */ NULL,
+ /* freeRuntimeData */ NULL,
};