//Returns true if the vertex's matrix node is changed. private bool Weight(float value, Vertex3 vertex, WeightType type) { if (_targetBone == null || _targetBone.Locked) { return(false); } Influence targetInf = null; BoneWeight targetWeight = null; float max = 1.0f; //Get the matrix that influences this vertex IMatrixNode node = vertex.GetMatrixNode(); bool startsAsBone = false; //Convert a single bone into an influence so bones can be added to it later if (node is MDL0BoneNode) { startsAsBone = true; node = new Influence(node as MDL0BoneNode); } //Duplicate the influence if it affects more than just this vertex targetInf = node.Users.Count > 1 ? (node as Influence).Clone() : node as Influence; //Find or add the current bone to the influence List <BoneWeight> weights = targetInf.Weights; int selectedIndex = weights.Select(x => x.Bone).ToArray().IndexOf(TargetBone); if (selectedIndex < 0) { weights.Add(new BoneWeight(TargetBone, 0.0f)); selectedIndex = weights.Count - 1; } //Get the weight at the index of the current bone targetWeight = targetInf.Weights[selectedIndex]; //Can't do anything to a locked weight if (targetWeight.Locked) { return(false); } //Get the sum of all weights that can be edited by subtracting all locked values from 1.0f max = 1.0f; foreach (BoneWeight b in weights) { if (b.Locked) { max -= b.Weight; } } //Get the new value for the target weight //Clamp it between 0.0f and the max value switch (type) { default: value = RoundValue(value, max); break; case WeightType.Add: value = RoundValue(targetWeight.Weight + value, max); break; case WeightType.Multiply: value = RoundValue(targetWeight.Weight * value, max); break; } //Nothing to do if there's no change in value if (targetWeight.Weight == value) { return(false); } //Collect all unlocked weights that are not the current weight //These are weights that will be changed to accomodate the current weight edit List <int> editableWeights = new List <int>(); for (int i = 0; i < targetInf.Weights.Count; i++) { if (!targetInf.Weights[i].Locked && i != selectedIndex) { editableWeights.Add(i); } } //Return if nothing can be edited if (editableWeights.Count == 0) { return(false); } //Set the current weight with the calculated value targetWeight.Weight = value; //Get the change in value, divide it by all other editable weights, //and then add that value to those weights to bring the overall weight sum back to 1.0f float perBoneDiff = (targetWeight.Weight - value) / editableWeights.Count; if (value < max) { foreach (int i in editableWeights) { targetInf.Weights[i].Weight = RoundValue(targetInf.Weights[i].Weight + perBoneDiff, 1.0f); } } else { foreach (int i in editableWeights) { targetInf.Weights[i].Weight = 0.0f; } } //Normalize the influence just in case, this will scale all weights so they add up to 1.0f //Don't let the modified value be normalized, lock it bool locked = targetWeight.Locked; targetWeight.Locked = true; targetInf.Normalize(); targetWeight.Locked = locked; //Clean influence by removing zero weights for (int i = 0; i < targetInf.Weights.Count; i++) { if (targetInf.Weights[i].Weight <= 0.0f) { targetInf.Weights.RemoveAt(i--); } } MDL0ObjectNode obj = vertex.Parent as MDL0ObjectNode; MDL0Node model = obj.Model; IMatrixNode matrixNode; //See if the influence is just one bone if (targetInf.Weights.Count == 1) { matrixNode = targetInf.Weights[0].Bone; if (!startsAsBone && !_anyConverted.Contains(obj)) { _anyConverted.Add(obj); } } else { matrixNode = model._influences.FindOrCreate(targetInf); if (startsAsBone && !_anyConverted.Contains(obj)) { _anyConverted.Add(obj); } } //Move influence to each vertex before modifying the influence of one vertex if (obj.MatrixNode != null) { obj.TryConvertMatrixToVertex(); } vertex.DeferUpdateAssets(); vertex.MatrixNode = matrixNode; return(true); }