// Update state machine. That means: // Change state if transition conditions are fulfilled. // Update skeleton. public void Update(ref MyAnimationUpdateData data) { if (data.CharacterBones == null) { return; // safety } CurrentUpdateData = data; // local copy CurrentUpdateData.VisitedTreeNodesCounter = 0; CurrentUpdateData.VisitedTreeNodesPath = m_lastVisitedTreeNodesPath; CurrentUpdateData.VisitedTreeNodesPath[0] = 0; if (BoneMask == null) // rebuild bone mask array if not done yet { RebuildBoneMask(); } data.LayerBoneMask = CurrentUpdateData.LayerBoneMask = BoneMask; // pass bone mask array to all subnodes and result Debug.Assert(data.LayerBoneMask != null); // setting animation finished flag MyAnimationStateMachineNode currentAnimationNode = CurrentNode as MyAnimationStateMachineNode; if (currentAnimationNode != null && currentAnimationNode.RootAnimationNode != null) { float finishedPercent = currentAnimationNode.RootAnimationNode.GetLocalTimeNormalized(); data.Controller.Variables.SetValue(MyAnimationVariableStorageHints.StrIdAnimationFinished, finishedPercent); } else { data.Controller.Variables.SetValue(MyAnimationVariableStorageHints.StrIdAnimationFinished, 0); } base.Update(); int[] swapVisitedTreeNodesPath = VisitedTreeNodesPath; VisitedTreeNodesPath = m_lastVisitedTreeNodesPath; m_lastVisitedTreeNodesPath = swapVisitedTreeNodesPath; CurrentUpdateData.VisitedTreeNodesPath = null; // disconnect our array // blended transitions // - from most recent to oldest // - please preserve order of blending for three or more states // when CurrentState changes float weightMultiplier = 1.0f; for (int i = 0; i < m_stateTransitionBlending.Count; i++) { MyStateTransitionBlending stateTransitionBlending = m_stateTransitionBlending[i]; float localWeight = (float)(stateTransitionBlending.TimeLeftInSeconds * stateTransitionBlending.InvTotalTime); // 1 to 0 over time weightMultiplier *= localWeight; // update nodes that we are just leaving var lastResult = CurrentUpdateData.BonesResult; CurrentUpdateData.BonesResult = null; stateTransitionBlending.SourceState.OnUpdate(this); if (lastResult != null && CurrentUpdateData.BonesResult != null) { for (int j = 0; j < lastResult.Count; j++) { if (data.LayerBoneMask[j]) { // these nodes lose their weight, it goes from 1 to 0 // we need to blend them to last result (current node or another node that we are leaving) float w = ComputeEaseInEaseOut(MathHelper.Clamp(weightMultiplier, 0, 1)); CurrentUpdateData.BonesResult[j].Rotation = Quaternion.Slerp(lastResult[j].Rotation, CurrentUpdateData.BonesResult[j].Rotation, w); CurrentUpdateData.BonesResult[j].Translation = Vector3.Lerp(lastResult[j].Translation, CurrentUpdateData.BonesResult[j].Translation, w); } } // give back last result (free list of bones), we dont need it anymore data.Controller.ResultBonesPool.Free(lastResult); } // update, decrease remaining time stateTransitionBlending.TimeLeftInSeconds -= data.DeltaTimeInSeconds; m_stateTransitionBlending[i] = stateTransitionBlending; if (stateTransitionBlending.TimeLeftInSeconds <= 0) { // skip older blended states and mark them for deletion, because their (global) weight is now zero for (int j = i + 1; j < m_stateTransitionBlending.Count; j++) { var temp = m_stateTransitionBlending[j]; temp.TimeLeftInSeconds = 0; // m_stateTransitionBlending[j] = temp; } break; } } m_stateTransitionBlending.RemoveAll(s => s.TimeLeftInSeconds <= 0.0); data.BonesResult = CurrentUpdateData.BonesResult; // local copy contains resulting list of bones }
// Initialize state machine of one layer. private static bool InitLayerNodes(MyAnimationStateMachine layer, string stateMachineName, MyAnimationControllerDefinition animControllerDefinition, MyAnimationController animationController, string currentNodeNamePrefix, MyAnimationVirtualNodes virtualNodes) { var objBuilderStateMachine = animControllerDefinition.StateMachines.FirstOrDefault(x => x.Name == stateMachineName); if (objBuilderStateMachine == null) { Debug.Fail("Animation state machine " + stateMachineName + " was not found."); return(false); } bool result = true; // 1st step: generate nodes if (objBuilderStateMachine.Nodes != null) { foreach (var objBuilderNode in objBuilderStateMachine.Nodes) { string absoluteNodeName = currentNodeNamePrefix + objBuilderNode.Name; if (objBuilderNode.StateMachineName != null) { // embedded state machine, copy its nodes if (!InitLayerNodes(layer, objBuilderNode.StateMachineName, animControllerDefinition, animationController, absoluteNodeName + "/", virtualNodes)) { result = false; } } else { var smNode = new VRage.Animations.MyAnimationStateMachineNode(absoluteNodeName); if (objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.PassThrough || objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.Any || objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.AnyExceptTarget) { smNode.PassThrough = true; } else { smNode.PassThrough = false; } if (objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.Any || objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.AnyExceptTarget) { virtualNodes.NodesAny.Add(absoluteNodeName, new MyAnimationVirtualNodeData() { AnyNodePrefix = currentNodeNamePrefix, ExceptTarget = (objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.AnyExceptTarget) }); } layer.AddNode(smNode); if (objBuilderNode.AnimationTree != null) { var smNodeAnimTree = InitNodeAnimationTree(objBuilderNode.AnimationTree.Child); smNode.RootAnimationNode = smNodeAnimTree; } else { smNode.RootAnimationNode = new MyAnimationTreeNodeDummy(); } } } } // 2nd step: generate transitions if (objBuilderStateMachine.Transitions != null) { foreach (var objBuilderTransition in objBuilderStateMachine.Transitions) { string absoluteNameNodeFrom = currentNodeNamePrefix + objBuilderTransition.From; string absoluteNameNodeTo = currentNodeNamePrefix + objBuilderTransition.To; MyAnimationVirtualNodeData virtualNodeData; if (virtualNodes.NodesAny.TryGetValue(absoluteNameNodeFrom, out virtualNodeData)) { // nodes of type "any": // "any" node is source: we create transitions directly from all nodes // "any" node is target: we will use "any" node as pass through foreach (var nodeFromCandidate in layer.AllNodes) { if (nodeFromCandidate.Key.StartsWith(virtualNodeData.AnyNodePrefix) && // select nodes in the same SM nodeFromCandidate.Key != absoluteNameNodeFrom) // disallow from "any" to the same "any" { // create transition if target is different from source or when we don't care about it if (!virtualNodeData.ExceptTarget || absoluteNameNodeTo != nodeFromCandidate.Key) { CreateTransition(layer, animationController, nodeFromCandidate.Key, absoluteNameNodeTo, objBuilderTransition); } } } } CreateTransition(layer, animationController, absoluteNameNodeFrom, absoluteNameNodeTo, objBuilderTransition); } } return(result); }
// Initialize state machine of one layer. private static bool InitLayerNodes(VRage.Animations.MyAnimationStateMachine layer, string stateMachineName, MyAnimationControllerDefinition animControllerDefinition, VRage.Animations.MyAnimationController animationController, string currentNodeNamePrefix = "") { var objBuilderStateMachine = animControllerDefinition.StateMachines.FirstOrDefault(x => x.Name == stateMachineName); if (objBuilderStateMachine == null) { Debug.Fail("Animation state machine " + stateMachineName + " was not found."); return(false); } bool result = true; // 1st step: generate nodes if (objBuilderStateMachine.Nodes != null) { foreach (var objBuilderNode in objBuilderStateMachine.Nodes) { string absoluteNodeName = currentNodeNamePrefix + objBuilderNode.Name; if (objBuilderNode.StateMachineName != null) { // embedded state machine, copy its nodes if (!InitLayerNodes(layer, objBuilderNode.StateMachineName, animControllerDefinition, animationController, absoluteNodeName + "/")) { result = false; } } else { if (objBuilderNode.AnimationTree == null) { // no animation tree, just skip continue; } var smNode = new VRage.Animations.MyAnimationStateMachineNode(absoluteNodeName); layer.AddNode(smNode); var smNodeAnimTree = InitNodeAnimationTree(objBuilderNode.AnimationTree.Child); smNode.RootAnimationNode = smNodeAnimTree; } } } // 2nd step: generate transitions if (objBuilderStateMachine.Transitions != null) { foreach (var objBuilderTransition in objBuilderStateMachine.Transitions) { int conditionConjunctionIndex = 0; do { // generate transition for each condition conjunction var transition = layer.AddTransition(objBuilderTransition.From, objBuilderTransition.To, new VRage.Animations.MyAnimationStateMachineTransition()) as VRage.Animations.MyAnimationStateMachineTransition; // if ok, fill in conditions if (transition != null) { transition.Name = MyStringId.GetOrCompute(objBuilderTransition.Name); transition.TransitionTimeInSec = objBuilderTransition.TimeInSec; transition.Sync = objBuilderTransition.Sync; if (objBuilderTransition.Conditions != null && objBuilderTransition.Conditions[conditionConjunctionIndex] != null) { var conjunctionOfConditions = objBuilderTransition.Conditions[conditionConjunctionIndex].Conditions; foreach (var objBuilderCondition in conjunctionOfConditions) { var condition = ParseOneCondition(animationController, objBuilderCondition); if (condition != null) { transition.Conditions.Add(condition); } } } } conditionConjunctionIndex++; } while (objBuilderTransition.Conditions != null && conditionConjunctionIndex < objBuilderTransition.Conditions.Length); } } return(result); }
// Update state machine. That means: // Change state if transition conditions are fulfilled. // Update skeleton. public void Update(ref MyAnimationUpdateData data) { if (data.CharacterBones == null) { return; // safety } CurrentUpdateData = data; // local copy if (BoneMask == null) // rebuild bone mask array if not done yet { RebuildBoneMask(); } data.LayerBoneMask = CurrentUpdateData.LayerBoneMask = BoneMask; // pass bone mask array to all subnodes and result Debug.Assert(data.LayerBoneMask != null); base.Update(); // blended transitions // - from most recent to oldest // - please preserve order of blending for three or more states // when CurrentState changes for (int i = 0; i < m_stateTransitionBlending.Count; i++) { MyStateTransitionBlending stateTransitionBlending = m_stateTransitionBlending[i]; float weight = 1 - (float)(stateTransitionBlending.TimeLeftInSeconds * stateTransitionBlending.InvTotalTime); // update nodes that we are just leaving var lastResult = CurrentUpdateData.BonesResult; CurrentUpdateData.BonesResult = null; stateTransitionBlending.SourceState.OnUpdate(this); if (lastResult != null && CurrentUpdateData.BonesResult != null) { for (int j = 0; j < lastResult.Count; j++) { if (data.LayerBoneMask[j]) { // these nodes lose their weight, it goes from 1 to 0 // we need to blend them to last result (current node or another node that we are leaving) CurrentUpdateData.BonesResult[j].Rotation = Quaternion.Slerp(lastResult[j].Rotation, CurrentUpdateData.BonesResult[j].Rotation, 1 - weight); CurrentUpdateData.BonesResult[j].Translation = Vector3.Lerp(lastResult[j].Translation, CurrentUpdateData.BonesResult[j].Translation, weight); } } // give back last result (free list of bones), we dont need it anymore if (lastResult != null) { data.Controller.ResultBonesPool.Free(lastResult); } } // update, decrease remaining time stateTransitionBlending.TimeLeftInSeconds -= data.DeltaTimeInSeconds; m_stateTransitionBlending[i] = stateTransitionBlending; } m_stateTransitionBlending.RemoveAll(s => s.TimeLeftInSeconds <= 0.0); data.BonesResult = CurrentUpdateData.BonesResult; // local copy contains resulting list of bones // setting animation finished flag MyAnimationStateMachineNode currentAnimationNode = CurrentNode as MyAnimationStateMachineNode; if (currentAnimationNode != null && currentAnimationNode.RootAnimationNode != null) { float finishedPercent = currentAnimationNode.RootAnimationNode.GetLocalTimeNormalized(); data.Controller.Variables.SetValue(m_variableAnimationFinished, finishedPercent); } }
// Initialize state machine of one layer. private static bool InitLayerNodes(VRage.Animations.MyAnimationStateMachine layer, string stateMachineName, MyAnimationControllerDefinition animControllerDefinition, VRage.Animations.MyAnimationController animationController, string currentNodeNamePrefix = "") { var objBuilderStateMachine = animControllerDefinition.StateMachines.FirstOrDefault(x => x.Name == stateMachineName); if (objBuilderStateMachine == null) { Debug.Fail("Animation state machine " + stateMachineName + " was not found."); return false; } bool result = true; // 1st step: generate nodes if (objBuilderStateMachine.Nodes != null) foreach (var objBuilderNode in objBuilderStateMachine.Nodes) { string absoluteNodeName = currentNodeNamePrefix + objBuilderNode.Name; if (objBuilderNode.StateMachineName != null) { // embedded state machine, copy its nodes if (!InitLayerNodes(layer, objBuilderNode.StateMachineName, animControllerDefinition, animationController, absoluteNodeName + "/")) result = false; } else { if (objBuilderNode.AnimationTree == null) { // no animation tree, just skip continue; } var smNode = new VRage.Animations.MyAnimationStateMachineNode(absoluteNodeName); layer.AddNode(smNode); var smNodeAnimTree = InitNodeAnimationTree(objBuilderNode.AnimationTree.Child); smNode.RootAnimationNode = smNodeAnimTree; } } // 2nd step: generate transitions if (objBuilderStateMachine.Transitions != null) foreach (var objBuilderTransition in objBuilderStateMachine.Transitions) { int conditionConjunctionIndex = 0; do { // generate transition for each condition conjunction var transition = layer.AddTransition(objBuilderTransition.From, objBuilderTransition.To, new VRage.Animations.MyAnimationStateMachineTransition()) as VRage.Animations.MyAnimationStateMachineTransition; // if ok, fill in conditions if (transition != null) { transition.Name = MyStringId.GetOrCompute(objBuilderTransition.Name); transition.TransitionTimeInSec = objBuilderTransition.TimeInSec; transition.Sync = objBuilderTransition.Sync; if (objBuilderTransition.Conditions != null && objBuilderTransition.Conditions[conditionConjunctionIndex] != null) { var conjunctionOfConditions = objBuilderTransition.Conditions[conditionConjunctionIndex].Conditions; foreach (var objBuilderCondition in conjunctionOfConditions) { var condition = ParseOneCondition(animationController, objBuilderCondition); if (condition != null) transition.Conditions.Add(condition); } } } conditionConjunctionIndex++; } while (objBuilderTransition.Conditions != null && conditionConjunctionIndex < objBuilderTransition.Conditions.Length); } return result; }
// Initialize state machine of one layer. private static bool InitLayerNodes(MyAnimationStateMachine layer, string stateMachineName, MyAnimationControllerDefinition animControllerDefinition, MyAnimationController animationController, string currentNodeNamePrefix, MyAnimationVirtualNodes virtualNodes) { var objBuilderStateMachine = animControllerDefinition.StateMachines.FirstOrDefault(x => x.Name == stateMachineName); if (objBuilderStateMachine == null) { Debug.Fail("Animation state machine " + stateMachineName + " was not found."); return false; } bool result = true; // 1st step: generate nodes if (objBuilderStateMachine.Nodes != null) foreach (var objBuilderNode in objBuilderStateMachine.Nodes) { string absoluteNodeName = currentNodeNamePrefix + objBuilderNode.Name; if (objBuilderNode.StateMachineName != null) { // embedded state machine, copy its nodes if (!InitLayerNodes(layer, objBuilderNode.StateMachineName, animControllerDefinition, animationController, absoluteNodeName + "/", virtualNodes)) result = false; } else { var smNode = new VRage.Animations.MyAnimationStateMachineNode(absoluteNodeName); if (objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.PassThrough || objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.Any || objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.AnyExceptTarget) { smNode.PassThrough = true; } else { smNode.PassThrough = false; } if (objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.Any || objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.AnyExceptTarget) { virtualNodes.NodesAny.Add(absoluteNodeName, new MyAnimationVirtualNodeData() { AnyNodePrefix = currentNodeNamePrefix, ExceptTarget = (objBuilderNode.Type == MyObjectBuilder_AnimationSMNode.MySMNodeType.AnyExceptTarget) }); } layer.AddNode(smNode); if (objBuilderNode.AnimationTree != null) { var smNodeAnimTree = InitNodeAnimationTree(objBuilderNode.AnimationTree.Child); smNode.RootAnimationNode = smNodeAnimTree; } else { smNode.RootAnimationNode = new MyAnimationTreeNodeDummy(); } } } // 2nd step: generate transitions if (objBuilderStateMachine.Transitions != null) foreach (var objBuilderTransition in objBuilderStateMachine.Transitions) { string absoluteNameNodeFrom = currentNodeNamePrefix + objBuilderTransition.From; string absoluteNameNodeTo = currentNodeNamePrefix + objBuilderTransition.To; MyAnimationVirtualNodeData virtualNodeData; if (virtualNodes.NodesAny.TryGetValue(absoluteNameNodeFrom, out virtualNodeData)) { // nodes of type "any": // "any" node is source: we create transitions directly from all nodes // "any" node is target: we will use "any" node as pass through foreach (var nodeFromCandidate in layer.AllNodes) { if (nodeFromCandidate.Key.StartsWith(virtualNodeData.AnyNodePrefix) // select nodes in the same SM && nodeFromCandidate.Key != absoluteNameNodeFrom) // disallow from "any" to the same "any" { // create transition if target is different from source or when we don't care about it if (!virtualNodeData.ExceptTarget || absoluteNameNodeTo != nodeFromCandidate.Key) CreateTransition(layer, animationController, nodeFromCandidate.Key, absoluteNameNodeTo, objBuilderTransition); } } } CreateTransition(layer, animationController, absoluteNameNodeFrom, absoluteNameNodeTo, objBuilderTransition); } return result; }