Ejemplo n.º 1
0
        //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);
        }