diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index ea3b5f000ec..4be1f55b3b8 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -769,6 +769,13 @@ class ConstraintButtonsPanel: rowsub.prop(con, "use_invert_cull") layout.prop(con, "project_limit") + if con.shrinkwrap_type in ['PROJECT', 'NEAREST_SURFACE']: + layout.prop(con, "use_track_normal") + + row = layout.row(align=True) + row.active = con.use_track_normal + row.prop(con, "track_axis", expand=True) + def DAMPED_TRACK(self, context, layout, con): self.target_template(layout, con) diff --git a/source/blender/blenkernel/BKE_shrinkwrap.h b/source/blender/blenkernel/BKE_shrinkwrap.h index 40f3808b94b..d1a14003b04 100644 --- a/source/blender/blenkernel/BKE_shrinkwrap.h +++ b/source/blender/blenkernel/BKE_shrinkwrap.h @@ -55,27 +55,25 @@ struct ShrinkwrapModifierData; struct BVHTree; struct SpaceTransform; +typedef struct ShrinkwrapTreeData { + Mesh *mesh; -typedef struct ShrinkwrapCalcData { - ShrinkwrapModifierData *smd; //shrinkwrap modifier data + BVHTree *bvh; + BVHTreeFromMesh treeData; - struct Object *ob; //object we are applying shrinkwrap to + float (*clnors)[3]; +} ShrinkwrapTreeData; - struct MVert *vert; //Array of verts being projected (to fetch normals or other data) - float (*vertexCos)[3]; //vertexs being shrinkwraped - int numVerts; +/* Checks if the modifier needs target normals with these settings. */ +bool BKE_shrinkwrap_needs_normals(int shrinkType, int shrinkMode); - struct MDeformVert *dvert; //Pointer to mdeform array - int vgroup; //Vertex group num - bool invert_vgroup; /* invert vertex group influence */ +/* Initializes the mesh data structure from the given mesh and settings. */ +bool BKE_shrinkwrap_init_tree(struct ShrinkwrapTreeData *data, Mesh *mesh, int shrinkType, int shrinkMode, bool force_normals); - struct Mesh *target; //mesh we are shrinking to - struct SpaceTransform local2target; //transform to move between local and target space - - float keepDist; //Distance to keep above target surface (units are in local space) - -} ShrinkwrapCalcData; +/* Frees the tree data if necessary. */ +void BKE_shrinkwrap_free_tree(struct ShrinkwrapTreeData *data); +/* Implementation of the Shrinkwrap modifier */ void shrinkwrapModifier_deform(struct ShrinkwrapModifierData *smd, struct Scene *scene, struct Object *ob, struct Mesh *mesh, float (*vertexCos)[3], int numVerts); @@ -91,12 +89,17 @@ void shrinkwrapModifier_deform(struct ShrinkwrapModifierData *smd, struct Scene */ bool BKE_shrinkwrap_project_normal( char options, const float vert[3], const float dir[3], const float ray_radius, - const struct SpaceTransform *transf, BVHTree *tree, BVHTreeRayHit *hit, - BVHTree_RayCastCallback callback, void *userdata); + const struct SpaceTransform *transf, struct ShrinkwrapTreeData *tree, BVHTreeRayHit *hit); + +/* Computes a smooth normal of the target (if applicable) at the hit location. */ +void BKE_shrinkwrap_compute_smooth_normal( + const struct ShrinkwrapTreeData *tree, const struct SpaceTransform *transform, + int looptri_idx, const float hit_co[3], const float hit_no[3], float r_no[3]); /* Apply the shrink to surface modes to the given original coordinates and nearest point. */ void BKE_shrinkwrap_snap_point_to_surface( - int mode, const float hit_co[3], const float hit_no[3], float goal_dist, + const struct ShrinkwrapTreeData *tree, const struct SpaceTransform *transform, + int mode, int hit_idx, const float hit_co[3], const float hit_no[3], float goal_dist, const float point_co[3], float r_point_co[3]); /* diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 8e85cc39321..f343a62eaf7 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -107,6 +107,8 @@ * type-info structs. */ +static void damptrack_do_transform(float matrix[4][4], const float tarvec[3], int track_axis); + /* -------------- Naming -------------- */ /* Find the first available, non-duplicate name for a given constraint */ @@ -3417,15 +3419,18 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, bool fail = false; float co[3] = {0.0f, 0.0f, 0.0f}; + bool track_normal = false; + float track_no[3] = {0.0f, 0.0f, 0.0f}; SpaceTransform transform; Mesh *target_eval = mesh_get_eval_final(depsgraph, DEG_get_input_scene(depsgraph), ct->tar, CD_MASK_BAREMESH); - BVHTreeFromMesh treeData = {NULL}; + copy_m4_m4(ct->matrix, cob->matrix); - unit_m4(ct->matrix); + bool do_track_normal = (scon->flag & CON_SHRINKWRAP_TRACK_NORMAL) != 0; + ShrinkwrapTreeData tree; - if (target_eval != NULL) { + if (BKE_shrinkwrap_init_tree(&tree, target_eval, scon->shrinkType, scon->shrinkMode, do_track_normal)) { BLI_space_transform_from_matrices(&transform, cob->matrix, ct->tar->obmat); switch (scon->shrinkType) { @@ -3437,19 +3442,10 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, nearest.index = -1; nearest.dist_sq = FLT_MAX; - if (scon->shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) - BKE_bvhtree_from_mesh_get(&treeData, target_eval, BVHTREE_FROM_VERTS, 2); - else - BKE_bvhtree_from_mesh_get(&treeData, target_eval, BVHTREE_FROM_LOOPTRI, 2); - - if (treeData.tree == NULL) { - fail = true; - break; - } - BLI_space_transform_apply(&transform, co); - BLI_bvhtree_find_nearest(treeData.tree, co, &nearest, treeData.nearest_callback, &treeData); + BVHTreeFromMesh *treeData = &tree.treeData; + BLI_bvhtree_find_nearest(treeData->tree, co, &nearest, treeData->nearest_callback, treeData); if (nearest.index < 0) { fail = true; @@ -3457,7 +3453,13 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, } if (scon->shrinkType == MOD_SHRINKWRAP_NEAREST_SURFACE) { - BKE_shrinkwrap_snap_point_to_surface(scon->shrinkMode, nearest.co, nearest.no, scon->dist, co, co); + if (do_track_normal) { + track_normal = true; + BKE_shrinkwrap_compute_smooth_normal(&tree, NULL, nearest.index, nearest.co, nearest.no, track_no); + BLI_space_transform_invert_normal(&transform, track_no); + } + + BKE_shrinkwrap_snap_point_to_surface(&tree, NULL, scon->shrinkMode, nearest.index, nearest.co, nearest.no, scon->dist, co, co); } else { const float dist = len_v3v3(co, nearest.co); @@ -3504,16 +3506,9 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, break; } - BKE_bvhtree_from_mesh_get(&treeData, target_eval, BVHTREE_FROM_LOOPTRI, 4); - if (treeData.tree == NULL) { - fail = true; - break; - } - char cull_mode = scon->flag & CON_SHRINKWRAP_PROJECT_CULL_MASK; - BKE_shrinkwrap_project_normal(cull_mode, co, no, 0.0f, &transform, treeData.tree, - &hit, treeData.raycast_callback, &treeData); + BKE_shrinkwrap_project_normal(cull_mode, co, no, 0.0f, &transform, &tree, &hit); if (scon->flag & CON_SHRINKWRAP_PROJECT_OPPOSITE) { float inv_no[3]; @@ -3523,8 +3518,7 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, cull_mode ^= CON_SHRINKWRAP_PROJECT_CULL_MASK; } - BKE_shrinkwrap_project_normal(cull_mode, co, inv_no, 0.0f, &transform, treeData.tree, - &hit, treeData.raycast_callback, &treeData); + BKE_shrinkwrap_project_normal(cull_mode, co, inv_no, 0.0f, &transform, &tree, &hit); } if (hit.index < 0) { @@ -3532,12 +3526,17 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, break; } - BKE_shrinkwrap_snap_point_to_surface(scon->shrinkMode, hit.co, hit.no, scon->dist, co, co); + if (do_track_normal) { + track_normal = true; + BKE_shrinkwrap_compute_smooth_normal(&tree, &transform, hit.index, hit.co, hit.no, track_no); + } + + BKE_shrinkwrap_snap_point_to_surface(&tree, &transform, scon->shrinkMode, hit.index, hit.co, hit.no, scon->dist, co, co); break; } } - free_bvhtree_from_mesh(&treeData); + BKE_shrinkwrap_free_tree(&tree); if (fail == true) { /* Don't move the point */ @@ -3547,6 +3546,11 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, /* co is in local object coordinates, change it to global and update target position */ mul_m4_v3(cob->matrix, co); copy_v3_v3(ct->matrix[3], co); + + if (track_normal) { + mul_mat3_m4_v3(cob->matrix, track_no); + damptrack_do_transform(ct->matrix, track_no, scon->trackAxis); + } } } } @@ -3557,7 +3561,7 @@ static void shrinkwrap_evaluate(bConstraint *UNUSED(con), bConstraintOb *cob, Li /* only evaluate if there is a target */ if (VALID_CONS_TARGET(ct)) { - copy_v3_v3(cob->matrix[3], ct->matrix[3]); + copy_m4_m4(cob->matrix, ct->matrix); } } @@ -3631,7 +3635,22 @@ static void damptrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t bConstraintTarget *ct = targets->first; if (VALID_CONS_TARGET(ct)) { - float obvec[3], tarvec[3], obloc[3]; + float tarvec[3]; + + /* find the (unit) direction vector going from the owner to the target */ + sub_v3_v3v3(tarvec, ct->matrix[3], cob->matrix[3]); + + damptrack_do_transform(cob->matrix, tarvec, data->trackflag); + } +} + +static void damptrack_do_transform(float matrix[4][4], const float tarvec_in[3], int track_axis) +{ + /* find the (unit) direction vector going from the owner to the target */ + float tarvec[3]; + + if (normalize_v3_v3(tarvec, tarvec_in) != 0.0f) { + float obvec[3], obloc[3]; float raxis[3], rangle; float rmat[3][3], tmat[4][4]; @@ -3640,24 +3659,15 @@ static void damptrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t * - the normalization step at the end should take care of any unwanted scaling * left over in the 3x3 matrix we used */ - copy_v3_v3(obvec, track_dir_vecs[data->trackflag]); - mul_mat3_m4_v3(cob->matrix, obvec); + copy_v3_v3(obvec, track_dir_vecs[track_axis]); + mul_mat3_m4_v3(matrix, obvec); if (normalize_v3(obvec) == 0.0f) { /* exceptional case - just use the track vector as appropriate */ - copy_v3_v3(obvec, track_dir_vecs[data->trackflag]); + copy_v3_v3(obvec, track_dir_vecs[track_axis]); } - /* find the (unit) direction vector going from the owner to the target */ - copy_v3_v3(obloc, cob->matrix[3]); - sub_v3_v3v3(tarvec, ct->matrix[3], obloc); - - if (normalize_v3(tarvec) == 0.0f) { - /* the target is sitting on the owner, so just make them use the same direction vectors */ - /* FIXME: or would it be better to use the pure direction vector? */ - copy_v3_v3(tarvec, obvec); - //copy_v3_v3(tarvec, track_dir_vecs[data->trackflag]); - } + copy_v3_v3(obloc, matrix[3]); /* determine the axis-angle rotation, which represents the smallest possible rotation * between the two rotation vectors (i.e. the 'damping' referred to in the name) @@ -3690,8 +3700,8 @@ static void damptrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t } rangle = M_PI; - copy_v3_v3(tmpvec, track_dir_vecs[(data->trackflag + 1) % 6]); - mul_mat3_m4_v3(cob->matrix, tmpvec); + copy_v3_v3(tmpvec, track_dir_vecs[(track_axis + 1) % 6]); + mul_mat3_m4_v3(matrix, tmpvec); cross_v3_v3v3(raxis, obvec, tmpvec); if (normalize_v3(raxis) == 0.0f) { @@ -3709,10 +3719,10 @@ static void damptrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t * we may have destroyed that in the process of multiplying the matrix */ unit_m4(tmat); - mul_m4_m3m4(tmat, rmat, cob->matrix); // m1, m3, m2 + mul_m4_m3m4(tmat, rmat, matrix); // m1, m3, m2 - copy_m4_m4(cob->matrix, tmat); - copy_v3_v3(cob->matrix[3], obloc); + copy_m4_m4(matrix, tmat); + copy_v3_v3(matrix[3], obloc); } } diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index 5408c07adf1..62cd318ac06 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -70,20 +70,88 @@ /* Util macros */ #define OUT_OF_MEMORY() ((void)printf("Shrinkwrap: Out of memory\n")) + +typedef struct ShrinkwrapCalcData { + ShrinkwrapModifierData *smd; //shrinkwrap modifier data + + struct Object *ob; //object we are applying shrinkwrap to + + struct MVert *vert; //Array of verts being projected (to fetch normals or other data) + float(*vertexCos)[3]; //vertexs being shrinkwraped + int numVerts; + + struct MDeformVert *dvert; //Pointer to mdeform array + int vgroup; //Vertex group num + bool invert_vgroup; /* invert vertex group influence */ + + struct Mesh *target; //mesh we are shrinking to + struct SpaceTransform local2target; //transform to move between local and target space + struct ShrinkwrapTreeData *tree; // mesh BVH tree data + + float keepDist; //Distance to keep above target surface (units are in local space) + +} ShrinkwrapCalcData; + typedef struct ShrinkwrapCalcCBData { ShrinkwrapCalcData *calc; - void *treeData; - void *auxData; - BVHTree *targ_tree; - BVHTree *aux_tree; - void *targ_callback; - void *aux_callback; + ShrinkwrapTreeData *tree; + ShrinkwrapTreeData *aux_tree; float *proj_axis; SpaceTransform *local2aux; } ShrinkwrapCalcCBData; + +/* Checks if the modifier needs target normals with these settings. */ +bool BKE_shrinkwrap_needs_normals(int shrinkType, int shrinkMode) +{ + return shrinkType != MOD_SHRINKWRAP_NEAREST_VERTEX && shrinkMode == MOD_SHRINKWRAP_ABOVE_SURFACE; +} + +/* Initializes the mesh data structure from the given mesh and settings. */ +bool BKE_shrinkwrap_init_tree(ShrinkwrapTreeData *data, Mesh *mesh, int shrinkType, int shrinkMode, bool force_normals) +{ + memset(data, 0, sizeof(*data)); + + if (!mesh || mesh->totvert <= 0) { + return false; + } + + data->mesh = mesh; + + if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) { + data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_VERTS, 2); + + return data->bvh != NULL; + } + else { + if (mesh->totpoly <= 0) { + return false; + } + + data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_LOOPTRI, 4); + + if (data->bvh == NULL) { + return false; + } + + if (force_normals || BKE_shrinkwrap_needs_normals(shrinkType, shrinkMode)) { + if ((mesh->flag & ME_AUTOSMOOTH) != 0) { + data->clnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL); + } + } + + return true; + } +} + +/* Frees the tree data if necessary. */ +void BKE_shrinkwrap_free_tree(ShrinkwrapTreeData *data) +{ + free_bvhtree_from_mesh(&data->treeData); +} + /* * Shrinkwrap to the nearest vertex * @@ -98,7 +166,7 @@ static void shrinkwrap_calc_nearest_vertex_cb_ex( ShrinkwrapCalcCBData *data = userdata; ShrinkwrapCalcData *calc = data->calc; - BVHTreeFromMesh *treeData = data->treeData; + BVHTreeFromMesh *treeData = &data->tree->treeData; BVHTreeNearest *nearest = tls->userdata_chunk; float *co = calc->vertexCos[i]; @@ -154,24 +222,13 @@ static void shrinkwrap_calc_nearest_vertex_cb_ex( static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) { - BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; - if (calc->target != NULL && calc->target->totvert == 0) { - return; - } - - TIMEIT_BENCH(BKE_bvhtree_from_mesh_get(&treeData, calc->target, BVHTREE_FROM_VERTS, 2), bvhtree_verts); - if (treeData.tree == NULL) { - OUT_OF_MEMORY(); - return; - } - /* Setup nearest */ nearest.index = -1; nearest.dist_sq = FLT_MAX; - ShrinkwrapCalcCBData data = {.calc = calc, .treeData = &treeData}; + ShrinkwrapCalcCBData data = {.calc = calc, .tree = calc->tree}; ParallelRangeSettings settings; BLI_parallel_range_settings_defaults(&settings); settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT); @@ -180,8 +237,6 @@ static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) BLI_task_parallel_range(0, calc->numVerts, &data, shrinkwrap_calc_nearest_vertex_cb_ex, &settings); - - free_bvhtree_from_mesh(&treeData); } @@ -196,8 +251,7 @@ static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) bool BKE_shrinkwrap_project_normal( char options, const float vert[3], const float dir[3], const float ray_radius, const SpaceTransform *transf, - BVHTree *tree, BVHTreeRayHit *hit, - BVHTree_RayCastCallback callback, void *userdata) + ShrinkwrapTreeData *tree, BVHTreeRayHit *hit) { /* don't use this because this dist value could be incompatible * this value used by the callback for comparing prev/new dist values. @@ -232,7 +286,7 @@ bool BKE_shrinkwrap_project_normal( hit_tmp.index = -1; - BLI_bvhtree_ray_cast(tree, co, no, ray_radius, &hit_tmp, callback, userdata); + BLI_bvhtree_ray_cast(tree->bvh, co, no, ray_radius, &hit_tmp, tree->treeData.raycast_callback, &tree->treeData); if (hit_tmp.index != -1) { /* invert the normal first so face culling works on rotated objects */ @@ -274,12 +328,8 @@ static void shrinkwrap_calc_normal_projection_cb_ex( ShrinkwrapCalcCBData *data = userdata; ShrinkwrapCalcData *calc = data->calc; - void *treeData = data->treeData; - void *auxData = data->auxData; - BVHTree *targ_tree = data->targ_tree; - BVHTree *aux_tree = data->aux_tree; - void *targ_callback = data->targ_callback; - void *aux_callback = data->aux_callback; + ShrinkwrapTreeData *tree = data->tree; + ShrinkwrapTreeData *aux_tree = data->aux_tree; float *proj_axis = data->proj_axis; SpaceTransform *local2aux = data->local2aux; @@ -321,19 +371,23 @@ static void shrinkwrap_calc_normal_projection_cb_ex( hit->index = -1; hit->dist = BVH_RAYCAST_DIST_MAX; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ + bool is_aux = false; + /* Project over positive direction of axis */ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { if (aux_tree) { - BKE_shrinkwrap_project_normal( + if (BKE_shrinkwrap_project_normal( 0, tmp_co, tmp_no, 0.0, - local2aux, aux_tree, hit, - aux_callback, auxData); + local2aux, aux_tree, hit)) { + is_aux = true; + } } - BKE_shrinkwrap_project_normal( + if (BKE_shrinkwrap_project_normal( calc->smd->shrinkOpts, tmp_co, tmp_no, 0.0, - &calc->local2target, targ_tree, hit, - targ_callback, treeData); + &calc->local2target, tree, hit)) { + is_aux = false; + } } /* Project over negative direction of axis */ @@ -348,16 +402,18 @@ static void shrinkwrap_calc_normal_projection_cb_ex( } if (aux_tree) { - BKE_shrinkwrap_project_normal( + if (BKE_shrinkwrap_project_normal( 0, tmp_co, inv_no, 0.0, - local2aux, aux_tree, hit, - aux_callback, auxData); + local2aux, aux_tree, hit)) { + is_aux = true; + } } - BKE_shrinkwrap_project_normal( + if (BKE_shrinkwrap_project_normal( options, tmp_co, inv_no, 0.0, - &calc->local2target, targ_tree, hit, - targ_callback, treeData); + &calc->local2target, tree, hit)) { + is_aux = false; + } } /* don't set the initial dist (which is more efficient), @@ -369,7 +425,17 @@ static void shrinkwrap_calc_normal_projection_cb_ex( } if (hit->index != -1) { - BKE_shrinkwrap_snap_point_to_surface(calc->smd->shrinkMode, hit->co, hit->no, calc->keepDist, tmp_co, hit->co); + if (is_aux) { + BKE_shrinkwrap_snap_point_to_surface( + aux_tree, local2aux, calc->smd->shrinkMode, + hit->index, hit->co, hit->no, calc->keepDist, tmp_co, hit->co); + } + else { + BKE_shrinkwrap_snap_point_to_surface( + tree, &calc->local2target, calc->smd->shrinkMode, + hit->index, hit->co, hit->no, calc->keepDist, tmp_co, hit->co); + } + interp_v3_v3v3(co, co, hit->co, weight); } } @@ -385,12 +451,12 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) * for finding the best hit, to get the real dist, * measure the len_v3v3() from the input coord to hit.co */ BVHTreeRayHit hit; - void *treeData = NULL; /* auxiliary target */ Mesh *auxMesh = NULL; bool auxMesh_free; - void *auxData = NULL; + ShrinkwrapTreeData *aux_tree = NULL; + ShrinkwrapTreeData aux_tree_stack; SpaceTransform local2aux; /* If the user doesn't allows to project in any direction of projection axis @@ -398,10 +464,6 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) if ((calc->smd->shrinkOpts & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0) return; - if (calc->target != NULL && calc->target->totpoly == 0) { - return; - } - /* Prepare data to retrieve the direction in which we should project each vertex */ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { if (calc->vert == NULL) return; @@ -428,67 +490,28 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) BLI_SPACE_TRANSFORM_SETUP(&local2aux, calc->ob, calc->smd->auxTarget); } - /* use editmesh to avoid array allocation */ - BMEditMesh *emtarget = NULL, *emaux = NULL; - union { - BVHTreeFromEditMesh emtreedata; - BVHTreeFromMesh dmtreedata; - } treedata_stack, auxdata_stack; - - BVHTree *targ_tree; - void *targ_callback; - if ((targ_tree = BKE_bvhtree_from_mesh_get( - &treedata_stack.dmtreedata, calc->target, BVHTREE_FROM_LOOPTRI, 4))) - { - targ_callback = treedata_stack.dmtreedata.raycast_callback; - treeData = &treedata_stack.dmtreedata; - - BVHTree *aux_tree = NULL; - void *aux_callback = NULL; - if (auxMesh != NULL && auxMesh->totpoly != 0) { - /* use editmesh to avoid array allocation */ - if ((aux_tree = BKE_bvhtree_from_mesh_get( - &auxdata_stack.dmtreedata, auxMesh, BVHTREE_FROM_LOOPTRI, 4)) != NULL) - { - aux_callback = auxdata_stack.dmtreedata.raycast_callback; - auxData = &auxdata_stack.dmtreedata; - } - } - /* After successfully build the trees, start projection vertices. */ - ShrinkwrapCalcCBData data = { - .calc = calc, - .treeData = treeData, .targ_tree = targ_tree, .targ_callback = targ_callback, - .auxData = auxData, .aux_tree = aux_tree, .aux_callback = aux_callback, - .proj_axis = proj_axis, .local2aux = &local2aux, - }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT); - settings.userdata_chunk = &hit; - settings.userdata_chunk_size = sizeof(hit); - BLI_task_parallel_range(0, calc->numVerts, - &data, - shrinkwrap_calc_normal_projection_cb_ex, - &settings); + if (BKE_shrinkwrap_init_tree(&aux_tree_stack, auxMesh, calc->smd->shrinkType, calc->smd->shrinkMode, false)) { + aux_tree = &aux_tree_stack; } + /* After successfully build the trees, start projection vertices. */ + ShrinkwrapCalcCBData data = { + .calc = calc, .tree = calc->tree, .aux_tree = aux_tree, + .proj_axis = proj_axis, .local2aux = &local2aux + }; + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT); + settings.userdata_chunk = &hit; + settings.userdata_chunk_size = sizeof(hit); + BLI_task_parallel_range(0, calc->numVerts, + &data, + shrinkwrap_calc_normal_projection_cb_ex, + &settings); + /* free data structures */ - if (treeData) { - if (emtarget) { - free_bvhtree_from_editmesh(treeData); - } - else { - free_bvhtree_from_mesh(treeData); - } - } - - if (auxData) { - if (emaux) { - free_bvhtree_from_editmesh(auxData); - } - else { - free_bvhtree_from_mesh(auxData); - } + if (aux_tree) { + BKE_shrinkwrap_free_tree(aux_tree); } if (auxMesh != NULL && auxMesh_free) { BKE_id_free(NULL, auxMesh); @@ -509,7 +532,7 @@ static void shrinkwrap_calc_nearest_surface_point_cb_ex( ShrinkwrapCalcCBData *data = userdata; ShrinkwrapCalcData *calc = data->calc; - BVHTreeFromMesh *treeData = data->treeData; + BVHTreeFromMesh *treeData = &data->tree->treeData; BVHTreeNearest *nearest = tls->userdata_chunk; float *co = calc->vertexCos[i]; @@ -547,7 +570,9 @@ static void shrinkwrap_calc_nearest_surface_point_cb_ex( /* Found the nearest vertex */ if (nearest->index != -1) { - BKE_shrinkwrap_snap_point_to_surface(calc->smd->shrinkMode, nearest->co, nearest->no, calc->keepDist, tmp_co, tmp_co); + BKE_shrinkwrap_snap_point_to_surface( + data->tree, NULL, calc->smd->shrinkMode, + nearest->index, nearest->co, nearest->no, calc->keepDist, tmp_co, tmp_co); /* Convert the coordinates back to mesh coordinates */ BLI_space_transform_invert(&calc->local2target, tmp_co); @@ -555,6 +580,67 @@ static void shrinkwrap_calc_nearest_surface_point_cb_ex( } } +/** + * Compute a smooth normal of the target (if applicable) at the hit location. + * + * tree: information about the mesh + * transform: transform from the hit coordinate space to the object space; may be null + * r_no: output in hit coordinate space; may be shared with inputs + */ +void BKE_shrinkwrap_compute_smooth_normal( + const struct ShrinkwrapTreeData *tree, const struct SpaceTransform *transform, + int looptri_idx, const float hit_co[3], const float hit_no[3], float r_no[3]) +{ + const BVHTreeFromMesh *treeData = &tree->treeData; + const MLoopTri *tri = &treeData->looptri[looptri_idx]; + + /* Interpolate smooth normals if enabled. */ + if ((tree->mesh->mpoly[tri->poly].flag & ME_SMOOTH) != 0) { + const MVert *verts[] = { + &treeData->vert[treeData->loop[tri->tri[0]].v], + &treeData->vert[treeData->loop[tri->tri[1]].v], + &treeData->vert[treeData->loop[tri->tri[2]].v], + }; + float w[3], no[3][3], tmp_co[3]; + + /* Custom and auto smooth split normals. */ + if (tree->clnors) { + copy_v3_v3(no[0], tree->clnors[tri->tri[0]]); + copy_v3_v3(no[1], tree->clnors[tri->tri[1]]); + copy_v3_v3(no[2], tree->clnors[tri->tri[2]]); + } + /* Ordinary vertex normals. */ + else { + normal_short_to_float_v3(no[0], verts[0]->no); + normal_short_to_float_v3(no[1], verts[1]->no); + normal_short_to_float_v3(no[2], verts[2]->no); + } + + /* Barycentric weights from hit point. */ + copy_v3_v3(tmp_co, hit_co); + + if (transform) { + BLI_space_transform_apply(transform, tmp_co); + } + + interp_weights_tri_v3(w, verts[0]->co, verts[1]->co, verts[2]->co, tmp_co); + + /* Interpolate using weights. */ + interp_v3_v3v3v3(r_no, no[0], no[1], no[2], w); + + if (transform) { + BLI_space_transform_invert_normal(transform, r_no); + } + else { + normalize_v3(r_no); + } + } + /* Use the looptri normal if flat. */ + else { + copy_v3_v3(r_no, hit_no); + } +} + /* Helper for MOD_SHRINKWRAP_INSIDE, MOD_SHRINKWRAP_OUTSIDE and MOD_SHRINKWRAP_OUTSIDE_SURFACE. */ static void shrinkwrap_snap_with_side(float r_point_co[3], const float point_co[3], const float hit_co[3], const float hit_no[3], float goal_dist, float forcesign, bool forcesnap) { @@ -582,13 +668,17 @@ static void shrinkwrap_snap_with_side(float r_point_co[3], const float point_co[ /** * Apply the shrink to surface modes to the given original coordinates and nearest point. - * r_point_co may be the same memory location as point_co, hit_co, or hit_no. + * + * tree: mesh data for smooth normals + * transform: transform from the hit coordinate space to the object space; may be null + * r_point_co: may be the same memory location as point_co, hit_co, or hit_no. */ void BKE_shrinkwrap_snap_point_to_surface( - int mode, const float hit_co[3], const float hit_no[3], float goal_dist, + const struct ShrinkwrapTreeData *tree, const struct SpaceTransform *transform, + int mode, int hit_idx, const float hit_co[3], const float hit_no[3], float goal_dist, const float point_co[3], float r_point_co[3]) { - float dist; + float dist, tmp[3]; switch (mode) { /* Offsets along the line between point_co and hit_co. */ @@ -620,7 +710,13 @@ void BKE_shrinkwrap_snap_point_to_surface( /* Offsets along the normal */ case MOD_SHRINKWRAP_ABOVE_SURFACE: - madd_v3_v3v3fl(r_point_co, hit_co, hit_no, goal_dist); + if (goal_dist > 0) { + BKE_shrinkwrap_compute_smooth_normal(tree, transform, hit_idx, hit_co, hit_no, tmp); + madd_v3_v3v3fl(r_point_co, hit_co, tmp, goal_dist); + } + else { + copy_v3_v3(r_point_co, hit_co); + } break; default: @@ -631,26 +727,14 @@ void BKE_shrinkwrap_snap_point_to_surface( static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) { - BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; - if (calc->target->totpoly == 0) { - return; - } - - /* Create a bvh-tree of the given target */ - BKE_bvhtree_from_mesh_get(&treeData, calc->target, BVHTREE_FROM_LOOPTRI, 2); - if (treeData.tree == NULL) { - OUT_OF_MEMORY(); - return; - } - /* Setup nearest */ nearest.index = -1; nearest.dist_sq = FLT_MAX; /* Find the nearest vertex */ - ShrinkwrapCalcCBData data = {.calc = calc, .treeData = &treeData}; + ShrinkwrapCalcCBData data = {.calc = calc, .tree = calc->tree}; ParallelRangeSettings settings; BLI_parallel_range_settings_defaults(&settings); settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT); @@ -660,8 +744,6 @@ static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) &data, shrinkwrap_calc_nearest_surface_point_cb_ex, &settings); - - free_bvhtree_from_mesh(&treeData); } /* Main shrinkwrap function */ @@ -745,7 +827,11 @@ void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd, struct Scene *scene, } /* Projecting target defined - lets work! */ - if (calc.target) { + ShrinkwrapTreeData tree; + + if (BKE_shrinkwrap_init_tree(&tree, calc.target, smd->shrinkType, smd->shrinkMode, false)) { + calc.tree = &tree; + switch (smd->shrinkType) { case MOD_SHRINKWRAP_NEAREST_SURFACE: TIMEIT_BENCH(shrinkwrap_calc_nearest_surface_point(&calc), deform_surface); @@ -759,6 +845,8 @@ void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd, struct Scene *scene, TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), deform_vertex); break; } + + BKE_shrinkwrap_free_tree(&tree); } /* free memory */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 56c2f32b387..c40df4cb74e 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -91,6 +91,7 @@ extern "C" { #include "BKE_particle.h" #include "BKE_rigidbody.h" #include "BKE_shader_fx.h" +#include "BKE_shrinkwrap.h" #include "BKE_sound.h" #include "BKE_tracking.h" #include "BKE_world.h" @@ -986,9 +987,20 @@ void DepsgraphRelationBuilder::build_constraints(ID *id, } } else if (con->type == CONSTRAINT_TYPE_SHRINKWRAP) { + bShrinkwrapConstraint *scon = (bShrinkwrapConstraint *) con->data; + /* Constraints which requires the target object surface. */ ComponentKey target_key(&ct->tar->id, DEG_NODE_TYPE_GEOMETRY); add_relation(target_key, constraint_op_key, cti->name); + + /* Add dependency on normal layers if necessary. */ + if (ct->tar->type == OB_MESH && scon->shrinkType != MOD_SHRINKWRAP_NEAREST_VERTEX) { + bool track = (scon->flag & CON_SHRINKWRAP_TRACK_NORMAL) != 0; + if (track || BKE_shrinkwrap_needs_normals(scon->shrinkType, scon->shrinkMode)) { + add_customdata_mask(target_key, CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL); + } + } + /* NOTE: obdata eval now doesn't necessarily depend on the * object's transform. */ diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index a92899deaa8..fd98774e948 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -430,7 +430,8 @@ typedef struct bShrinkwrapConstraint { float projLimit; /* distance to search */ char shrinkMode; /* inside/outside/on surface (see MOD shrinkwrap) */ char flag; /* options */ - char pad[2]; + char trackAxis; /* axis to align to normal */ + char pad; } bShrinkwrapConstraint; /* Follow Track constraints */ @@ -642,6 +643,8 @@ typedef enum eShrinkwrap_Flags { CON_SHRINKWRAP_PROJECT_OPPOSITE = (1 << 0), /* Invert the cull mode when projecting opposite. */ CON_SHRINKWRAP_PROJECT_INVERT_CULL = (1 << 1), + /* Align the specified axis to the target normal. */ + CON_SHRINKWRAP_TRACK_NORMAL = (1 << 2), /* Ignore front faces in project; same value as MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE */ CON_SHRINKWRAP_PROJECT_CULL_FRONTFACE = (1 << 3), diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 1e7ed90a4a5..51d4d93586c 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -138,6 +138,16 @@ static const EnumPropertyItem owner_space_pchan_items[] = { {0, NULL, 0, NULL, NULL} }; +static const EnumPropertyItem track_axis_items[] = { + {TRACK_X, "TRACK_X", 0, "X", ""}, + {TRACK_Y, "TRACK_Y", 0, "Y", ""}, + {TRACK_Z, "TRACK_Z", 0, "Z", ""}, + {TRACK_nX, "TRACK_NEGATIVE_X", 0, "-X", ""}, + {TRACK_nY, "TRACK_NEGATIVE_Y", 0, "-Y", ""}, + {TRACK_nZ, "TRACK_NEGATIVE_Z", 0, "-Z", ""}, + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME static const EnumPropertyItem space_object_items[] = { @@ -808,16 +818,6 @@ static void rna_def_constraint_track_to(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem track_items[] = { - {TRACK_X, "TRACK_X", 0, "X", ""}, - {TRACK_Y, "TRACK_Y", 0, "Y", ""}, - {TRACK_Z, "TRACK_Z", 0, "Z", ""}, - {TRACK_nX, "TRACK_NEGATIVE_X", 0, "-X", ""}, - {TRACK_nY, "TRACK_NEGATIVE_Y", 0, "-Y", ""}, - {TRACK_nZ, "TRACK_NEGATIVE_Z", 0, "-Z", ""}, - {0, NULL, 0, NULL, NULL} - }; - static const EnumPropertyItem up_items[] = { {TRACK_X, "UP_X", 0, "X", ""}, {TRACK_Y, "UP_Y", 0, "Y", ""}, @@ -836,7 +836,7 @@ static void rna_def_constraint_track_to(BlenderRNA *brna) prop = RNA_def_property(srna, "track_axis", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "reserved1"); - RNA_def_property_enum_items(prop, track_items); + RNA_def_property_enum_items(prop, track_axis_items); RNA_def_property_ui_text(prop, "Track Axis", "Axis that points to the target object"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); @@ -1151,16 +1151,6 @@ static void rna_def_constraint_locked_track(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem locktrack_items[] = { - {TRACK_X, "TRACK_X", 0, "X", ""}, - {TRACK_Y, "TRACK_Y", 0, "Y", ""}, - {TRACK_Z, "TRACK_Z", 0, "Z", ""}, - {TRACK_nX, "TRACK_NEGATIVE_X", 0, "-X", ""}, - {TRACK_nY, "TRACK_NEGATIVE_Y", 0, "-Y", ""}, - {TRACK_nZ, "TRACK_NEGATIVE_Z", 0, "-Z", ""}, - {0, NULL, 0, NULL, NULL} - }; - static const EnumPropertyItem lock_items[] = { {TRACK_X, "LOCK_X", 0, "X", ""}, {TRACK_Y, "LOCK_Y", 0, "Y", ""}, @@ -1180,7 +1170,7 @@ static void rna_def_constraint_locked_track(BlenderRNA *brna) prop = RNA_def_property(srna, "track_axis", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "trackflag"); - RNA_def_property_enum_items(prop, locktrack_items); + RNA_def_property_enum_items(prop, track_axis_items); RNA_def_property_ui_text(prop, "Track Axis", "Axis that points to the target object"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); @@ -1953,13 +1943,13 @@ static void rna_def_constraint_shrinkwrap(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "shrinkType"); RNA_def_property_enum_items(prop, type_items); RNA_def_property_ui_text(prop, "Shrinkwrap Type", "Select type of shrinkwrap algorithm for target position"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_dependency_update"); prop = RNA_def_property(srna, "wrap_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "shrinkMode"); RNA_def_property_enum_items(prop, rna_enum_modifier_shrinkwrap_mode_items); RNA_def_property_ui_text(prop, "Snap Mode", "Select how to constrain the object to the target surface"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_dependency_update"); prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "dist"); @@ -2005,6 +1995,17 @@ static void rna_def_constraint_shrinkwrap(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", CON_SHRINKWRAP_PROJECT_INVERT_CULL); RNA_def_property_ui_text(prop, "Invert Cull", "When projecting in the opposite direction invert the face cull mode"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + + prop = RNA_def_property(srna, "use_track_normal", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CON_SHRINKWRAP_TRACK_NORMAL); + RNA_def_property_ui_text(prop, "Align Axis To Normal", "Align the specified axis to the surface normal"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_dependency_update"); + + prop = RNA_def_property(srna, "track_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "trackAxis"); + RNA_def_property_enum_items(prop, track_axis_items); + RNA_def_property_ui_text(prop, "Track Axis", "Axis that is aligned to the normal"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); } static void rna_def_constraint_damped_track(BlenderRNA *brna) @@ -2012,16 +2013,6 @@ static void rna_def_constraint_damped_track(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem damptrack_items[] = { - {TRACK_X, "TRACK_X", 0, "X", ""}, - {TRACK_Y, "TRACK_Y", 0, "Y", ""}, - {TRACK_Z, "TRACK_Z", 0, "Z", ""}, - {TRACK_nX, "TRACK_NEGATIVE_X", 0, "-X", ""}, - {TRACK_nY, "TRACK_NEGATIVE_Y", 0, "-Y", ""}, - {TRACK_nZ, "TRACK_NEGATIVE_Z", 0, "-Z", ""}, - {0, NULL, 0, NULL, NULL} - }; - srna = RNA_def_struct(brna, "DampedTrackConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Damped Track Constraint", "Point toward target by taking the shortest rotation path"); @@ -2034,7 +2025,7 @@ rna_def_constraint_headtail_common(srna); prop = RNA_def_property(srna, "track_axis", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "trackflag"); - RNA_def_property_enum_items(prop, damptrack_items); + RNA_def_property_enum_items(prop, track_axis_items); RNA_def_property_ui_text(prop, "Track Axis", "Axis that points to the target object"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); } diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index fccdb4e16a2..de39f50ba9b 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -3187,13 +3187,13 @@ static void rna_def_modifier_shrinkwrap(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "shrinkType"); RNA_def_property_enum_items(prop, shrink_type_items); RNA_def_property_ui_text(prop, "Mode", ""); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); prop = RNA_def_property(srna, "wrap_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "shrinkMode"); RNA_def_property_enum_items(prop, rna_enum_modifier_shrinkwrap_mode_items); RNA_def_property_ui_text(prop, "Snap Mode", "Select how vertices are constrained to the target surface"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); prop = RNA_def_property(srna, "cull_face", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "shrinkOpts"); diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index 7864e888a48..4ba33461bbd 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -141,13 +141,19 @@ static void deformVertsEM( static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { ShrinkwrapModifierData *smd = (ShrinkwrapModifierData *)md; + CustomDataMask mask = 0; + + if (BKE_shrinkwrap_needs_normals(smd->shrinkType, smd->shrinkMode)) { + mask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL; + } + if (smd->target != NULL) { DEG_add_object_relation(ctx->node, smd->target, DEG_OB_COMP_TRANSFORM, "Shrinkwrap Modifier"); - DEG_add_object_relation(ctx->node, smd->target, DEG_OB_COMP_GEOMETRY, "Shrinkwrap Modifier"); + DEG_add_object_customdata_relation(ctx->node, smd->target, DEG_OB_COMP_GEOMETRY, mask, "Shrinkwrap Modifier"); } if (smd->auxTarget != NULL) { DEG_add_object_relation(ctx->node, smd->auxTarget, DEG_OB_COMP_TRANSFORM, "Shrinkwrap Modifier"); - DEG_add_object_relation(ctx->node, smd->auxTarget, DEG_OB_COMP_GEOMETRY, "Shrinkwrap Modifier"); + DEG_add_object_customdata_relation(ctx->node, smd->auxTarget, DEG_OB_COMP_GEOMETRY, mask, "Shrinkwrap Modifier"); } DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Shrinkwrap Modifier"); }