diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_data_transfer.h | 8 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_mesh_remap.h | 8 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/data_transfer.c | 27 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/mesh_remap.c | 295 | ||||
-rw-r--r-- | source/blender/editors/object/object_data_transfer.c | 16 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_datatransfer.c | 2 |
6 files changed, 292 insertions, 64 deletions
diff --git a/source/blender/blenkernel/BKE_data_transfer.h b/source/blender/blenkernel/BKE_data_transfer.h index cea093adca4..7c64d525575 100644 --- a/source/blender/blenkernel/BKE_data_transfer.h +++ b/source/blender/blenkernel/BKE_data_transfer.h @@ -136,8 +136,8 @@ bool BKE_object_data_transfer_mesh( struct Scene *scene, struct Object *ob_src, struct Object *ob_dst, const int data_types, bool use_create, const int map_vert_mode, const int map_edge_mode, const int map_loop_mode, const int map_poly_mode, - struct SpaceTransform *space_transform, const float max_distance, const float ray_radius, - const float islands_handling_precision, + struct SpaceTransform *space_transform, const bool auto_transform, + const float max_distance, const float ray_radius, const float islands_handling_precision, const int fromlayers_select[DT_MULTILAYER_INDEX_MAX], const int tolayers_select[DT_MULTILAYER_INDEX_MAX], const int mix_mode, const float mix_factor, const char *vgroup_name, const bool invert_vgroup, struct ReportList *reports); @@ -146,8 +146,8 @@ bool BKE_object_data_transfer_dm( struct Object *ob_src, struct Object *ob_dst, struct DerivedMesh *dm_dst, const int data_types, bool use_create, const int map_vert_mode, const int map_edge_mode, const int map_loop_mode, const int map_poly_mode, - struct SpaceTransform *space_transform, const float max_distance, const float ray_radius, - const float islands_handling_precision, + struct SpaceTransform *space_transform, const bool auto_transform, + const float max_distance, const float ray_radius, const float islands_handling_precision, const int fromlayers_select[DT_MULTILAYER_INDEX_MAX], const int tolayers_select[DT_MULTILAYER_INDEX_MAX], const int mix_mode, const float mix_factor, const char *vgroup_name, const bool invert_vgroup, struct ReportList *reports); diff --git a/source/blender/blenkernel/BKE_mesh_remap.h b/source/blender/blenkernel/BKE_mesh_remap.h index c6d8da16565..5c77ab8a94e 100644 --- a/source/blender/blenkernel/BKE_mesh_remap.h +++ b/source/blender/blenkernel/BKE_mesh_remap.h @@ -140,6 +140,14 @@ enum { MREMAP_MODE_TOPOLOGY = MREMAP_MODE_VERT | MREMAP_MODE_EDGE | MREMAP_MODE_LOOP | MREMAP_MODE_POLY, }; +float BKE_mesh_remap_calc_difference_from_dm( + const struct SpaceTransform *space_transform, + const struct MVert *verts_dst, const int numverts_dst, struct DerivedMesh *dm_src); + +void BKE_mesh_remap_find_best_match_from_dm( + const struct MVert *verts_dst, const int numverts_dst, struct DerivedMesh *dm_src, + struct SpaceTransform *r_space_transform); + /* TODO add mesh2mesh versions (we'll need mesh versions of bvhtree funcs too, though!). */ void BKE_mesh_remap_calc_verts_from_dm( diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index da00aecf9c0..53b6f4a1019 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -1064,8 +1064,8 @@ void BKE_object_data_transfer_layout( bool BKE_object_data_transfer_dm( Scene *scene, Object *ob_src, Object *ob_dst, DerivedMesh *dm_dst, const int data_types, bool use_create, const int map_vert_mode, const int map_edge_mode, const int map_loop_mode, const int map_poly_mode, - SpaceTransform *space_transform, const float max_distance, const float ray_radius, - const float islands_handling_precision, + SpaceTransform *space_transform, const bool auto_transform, + const float max_distance, const float ray_radius, const float islands_handling_precision, const int fromlayers_select[DT_MULTILAYER_INDEX_MAX], const int tolayers_select[DT_MULTILAYER_INDEX_MAX], const int mix_mode, const float mix_factor, const char *vgroup_name, const bool invert_vgroup, ReportList *reports) @@ -1076,6 +1076,8 @@ bool BKE_object_data_transfer_dm( #define PDATA 3 #define DATAMAX 4 + SpaceTransform auto_space_transform; + DerivedMesh *dm_src; Mesh *me_dst, *me_src; bool dirty_nors_dst = true; /* Assumed always true if not using a dm as destination. */ @@ -1126,6 +1128,17 @@ bool BKE_object_data_transfer_dm( return changed; } + if (auto_transform) { + MVert *verts_dst = dm_dst ? dm_dst->getVertArray(dm_dst) : me_dst->mvert; + const int num_verts_dst = dm_dst ? dm_dst->getNumVerts(dm_dst) : me_dst->totvert; + + if (space_transform == NULL) { + space_transform = &auto_space_transform; + } + + BKE_mesh_remap_find_best_match_from_dm(verts_dst, num_verts_dst, dm_src, space_transform); + } + /* Check all possible data types. * Note item mappings and dest mix weights are cached. */ for (i = 0; i < DT_TYPE_MAX; i++) { @@ -1388,15 +1401,17 @@ bool BKE_object_data_transfer_dm( bool BKE_object_data_transfer_mesh( Scene *scene, Object *ob_src, Object *ob_dst, const int data_types, const bool use_create, const int map_vert_mode, const int map_edge_mode, const int map_loop_mode, const int map_poly_mode, - SpaceTransform *space_transform, const float max_distance, const float ray_radius, - const float islands_handling_precision, + SpaceTransform *space_transform, const bool auto_transform, + const float max_distance, const float ray_radius, const float islands_handling_precision, const int fromlayers_select[DT_MULTILAYER_INDEX_MAX], const int tolayers_select[DT_MULTILAYER_INDEX_MAX], const int mix_mode, const float mix_factor, const char *vgroup_name, const bool invert_vgroup, ReportList *reports) { return BKE_object_data_transfer_dm( scene, ob_src, ob_dst, NULL, data_types, use_create, - map_vert_mode, map_edge_mode, map_loop_mode, map_poly_mode, space_transform, - max_distance, ray_radius, islands_handling_precision, fromlayers_select, tolayers_select, + map_vert_mode, map_edge_mode, map_loop_mode, map_poly_mode, + space_transform, auto_transform, + max_distance, ray_radius, islands_handling_precision, + fromlayers_select, tolayers_select, mix_mode, mix_factor, vgroup_name, invert_vgroup, reports); } diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index 08823c1c4a1..c818c6b9f19 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -51,6 +51,250 @@ /* -------------------------------------------------------------------- */ +/** \name Some generic helpers. + * \{ */ + +static bool mesh_remap_bvhtree_query_nearest( + BVHTreeFromMesh *treedata, BVHTreeNearest *nearest, + const float co[3], const float max_dist_sq, float *r_hit_dist) +{ + /* Use local proximity heuristics (to reduce the nearest search). */ + if (nearest->index != -1) { + nearest->dist_sq = min_ff(len_squared_v3v3(co, nearest->co), max_dist_sq); + } + else { + nearest->dist_sq = max_dist_sq; + } + /* Compute and store result. If invalid (-1 index), keep FLT_MAX dist. */ + BLI_bvhtree_find_nearest(treedata->tree, co, nearest, treedata->nearest_callback, treedata); + + if ((nearest->index != -1) && (nearest->dist_sq <= max_dist_sq)) { + *r_hit_dist = sqrtf(nearest->dist_sq); + return true; + } + else { + return false; + } +} + +static bool mesh_remap_bvhtree_query_raycast( + BVHTreeFromMesh *treedata, BVHTreeRayHit *rayhit, + const float co[3], const float no[3], const float radius, const float max_dist, float *r_hit_dist) +{ + BVHTreeRayHit rayhit_tmp; + float inv_no[3]; + + rayhit->index = -1; + rayhit->dist = max_dist; + BLI_bvhtree_ray_cast(treedata->tree, co, no, radius, rayhit, treedata->raycast_callback, treedata); + + /* Also cast in the other direction! */ + rayhit_tmp = *rayhit; + negate_v3_v3(inv_no, no); + BLI_bvhtree_ray_cast(treedata->tree, co, inv_no, radius, &rayhit_tmp, treedata->raycast_callback, treedata); + if (rayhit_tmp.dist < rayhit->dist) { + *rayhit = rayhit_tmp; + } + + if ((rayhit->index != -1) && (rayhit->dist <= max_dist)) { + *r_hit_dist = rayhit->dist; + return true; + } + else { + return false; + } +} + +/** \} */ + +/** + * \name Auto-match. + * + * Find transform of a mesh to get best match with another. + * \{ */ + +/** + * Compute a value of the difference between both given meshes. + * The smaller the result, the better the match. + * + * We return the inverse of the average of the inversed shortest distance from each dst vertex to src ones. + * In other words, beyond a certain (relatively small) distance, all differences have more or less the same weight + * in final result, which allows to reduce influence of a few high differences, in favor of a global good matching. + */ +float BKE_mesh_remap_calc_difference_from_dm( + const SpaceTransform *space_transform, const MVert *verts_dst, const int numverts_dst, DerivedMesh *dm_src) +{ + BVHTreeFromMesh treedata = {NULL}; + BVHTreeNearest nearest = {0}; + float hit_dist; + + float result = 0.0f; + int i; + + bvhtree_from_mesh_verts(&treedata, dm_src, 0.0f, 2, 6); + nearest.index = -1; + + for (i = 0; i < numverts_dst; i++) { + float tmp_co[3]; + + copy_v3_v3(tmp_co, verts_dst[i].co); + + /* Convert the vertex to tree coordinates, if needed. */ + if (space_transform) { + BLI_space_transform_apply(space_transform, tmp_co); + } + + if (mesh_remap_bvhtree_query_nearest(&treedata, &nearest, tmp_co, FLT_MAX, &hit_dist)) { + result += 1.0f / (hit_dist + 1.0f); + } + else { + /* No source for this dest vertex! */ + result += 1e-18f; + } + } + + result = ((float)numverts_dst / result) - 1.0f; + +// printf("%s: Computed difference between meshes (the lower the better): %f\n", __func__, result); + + return result; +} + +/* This helper computes the eigen values & vectors for covariance matrix of all given vertices coordinates. + * + * Those vectors define the 'average ellipsoid' of the mesh (i.e. the 'best fitting' ellipsoid + * containing 50% of the vertices). + * + * Note that it will not perform fantastic in case two or more eigen values are equal (e.g. a cylinder or + * parallelepiped with a square section give two identical eigenvalues, a sphere or tetrahedron give + * three identical ones, etc.), since you cannot really define all axes in those cases. We default to dummy + * generated orthogonal vectors in this case, instead of using eigen vectors. + */ +static void mesh_calc_eigen_matrix( + const MVert *verts, const float (*vcos)[3], const int numverts, float r_mat[4][4]) +{ + float center[3], covmat[3][3]; + float eigen_val[3], eigen_vec[3][3]; + float (*cos)[3] = (float (*)[3])vcos; + + bool eigen_success; + int i; + + if (verts) { + const MVert *mv; + float (*co)[3]; + + cos = MEM_mallocN(sizeof(*cos) * (size_t)numverts, __func__); + for (i = 0, co = cos, mv = verts; i < numverts; i++, co++, mv++) { + copy_v3_v3(*co, mv->co); + } + } + unit_m4(r_mat); + + /* Note: here we apply sample correction to covariance matrix, since we consider the vertices as a sample + * of the whole 'surface' population of our mesh... */ + BLI_covariance_m3_v3n(cos, numverts, true, covmat, center); + + eigen_success = BLI_eigen_solve_selfadjoint_m3(covmat, eigen_val, eigen_vec); + BLI_assert(eigen_success); + UNUSED_VARS_NDEBUG(eigen_success); + + /* Special handling of cases where some eigen values are (nearly) identical. */ + if (compare_ff_relative(eigen_val[0], eigen_val[1], FLT_EPSILON, 64)) { + if (compare_ff_relative(eigen_val[0], eigen_val[2], FLT_EPSILON, 64)) { + /* No preferred direction, that set of vertices has a spherical average, + * so we simply returned scaled/translated identity matrix (with no rotation). */ + unit_m3(eigen_vec); + } + else { + /* Ellipsoid defined by eigen values/vectors has a spherical section, + * we can only define one axis from eigen_vec[2] (two others computed eigen vecs + * are not so nice for us here, they tend to 'randomly' rotate around valid one). + * Note that eigen vectors as returned by BLI_eigen_solve_selfadjoint_m3() are normalized. */ + ortho_basis_v3v3_v3(eigen_vec[0], eigen_vec[1], eigen_vec[2]); + } + } + else if (compare_ff_relative(eigen_val[0], eigen_val[2], FLT_EPSILON, 64)) { + /* Same as above, but with eigen_vec[1] as valid axis. */ + ortho_basis_v3v3_v3(eigen_vec[2], eigen_vec[0], eigen_vec[1]); + } + else if (compare_ff_relative(eigen_val[1], eigen_val[2], FLT_EPSILON, 64)) { + /* Same as above, but with eigen_vec[0] as valid axis. */ + ortho_basis_v3v3_v3(eigen_vec[1], eigen_vec[2], eigen_vec[0]); + } + + for (i = 0; i < 3; i++) { + float evi = eigen_val[i]; + + /* Protect against 1D/2D degenerated cases! */ + /* Note: not sure why we need square root of eigen values here (which are equivalent to singular values, + * as far as I have understood), but it seems to heavily reduce (if not completly nullify) + * the error due to non-uniform scalings... */ + evi = (evi < 1e-6f && evi > -1e-6f) ? ((evi < 0.0f) ? -1e-3f : 1e-3f) : sqrtf_signed(evi); + mul_v3_fl(eigen_vec[i], evi); + } + + copy_m4_m3(r_mat, eigen_vec); + copy_v3_v3(r_mat[3], center); + + if (verts) { + MEM_freeN(cos); + } +} + +/** + * Set r_space_transform so that best bbox of dst matches best bbox of src. + */ +void BKE_mesh_remap_find_best_match_from_dm( + const MVert *verts_dst, const int numverts_dst, DerivedMesh *dm_src, SpaceTransform *r_space_transform) +{ + /* Note that those are done so that we successively get actual mirror matrix (by multiplication of columns)... */ + const float mirrors[][3] = { + {-1.0f, 1.0f, 1.0f}, /* -> -1, 1, 1 */ + { 1.0f, -1.0f, 1.0f}, /* -> -1, -1, 1 */ + { 1.0f, 1.0f, -1.0f}, /* -> -1, -1, -1 */ + { 1.0f, -1.0f, 1.0f}, /* -> -1, 1, -1 */ + {-1.0f, 1.0f, 1.0f}, /* -> 1, 1, -1 */ + { 1.0f, -1.0f, 1.0f}, /* -> 1, -1, -1 */ + { 1.0f, 1.0f, -1.0f}, /* -> 1, -1, 1 */ + {0.0f, 0.0f, 0.0f}, + }; + const float (*mirr)[3]; + + float mat_src[4][4], mat_dst[4][4], best_mat_dst[4][4]; + float best_match = FLT_MAX, match; + + const int numverts_src = dm_src->getNumVerts(dm_src); + float (*vcos_src)[3] = MEM_mallocN(sizeof(*vcos_src) * (size_t)numverts_src, __func__); + dm_src->getVertCos(dm_src, vcos_src); + + mesh_calc_eigen_matrix(NULL, vcos_src, numverts_src, mat_src); + mesh_calc_eigen_matrix(verts_dst, NULL, numverts_dst, mat_dst); + + BLI_space_transform_global_from_matrices(r_space_transform, mat_dst, mat_src); + match = BKE_mesh_remap_calc_difference_from_dm(r_space_transform, verts_dst, numverts_dst, dm_src); + best_match = match; + copy_m4_m4(best_mat_dst, mat_dst); + + /* And now, we have to check the otehr sixth possible mirrored versions... */ + for (mirr = mirrors; (*mirr)[0]; mirr++) { + mul_v3_fl(mat_dst[0], (*mirr)[0]); + mul_v3_fl(mat_dst[1], (*mirr)[1]); + mul_v3_fl(mat_dst[2], (*mirr)[2]); + + BLI_space_transform_global_from_matrices(r_space_transform, mat_dst, mat_src); + match = BKE_mesh_remap_calc_difference_from_dm(r_space_transform, verts_dst, numverts_dst, dm_src); + if (match < best_match) { + best_match = match; + copy_m4_m4(best_mat_dst, mat_dst); + } + } + + BLI_space_transform_global_from_matrices(r_space_transform, best_mat_dst, mat_src); +} + +/** \} */ + /** \name Mesh to mesh mapping * \{ */ @@ -147,57 +391,6 @@ static int mesh_remap_interp_poly_data_get( return sources_num; } -static bool mesh_remap_bvhtree_query_nearest( - BVHTreeFromMesh *treedata, BVHTreeNearest *nearest, - float co[3], const float max_dist_sq, float *r_hit_dist) -{ - /* Use local proximity heuristics (to reduce the nearest search). */ - if (nearest->index != -1) { - nearest->dist_sq = min_ff(len_squared_v3v3(co, nearest->co), max_dist_sq); - } - else { - nearest->dist_sq = max_dist_sq; - } - /* Compute and store result. If invalid (-1 index), keep FLT_MAX dist. */ - BLI_bvhtree_find_nearest(treedata->tree, co, nearest, treedata->nearest_callback, treedata); - - if ((nearest->index != -1) && (nearest->dist_sq <= max_dist_sq)) { - *r_hit_dist = sqrtf(nearest->dist_sq); - return true; - } - else { - return false; - } -} - -static bool mesh_remap_bvhtree_query_raycast( - BVHTreeFromMesh *treedata, BVHTreeRayHit *rayhit, - const float co[3], const float no[3], const float radius, const float max_dist, float *r_hit_dist) -{ - BVHTreeRayHit rayhit_tmp; - float inv_no[3]; - - rayhit->index = -1; - rayhit->dist = max_dist; - BLI_bvhtree_ray_cast(treedata->tree, co, no, radius, rayhit, treedata->raycast_callback, treedata); - - /* Also cast in the other direction! */ - rayhit_tmp = *rayhit; - negate_v3_v3(inv_no, no); - BLI_bvhtree_ray_cast(treedata->tree, co, inv_no, radius, &rayhit_tmp, treedata->raycast_callback, treedata); - if (rayhit_tmp.dist < rayhit->dist) { - *rayhit = rayhit_tmp; - } - - if ((rayhit->index != -1) && (rayhit->dist <= max_dist)) { - *r_hit_dist = rayhit->dist; - return true; - } - else { - return false; - } -} - /* Little helper when dealing with source islands */ typedef struct IslandResult { float factor; /* A factor, based on which best island for a given set of elements will be selected. */ diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index d6a1694b4b0..0b604a73977 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -339,6 +339,7 @@ static int data_transfer_exec(bContext *C, wmOperator *op) const int map_loop_mode = RNA_enum_get(op->ptr, "loop_mapping"); const int map_poly_mode = RNA_enum_get(op->ptr, "poly_mapping"); + const bool use_auto_transform = RNA_boolean_get(op->ptr, "use_auto_transform"); const bool use_object_transform = RNA_boolean_get(op->ptr, "use_object_transform"); const bool use_max_distance = RNA_boolean_get(op->ptr, "use_max_distance"); const float max_distance = use_max_distance ? RNA_float_get(op->ptr, "max_distance") : FLT_MAX; @@ -355,7 +356,7 @@ static int data_transfer_exec(bContext *C, wmOperator *op) const float mix_factor = RNA_float_get(op->ptr, "mix_factor"); SpaceTransform space_transform_data; - SpaceTransform *space_transform = use_object_transform ? &space_transform_data : NULL; + SpaceTransform *space_transform = (use_object_transform && !use_auto_transform) ? &space_transform_data : NULL; if (reverse_transfer && ((ID *)(ob_src->data))->lib) { /* Do not transfer to linked data, not supported. */ @@ -384,7 +385,8 @@ static int data_transfer_exec(bContext *C, wmOperator *op) if (BKE_object_data_transfer_mesh( scene, ob_src, ob_dst, data_type, use_create, map_vert_mode, map_edge_mode, map_loop_mode, map_poly_mode, - space_transform, max_distance, ray_radius, islands_precision, + space_transform, use_auto_transform, + max_distance, ray_radius, islands_precision, layers_select_src, layers_select_dst, mix_mode, mix_factor, NULL, false, op->reports)) { @@ -428,9 +430,13 @@ static bool data_transfer_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) const char *prop_id = RNA_property_identifier(prop); const int data_type = RNA_enum_get(ptr, "data_type"); + bool use_auto_transform = false; bool use_max_distance = false; bool use_modifier = false; + if ((prop_other = RNA_struct_find_property(ptr, "use_auto_transform"))) { + use_auto_transform = RNA_property_boolean_get(ptr, prop_other); + } if ((prop_other = RNA_struct_find_property(ptr, "use_max_distance"))) { use_max_distance = RNA_property_boolean_get(ptr, prop_other); } @@ -447,6 +453,9 @@ static bool data_transfer_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) return false; } + if (STREQ(prop_id, "use_object_transform") && use_auto_transform) { + return false; + } if (STREQ(prop_id, "max_distance") && !use_max_distance) { return false; } @@ -530,6 +539,9 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot) "Method used to map source faces to destination ones"); /* Mapping options and filtering. */ + RNA_def_boolean(ot->srna, "use_auto_transform", false, "Auto Transform", + "Automatically compute transformation to get the best possible match between source and " + "destination meshes (WARNING: results will never be as good as manual matching of objects)"); RNA_def_boolean(ot->srna, "use_object_transform", true, "Object Transform", "Evaluate source and destination meshes in global space"); RNA_def_boolean(ot->srna, "use_max_distance", false, "Only Neighbor Geometry", diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index e133c4785b7..85e9b4ee185 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -208,7 +208,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *der /* Note: no islands precision for now here. */ BKE_object_data_transfer_dm(md->scene, dtmd->ob_source, ob, dm, dtmd->data_types, false, dtmd->vmap_mode, dtmd->emap_mode, dtmd->lmap_mode, dtmd->pmap_mode, - space_transform, max_dist, dtmd->map_ray_radius, 0.0f, + space_transform, false, max_dist, dtmd->map_ray_radius, 0.0f, dtmd->layers_select_src, dtmd->layers_select_dst, dtmd->mix_mode, dtmd->mix_factor, dtmd->defgrp_name, invert_vgroup, &reports); |