Пример #1
0
        /// <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);
        }
Пример #2
0
        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);
        }
Пример #4
0
        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);
            }
        }
Пример #5
0
            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]));
                }
            }
        }
    }