/* * This function raycast a single vertex and updates the hit if the "hit" is considered valid. * Returns TRUE if "hit" was updated. * Opts control whether an hit is valid or not * Supported options are: * MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE (front faces hits are ignored) * MOD_SHRINKWRAP_CULL_TARGET_BACKFACE (back faces hits are ignored) */ int normal_projection_project_vertex(char options, const float *vert, const float *dir, const SpaceTransform *transf, BVHTree *tree, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata) { float tmp_co[3], tmp_no[3]; const float *co, *no; BVHTreeRayHit hit_tmp; //Copy from hit (we need to convert hit rays from one space coordinates to the other memcpy(&hit_tmp, hit, sizeof(hit_tmp)); //Apply space transform (TODO readjust dist) if (transf) { copy_v3_v3(tmp_co, vert); space_transform_apply(transf, tmp_co); co = tmp_co; copy_v3_v3(tmp_no, dir); space_transform_apply_normal(transf, tmp_no); no = tmp_no; hit_tmp.dist *= mat4_to_scale(((SpaceTransform*)transf)->local2target); } else { co = vert; no = dir; } hit_tmp.index = -1; BLI_bvhtree_ray_cast(tree, co, no, 0.0f, &hit_tmp, callback, userdata); if (hit_tmp.index != -1) { /* invert the normal first so face culling works on rotated objects */ if (transf) { space_transform_invert_normal(transf, hit_tmp.no); } if (options & (MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE|MOD_SHRINKWRAP_CULL_TARGET_BACKFACE)) { /* apply backface */ const float dot= dot_v3v3(dir, hit_tmp.no); if ( ((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) || ((options & MOD_SHRINKWRAP_CULL_TARGET_BACKFACE) && dot >= 0.0f) ) { return FALSE; /* Ignore hit */ } } if (transf) { /* Inverting space transform (TODO make coeherent with the initial dist readjust) */ space_transform_invert(transf, hit_tmp.co); hit_tmp.dist = len_v3v3((float *)vert, hit_tmp.co); } memcpy(hit, &hit_tmp, sizeof(hit_tmp)); return TRUE; } return FALSE; }
/* * 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); }
/* * This function raycast a single vertex and updates the hit if the "hit" is considered valid. * Returns true if "hit" was updated. * Opts control whether an hit is valid or not * Supported options are: * MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE (front faces hits are ignored) * MOD_SHRINKWRAP_CULL_TARGET_BACKFACE (back faces hits are ignored) */ bool BKE_shrinkwrap_project_normal( char options, const float vert[3], const float dir[3], const SpaceTransform *transf, BVHTree *tree, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata) { /* don't use this because this dist value could be incompatible * this value used by the callback for comparing prev/new dist values. * also, at the moment there is no need to have a corrected 'dist' value */ // #define USE_DIST_CORRECT float tmp_co[3], tmp_no[3]; const float *co, *no; BVHTreeRayHit hit_tmp; /* Copy from hit (we need to convert hit rays from one space coordinates to the other */ memcpy(&hit_tmp, hit, sizeof(hit_tmp)); /* Apply space transform (TODO readjust dist) */ if (transf) { copy_v3_v3(tmp_co, vert); space_transform_apply(transf, tmp_co); co = tmp_co; copy_v3_v3(tmp_no, dir); space_transform_apply_normal(transf, tmp_no); no = tmp_no; #ifdef USE_DIST_CORRECT hit_tmp.dist *= mat4_to_scale(((SpaceTransform *)transf)->local2target); #endif } else { co = vert; no = dir; } hit_tmp.index = -1; BLI_bvhtree_ray_cast(tree, co, no, 0.0f, &hit_tmp, callback, userdata); if (hit_tmp.index != -1) { /* invert the normal first so face culling works on rotated objects */ if (transf) { space_transform_invert_normal(transf, hit_tmp.no); } if (options & (MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE | MOD_SHRINKWRAP_CULL_TARGET_BACKFACE)) { /* apply backface */ const float dot = dot_v3v3(dir, hit_tmp.no); if (((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) || ((options & MOD_SHRINKWRAP_CULL_TARGET_BACKFACE) && dot >= 0.0f)) { return false; /* Ignore hit */ } } if (transf) { /* Inverting space transform (TODO make coeherent with the initial dist readjust) */ space_transform_invert(transf, hit_tmp.co); #ifdef USE_DIST_CORRECT hit_tmp.dist = len_v3v3(vert, hit_tmp.co); #endif } BLI_assert(hit_tmp.dist <= hit->dist); memcpy(hit, &hit_tmp, sizeof(hit_tmp)); return true; } return false; }
/* 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]); } } } }
/** * Find nearest vertex and/or edge and/or face, for each vertex (adapted from shrinkwrap.c). */ static void get_vert2geom_distance(int numVerts, float (*v_cos)[3], float *dist_v, float *dist_e, float *dist_f, DerivedMesh *target, const SpaceTransform *loc2trgt) { int i; BVHTreeFromMesh treeData_v = NULL_BVHTreeFromMesh; BVHTreeFromMesh treeData_e = NULL_BVHTreeFromMesh; BVHTreeFromMesh treeData_f = NULL_BVHTreeFromMesh; BVHTreeNearest nearest_v = NULL_BVHTreeNearest; BVHTreeNearest nearest_e = NULL_BVHTreeNearest; BVHTreeNearest nearest_f = NULL_BVHTreeNearest; if (dist_v) { /* Create a bvh-tree of the given target's verts. */ bvhtree_from_mesh_verts(&treeData_v, target, 0.0, 2, 6); if (treeData_v.tree == NULL) { OUT_OF_MEMORY(); return; } } if (dist_e) { /* Create a bvh-tree of the given target's edges. */ bvhtree_from_mesh_edges(&treeData_e, target, 0.0, 2, 6); if (treeData_e.tree == NULL) { OUT_OF_MEMORY(); return; } } if (dist_f) { /* Create a bvh-tree of the given target's faces. */ bvhtree_from_mesh_faces(&treeData_f, target, 0.0, 2, 6); if (treeData_f.tree == NULL) { OUT_OF_MEMORY(); return; } } /* Setup nearest. */ nearest_v.index = nearest_e.index = nearest_f.index = -1; /*nearest_v.dist = nearest_e.dist = nearest_f.dist = FLT_MAX;*/ /* Find the nearest vert/edge/face. */ #ifndef __APPLE__ #pragma omp parallel for default(none) private(i) firstprivate(nearest_v,nearest_e,nearest_f) \ shared(treeData_v,treeData_e,treeData_f,numVerts,v_cos,dist_v,dist_e, \ dist_f,loc2trgt) \ schedule(static) #endif for (i = 0; i < numVerts; i++) { float tmp_co[3]; /* Convert the vertex to tree coordinates. */ copy_v3_v3(tmp_co, v_cos[i]); space_transform_apply(loc2trgt, 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 (dist_v) { nearest_v.dist = nearest_v.index != -1 ? len_squared_v3v3(tmp_co, nearest_v.co) : FLT_MAX; /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */ BLI_bvhtree_find_nearest(treeData_v.tree, tmp_co, &nearest_v, treeData_v.nearest_callback, &treeData_v); dist_v[i] = sqrtf(nearest_v.dist); } if (dist_e) { nearest_e.dist = nearest_e.index != -1 ? len_squared_v3v3(tmp_co, nearest_e.co) : FLT_MAX; /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */ BLI_bvhtree_find_nearest(treeData_e.tree, tmp_co, &nearest_e, treeData_e.nearest_callback, &treeData_e); dist_e[i] = sqrtf(nearest_e.dist); } if (dist_f) { nearest_f.dist = nearest_f.index != -1 ? len_squared_v3v3(tmp_co, nearest_f.co) : FLT_MAX; /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */ BLI_bvhtree_find_nearest(treeData_f.tree, tmp_co, &nearest_f, treeData_f.nearest_callback, &treeData_f); dist_f[i] = sqrtf(nearest_f.dist); } } if (dist_v) free_bvhtree_from_mesh(&treeData_v); if (dist_e) free_bvhtree_from_mesh(&treeData_e); if (dist_f) free_bvhtree_from_mesh(&treeData_f); }
/* * 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); }