/* * Shrinkwrap moving vertexs to the nearest surface point on the target * * it builds a BVHTree from the target mesh and then performs a * NN matches for each vertex */ static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) { int i; BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; /* Create a bvh-tree of the given target */ bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 2, 6); if (treeData.tree == NULL) { OUT_OF_MEMORY(); return; } /* Setup nearest */ nearest.index = -1; nearest.dist_sq = FLT_MAX; /* Find the nearest vertex */ #ifndef __APPLE__ #pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(calc, treeData) schedule(static) #endif for (i = 0; i < calc->numVerts; ++i) { float *co = calc->vertexCos[i]; float tmp_co[3]; float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); if (weight == 0.0f) continue; /* Convert the vertex to tree coordinates */ if (calc->vert) { copy_v3_v3(tmp_co, calc->vert[i].co); } else { copy_v3_v3(tmp_co, co); } space_transform_apply(&calc->local2target, tmp_co); /* Use local proximity heuristics (to reduce the nearest search) * * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex * so we can initiate the "nearest.dist" with the expected value to that last hit. * This will lead in pruning of the search tree. */ if (nearest.index != -1) nearest.dist_sq = len_squared_v3v3(tmp_co, nearest.co); else nearest.dist_sq = FLT_MAX; BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); /* Found the nearest vertex */ if (nearest.index != -1) { if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { /* Make the vertex stay on the front side of the face */ madd_v3_v3v3fl(tmp_co, nearest.co, nearest.no, calc->keepDist); } else { /* Adjusting the vertex weight, * so that after interpolating it keeps a certain distance from the nearest position */ const float dist = sasqrt(nearest.dist_sq); if (dist > FLT_EPSILON) { /* linear interpolation */ interp_v3_v3v3(tmp_co, tmp_co, nearest.co, (dist - calc->keepDist) / dist); } else { copy_v3_v3(tmp_co, nearest.co); } } /* Convert the coordinates back to mesh coordinates */ space_transform_invert(&calc->local2target, tmp_co); interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ } } free_bvhtree_from_mesh(&treeData); }
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for_render) { int i; /* Options about projection direction */ const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit; float proj_axis[3] = {0.0f, 0.0f, 0.0f}; /* Raycast and tree stuff */ /** \note 'hit.dist' is kept in the targets space, this is only used * for finding the best hit, to get the real dist, * measure the len_v3v3() from the input coord to hit.co */ BVHTreeRayHit hit; BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; /* auxiliary target */ DerivedMesh *auxMesh = NULL; BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh; SpaceTransform local2aux; /* If the user doesn't allows to project in any direction of projection axis * then theres nothing todo. */ if ((calc->smd->shrinkOpts & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 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; } else { /* The code supports any axis that is a combination of X,Y,Z * although currently UI only allows to set the 3 different axis */ if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f; normalize_v3(proj_axis); /* Invalid projection direction */ if (len_squared_v3(proj_axis) < FLT_EPSILON) { return; } } if (calc->smd->auxTarget) { auxMesh = object_get_derived_final(calc->smd->auxTarget, for_render); if (!auxMesh) return; SPACE_TRANSFORM_SETUP(&local2aux, calc->ob, calc->smd->auxTarget); } /* After sucessufuly build the trees, start projection vertexs */ if (bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 4, 6) && (auxMesh == NULL || bvhtree_from_mesh_faces(&auxData, auxMesh, 0.0, 4, 6))) { #ifndef __APPLE__ #pragma omp parallel for private(i, hit) schedule(static) #endif for (i = 0; i < calc->numVerts; ++i) { float *co = calc->vertexCos[i]; float tmp_co[3], tmp_no[3]; const float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); if (weight == 0.0f) { continue; } if (calc->vert) { /* calc->vert contains verts from derivedMesh */ /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { copy_v3_v3(tmp_co, calc->vert[i].co); normal_short_to_float_v3(tmp_no, calc->vert[i].no); } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } hit.index = -1; hit.dist = 10000.0f; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ /* Project over positive direction of axis */ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { if (auxData.tree) { BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); } BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, tmp_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } /* Project over negative direction of axis */ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) { float inv_no[3]; negate_v3_v3(inv_no, tmp_no); if (auxData.tree) { BKE_shrinkwrap_project_normal(0, tmp_co, inv_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); } BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, inv_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } /* don't set the initial dist (which is more efficient), * because its calculated in the targets space, we want the dist in our own space */ if (proj_limit_squared != 0.0f) { if (len_squared_v3v3(hit.co, co) > proj_limit_squared) { hit.index = -1; } } if (hit.index != -1) { madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); interp_v3_v3v3(co, co, hit.co, weight); } } } /* free data structures */ free_bvhtree_from_mesh(&treeData); free_bvhtree_from_mesh(&auxData); }
/* simple deform modifier */ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, struct Object *ob, struct DerivedMesh *dm, float (*vertexCos)[3], int numVerts) { static const float lock_axis[2] = {0.0f, 0.0f}; int i; int limit_axis = 0; float smd_limit[2], smd_factor; SpaceTransform *transf = NULL, tmp_transf; void (*simpleDeform_callback)(const float factor, const float dcut[3], float co[3]) = NULL; /* Mode callback */ int vgroup; MDeformVert *dvert; /* Safe-check */ if (smd->origin == ob) smd->origin = NULL; /* No self references */ if (smd->limit[0] < 0.0f) smd->limit[0] = 0.0f; if (smd->limit[0] > 1.0f) smd->limit[0] = 1.0f; smd->limit[0] = min_ff(smd->limit[0], smd->limit[1]); /* Upper limit >= than lower limit */ /* Calculate matrixs do convert between coordinate spaces */ if (smd->origin) { transf = &tmp_transf; if (smd->originOpts & MOD_SIMPLEDEFORM_ORIGIN_LOCAL) { space_transform_from_matrixs(transf, ob->obmat, smd->origin->obmat); } else { copy_m4_m4(transf->local2target, smd->origin->obmat); invert_m4_m4(transf->target2local, transf->local2target); } } /* Setup vars, * Bend limits on X.. all other modes limit on Z */ limit_axis = (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) ? 0 : 2; /* Update limits if needed */ { float lower = FLT_MAX; float upper = -FLT_MAX; for (i = 0; i < numVerts; i++) { float tmp[3]; copy_v3_v3(tmp, vertexCos[i]); if (transf) space_transform_apply(transf, tmp); lower = min_ff(lower, tmp[limit_axis]); upper = max_ff(upper, tmp[limit_axis]); } /* SMD values are normalized to the BV, calculate the absolut values */ smd_limit[1] = lower + (upper - lower) * smd->limit[1]; smd_limit[0] = lower + (upper - lower) * smd->limit[0]; smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]); } modifier_get_vgroup(ob, dm, smd->vgroup_name, &dvert, &vgroup); switch (smd->mode) { case MOD_SIMPLEDEFORM_MODE_TWIST: simpleDeform_callback = simpleDeform_twist; break; case MOD_SIMPLEDEFORM_MODE_BEND: simpleDeform_callback = simpleDeform_bend; break; case MOD_SIMPLEDEFORM_MODE_TAPER: simpleDeform_callback = simpleDeform_taper; break; case MOD_SIMPLEDEFORM_MODE_STRETCH: simpleDeform_callback = simpleDeform_stretch; break; default: return; /* No simpledeform mode? */ } for (i = 0; i < numVerts; i++) { float weight = defvert_array_find_weight_safe(dvert, i, vgroup); if (weight != 0.0f) { float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; if (transf) { space_transform_apply(transf, vertexCos[i]); } copy_v3_v3(co, vertexCos[i]); /* Apply axis limits */ if (smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) { /* Bend mode shoulnt have any lock axis */ if (smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) axis_limit(0, lock_axis, co, dcut); if (smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) axis_limit(1, lock_axis, co, dcut); } axis_limit(limit_axis, smd_limit, co, dcut); simpleDeform_callback(smd_factor, dcut, co); /* apply deform */ interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight); /* Use vertex weight has coef of linear interpolation */ if (transf) { space_transform_invert(transf, vertexCos[i]); } } } }
/* simple deform modifier */ static void SimpleDeformModifier_do( SimpleDeformModifierData *smd, struct Object *ob, struct DerivedMesh *dm, float (*vertexCos)[3], int numVerts) { const float base_limit[2] = {0.0f, 0.0f}; int i; float smd_limit[2], smd_factor; SpaceTransform *transf = NULL, tmp_transf; void (*simpleDeform_callback)(const float factor, const int axis, const float dcut[3], float co[3]) = NULL; /* Mode callback */ int vgroup; MDeformVert *dvert; /* This is historically the lock axis, _not_ the deform axis as the name would imply */ const int deform_axis = smd->deform_axis; int lock_axis = smd->axis; if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) { /* Bend mode shouldn't have any lock axis */ lock_axis = 0; } else { /* Don't lock axis if it is the chosen deform axis, as this flattens * the geometry */ if (deform_axis == 0) { lock_axis &= ~MOD_SIMPLEDEFORM_LOCK_AXIS_X; } if (deform_axis == 1) { lock_axis &= ~MOD_SIMPLEDEFORM_LOCK_AXIS_Y; } if (deform_axis == 2) { lock_axis &= ~MOD_SIMPLEDEFORM_LOCK_AXIS_Z; } } /* Safe-check */ if (smd->origin == ob) smd->origin = NULL; /* No self references */ if (smd->limit[0] < 0.0f) smd->limit[0] = 0.0f; if (smd->limit[0] > 1.0f) smd->limit[0] = 1.0f; smd->limit[0] = min_ff(smd->limit[0], smd->limit[1]); /* Upper limit >= than lower limit */ /* Calculate matrixs do convert between coordinate spaces */ if (smd->origin) { transf = &tmp_transf; BLI_SPACE_TRANSFORM_SETUP(transf, ob, smd->origin); } /* Update limits if needed */ int limit_axis = deform_axis; if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) { /* Bend is a special case. */ switch (deform_axis) { case 0: ATTR_FALLTHROUGH; case 1: limit_axis = 2; break; default: limit_axis = 0; } } { float lower = FLT_MAX; float upper = -FLT_MAX; for (i = 0; i < numVerts; i++) { float tmp[3]; copy_v3_v3(tmp, vertexCos[i]); if (transf) { BLI_space_transform_apply(transf, tmp); } lower = min_ff(lower, tmp[limit_axis]); upper = max_ff(upper, tmp[limit_axis]); } /* SMD values are normalized to the BV, calculate the absolute values */ smd_limit[1] = lower + (upper - lower) * smd->limit[1]; smd_limit[0] = lower + (upper - lower) * smd->limit[0]; smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]); } switch (smd->mode) { case MOD_SIMPLEDEFORM_MODE_TWIST: simpleDeform_callback = simpleDeform_twist; break; case MOD_SIMPLEDEFORM_MODE_BEND: simpleDeform_callback = simpleDeform_bend; break; case MOD_SIMPLEDEFORM_MODE_TAPER: simpleDeform_callback = simpleDeform_taper; break; case MOD_SIMPLEDEFORM_MODE_STRETCH: simpleDeform_callback = simpleDeform_stretch; break; default: return; /* No simpledeform mode? */ } if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) { if (fabsf(smd_factor) < BEND_EPS) { return; } } modifier_get_vgroup(ob, dm, smd->vgroup_name, &dvert, &vgroup); const bool invert_vgroup = (smd->flag & MOD_SIMPLEDEFORM_FLAG_INVERT_VGROUP) != 0; const uint *axis_map = axis_map_table[(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? deform_axis : 2]; for (i = 0; i < numVerts; i++) { float weight = defvert_array_find_weight_safe(dvert, i, vgroup); if (invert_vgroup) { weight = 1.0f - weight; } if (weight != 0.0f) { float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; if (transf) { BLI_space_transform_apply(transf, vertexCos[i]); } copy_v3_v3(co, vertexCos[i]); /* Apply axis limits, and axis mappings */ if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) { axis_limit(0, base_limit, co, dcut); } if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) { axis_limit(1, base_limit, co, dcut); } if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) { axis_limit(2, base_limit, co, dcut); } axis_limit(limit_axis, smd_limit, co, dcut); /* apply the deform to a mapped copy of the vertex, and then re-map it back. */ float co_remap[3]; float dcut_remap[3]; copy_v3_v3_map(co_remap, co, axis_map); copy_v3_v3_map(dcut_remap, dcut, axis_map); simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap); /* apply deform */ copy_v3_v3_unmap(co, co_remap, axis_map); interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight); /* Use vertex weight has coef of linear interpolation */ if (transf) { BLI_space_transform_invert(transf, vertexCos[i]); } } } }
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) { int i; //Options about projection direction const char use_normal = calc->smd->shrinkOpts; float proj_axis[3] = {0.0f, 0.0f, 0.0f}; //Raycast and tree stuff BVHTreeRayHit hit; BVHTreeFromMesh treeData= NULL_BVHTreeFromMesh; //auxiliary target DerivedMesh *auxMesh = NULL; BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh; SpaceTransform local2aux; //If the user doesn't allows to project in any direction of projection axis //then theres nothing todo. if ((use_normal & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 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; } else { //The code supports any axis that is a combination of X,Y,Z //although currently UI only allows to set the 3 different axis if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f; normalize_v3(proj_axis); //Invalid projection direction if (dot_v3v3(proj_axis, proj_axis) < FLT_EPSILON) return; } if (calc->smd->auxTarget) { auxMesh = object_get_derived_final(calc->smd->auxTarget); if (!auxMesh) return; space_transform_setup(&local2aux, calc->ob, calc->smd->auxTarget); } //After sucessufuly build the trees, start projection vertexs if (bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 4, 6) && (auxMesh == NULL || bvhtree_from_mesh_faces(&auxData, auxMesh, 0.0, 4, 6))) { #ifndef __APPLE__ #pragma omp parallel for private(i,hit) schedule(static) #endif for (i = 0; i<calc->numVerts; ++i) { float *co = calc->vertexCos[i]; float tmp_co[3], tmp_no[3]; float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); if (weight == 0.0f) continue; if (calc->vert) { /* calc->vert contains verts from derivedMesh */ /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { copy_v3_v3(tmp_co, calc->vert[i].co); normal_short_to_float_v3(tmp_no, calc->vert[i].no); } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } hit.index = -1; hit.dist = 10000.0f; //TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that //Project over positive direction of axis if (use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { if (auxData.tree) normal_projection_project_vertex(0, tmp_co, tmp_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, tmp_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } //Project over negative direction of axis if (use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR && hit.index == -1) { float inv_no[3]; negate_v3_v3(inv_no, tmp_no); if (auxData.tree) normal_projection_project_vertex(0, tmp_co, inv_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, inv_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } if (hit.index != -1) { madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); interp_v3_v3v3(co, co, hit.co, weight); } } } //free data structures free_bvhtree_from_mesh(&treeData); free_bvhtree_from_mesh(&auxData); }
/* * Shrinkwrap to the nearest vertex * * it builds a kdtree of vertexs we can attach to and then * for each vertex performs a nearest vertex search on the tree */ static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) { int i; BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; BENCH(bvhtree_from_mesh_verts(&treeData, calc->target, 0.0, 2, 6)); if (treeData.tree == NULL) { OUT_OF_MEMORY(); return; } //Setup nearest nearest.index = -1; nearest.dist = FLT_MAX; #ifndef __APPLE__ #pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(treeData,calc) schedule(static) #endif for (i = 0; i<calc->numVerts; ++i) { float *co = calc->vertexCos[i]; float tmp_co[3]; float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); if (weight == 0.0f) continue; //Convert the vertex to tree coordinates if (calc->vert) { copy_v3_v3(tmp_co, calc->vert[i].co); } else { copy_v3_v3(tmp_co, co); } space_transform_apply(&calc->local2target, tmp_co); //Use local proximity heuristics (to reduce the nearest search) // //If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex //so we can initiate the "nearest.dist" with the expected value to that last hit. //This will lead in prunning of the search tree. if (nearest.index != -1) nearest.dist = len_squared_v3v3(tmp_co, nearest.co); else nearest.dist = FLT_MAX; BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); //Found the nearest vertex if (nearest.index != -1) { //Adjusting the vertex weight, so that after interpolating it keeps a certain distance from the nearest position float dist = sasqrt(nearest.dist); if (dist > FLT_EPSILON) weight *= (dist - calc->keepDist)/dist; //Convert the coordinates back to mesh coordinates copy_v3_v3(tmp_co, nearest.co); space_transform_invert(&calc->local2target, tmp_co); interp_v3_v3v3(co, co, tmp_co, weight); //linear interpolation } } free_bvhtree_from_mesh(&treeData); }