/** * Callback used by BLI_task 'for loop' helper. */ static void vert2geom_task_cb(void *userdata, void *userdata_chunk, int iter) { Vert2GeomData *data = userdata; Vert2GeomDataChunk *data_chunk = userdata_chunk; float tmp_co[3]; int i; /* Convert the vertex to tree coordinates. */ copy_v3_v3(tmp_co, data->v_cos[iter]); BLI_space_transform_apply(data->loc2trgt, tmp_co); for (i = 0; i < ARRAY_SIZE(data->dist); i++) { if (data->dist[i]) { BVHTreeNearest nearest = {0}; /* Note that we use local proximity heuristics (to reduce the nearest search). * * If we already had an hit before in same chunk of tasks (i.e. previous vertex by index), * 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. */ nearest.dist_sq = data_chunk->is_init[i] ? len_squared_v3v3(tmp_co, data_chunk->last_hit_co[i]) : FLT_MAX; nearest.index = -1; /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */ BLI_bvhtree_find_nearest(data->treeData[i]->tree, tmp_co, &nearest, data->treeData[i]->nearest_callback, data->treeData[i]); data->dist[i][iter] = sqrtf(nearest.dist_sq); if (nearest.index != -1) { copy_v3_v3(data_chunk->last_hit_co[i], nearest.co); data_chunk->is_init[i] = true; } } } }
/* 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]); } } } }
/* 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; BLI_SPACE_TRANSFORM_SETUP(transf, ob, smd->origin); } /* 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) { 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 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]); } 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); 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) { BLI_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) { BLI_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 treeData_e = {NULL}; BVHTreeFromMesh treeData_f = {NULL}; BVHTreeNearest nearest_v = {0}; BVHTreeNearest nearest_e = {0}; BVHTreeNearest nearest_f = {0}; 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. */ #pragma omp parallel for default(shared) private(i) firstprivate(nearest_v, nearest_e, nearest_f) \ schedule(static) if (numVerts > 10000) for (i = 0; i < numVerts; i++) { float tmp_co[3]; /* Convert the vertex to tree coordinates. */ copy_v3_v3(tmp_co, v_cos[i]); BLI_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_sq = 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_sq); } if (dist_e) { nearest_e.dist_sq = 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_sq); } if (dist_f) { nearest_f.dist_sq = 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_sq); } } 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); }