svn merge ^/trunk/blender -r47023:HEAD
[blender-staging.git] / source / blender / editors / object / object_vgroup.c
index b5a0451b50719857e82c3108a9ba214849eccde7..f56f9f657bc7003055907e0938d73d97c3f30467 100644 (file)
@@ -20,7 +20,7 @@
  *
  * The Original Code is: all of this file.
  *
- * Contributor(s): none yet.
+ * Contributor(s): Ove M Henriksen.
  *
  * ***** END GPL LICENSE BLOCK *****
  */
@@ -29,7 +29,6 @@
  *  \ingroup edobj
  */
 
-
 #include <string.h>
 #include <stddef.h>
 #include <math.h>
@@ -376,6 +375,283 @@ int ED_vgroup_copy_array(Object *ob, Object *ob_from)
        return 1;
 }
 
+/***********************Start transfer weight*********************************/
+
+typedef enum VertexGroupOption {
+       REPLACE_ACTIVE_VERTEX_GROUP = 1,
+       REPLACE_ALL_VERTEX_GROUPS = 2
+} VertexGroupOption;
+
+typedef enum MethodOption {
+       BY_INDEX = 1,
+       BY_NEAREST_VERTEX = 2,
+       BY_NEAREST_FACE = 3,
+       BY_NEAREST_VERTEX_IN_FACE = 4
+} MethodOption;
+
+typedef enum ReplaceOption {
+       REPLACE_ALL_WEIGHTS = 1,
+       REPLACE_EMPTY_WEIGHTS = 2,
+       REPLACE_SELECTED_WEIGHTS = 3
+} ReplaceOption;
+
+static EnumPropertyItem vertex_group_option_item[] = {
+       {REPLACE_ACTIVE_VERTEX_GROUP, "REPLACE_ACTIVE_VERTEX_GROUP", 1, "Active", "Transfer active vertex group from selected to active mesh."},
+       {REPLACE_ALL_VERTEX_GROUPS, "REPLACE_ALL_VERTEX_GROUPS", 1, "All", "Transfer all vertex groups from selected to active mesh."},
+       {0, NULL, 0, NULL, NULL}
+};
+
+static EnumPropertyItem method_option_item[] = {
+       {BY_INDEX, "BY_INDEX", 1, "Vertex index", "Copy for identical meshes."},
+       {BY_NEAREST_VERTEX, "BY_NEAREST_VERTEX", 1, "Nearest vertex", "Copy weight from closest vertex."},
+       {BY_NEAREST_FACE, "BY_NEAREST_FACE", 1, "Nearest face", "Barycentric interpolation from nearest face."},
+       {BY_NEAREST_VERTEX_IN_FACE, "BY_NEAREST_VERTEX_IN_FACE", 1, "Nearest vertex in face", "Copy weight from closest vertex in nearest face."},
+       {0, NULL, 0, NULL, NULL}
+};
+
+static EnumPropertyItem replace_option_item[] = {
+       {REPLACE_ALL_WEIGHTS, "REPLACE_ALL_WEIGHTS", 1, "All", "Overwrites all weights."},
+       {REPLACE_EMPTY_WEIGHTS, "REPLACE_EMPTY_WEIGHTS", 1, "Empty", "Adds weights to vertices with no weight."},
+       {REPLACE_SELECTED_WEIGHTS, "REPLACE_SELECTED_WEIGHTS", 1, "Selected", "Replace selected weights."},
+       {0, NULL, 0, NULL, NULL}
+};
+
+/*copy weight*/
+void vgroup_transfer_weight(MVert *mv_dst, float *weight_dst, float weight_src, ReplaceOption replace_option)
+{
+       switch (replace_option) {
+
+               case REPLACE_ALL_WEIGHTS:
+                       *weight_dst = weight_src;
+                       break;
+
+               case REPLACE_EMPTY_WEIGHTS:
+                       if (*weight_dst == 0) *weight_dst = weight_src;
+                       break;
+
+               case REPLACE_SELECTED_WEIGHTS:
+                       if (mv_dst->flag & SELECT) *weight_dst = weight_src;
+                       break;
+       }
+}
+
+int ED_vgroup_transfer_weight(Object *ob_dst, Object *ob_src, bDeformGroup *dg_src, Scene *scene, MethodOption method_option, ReplaceOption replace_option)
+{
+       bDeformGroup *dg_dst;
+       Mesh *me_dst, *me_src;
+       DerivedMesh *dmesh_src;
+       BVHTreeFromMesh tree_mesh_vertices_src, tree_mesh_faces_src = {NULL};
+       MDeformVert **dv_array_src, **dv_array_dst, **dv_src, **dv_dst;
+       MVert *mv_dst, *mv_src;
+       MFace *mface_src;
+       BVHTreeNearest nearest;
+       MDeformWeight *dw_dst, *dw_src;
+       int dv_tot_src, dv_tot_dst, i, index_dst, index_src, index_nearest, index_nearest_vertex;
+       float weight, tmp_weight[4], tmp_co[3], normal[3], tmp_mat[4][4], dist_v1, dist_v2, dist_v3, dist_v4;
+
+       /* create new and overwrite vertex group on destination without data */
+       if (!defgroup_find_name(ob_dst, dg_src->name) || replace_option == REPLACE_ALL_WEIGHTS) {
+               ED_vgroup_delete(ob_dst, defgroup_find_name(ob_dst, dg_src->name));
+               ED_vgroup_add_name(ob_dst, dg_src->name);
+       }
+
+       /* get destination deformgroup */
+       dg_dst = defgroup_find_name(ob_dst, dg_src->name);
+
+       /* get meshes */
+       dmesh_src = mesh_get_derived_deform(scene, ob_src, CD_MASK_BAREMESH | CD_MASK_ORIGINDEX);
+       me_dst = ob_dst->data;
+       me_src = ob_src->data;
+
+       /* sanity check */
+       if (!me_src->dvert) return 0;
+
+       /* create data in memory when nothing there */
+       if (!me_dst->dvert) ED_vgroup_data_create(ob_dst->data);
+
+       /* get vertex group arrays */
+       ED_vgroup_give_parray(ob_src->data, &dv_array_src, &dv_tot_src, FALSE);
+       ED_vgroup_give_parray(ob_dst->data, &dv_array_dst, &dv_tot_dst, FALSE);
+
+       /* get indexes of vertex groups */
+       index_src = BLI_findindex(&ob_src->defbase, dg_src);
+       index_dst = BLI_findindex(&ob_dst->defbase, dg_dst);
+
+       /* get vertices */
+       mv_dst = me_dst->mvert;
+       mv_src = dmesh_src->getVertArray(dmesh_src);
+
+       /* prepare transformation matrix */
+       invert_m4_m4(ob_src->imat, ob_src->obmat);
+       mult_m4_m4m4(tmp_mat, ob_src->imat, ob_dst->obmat);
+
+       switch (method_option) {
+
+               case BY_INDEX:
+                       /* check if indices are matching, delete and return if not */
+                       if (ob_dst == ob_src || dv_tot_dst == 0 || dv_tot_dst != dv_tot_src ||
+                           dv_array_src == NULL || dv_array_dst == NULL)
+                       {
+                               ED_vgroup_delete(ob_dst, defgroup_find_name(ob_dst, dg_dst->name));
+                               if (dv_array_src) MEM_freeN(dv_array_src);
+                               if (dv_array_dst) MEM_freeN(dv_array_dst);
+                               dmesh_src->release(dmesh_src);
+                               return 0;
+                       }
+
+                       /* loop through the vertices*/
+                       for(i = 0, dv_src = dv_array_src, dv_dst = dv_array_dst; i < me_dst->totvert; i++, dv_dst++, dv_src++, mv_src++) {
+
+                               /* copy weight */
+                               dw_src = defvert_verify_index(*dv_src, index_src);
+                               dw_dst = defvert_verify_index(*dv_dst, index_dst);
+                               vgroup_transfer_weight(mv_dst, &dw_dst->weight, dw_src->weight, replace_option);
+                       }
+                       break;
+
+               case BY_NEAREST_VERTEX:
+                       /* make node tree */
+                       bvhtree_from_mesh_verts(&tree_mesh_vertices_src, dmesh_src, FLT_EPSILON, 2, 6);
+
+                       /* loop trough vertices */
+                       for(i = 0, dv_dst = dv_array_dst; i < me_dst->totvert; i++, dv_dst++, mv_dst++){
+
+                               /* reset nearest */
+                               nearest.dist = FLT_MAX;
+                               /* With current binary tree its marginally faster to start searching at the top, as opposed to previous search. */
+                               nearest.index = -1;
+
+                               /* transform into target space */
+                               mul_v3_m4v3(tmp_co, tmp_mat, mv_dst->co);
+
+                               /* node tree accelerated search for closest vetex */
+                               BLI_bvhtree_find_nearest(tree_mesh_vertices_src.tree, tmp_co,
+                                                        &nearest, tree_mesh_vertices_src.nearest_callback, &tree_mesh_vertices_src);
+
+                               /* copy weight */
+                               dw_src = defvert_verify_index(dv_array_src[nearest.index], index_src);
+                               dw_dst = defvert_verify_index(*dv_dst, index_dst);
+                               vgroup_transfer_weight(mv_dst, &dw_dst->weight, dw_src->weight, replace_option);
+                       }
+
+                       /* free memory */
+                       free_bvhtree_from_mesh(&tree_mesh_vertices_src);
+                       break;
+
+               case BY_NEAREST_FACE:
+                       /* get faces */
+                       DM_ensure_tessface(dmesh_src);
+                       mface_src = dmesh_src->getTessFaceArray(dmesh_src);
+
+                       /* make node tree */
+                       bvhtree_from_mesh_faces(&tree_mesh_faces_src, dmesh_src, FLT_EPSILON, 2, 6);
+
+                       /* loop through the vertices */
+                       for(i = 0, dv_dst = dv_array_dst; i < me_dst->totvert; i++, dv_dst++, mv_dst++) {
+
+                               /* reset nearest */
+                               nearest.dist = FLT_MAX;
+                               /* With current binary tree its marginally faster to start searching at the top, as opposed to previous search. */
+                               nearest.index = -1;
+
+                               /* transform into target space */
+                               mul_v3_m4v3(tmp_co, tmp_mat, mv_dst->co);
+
+                               /* node tree accelerated search for closest face */
+                               BLI_bvhtree_find_nearest(tree_mesh_faces_src.tree, tmp_co,
+                                                        &nearest, tree_mesh_faces_src.nearest_callback, &tree_mesh_faces_src);
+                               index_nearest = nearest.index;
+
+                               /* project onto face */
+                               normal_tri_v3(normal, mv_src[mface_src[nearest.index].v1].co,
+                                             mv_src[mface_src[index_nearest].v2].co,
+                                             mv_src[mface_src[index_nearest].v3].co);
+
+                               project_v3_plane(tmp_co, normal, mv_src[mface_src[index_nearest].v1].co);
+
+                               /* interpolate weights */
+                               interp_weights_face_v3(tmp_weight, mv_src[mface_src[index_nearest].v1].co,
+                                                      mv_src[mface_src[index_nearest].v2].co,
+                                                      mv_src[mface_src[index_nearest].v3].co,
+                                                      mv_src[mface_src[index_nearest].v4].co, tmp_co);
+
+                               /* get weights */
+                               weight = tmp_weight[0] * defvert_verify_index(dv_array_src[mface_src[index_nearest].v1], index_src)->weight;
+                               weight += tmp_weight[1] * defvert_verify_index(dv_array_src[mface_src[index_nearest].v2], index_src)->weight;
+                               weight += tmp_weight[2] * defvert_verify_index(dv_array_src[mface_src[index_nearest].v3], index_src)->weight;
+                               if (mface_src[index_nearest].v4 || mface_src[index_nearest].v4 == 0) {
+                                       weight += tmp_weight[3] * defvert_verify_index(dv_array_src[mface_src[index_nearest].v4], index_src)->weight;
+                               }
+
+                               /* copy weight */
+                               dw_dst = defvert_verify_index(*dv_dst, index_dst);
+                               vgroup_transfer_weight(mv_dst, &dw_dst->weight, weight, replace_option);
+                       }
+
+                       /* free memory */
+                       free_bvhtree_from_mesh(&tree_mesh_faces_src);
+                       break;
+
+               case BY_NEAREST_VERTEX_IN_FACE:
+                       /* get faces */
+                       DM_ensure_tessface(dmesh_src);
+                       mface_src = dmesh_src->getTessFaceArray(dmesh_src);
+
+                       /* make node tree */
+                       bvhtree_from_mesh_faces(&tree_mesh_faces_src, dmesh_src, FLT_EPSILON, 2, 6);
+
+                       /* loop through the vertices */
+                       for(i = 0, dv_dst = dv_array_dst; i < me_dst->totvert; i++, dv_dst++, mv_dst++){
+
+                               /* reset nearest */
+                               nearest.dist = FLT_MAX;
+                               /* With current binary tree its marginally faster to start searching at the top, as opposed to previous search. */
+                               nearest.index = -1;
+
+                               /* transform into target space */
+                               mul_v3_m4v3(tmp_co, tmp_mat, mv_dst->co);
+
+                               /* node tree accelerated search for closest face */
+                               BLI_bvhtree_find_nearest(tree_mesh_faces_src.tree, tmp_co,
+                                                        &nearest, tree_mesh_faces_src.nearest_callback, &tree_mesh_faces_src);
+                               index_nearest = nearest.index;
+
+                               /* get distances */
+                               dist_v1 = len_squared_v3v3(tmp_co, mv_src[mface_src[index_nearest].v1].co);
+                               dist_v2 = len_squared_v3v3(tmp_co, mv_src[mface_src[index_nearest].v2].co);
+                               dist_v3 = len_squared_v3v3(tmp_co, mv_src[mface_src[index_nearest].v3].co);
+
+                               /* get closest vertex */
+                               if (dist_v1 < dist_v2 && dist_v1 < dist_v3) index_nearest_vertex = mface_src[index_nearest].v1;
+                               else if (dist_v2 < dist_v3) index_nearest_vertex = mface_src[index_nearest].v2;
+                               else index_nearest_vertex = mface_src[index_nearest].v3;
+                               if (mface_src[index_nearest].v4 || mface_src[index_nearest].v4 == 0) {
+                                       dist_v4 = len_squared_v3v3(tmp_co, mv_src[mface_src[index_nearest].v4].co);
+                                       if (dist_v4 < dist_v1 && dist_v4 < dist_v2 && dist_v4 < dist_v3) {
+                                               index_nearest_vertex = mface_src[index_nearest].v4;
+                                       }
+                               }
+
+                               /* copy weight */
+                               dw_src = defvert_verify_index(dv_array_src[index_nearest_vertex], index_src);
+                               dw_dst = defvert_verify_index(*dv_dst, index_dst);
+                               vgroup_transfer_weight(mv_dst, &dw_dst->weight, dw_src->weight, replace_option);
+                       }
+
+                       /* free memory */
+                       free_bvhtree_from_mesh(&tree_mesh_faces_src);
+                       break;
+       }
+
+       /*free memory*/
+       if (dv_array_src) MEM_freeN(dv_array_src);
+       if (dv_array_dst) MEM_freeN(dv_array_dst);
+       dmesh_src->release(dmesh_src);
+
+       return 1;
+}
+
+/***********************End transfer weight***********************************/
 
 /* for Mesh in Object mode */
 /* allows editmode for Lattice */
@@ -2725,7 +3001,7 @@ static int vertex_group_copy_to_selected_exec(bContext *C, wmOperator *op)
        int change = 0;
        int fail = 0;
 
-       CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects)
+       CTX_DATA_BEGIN(C, Object *, ob, selected_editable_objects)
        {
                if (obact != ob) {
                        if (ED_vgroup_copy_array(ob, obact)) change++;
@@ -2743,7 +3019,6 @@ static int vertex_group_copy_to_selected_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-
 void OBJECT_OT_vertex_group_copy_to_selected(wmOperatorType *ot)
 {
        /* identifiers */
@@ -2759,6 +3034,67 @@ void OBJECT_OT_vertex_group_copy_to_selected(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 }
 
+static int vertex_group_transfer_weight_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene = CTX_data_scene(C);
+       Object *ob_act = CTX_data_active_object(C);
+       bDeformGroup *dg_src;
+
+       VertexGroupOption vertex_group_option = RNA_enum_get(op->ptr, "vertex_group_option");
+       MethodOption method_option = RNA_enum_get(op->ptr, "method_option");
+       ReplaceOption replace_option = RNA_enum_get(op->ptr, "replace_option");
+
+       /* Macro to loop through selected objects and perform operation depending on function, option and method */
+       CTX_DATA_BEGIN(C, Object *, ob_slc, selected_editable_objects)
+       {
+
+               if (ob_act != ob_slc && ob_slc->defbase.first) {
+                       switch (vertex_group_option) {
+
+                               case REPLACE_ACTIVE_VERTEX_GROUP:
+                                       ED_vgroup_transfer_weight(ob_act, ob_slc, BLI_findlink(&ob_slc->defbase, ob_slc->actdef - 1), scene, method_option, replace_option);
+                                       break;
+
+                               case REPLACE_ALL_VERTEX_GROUPS:
+                                       for (dg_src = ob_slc->defbase.first; dg_src; dg_src = dg_src->next) {
+                                               ED_vgroup_transfer_weight(ob_act, ob_slc, dg_src, scene, method_option, replace_option);
+                                       }
+                                       break;
+                       }
+               }
+       }
+
+       /* Event notifiers for correct display of data */
+       DAG_id_tag_update(&ob_slc->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob_slc);
+       WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob_slc->data);
+
+       CTX_DATA_END;
+
+       return OPERATOR_FINISHED;
+}
+
+/* transfers weight from active to selected */
+void OBJECT_OT_vertex_group_transfer_weight(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Transfer weight";
+       ot->idname = "OBJECT_OT_vertex_group_transfer_weight";
+       ot->description = "Transfer weight paint to active from selected mesh";
+
+       /* api callbacks */
+       ot->poll = vertex_group_poll;
+       ot->exec = vertex_group_transfer_weight_exec;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* properties */
+       ot->prop = RNA_def_enum(ot->srna, "vertex_group_option", vertex_group_option_item, 1, "Group", "");
+       ot->prop = RNA_def_enum(ot->srna, "method_option", method_option_item, 3, "Method", "");
+       ot->prop = RNA_def_enum(ot->srna, "replace_option", replace_option_item, 1, "Replace", "");
+}
+
 static EnumPropertyItem vgroup_items[] = {
        {0, NULL, 0, NULL, NULL}
 };