/// <summary> /// Validates if the Editor and the provided RigBuilder are in a correct state to do motion transfer. /// </summary> /// <param name="rigBuilder">The RigBuilder that will be used for motion transfer.</param> /// <returns>Returns true if both the editor and the provided RigBuilder are in a valid state for motion transfer. Returns false if the requirements are not met.</returns> public static bool TransferMotionValidate(RigBuilder rigBuilder) { if (!AnimationWindowUtils.isPreviewing || AnimationWindowUtils.activeAnimationClip == null) { return(false); } var selected = Selection.instanceIDs; if (selected.Length != 1) { return(false); } var selectedGO = EditorUtility.InstanceIDToObject(selected[0]) as GameObject; if (selectedGO != rigBuilder.gameObject) { return(false); } var animator = rigBuilder.GetComponent <Animator>(); if (animator.isHuman) { return(false); } return(true); }
private static void BakeCurvesToClip(AnimationClip clip, IEnumerable <EditorCurveBinding> bindings, RigBuilder rigBuilder, IEvaluationGraph graph, CurveFilterOptions filterOptions) { if (rigBuilder == null) { throw new ArgumentNullException("It is not possible to bake curves without an RigBuilder."); } if (clip == null) { throw new ArgumentNullException("It is not possible to bake curves to a clip that is null."); } if (!AnimationMode.InAnimationMode()) { throw new ArgumentException("AnimationMode must be active during bake operation."); } var animator = rigBuilder.GetComponent <Animator>(); var recorder = new GameObjectRecorder(animator.gameObject); foreach (var binding in bindings) { recorder.Bind(binding); } var frameCount = (int)(clip.length * clip.frameRate); float dt = 1f / clip.frameRate; float time = 0f; graph?.Evaluate(0f); recorder.TakeSnapshot(0f); for (int frame = 1; frame <= frameCount; ++frame) { time = frame / clip.frameRate; graph?.Evaluate(time); recorder.TakeSnapshot(dt); } var tempClip = new AnimationClip(); recorder.SaveToClip(tempClip, clip.frameRate, filterOptions); CopyCurvesToClip(tempClip, clip); }
public EvaluationGraph(RigBuilder rigBuilder, AnimationClip clip) { m_SyncSceneToStreamLayer = new SyncSceneToStreamLayer(); var layers = rigBuilder.layers; m_RigLayers = new List <IRigLayer>(layers.Count); for (int i = 0; i < layers.Count; ++i) { if (!layers[i].active) { continue; } m_RigLayers.Add(new RigLayer(layers[i].rig)); } m_Graph = PlayableGraph.Create("Evaluation-Graph"); m_Graph.SetTimeUpdateMode(DirectorUpdateMode.Manual); var animator = rigBuilder.GetComponent <Animator>(); var clipPlayable = AnimationClipPlayable.Create(m_Graph, clip); Playable inputPlayable = clipPlayable; var playableChains = RigBuilderUtils.BuildPlayables(animator, m_Graph, m_RigLayers, m_SyncSceneToStreamLayer); foreach (var chain in playableChains) { if (!chain.IsValid()) { continue; } chain.playables[0].AddInput(inputPlayable, 0, 1); inputPlayable = chain.playables[chain.playables.Length - 1]; } m_Output = AnimationPlayableOutput.Create(m_Graph, "Evaluation-Graph-Output", animator); m_Output.SetSourcePlayable(inputPlayable); }
private static void TransferMotionToConstraint(RigBuilder rigBuilder, IEnumerable <Rig> rigs) { var constraints = new List <IRigConstraint>(); foreach (var rig in rigs) { constraints.AddRange(RigUtils.GetConstraints(rig)); } var clip = AnimationWindowUtils.activeAnimationClip; // Make sure we have a clip selected if (clip == null) { throw new InvalidOperationException( "There is no clip to work on." + " The animation window must be open with an active clip!"); } AnimationClip editableClip = clip; if (!GetEditableClip(ref editableClip)) { return; } AnimationClip defaultPoseClip = CreateDefaultPose(rigBuilder); Undo.RegisterCompleteObjectUndo(editableClip, kBakeToConstraintUndoLabel); var animator = rigBuilder.GetComponent <Animator>(); if (editableClip != clip) { AddClipToAnimatorController(animator, editableClip); } var bindingsToRemove = new HashSet <EditorCurveBinding>(); // Remove weight curve & force constraint to be active if (Preferences.forceConstraintWeightOnBake) { AnimationCurve oneWeightCurve = AnimationCurve.Constant(0f, editableClip.length, 1f); foreach (var rig in rigs) { AnimationUtility.SetEditorCurve(editableClip, GetWeightCurveBinding(rigBuilder, rig), oneWeightCurve); } } foreach (IRigConstraint constraint in constraints) { var bakeParameters = FindBakeParameters(constraint); if (bakeParameters == null || !bakeParameters.canBakeToConstraint) { continue; } // Flush out animation mode modifications AnimationMode.BeginSampling(); AnimationMode.EndSampling(); var bindings = bakeParameters.GetSourceCurveBindings(rigBuilder, constraint); BakeToConstraint(rigBuilder, constraint, editableClip, defaultPoseClip, bindings, Preferences.bakeToConstraintCurveFilterOptions); bindingsToRemove.UnionWith(bakeParameters.GetConstrainedCurveBindings(rigBuilder, constraint)); } if (Preferences.bakeToConstraintAndRemoveCurves) { RemoveCurves(editableClip, bindingsToRemove); } }
public EvaluationGraph(RigBuilder rigBuilder, AnimationClip clip, AnimationClip defaultPoseClip, IReadOnlyDictionary <IRigConstraint, IRigConstraint> overrides, IRigConstraint lastConstraint = null) { m_SyncSceneToStreamLayer = new SyncSceneToStreamLayer(); bool stopBuilding = false; var layers = rigBuilder.layers; m_RigLayers = new List <IRigLayer>(layers.Count); for (int i = 0; i < layers.Count; ++i) { if (stopBuilding == true) { break; } if (layers[i].rig == null || !layers[i].active) { continue; } IRigConstraint[] constraints = RigUtils.GetConstraints(layers[i].rig); if (constraints == null || constraints.Length == 0) { continue; } var newConstraints = new List <IRigConstraint>(constraints.Length); foreach (IRigConstraint constraint in constraints) { if (overrides.TryGetValue(constraint, out IRigConstraint newConstraint)) { if (newConstraint != null) { newConstraints.Add(newConstraint); } } else { newConstraints.Add(constraint); } if (constraint == lastConstraint) { stopBuilding = true; break; } } m_RigLayers.Add(new OverrideRigLayer(layers[i].rig, newConstraints.ToArray())); } m_Graph = PlayableGraph.Create("Evaluation-Graph"); m_Graph.SetTimeUpdateMode(DirectorUpdateMode.Manual); var animator = rigBuilder.GetComponent <Animator>(); m_Clip = clip; var settings = AnimationUtility.GetAnimationClipSettings(m_Clip); m_ClipLoopTime = settings.loopTime; // Override loop time in clip asset. settings.loopTime = false; AnimationUtility.SetAnimationClipSettings(m_Clip, settings); var defaultPosePlayable = AnimationClipPlayable.Create(m_Graph, defaultPoseClip); var clipPlayable = AnimationClipPlayable.Create(m_Graph, m_Clip); defaultPosePlayable.SetApplyFootIK(false); clipPlayable.SetApplyFootIK(false); AnimationLayerMixerPlayable mixer = AnimationLayerMixerPlayable.Create(m_Graph, 2); mixer.ConnectInput(0, defaultPosePlayable, 0, 1.0f); mixer.ConnectInput(1, clipPlayable, 0, 1.0f); Playable inputPlayable = mixer; var playableChains = RigBuilderUtils.BuildPlayables(animator, m_Graph, m_RigLayers, m_SyncSceneToStreamLayer); foreach (var chain in playableChains) { if (!chain.IsValid()) { continue; } chain.playables[0].AddInput(inputPlayable, 0, 1); inputPlayable = chain.playables[chain.playables.Length - 1]; } var output = AnimationPlayableOutput.Create(m_Graph, "bake-output", animator); output.SetSourcePlayable(inputPlayable); }
public static void TestTransferMotionToConstraint <T>(T constraint, RigBuilder rigBuilder, AnimationClip clip, IList <Transform> targetTransforms, CompareFlags flags) where T : MonoBehaviour, IRigConstraint { Animator animator = rigBuilder.GetComponent <Animator>(); int numberOfFrames = 60; float dt = 1f / numberOfFrames; var defaultPoseClip = CreateDefaultPose(rigBuilder.gameObject); #if DEBUG_BAKE_TO_CONSTRAINT AssetDatabase.CreateAsset(clip, "Assets/bakeToConstraintBefore.anim"); clip = UnityEngine.Object.Instantiate(clip) as AnimationClip; #endif // Evaluate clip without constraint and take snapshots. var translationsBeforeBaking = new Vector3[targetTransforms.Count, numberOfFrames + 1]; var rotationsBeforeBaking = new Quaternion[targetTransforms.Count, numberOfFrames + 1]; using (var graph = new EvaluationGraph(rigBuilder.GetComponent <Animator>(), clip)) { graph.Evaluate(0f); for (int transformIndex = 0; transformIndex < targetTransforms.Count; ++transformIndex) { translationsBeforeBaking[transformIndex, 0] = targetTransforms[transformIndex].position; rotationsBeforeBaking[transformIndex, 0] = targetTransforms[transformIndex].rotation; } for (int frame = 1; frame <= numberOfFrames; ++frame) { graph.Evaluate(dt); for (int transformIndex = 0; transformIndex < targetTransforms.Count; ++transformIndex) { translationsBeforeBaking[transformIndex, frame] = targetTransforms[transformIndex].position; rotationsBeforeBaking[transformIndex, frame] = targetTransforms[transformIndex].rotation; } } } RestoreDefaultPose(animator, defaultPoseClip); // Bake and inverse solve to constraint. var bakeParameters = BakeUtils.FindBakeParameters(constraint); Assert.That(bakeParameters, Is.Not.Null); var bindings = bakeParameters.GetSourceCurveBindings(rigBuilder, constraint); AnimationMode.StartAnimationMode(); BakeUtils.BakeToConstraint(constraint, clip, defaultPoseClip, bindings, k_DefaultCurveFilterOptions); AnimationMode.StopAnimationMode(); #if DEBUG_BAKE_TO_CONSTRAINT AssetDatabase.CreateAsset(clip, "Assets/bakeToConstraintAfter.anim"); #endif RestoreDefaultPose(animator, defaultPoseClip); // Evaluate again with constraint active and take snapshots. var translationsAfterBaking = new Vector3[targetTransforms.Count, numberOfFrames + 1]; var rotationsAfterBaking = new Quaternion[targetTransforms.Count, numberOfFrames + 1]; using (var graph = new EvaluationGraph(rigBuilder, clip)) { graph.Evaluate(0f); for (int transformIndex = 0; transformIndex < targetTransforms.Count; ++transformIndex) { translationsAfterBaking[transformIndex, 0] = targetTransforms[transformIndex].position; rotationsAfterBaking[transformIndex, 0] = targetTransforms[transformIndex].rotation; } for (int frame = 1; frame <= numberOfFrames; ++frame) { graph.Evaluate(dt); for (int transformIndex = 0; transformIndex < targetTransforms.Count; ++transformIndex) { translationsAfterBaking[transformIndex, frame] = targetTransforms[transformIndex].position; rotationsAfterBaking[transformIndex, frame] = targetTransforms[transformIndex].rotation; } } } RestoreDefaultPose(animator, defaultPoseClip); if ((flags & CompareFlags.Rotation) != 0) { // Compare rotations var quaternionComparer = new RuntimeRiggingTestFixture.QuaternionEqualityComparer(k_RotationEpsilon); for (int transformIndex = 0; transformIndex < targetTransforms.Count; ++transformIndex) { for (int frame = 0; frame <= numberOfFrames; ++frame) { Assert.That(rotationsAfterBaking[transformIndex, frame], Is.EqualTo(rotationsBeforeBaking[transformIndex, frame]).Using(quaternionComparer), String.Format("Transform '{0}' rotation is set to {1} at frame {2}, but was expected to be {3}", targetTransforms[transformIndex].name, rotationsAfterBaking[transformIndex, frame].eulerAngles, frame, rotationsBeforeBaking[transformIndex, frame].eulerAngles)); } } } if ((flags & CompareFlags.Translation) != 0) { // Compare translations var positionComparer = new RuntimeRiggingTestFixture.Vector3EqualityComparer(k_Epsilon); for (int transformIndex = 0; transformIndex < targetTransforms.Count; ++transformIndex) { for (int frame = 0; frame <= numberOfFrames; ++frame) { Assert.That(translationsAfterBaking[transformIndex, frame], Is.EqualTo(translationsBeforeBaking[transformIndex, frame]).Using(positionComparer), String.Format("Transform '{0}' position is set to {1} at frame {2}, but was expected to be {3}", targetTransforms[transformIndex].name, translationsAfterBaking[transformIndex, frame], frame, translationsBeforeBaking[transformIndex, frame])); } } } }