public AdditiveBlend( Clip c , TreeNode b , string blend_control , string add_control ) { children = new TreeNode[2]; children[0] = c; children[1] = b; controls = new string[2]; controls[0] = blend_control; controls[1] = add_control; }
private RootMotionResult _CalculateRootMotion() { if (lastRootMotionResult == null) { RootMotionResult result = new RootMotionResult(); // --- // 1. Reverse Engineer the Unity Weights based on the amount and the layer // --- // 1a) temporarily accumulate all current AnimationStates and layers foreach (AnimationState s in Target.GetComponent <Animation>()) { Mixamo.Clip c = graph.GetClipByAnimName(s.name); c.Weight = s.weight; } SortedList <int, List <AnimationState> > lst = new SortedList <int, List <AnimationState> >(new DescIntComparer()); foreach (AnimationState aState in Target.GetComponent <Animation>()) { if (aState.layer == 0) { if (!lst.ContainsKey(aState.layer)) { lst.Add(aState.layer, new List <AnimationState>()); } lst[aState.layer].Add(aState); } } // 1b) iterate thru each layer in descending order and calculate the "real" normalized weight of each AnimationState in that layer float remainingWeight = 1f; List <AnimationState> lastWeightedList = null; float lastRemainingWeight = 0f; float lastSumWeight = 0f; foreach (KeyValuePair <int, List <AnimationState> > kvp in lst) { if (remainingWeight > 0f) { float sumWeight = 0f; foreach (AnimationState s in kvp.Value) { if (s.enabled) { sumWeight += s.weight; } } if (sumWeight > 0f) { lastWeightedList = kvp.Value; lastRemainingWeight = remainingWeight; lastSumWeight = sumWeight; } float normalizeBy = 1f; if (sumWeight > 1f) { normalizeBy = 1f / sumWeight; } float lostWeight = 0f; foreach (AnimationState s in kvp.Value) { Mixamo.Clip c = graph.GetClipByAnimName(s.name); if (s.enabled) { c.NormalizedRootWeight = s.weight * normalizeBy * remainingWeight; lostWeight += c.NormalizedRootWeight; } else { c.NormalizedRootWeight = 0f; } } remainingWeight -= lostWeight; } else { foreach (AnimationState s in kvp.Value) { Mixamo.Clip c = graph.GetClipByAnimName(s.name); c.NormalizedRootWeight = 0f; } } } if (remainingWeight > 0f && lastWeightedList != null && lastRemainingWeight > 0f && lastSumWeight > 0f) { foreach (AnimationState s in lastWeightedList) { Mixamo.Clip c = graph.GetClipByAnimName(s.name); if (s.enabled) { c.NormalizedRootWeight = (s.weight / lastSumWeight) * lastRemainingWeight; remainingWeight -= c.NormalizedRootWeight; } } } // --- // 2. Calculate Root Motion // --- // 2a) turn off all animations, so we can look at one by one foreach (AnimationState s in Target.GetComponent <Animation>()) { s.weight = 0f; } result.LocalTranslation = Vector3.zero; result.PelvisLocalPosition = Vector3.zero; result.GlobalTranslation = Vector3.zero; result.LocalDeltaRotation = Vector3.zero; result.PelvisLocalRotation = Vector3.zero; result.GlobalRotation = Vector3.zero; // 2b) based on the json graph, iterate thru each clip that is set up to have its root motion extracted, sample that clip, and save the root motion change foreach (Mixamo.Clip clip in graph.clips) { if (clip.RootMotion.ExtractionType != Mixamo.RootMotionExtractionType.None || clip.RootMotion.RotExtractionType != Mixamo.RootMotionRotExtractionType.None) { // TODO: doesn't need to be computed every time (should optimize this by storing precalculated root motion) // sample animation clip.anim_state.weight = 1f; Target.GetComponent <Animation>().Sample(); // compute root motion Vector3 currPos = rmSetup.GetPelvisPosition(); Vector3 currRot = rmSetup.GetPelvisRotation(); clip.RootMotion.ComputeRootMotion(currPos, currRot, clip.anim_state.normalizedTime, clip.anim_state.wrapMode); clip.anim_state.weight = 0f; if (clip.RootMotion.ExtractionType != Mixamo.RootMotionExtractionType.None) { // calculate root motion translation Vector3 axis = Vector3.one; switch (clip.RootMotion.ExtractionType) { case Mixamo.RootMotionExtractionType.Z: axis = Vector3.forward; break; case Mixamo.RootMotionExtractionType.X: axis = Vector3.right; break; case Mixamo.RootMotionExtractionType.Y: axis = Vector3.up; break; case Mixamo.RootMotionExtractionType.XY: axis = new Vector3(1f, 1f, 0f); break; case Mixamo.RootMotionExtractionType.YZ: axis = new Vector3(0f, 1f, 1f); break; case Mixamo.RootMotionExtractionType.XZ: axis = new Vector3(1f, 0f, 1f); break; case Mixamo.RootMotionExtractionType.XYZ: axis = Vector3.one; break; } Matrix4x4 axis_m = Matrix4x4.Scale(axis); result.LocalTranslation += axis_m.MultiplyVector(clip.RootMotion.DeltaPosition * clip.NormalizedRootWeight); result.PelvisLocalPosition += axis_m.MultiplyVector((currPos - clip.RootMotion.StartPosition) * clip.NormalizedRootWeight); } if (clip.RootMotion.RotExtractionType != Mixamo.RootMotionRotExtractionType.None) { // calculate root motion rotation Vector3 rotAxis = Vector3.one; if (clip.RootMotion.RotExtractionType == Mixamo.RootMotionRotExtractionType.Y) { rotAxis = Vector3.up; } else if (clip.RootMotion.RotExtractionType == Mixamo.RootMotionRotExtractionType.X) { rotAxis = Vector3.right; } else if (clip.RootMotion.RotExtractionType == Mixamo.RootMotionRotExtractionType.Z) { rotAxis = Vector3.forward; } Matrix4x4 rotAxis_m = Matrix4x4.Scale(rotAxis); result.LocalDeltaRotation += rotAxis_m.MultiplyVector(clip.RootMotion.DeltaRotation * clip.NormalizedRootWeight); result.PelvisLocalRotation += rotAxis_m.MultiplyVector(currRot * clip.NormalizedRootWeight); } } } // 2c) return to normal position based on animation foreach (AnimationState s in Target.GetComponent <Animation>()) { Mixamo.Clip c = graph.GetClipByAnimName(s.name); s.weight = c.Weight; } Target.GetComponent <Animation>().Sample(); // 2d) remove the calculated root translation from the root node and optionally apply it to the current transform attached to this script // save root position to separate node RootMotionLocalPosition.localPosition = result.PelvisLocalPosition; // remove root position from pelvis rmSetup.pelvis.localPosition -= result.PelvisLocalPosition; // calculate global root motion translation Vector3 startPosition = rmSetup.pelvis.position; rmSetup.pelvis.localPosition += result.LocalTranslation; Vector3 endPosition = rmSetup.pelvis.position; result.GlobalTranslation = endPosition - startPosition; rmSetup.pelvis.localPosition -= result.LocalTranslation; // 2e) remove the calculated root rotation and optionally apply it to the current transform // TODO: uncomment out this code and make it work. Basically I've calculated the root rotation the same way I've calculated translation, the problem is // that this calculation below (removing the rotation and translation from the pelvis node and applying it to the root node) is wrong. // I know I need to apply the rotation displaced to the translation or something like that, but I just can't figure it out. /* * // remove root motion rotation from pelvis * RootMotionLocalPosition.localEulerAngles = result.PelvisLocalRotation; * * rmSetup.pelvis.localEulerAngles -= result.PelvisLocalRotation; * * // calculate global root motion rotation * Vector3 startRot = rmSetup.pelvis.eulerAngles; * rmSetup.pelvis.localEulerAngles += result.LocalDeltaRotation; * Vector3 endRot = rmSetup.pelvis.eulerAngles; * result.GlobalRotation = (endRot - startRot); * rmSetup.pelvis.localEulerAngles -= result.LocalDeltaRotation; */ if (RootMotionMode == AnimationStateMachine.RootMotionModeType.Automatic) { // TODO: uncomment out this code and get it to work, see above step 2e // rmSetup.root.Rotate( result.GlobalRotation , Space.World ); CharacterController cc = this.GetComponent <CharacterController>(); if (cc == null) { rmSetup.root.Translate(result.GlobalTranslation, Space.World); } else { cc.Move(result.GlobalTranslation); } } lastRootMotionResult = result; } return(lastRootMotionResult); }
public ClipTransition( Clip c , State source , State dest , float dur_in , float dur_out , string[] guards ) { clip = c; clip.anim_state.wrapMode = WrapMode.ClampForever; clip.anim_state.enabled = true; this.source = source; this.start_destination = dest; duration_in = dur_in; if( duration_in == 0f ) { WaitTillEnd = true; } duration_out = dur_out; this.guards = guards; }