Transfer data: cleanup: Remove 'vertex_group_transfer_weight' operator.

We can now use 'generic' data transfer instead.

Note new one is not an exact replacement, it should be able to do
everyting old op could do though, and more.
This commit is contained in:
Bastien Montagne 2015-01-11 18:33:12 +01:00
parent a628a8240e
commit 1b3b011354
5 changed files with 7 additions and 504 deletions

View File

@ -1603,7 +1603,9 @@ class VIEW3D_MT_paint_weight(Menu):
layout.operator("object.vertex_group_quantize", text="Quantize")
layout.operator("object.vertex_group_levels", text="Levels")
layout.operator("object.vertex_group_blend", text="Blend")
layout.operator("object.vertex_group_transfer_weight", text="Transfer Weights")
prop = layout.operator("object.data_transfer", text="Transfer Weights")
prop.use_reverse_transfer = True
prop.data_type = 'VGROUP_WEIGHTS'
layout.operator("object.vertex_group_limit_total", text="Limit Total")
layout.operator("object.vertex_group_fix", text="Fix Deforms")

View File

@ -1597,7 +1597,9 @@ class VIEW3D_PT_tools_weightpaint(View3DPanel, Panel):
col = layout.column()
col.operator("paint.weight_gradient")
col.operator("object.vertex_group_transfer_weight", text="Transfer Weights")
prop = col.operator("object.data_transfer", text="Transfer Weights")
prop.use_reverse_transfer = True
prop.data_type = 'VGROUP_WEIGHTS'
class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel):

View File

@ -225,7 +225,6 @@ void OBJECT_OT_vertex_group_remove_from(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_select(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_deselect(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_copy_to_linked(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_transfer_weight(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_copy_to_selected(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_copy(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_normalize(struct wmOperatorType *ot);

View File

@ -177,7 +177,6 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_vertex_group_select);
WM_operatortype_append(OBJECT_OT_vertex_group_deselect);
WM_operatortype_append(OBJECT_OT_vertex_group_copy_to_linked);
WM_operatortype_append(OBJECT_OT_vertex_group_transfer_weight);
WM_operatortype_append(OBJECT_OT_vertex_group_copy_to_selected);
WM_operatortype_append(OBJECT_OT_vertex_group_copy);
WM_operatortype_append(OBJECT_OT_vertex_group_normalize);

View File

@ -243,31 +243,6 @@ bool ED_vgroup_parray_alloc(ID *id, MDeformVert ***dvert_arr, int *dvert_tot, co
return false;
}
static bool ed_vgroup_dm_parray_alloc(DerivedMesh *dm, MDeformVert ***dvert_arr, int *dvert_tot)
{
*dvert_tot = 0;
*dvert_arr = NULL;
if (dm) {
MDeformVert *dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
if (dvert) {
int i, totvert = dm->getNumVerts(dm);
*dvert_tot = totvert;
*dvert_arr = MEM_mallocN(sizeof(void *) * totvert, "vgroup parray from me");
for (i = 0; i < totvert; i++) {
(*dvert_arr)[i] = dvert + i;
}
return true;
}
}
return false;
}
/**
* For use with tools that use ED_vgroup_parray_alloc with \a use_vert_sel == true.
* This finds the unselected mirror deform verts and copys the weights to them from the selected.
@ -635,51 +610,6 @@ static void vgroup_copy_active_to_sel(Object *ob, eVGroupSelect subset_type)
/***********************Start weight transfer (WT)*********************************/
typedef enum WT_VertexGroupMode {
WT_REPLACE_ACTIVE_VERTEX_GROUP = 1,
WT_REPLACE_ALL_VERTEX_GROUPS = 2
} WT_VertexGroupMode;
typedef enum WT_Method {
WT_BY_INDEX = 1,
WT_BY_NEAREST_VERTEX = 2,
WT_BY_NEAREST_FACE = 3,
WT_BY_NEAREST_VERTEX_IN_FACE = 4
} WT_Method;
typedef enum WT_ReplaceMode {
WT_REPLACE_ALL_WEIGHTS = 1,
WT_REPLACE_EMPTY_WEIGHTS = 2
} WT_ReplaceMode;
static EnumPropertyItem WT_vertex_group_mode_item[] = {
{WT_REPLACE_ACTIVE_VERTEX_GROUP,
"WT_REPLACE_ACTIVE_VERTEX_GROUP", 0, "Active", "Transfer active vertex group from selected to active mesh"},
{WT_REPLACE_ALL_VERTEX_GROUPS,
"WT_REPLACE_ALL_VERTEX_GROUPS", 0, "All", "Transfer all vertex groups from selected to active mesh"},
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem WT_method_item[] = {
{WT_BY_INDEX,
"WT_BY_INDEX", 0, "Vertex index", "Copy for identical meshes"},
{WT_BY_NEAREST_VERTEX,
"WT_BY_NEAREST_VERTEX", 0, "Nearest vertex", "Copy weight from closest vertex"},
{WT_BY_NEAREST_FACE,
"WT_BY_NEAREST_FACE", 0, "Nearest face", "Barycentric interpolation from nearest face"},
{WT_BY_NEAREST_VERTEX_IN_FACE,
"WT_BY_NEAREST_VERTEX_IN_FACE", 0, "Nearest vertex in face", "Copy weight from closest vertex in nearest face"},
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem WT_replace_mode_item[] = {
{WT_REPLACE_ALL_WEIGHTS,
"WT_REPLACE_ALL_WEIGHTS", 0, "All", "Overwrite all weights"},
{WT_REPLACE_EMPTY_WEIGHTS,
"WT_REPLACE_EMPTY_WEIGHTS", 0, "Empty", "Add weights to vertices with no weight"},
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem WT_vertex_group_select_item[] = {
{WT_VGROUP_ACTIVE,
"ACTIVE", 0, "Active Group", "The active Vertex Group"},
@ -755,298 +685,6 @@ static void vgroup_operator_subset_select_props(wmOperatorType *ot, bool use_act
ot->prop = prop;
}
/* Copy weight.*/
static void vgroup_transfer_weight(float *r_weight_dst, const float weight_src, const WT_ReplaceMode replace_mode)
{
switch (replace_mode) {
case WT_REPLACE_ALL_WEIGHTS:
*r_weight_dst = weight_src;
break;
case WT_REPLACE_EMPTY_WEIGHTS:
if (*r_weight_dst == 0.0f) {
*r_weight_dst = weight_src;
}
break;
default:
BLI_assert(0);
break;
}
}
/* Could be exposed externally by implementing it in header with the rest.
* Simple refactoring will break something.
* For now, naming is ed_ instead of ED_*/
static bool ed_vgroup_transfer_weight(Object *ob_dst, Object *ob_src, bDeformGroup *dg_src, Scene *scene,
WT_Method method, WT_ReplaceMode replace_mode, wmOperator *op)
{
bDeformGroup *dg_dst;
Mesh *me_dst;
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, *mf;
BVHTreeNearest nearest;
MDeformWeight *dw_dst, *dw_src;
int dv_tot_src, dv_tot_dst, i, v_index, index_dst, index_src, index_nearest, index_nearest_vertex;
unsigned int f_index;
float weight, tmp_weight[4], tmp_co[3], normal[3], tmp_mat[4][4], dist_v1, dist_v2, dist_v3, dist_v4;
const int use_vert_sel = vertex_group_use_vert_sel(ob_dst);
bool is_dg_dst_new = false;
/* Ensure vertex group on target.*/
if ((dg_dst = defgroup_find_name(ob_dst, dg_src->name)) == NULL) {
dg_dst = BKE_defgroup_new(ob_dst, dg_src->name);
is_dg_dst_new = true;
}
/* Get meshes.*/
dmesh_src = mesh_get_derived_final(scene, ob_src, CD_MASK_BAREMESH | CD_MASK_MDEFORMVERT);
me_dst = ob_dst->data;
/* Get vertex group array from source mesh */
if (!ed_vgroup_dm_parray_alloc(dmesh_src, &dv_array_src, &dv_tot_src)) {
BKE_report(op->reports, RPT_ERROR, "Transfer failed (source mesh does not have any vertex groups)");
return false;
}
/* Create data in memory when nothing there.*/
if (!me_dst->dvert) BKE_object_defgroup_data_create(&me_dst->id);
/* Get vertex group for destination mesh */
ED_vgroup_parray_alloc(&me_dst->id, &dv_array_dst, &dv_tot_dst, use_vert_sel);
/* 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);
mul_m4_m4m4(tmp_mat, ob_src->imat, ob_dst->obmat);
/* Clear weights.*/
if (replace_mode == WT_REPLACE_ALL_WEIGHTS) {
for (i = 0, dv_dst = dv_array_dst; i < me_dst->totvert; i++, dv_dst++) {
if (*dv_dst == NULL) continue;
dw_dst = defvert_find_index(*dv_dst, index_dst);
/* Remove vertex from group.*/
if (dw_dst) defvert_remove_group(*dv_dst, dw_dst);
}
}
switch (method) {
case WT_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)
{
if (is_dg_dst_new) {
BKE_object_defgroup_remove(ob_dst, dg_dst);
}
if (dv_array_src) MEM_freeN(dv_array_src);
if (dv_array_dst) MEM_freeN(dv_array_dst);
dmesh_src->release(dmesh_src);
BKE_report(op->reports, RPT_ERROR, "Transfer failed (indices are not matching)");
return false;
}
/* 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++, mv_dst++)
{
if (*dv_dst == NULL) {
continue;
}
/* Copy weight.*/
dw_src = defvert_find_index(*dv_src, index_src);
if (dw_src && dw_src->weight) {
dw_dst = defvert_verify_index(*dv_dst, index_dst);
vgroup_transfer_weight(&dw_dst->weight, dw_src->weight, replace_mode);
}
}
break;
case WT_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++) {
if (*dv_dst == NULL) {
continue;
}
/* Reset nearest.*/
nearest.dist_sq = FLT_MAX;
/* It is faster to start searching at the top of the tree instead of previous search result.*/
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 that are not NULL including weight value 0. In relevant cases, existing weights are
* overwritten prior to this. See the "Clear weights." step above.*/
dw_src = defvert_find_index(dv_array_src[nearest.index], index_src);
if (dw_src && dw_src->weight) {
dw_dst = defvert_verify_index(*dv_dst, index_dst);
vgroup_transfer_weight(&dw_dst->weight, dw_src->weight, replace_mode);
}
}
/* Free memory.*/
free_bvhtree_from_mesh(&tree_mesh_vertices_src);
break;
case WT_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++) {
if (*dv_dst == NULL) {
continue;
}
/* Reset nearest.*/
nearest.dist_sq = FLT_MAX;
/* It is faster to start searching at the top of the tree instead of previous search result.*/
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.*/
mf = &mface_src[index_nearest];
normal_tri_v3(normal, mv_src[mf->v1].co, mv_src[mf->v2].co, mv_src[mf->v3].co);
project_v3_plane(tmp_co, normal, mv_src[mf->v1].co);
/* Interpolate weights over face.*/
interp_weights_face_v3(tmp_weight,
mv_src[mf->v1].co,
mv_src[mf->v2].co,
mv_src[mf->v3].co,
mf->v4 ? mv_src[mf->v4].co : NULL,
tmp_co);
/* Get weights from face.*/
f_index = mf->v4 ? 3 : 2;
weight = 0.0f;
do {
v_index = (&mf->v1)[f_index];
weight += tmp_weight[f_index] * defvert_find_weight(dv_array_src[v_index], index_src);
} while (f_index--);
/* Copy weight that are not NULL including weight value 0. In relevant cases, existing weights are
* overwritten prior to this. See the "Clear weights." step above.*/
if (weight > 0.0f) {
dw_dst = defvert_verify_index(*dv_dst, index_dst);
vgroup_transfer_weight(&dw_dst->weight, weight, replace_mode);
}
}
/* Free memory.*/
free_bvhtree_from_mesh(&tree_mesh_faces_src);
break;
case WT_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++) {
if (*dv_dst == NULL) {
continue;
}
/* Reset nearest.*/
nearest.dist_sq = FLT_MAX;
/* It is faster to start searching at the top of the tree instead of previous search result.*/
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.*/
mf = &mface_src[index_nearest];
dist_v1 = len_squared_v3v3(tmp_co, mv_src[mf->v1].co);
dist_v2 = len_squared_v3v3(tmp_co, mv_src[mf->v2].co);
dist_v3 = len_squared_v3v3(tmp_co, mv_src[mf->v3].co);
/* Get closest vertex.*/
f_index = mf->v4 ? 3 : 2;
if (dist_v1 < dist_v2 && dist_v1 < dist_v3) index_nearest_vertex = mf->v1;
else if (dist_v2 < dist_v3) index_nearest_vertex = mf->v2;
else index_nearest_vertex = mf->v3;
if (f_index == 3) {
dist_v4 = len_squared_v3v3(tmp_co, mv_src[mf->v4].co);
if (dist_v4 < dist_v1 && dist_v4 < dist_v2 && dist_v4 < dist_v3) {
index_nearest_vertex = mf->v4;
}
}
/* Copy weight that are not NULL including weight value 0. In relevant cases, existing weights are
* overwritten prior to this. See the "Clear weights." step above.*/
dw_src = defvert_find_index(dv_array_src[index_nearest_vertex], index_src);
if (dw_src && dw_src->weight) {
dw_dst = defvert_verify_index(*dv_dst, index_dst);
vgroup_transfer_weight(&dw_dst->weight, dw_src->weight, replace_mode);
}
}
/* Free memory.*/
free_bvhtree_from_mesh(&tree_mesh_faces_src);
break;
default:
BLI_assert(0);
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 true;
}
/***********************End weight transfer (WT)***********************************/
@ -2657,7 +2295,7 @@ static int vertex_group_mesh_poll(bContext *C)
ob->defbase.first);
}
static int vertex_group_mesh_supported_poll(bContext *C)
static int UNUSED_FUNCTION(vertex_group_mesh_supported_poll)(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
@ -3539,143 +3177,6 @@ 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_act = BLI_findlink(&ob_act->defbase, (ob_act->actdef - 1));
char dg_act_name[MAX_VGROUP_NAME]; /* may be freed so copy */
int fail = 0;
bool changed = false;
WT_VertexGroupMode vertex_group_mode = RNA_enum_get(op->ptr, "group_select_mode");
WT_Method method = RNA_enum_get(op->ptr, "method");
WT_ReplaceMode replace_mode = RNA_enum_get(op->ptr, "replace_mode");
if (vertex_group_mode == WT_REPLACE_ACTIVE_VERTEX_GROUP) {
if (!dg_act) {
BKE_report(op->reports, RPT_WARNING, "Failed, active object has no active groups");
return OPERATOR_FINISHED; /* to get the chance to make changes in the redo panel*/
}
}
if (dg_act) {
BLI_strncpy(dg_act_name, dg_act->name, sizeof(dg_act_name));
}
/* Macro to loop through selected objects and perform operation depending on function, option and method.*/
CTX_DATA_BEGIN (C, Object *, ob_src, selected_editable_objects)
{
if (ob_act != ob_src) {
if (BLI_listbase_is_empty(&ob_src->defbase)) {
BKE_reportf(op->reports, RPT_WARNING,
"Skipping object '%s' it has no vertex groups", ob_src->id.name + 2);
continue;
}
else if (ob_src->type != OB_MESH) {
/* armatures can be in pose mode so ignore them */
if (ob_src->type != OB_ARMATURE) {
BKE_reportf(op->reports, RPT_WARNING,
"Skipping object '%s' only copying from meshes is supported", ob_src->id.name + 2);
}
continue;
}
switch (vertex_group_mode) {
case WT_REPLACE_ACTIVE_VERTEX_GROUP:
{
bDeformGroup *dg_src;
dg_src = defgroup_find_name(ob_src, dg_act_name);
if (dg_src == NULL) {
BKE_reportf(op->reports, RPT_WARNING,
"Skipping object '%s' no group '%s' found", ob_src->id.name + 2, dg_act_name);
continue;
}
if (ed_vgroup_transfer_weight(ob_act, ob_src, dg_src, scene, method, replace_mode, op)) {
changed = true;
}
else {
fail++;
}
break;
}
case WT_REPLACE_ALL_VERTEX_GROUPS:
{
bDeformGroup *dg_src;
for (dg_src = ob_src->defbase.first; dg_src; dg_src = dg_src->next) {
if (ed_vgroup_transfer_weight(ob_act, ob_src, dg_src, scene, method, replace_mode, op)) {
changed = true;
}
else {
fail++;
}
}
break;
}
default:
BLI_assert(0);
break;
}
}
}
CTX_DATA_END;
if (changed) {
/* possible the active vertex group changed because of adding/removing */
/* note!, dg_act may be realloc'd, only check its not NULL */
if (dg_act) {
ED_vgroup_select_by_name(ob_act, dg_act_name);
}
else {
ED_vgroup_sync_from_pose(ob_act);
}
/* Event notifiers for correct display of data.*/
DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob_act); /* for buttons */
WM_event_add_notifier(C, NC_GEOM | ND_VERTEX_GROUP, ob_act);
return OPERATOR_FINISHED;
}
else {
if (BLI_listbase_is_empty(&op->reports->list)) {
BKE_report(op->reports, RPT_WARNING, "Failed, no other selected objects with vertex groups found");
}
return OPERATOR_FINISHED; /* to get the chance to make changes in the redo panel */
}
}
/* transfers weight from active to selected */
void OBJECT_OT_vertex_group_transfer_weight(wmOperatorType *ot)
{
/* Identifiers.*/
ot->name = "Transfer Weights";
ot->idname = "OBJECT_OT_vertex_group_transfer_weight";
ot->description = "Transfer weight paint to active from selected mesh";
/* API callbacks.*/
ot->poll = vertex_group_mesh_supported_poll;
ot->exec = vertex_group_transfer_weight_exec;
/* Flags.*/
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* Properties.*/
/* TODO, use vgroup_operator_subset_select_props for group_select_mode */
ot->prop = RNA_def_enum(ot->srna, "group_select_mode", WT_vertex_group_mode_item, WT_REPLACE_ALL_VERTEX_GROUPS, "Group", "");
ot->prop = RNA_def_enum(ot->srna, "method", WT_method_item, WT_BY_NEAREST_FACE, "Method", "");
ot->prop = RNA_def_enum(ot->srna, "replace_mode", WT_replace_mode_item, WT_REPLACE_ALL_WEIGHTS, "Replace", "");
}
static int set_active_group_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);