[Bf-blender-cvs] [3b3b355] master: Weight Paint: Add lock-aware normalize

Campbell Barton noreply at git.blender.org
Mon Jan 18 23:31:08 CET 2016


Commit: 3b3b35546e2a0752db5460b2cc9246f11f4219ee
Author: Campbell Barton
Date:   Tue Jan 19 09:14:35 2016 +1100
Branches: master
https://developer.blender.org/rB3b3b35546e2a0752db5460b2cc9246f11f4219ee

Weight Paint: Add lock-aware normalize

D1713 from @angavrilov (with own edits)

The way current weight paint code works is that instead of making normalization lock aware, a separate `enforce_locks` function is called to do a different kind of normalization, hoping that by the time real normalize is called, there is nothing for it to do. The problem is that:

- That different normalization is based on adding the same amount to all unlocked groups, whereas true normalize uses multiplication to preserve ratio between them. The multiplicative approach should match the way weights operate better.
- If `enforce_locks` fails to achieve perfect normalization, true normalize will change locked groups.

Try to fix this by replacing `enforce_locks` with true lock-aware normalization support.

Supporting locked groups in normalize means that freezing the active group can make full normalization impossible if too much weight was added or removed, so it may be necessary to do two normalize passes. This is similar to how enforce_locks operates.

Also, now there is no need to go through the multi-paint branch just because of the locks. In the actual multi-paint case, the same normalize code can be used via a temporary flag array that represents the union of selected and locked groups.

User-visible effect should be:

- Auto-Normalize doesn't change behavior vertex to vertex depending on whether it has any locked groups.
- It never changes locked groups; if you lock some groups and start painting with seriously non-normalized weights, it's assumed you intended that.
- It never adds vertices to new groups, since the computer can't do that intelligently anyway - it was especially broken in case of mirroring.
- It always acts to preserve the ratio between groups it changes, instead of (sometimes, see point 1) adding or subtracting the same amount.

===================================================================

M	source/blender/editors/sculpt_paint/paint_vertex.c

===================================================================

diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 7973665..140c86a 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -33,6 +33,7 @@
 
 #include "BLI_blenlib.h"
 #include "BLI_math.h"
+#include "BLI_array_utils.h"
 #include "BLI_bitmap.h"
 
 #include "IMB_imbuf.h"
@@ -1213,7 +1214,7 @@ static void do_weight_paint_normalize_all(MDeformVert *dvert, const int defbase_
  * A version of #do_weight_paint_normalize_all that includes locked weights
  * but only changes unlocked weights.
  */
-static void do_weight_paint_normalize_all_locked(
+static bool do_weight_paint_normalize_all_locked(
         MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap,
         const bool *lock_flags)
 {
@@ -1223,6 +1224,10 @@ static void do_weight_paint_normalize_all_locked(
 	unsigned int i, tot = 0;
 	MDeformWeight *dw;
 
+	if (lock_flags == NULL) {
+		do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_validmap);
+		return true;
+	}
 
 	for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
 		if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
@@ -1238,11 +1243,17 @@ static void do_weight_paint_normalize_all_locked(
 		}
 	}
 
-	if ((tot == 0) || (sum == 1.0f)) {
-		return;
+	if (sum == 1.0f) {
+		return true;
+	}
+
+	if (tot == 0) {
+		return false;
 	}
 
 	if (lock_weight >= 1.0f) {
+		/* locked groups make it impossible to fully normalize,
+		 * zero out what we can and return false */
 		for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
 			if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
 				if (lock_flags[dw->def_nr] == false) {
@@ -1251,9 +1262,10 @@ static void do_weight_paint_normalize_all_locked(
 			}
 		}
 
+		return (lock_weight == 1.0f);
 	}
 	else if (sum_unlock != 0.0f) {
-		fac = (1.0f - lock_weight) / sum;
+		fac = (1.0f - lock_weight) / sum_unlock;
 
 		for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
 			if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
@@ -1279,87 +1291,38 @@ static void do_weight_paint_normalize_all_locked(
 			}
 		}
 	}
+
+	return true;
 }
 
-/* same as function above except it normalizes against the active vgroup which remains unchanged
- *
- * note that the active is just the group which is unchanged, it can be any,
- * can also be -1 to normalize all but in that case call 'do_weight_paint_normalize_all' */
-static void do_weight_paint_normalize_all_active(MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap,
-                                                 const int vgroup_active)
+/**
+ * \note same as function above except it does a second pass without active group
+ * if nomalize fails with it.
+ */
+static void do_weight_paint_normalize_all_locked_try_active(
+        MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap,
+        const bool *lock_flags, const bool *lock_with_active)
 {
-	float sum = 0.0f, fac;
-	unsigned int i, tot = 0;
-	MDeformWeight *dw;
-	float act_weight = 0.0f;
+	/* first pass with both active and explicitly locked groups restricted from change */
 
-	for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
-		if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
-			if (dw->def_nr != vgroup_active) {
-				sum += dw->weight;
-				tot++;
-			}
-			else {
-				act_weight = dw->weight;
-			}
-		}
-	}
+	bool success = do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_with_active);
 
-	if ((tot == 0) || (sum + act_weight == 1.0f)) {
-		return;
-	}
-
-	if (sum != 0.0f) {
-		fac = (1.0f - act_weight) / sum;
-
-		for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
-			if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
-				if (dw->def_nr != vgroup_active) {
-					dw->weight *= fac;
-
-					/* paranoid but possibly with float error */
-					CLAMP(dw->weight, 0.0f, 1.0f);
-				}
-			}
-		}
-	}
-	else {
-		/* corner case where we need to scale all weights evenly because they're all zero */
-
-		/* hrmf, not a factor in this case */
-		fac = (1.0f - act_weight) / tot;
-
-		/* paranoid but possibly with float error */
-		CLAMP(fac, 0.0f, 1.0f);
-
-		for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
-			if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
-				if (dw->def_nr != vgroup_active) {
-					dw->weight = fac;
-				}
-			}
-		}
+	if (!success) {
+		/**
+		 * Locks prevented the first pass from full completion, so remove restriction on active group; e.g:
+		 *
+		 * - With 1.0 weight painted into active:
+		 *   nonzero locked weight; first pass zeroed out unlocked weight; scale 1 down to fit.
+		 * - With 0.0 weight painted into active:
+		 *   no unlocked groups; first pass did nothing; increaze 0 to fit.
+		 */
+		do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_flags);
 	}
 }
 
 /*
  * See if the current deform vertex has a locked group
  */
-static bool has_locked_group(MDeformVert *dvert, const int defbase_tot,
-                             const bool *bone_groups, const bool *lock_flags)
-{
-	int i;
-	MDeformWeight *dw;
-
-	for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
-		if (dw->def_nr < defbase_tot) {
-			if (bone_groups[dw->def_nr] && lock_flags[dw->def_nr] && dw->weight > 0.0f) {
-				return true;
-			}
-		}
-	}
-	return false;
-}
 
 static bool has_locked_group_selected(int defbase_tot, const bool *defbase_sel, const bool *lock_flags)
 {
@@ -1427,179 +1390,6 @@ static void multipaint_selection(MDeformVert *dvert, const int defbase_tot, floa
 	}
 }
 
-/* move all change onto valid, unchanged groups.  If there is change left over,
- * then return it.
- * assumes there are valid groups to shift weight onto */
-static float redistribute_change(MDeformVert *ndv, const int defbase_tot,
-                                 char *change_status, const char change_me, int changeto,
-                                 float totchange, float total_valid,
-                                 bool do_auto_normalize)
-{
-	bool changed;
-	float change;
-	float oldval;
-	MDeformWeight *ndw;
-	int i;
-	do {
-		/* assume there is no change until you see one */
-		changed = false;
-		/* change each group by the same amount each time */
-		change = totchange / total_valid;
-		for (i = 0; i < ndv->totweight && total_valid && totchange; i++) {
-			ndw = (ndv->dw + i);
-
-			/* ignore anything outside the value range */
-			if (ndw->def_nr < defbase_tot) {
-
-				/* change only the groups with a valid status */
-				if (change_status[ndw->def_nr] == change_me) {
-					oldval = ndw->weight;
-					/* if auto normalize is active, don't worry about upper bounds */
-					if (do_auto_normalize == false && ndw->weight + change > 1) {
-						totchange -= 1.0f - ndw->weight;
-						ndw->weight = 1.0f;
-						/* stop the changes to this group */
-						change_status[ndw->def_nr] = changeto;
-						total_valid--;
-					}
-					else if (ndw->weight + change < 0) { /* check the lower bound */
-						totchange += ndw->weight;
-						ndw->weight = 0;
-						change_status[ndw->def_nr] = changeto;
-						total_valid--;
-					}
-					else { /* a perfectly valid change occurred to ndw->weight */
-						totchange -= change;
-						ndw->weight += change;
-					}
-					/* see if there was a change */
-					if (oldval != ndw->weight) {
-						changed = true;
-					}
-				}
-			}
-		}
-		/* don't go again if there was no change, if there is no valid group,
-		 * or there is no change left */
-	} while (changed && total_valid && totchange);
-	/* left overs */
-	return totchange;
-}
-static float get_mp_change(MDeformVert *odv, const int defbase_tot, const bool *defbase_sel, float brush_change);
-/* observe the changes made to the weights of groups.
- * make sure all locked groups on the vertex have the same deformation
- * by moving the changes made to groups onto other unlocked groups */
-static void enforce_locks(MDeformVert *odv, MDeformVert *ndv,
-                          const int defbase_tot, const bool *defbase_sel,
-                          const bool *lock_flags, const bool *vgroup_validmap,
-                          bool do_auto_normalize, bool do_multipaint)
-{
-	float totchange = 0.0f;
-	float totchange_allowed = 0.0f;
-	float left_over;
-
-	int total_valid = 0;
-	int total_changed = 0;
-	unsigned int i;
-	MDeformWeight *ndw;
-	MDeformWeight *odw;
-
-	// float changed_sum = 0.0f;  // UNUSED
-
-	char *change_status;
-
-	if (!lock_flags || !has_locked_group(ndv, defbase_tot, vgroup_validmap, lock_flags)) {
-		return;
-	}
-	/* record if a group was changed, unlocked and not changed, or locked */
-	change_status = MEM_callocN(sizeof(char) * defbase_tot, "unlocked_unchanged");
-
-	for (i = 0; i < defbase_tot; i++) {
-		ndw = defvert_find_index(ndv, i);
-		odw = defvert_find_index(odv, i);
-		/* the weights are zero, so we can assume a lot */
-		if (!ndw || !odw) {
-			if (!lock_flags[i] && vgroup_validmap[i]) {
-				defvert_verify_index(odv, i);
-				defvert_verify_index(ndv, i);
-				total_valid++;
-				change_status[i] = 1; /* can be altered while redistributing */
-			}
-			continue;
-		}
-		/* locked groups should not be changed */
-		if (lock_flags[i]) {
-			ndw->weight = odw->weight;
-		}
-		else if (ndw->weight != odw->weight) { /* changed groups are handled here */
-			totchange += ndw->weight - odw->weight;
-			// changed_sum += ndw->weight;  // UNUSED
-			change_status[i] = 2; /* was altered already */
-			total_changed++;
-		} /* unchanged, unlocked bone groups are handled here */
-		else if (vgroup_validmap[i]) {
-			totchange_allowed += ndw->weight;
-			total_valid++;
-			change_status[i] = 1; /* can be altered while redistributing */
-		}
-	}
-	/* if there was any change, and somewhere to redistribute it, do it */
-	if (total_changed && total_valid) {
-		/* auto normalize will allow weights to temporarily go above 1 in redistribution */
-		if (vgroup_validmap && total_changed < 0 && total_valid) {
-			totchange_allowed = total_valid;
-		}
-		/* the way you modify the unlocked + unchanged groups is differe

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list