Example #1
0
        /// <summary>
        /// BuildBlendTree is called every frame from the animation system when the <see cref="AnimationComponent"/> needs to be evaluated
        /// It overrides the default behavior of the <see cref="AnimationComponent"/> by setting a custom blend tree
        /// </summary>
        /// <param name="blendStack">The stack of animation operations to be blended</param>
        public void BuildBlendTree(FastList <AnimationOperation> blendStack)
        {
            switch (state)
            {
            case AnimationState.Walking:
            {
                // Note! The tree is laid out as a stack and has to be flattened before returning it to the animation system!
                blendStack.Add(AnimationOperation.NewPush(animEvaluatorWalkLerp1,
                                                          TimeSpan.FromTicks((long)(currentTime * animationClipWalkLerp1.Duration.Ticks))));
                blendStack.Add(AnimationOperation.NewPush(animEvaluatorWalkLerp2,
                                                          TimeSpan.FromTicks((long)(currentTime * animationClipWalkLerp2.Duration.Ticks))));
                blendStack.Add(AnimationOperation.NewBlend(CoreAnimationOperation.Blend, walkLerpFactor));
            }
            break;

            case AnimationState.Jumping:
            {
                blendStack.Add(AnimationOperation.NewPush(animEvaluatorJumpStart,
                                                          TimeSpan.FromTicks((long)(currentTime * AnimationJumpStart.Duration.Ticks))));
            }
            break;

            case AnimationState.Airborne:
            {
                blendStack.Add(AnimationOperation.NewPush(animEvaluatorJumpMid,
                                                          TimeSpan.FromTicks((long)(currentTime * AnimationJumpMid.Duration.Ticks))));
            }
            break;

            case AnimationState.Landing:
            {
                blendStack.Add(AnimationOperation.NewPush(animEvaluatorJumpEnd,
                                                          TimeSpan.FromTicks((long)(currentTime * AnimationJumpEnd.Duration.Ticks))));
            }
            break;
            }
        }
Example #2
0
        private unsafe object ExportAnimation(ICommandContext commandContext, ContentManager contentManager, bool failOnEmptyAnimation)
        {
            // Read from model file
            var modelSkeleton = LoadSkeleton(commandContext, contentManager); // we get model skeleton to compare it to real skeleton we need to map to

            AdjustSkeleton(modelSkeleton);

            TimeSpan duration;
            var      animationClips = LoadAnimation(commandContext, contentManager, out duration);

            // Fix the animation frames
            double startFrameSeconds = StartFrame.TotalSeconds;
            double endFrameSeconds   = EndFrame.TotalSeconds;
            var    startTime         = CompressedTimeSpan.FromSeconds(-startFrameSeconds);

            foreach (var clip in animationClips)
            {
                foreach (var animationCurve in clip.Value.Curves)
                {
                    animationCurve.ShiftKeys(startTime);
                }
            }

            var durationTimeSpan = TimeSpan.FromSeconds((endFrameSeconds - startFrameSeconds));

            if (duration > durationTimeSpan)
            {
                duration = durationTimeSpan;
            }

            var animationClip = new AnimationClip {
                Duration = duration
            };

            if (animationClips.Count > 0)
            {
                AnimationClip rootMotionAnimationClip = null;

                // If root motion is explicitely enabled, or if there is no skeleton, try to find root node and apply animation directly on TransformComponent
                if ((AnimationRootMotion || SkeletonUrl == null) && modelSkeleton.Nodes.Length >= 1)
                {
                    // No skeleton, map root node only
                    // TODO: For now, it seems to be located on node 1 in FBX files. Need to check if always the case, and what happens with Assimp
                    var rootNode0 = modelSkeleton.Nodes.Length >= 1 ? modelSkeleton.Nodes[0].Name : null;
                    var rootNode1 = modelSkeleton.Nodes.Length >= 2 ? modelSkeleton.Nodes[1].Name : null;
                    if ((rootNode0 != null && animationClips.TryGetValue(rootNode0, out rootMotionAnimationClip)) ||
                        (rootNode1 != null && animationClips.TryGetValue(rootNode1, out rootMotionAnimationClip)))
                    {
                        foreach (var channel in rootMotionAnimationClip.Channels)
                        {
                            var curve = rootMotionAnimationClip.Curves[channel.Value.CurveIndex];

                            // Root motion
                            var channelName = channel.Key;
                            if (channelName.StartsWith("Transform."))
                            {
                                animationClip.AddCurve($"[TransformComponent.Key]." + channelName.Replace("Transform.", string.Empty), curve);
                            }

                            // Also apply Camera curves
                            // TODO: Add some other curves?
                            if (channelName.StartsWith("Camera."))
                            {
                                animationClip.AddCurve($"[CameraComponent.Key]." + channelName.Replace("Camera.", string.Empty), curve);
                            }
                        }
                    }
                }

                // Load asset reference skeleton
                if (SkeletonUrl != null)
                {
                    var skeleton        = contentManager.Load <Skeleton>(SkeletonUrl);
                    var skeletonMapping = new SkeletonMapping(skeleton, modelSkeleton);

                    // Process missing nodes
                    foreach (var nodeAnimationClipEntry in animationClips)
                    {
                        var nodeName          = nodeAnimationClipEntry.Key;
                        var nodeAnimationClip = nodeAnimationClipEntry.Value;
                        var nodeIndex         = modelSkeleton.Nodes.IndexOf(x => x.Name == nodeName);

                        // Node doesn't exist in skeleton? skip it
                        if (nodeIndex == -1 || skeletonMapping.SourceToSource[nodeIndex] != nodeIndex)
                        {
                            continue;
                        }

                        // Skip root motion node (if any)
                        if (nodeAnimationClip == rootMotionAnimationClip)
                        {
                            continue;
                        }

                        // Find parent node
                        var parentNodeIndex = modelSkeleton.Nodes[nodeIndex].ParentIndex;

                        if (parentNodeIndex != -1 && skeletonMapping.SourceToSource[parentNodeIndex] != parentNodeIndex)
                        {
                            // Some nodes were removed, we need to concat the anim curves
                            var currentNodeIndex = nodeIndex;
                            var nodesToMerge     = new List <Tuple <ModelNodeDefinition, AnimationBlender, AnimationClipEvaluator> >();
                            while (currentNodeIndex != -1 && currentNodeIndex != skeletonMapping.SourceToSource[parentNodeIndex])
                            {
                                AnimationClip          animationClipToMerge;
                                AnimationClipEvaluator animationClipEvaluator = null;
                                AnimationBlender       animationBlender       = null;
                                if (animationClips.TryGetValue(modelSkeleton.Nodes[currentNodeIndex].Name, out animationClipToMerge))
                                {
                                    animationBlender       = new AnimationBlender();
                                    animationClipEvaluator = animationBlender.CreateEvaluator(animationClipToMerge);
                                }
                                nodesToMerge.Add(Tuple.Create(modelSkeleton.Nodes[currentNodeIndex], animationBlender, animationClipEvaluator));
                                currentNodeIndex = modelSkeleton.Nodes[currentNodeIndex].ParentIndex;
                            }

                            // Put them in proper parent to children order
                            nodesToMerge.Reverse();

                            // Find all key times
                            // TODO: We should detect discontinuities and keep them
                            var animationKeysSet = new HashSet <CompressedTimeSpan>();

                            foreach (var node in nodesToMerge)
                            {
                                if (node.Item3 != null)
                                {
                                    foreach (var curve in node.Item3.Clip.Curves)
                                    {
                                        foreach (CompressedTimeSpan time in curve.Keys)
                                        {
                                            animationKeysSet.Add(time);
                                        }
                                    }
                                }
                            }

                            // Sort key times
                            var animationKeys = animationKeysSet.ToList();
                            animationKeys.Sort();

                            var animationOperations = new FastList <AnimationOperation>();

                            var combinedAnimationClip = new AnimationClip();

                            var translationCurve = new AnimationCurve <Vector3>();
                            var rotationCurve    = new AnimationCurve <Quaternion>();
                            var scaleCurve       = new AnimationCurve <Vector3>();

                            // Evaluate at every key frame
                            foreach (var animationKey in animationKeys)
                            {
                                var matrix = Matrix.Identity;

                                // Evaluate node
                                foreach (var node in nodesToMerge)
                                {
                                    // Needs to be an array in order for it to be modified by the UpdateEngine, otherwise it would get passed by value
                                    var modelNodeDefinitions = new ModelNodeDefinition[1] {
                                        node.Item1
                                    };

                                    if (node.Item2 != null && node.Item3 != null)
                                    {
                                        // Compute
                                        AnimationClipResult animationClipResult = null;
                                        animationOperations.Clear();
                                        animationOperations.Add(AnimationOperation.NewPush(node.Item3, animationKey));
                                        node.Item2.Compute(animationOperations, ref animationClipResult);

                                        var updateMemberInfos = new List <UpdateMemberInfo>();
                                        foreach (var channel in animationClipResult.Channels)
                                        {
                                            if (channel.IsUserCustomProperty)
                                            {
                                                continue;
                                            }

                                            updateMemberInfos.Add(new UpdateMemberInfo {
                                                Name = "[0]." + channel.PropertyName, DataOffset = channel.Offset
                                            });
                                        }

                                        // TODO: Cache this
                                        var compiledUpdate = UpdateEngine.Compile(typeof(ModelNodeDefinition[]), updateMemberInfos);

                                        fixed(byte *data = animationClipResult.Data)
                                        {
                                            UpdateEngine.Run(modelNodeDefinitions, compiledUpdate, (IntPtr)data, null);
                                        }
                                    }

                                    Matrix localMatrix;
                                    var    transformTRS = modelNodeDefinitions[0].Transform;
                                    Matrix.Transformation(ref transformTRS.Scale, ref transformTRS.Rotation, ref transformTRS.Position,
                                                          out localMatrix);
                                    matrix = Matrix.Multiply(localMatrix, matrix);
                                }

                                // Done evaluating, let's decompose matrix
                                TransformTRS transform;
                                matrix.Decompose(out transform.Scale, out transform.Rotation, out transform.Position);

                                // Create a key
                                translationCurve.KeyFrames.Add(new KeyFrameData <Vector3>(animationKey, transform.Position));
                                rotationCurve.KeyFrames.Add(new KeyFrameData <Quaternion>(animationKey, transform.Rotation));
                                scaleCurve.KeyFrames.Add(new KeyFrameData <Vector3>(animationKey, transform.Scale));
                            }

                            combinedAnimationClip.AddCurve($"{nameof(ModelNodeTransformation.Transform)}.{nameof(TransformTRS.Position)}", translationCurve);
                            combinedAnimationClip.AddCurve($"{nameof(ModelNodeTransformation.Transform)}.{nameof(TransformTRS.Rotation)}", rotationCurve);
                            combinedAnimationClip.AddCurve($"{nameof(ModelNodeTransformation.Transform)}.{nameof(TransformTRS.Scale)}", scaleCurve);
                            nodeAnimationClip = combinedAnimationClip;
                        }

                        var transformStart    = $"{nameof(ModelNodeTransformation.Transform)}.";
                        var transformPosition = $"{nameof(ModelNodeTransformation.Transform)}.{nameof(TransformTRS.Position)}";

                        foreach (var channel in nodeAnimationClip.Channels)
                        {
                            var curve = nodeAnimationClip.Curves[channel.Value.CurveIndex];

                            // TODO: Root motion
                            var channelName = channel.Key;
                            if (channelName.StartsWith(transformStart))
                            {
                                if (channelName == transformPosition)
                                {
                                    // Translate node with parent 0 using PivotPosition
                                    var keyFrames = ((AnimationCurve <Vector3>)curve).KeyFrames;
                                    for (int i = 0; i < keyFrames.Count; ++i)
                                    {
                                        if (parentNodeIndex == 0)
                                        {
                                            keyFrames.Items[i].Value -= PivotPosition;
                                        }
                                        keyFrames.Items[i].Value *= ScaleImport;
                                    }
                                }
                                animationClip.AddCurve($"[ModelComponent.Key].Skeleton.NodeTransformations[{skeletonMapping.SourceToTarget[nodeIndex]}]." + channelName, curve);
                            }
                        }
                    }
                }

                if (ImportCustomAttributes)
                {
                    // Add clips clips animating other properties than node transformations
                    foreach (var nodeAnimationClipPair in animationClips)
                    {
                        var nodeName          = nodeAnimationClipPair.Key;
                        var nodeAnimationClip = nodeAnimationClipPair.Value;

                        foreach (var channel in nodeAnimationClip.Channels)
                        {
                            var channelName  = channel.Key;
                            var channelValue = channel.Value;
                            if (channelValue.IsUserCustomProperty)
                            {
                                animationClip.AddCurve(nodeName + "_" + channelName, nodeAnimationClip.Curves[channel.Value.CurveIndex], true);
                            }
                        }
                    }
                }
            }

            if (animationClip.Channels.Count == 0)
            {
                var logString = $"File {SourcePath} doesn't have any animation information.";

                if (failOnEmptyAnimation)
                {
                    commandContext.Logger.Error(logString);
                    return(null);
                }

                commandContext.Logger.Info(logString);
            }
            else
            {
                if (animationClip.Duration.Ticks == 0)
                {
                    commandContext.Logger.Verbose($"File {SourcePath} has a 0 tick long animation.");
                }

                // Optimize and set common parameters
                animationClip.RepeatMode = AnimationRepeatMode;
                animationClip.Optimize();
            }
            return(animationClip);
        }
Example #3
0
            private AnimationClip SubtractAnimations(AnimationClip baseAnimation, AnimationClip sourceAnimation)
            {
                if (baseAnimation == null)
                {
                    throw new ArgumentNullException("baseAnimation");
                }
                if (sourceAnimation == null)
                {
                    throw new ArgumentNullException("sourceAnimation");
                }

                var animationBlender = new AnimationBlender();

                var baseEvaluator   = animationBlender.CreateEvaluator(baseAnimation);
                var sourceEvaluator = animationBlender.CreateEvaluator(sourceAnimation);

                // Create a result animation with same channels
                var resultAnimation = new AnimationClip();

                foreach (var channel in sourceAnimation.Channels)
                {
                    // Create new instance of curve
                    var newCurve = (AnimationCurve)Activator.CreateInstance(typeof(AnimationCurve <>).MakeGenericType(channel.Value.ElementType));

                    // Quaternion curve are linear, others are cubic
                    if (newCurve.ElementType != typeof(Quaternion))
                    {
                        newCurve.InterpolationType = AnimationCurveInterpolationType.Cubic;
                    }

                    resultAnimation.AddCurve(channel.Key, newCurve);
                }

                var resultEvaluator = animationBlender.CreateEvaluator(resultAnimation);

                var animationOperations = new FastList <AnimationOperation>();

                // Perform animation blending for each frame and upload results in a new animation
                // Note that it does a simple per-frame sampling, so animation discontinuities will be lost.
                // TODO: Framerate is hardcoded at 30 FPS.
                var frameTime = TimeSpan.FromSeconds(1.0f / 30.0f);

                for (var time = TimeSpan.Zero; time < sourceAnimation.Duration + frameTime; time += frameTime)
                {
                    // Last frame, round it to end of animation
                    if (time > sourceAnimation.Duration)
                    {
                        time = sourceAnimation.Duration;
                    }

                    TimeSpan baseTime;
                    switch (Parameters.Mode)
                    {
                    case AdditiveAnimationBaseMode.FirstFrame:
                        baseTime = TimeSpan.Zero;
                        break;

                    case AdditiveAnimationBaseMode.Animation:
                        baseTime = TimeSpan.FromTicks(time.Ticks % baseAnimation.Duration.Ticks);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    // Generates result = source - base
                    animationOperations.Clear();
                    animationOperations.Add(AnimationOperation.NewPush(sourceEvaluator, time));
                    animationOperations.Add(AnimationOperation.NewPush(baseEvaluator, baseTime));
                    animationOperations.Add(AnimationOperation.NewBlend(CoreAnimationOperation.Subtract, 1.0f));
                    animationOperations.Add(AnimationOperation.NewPop(resultEvaluator, time));

                    // Compute
                    AnimationClipResult animationClipResult = null;
                    animationBlender.Compute(animationOperations, ref animationClipResult);
                }

                resultAnimation.Duration   = sourceAnimation.Duration;
                resultAnimation.RepeatMode = sourceAnimation.RepeatMode;

                return(resultAnimation);
            }
Example #4
0
 /// <summary>
 /// BuildBlendTree is called every frame from the animation system when the <see cref="AnimationComponent"/> needs to be evaluated
 /// It overrides the default behavior of the <see cref="AnimationComponent"/> by setting a custom blend tree
 /// </summary>
 /// <param name="blendStack">The stack of animation operations to be blended</param>
 public void BuildBlendTree(FastList <AnimationOperation> blendStack)
 {
     blendStack.Add(AnimationOperation.NewPush(currentEvaluator, TimeSpan.FromTicks((long)(currentTime * currentClip.Duration.Ticks))));
 }
Example #5
0
    private void ShowResults(TrainingProgramData programData)
    {
        sessionNameText.text = $"{programData.programName} - Results";

        int programTotalScore   = programData.GetTotalScore();
        int programMaximumScore = programData.GetMaximumScore();

        totalScoreText.text = $"{programTotalScore}/{programMaximumScore}";

        float ratingValue    = (float)programTotalScore / programMaximumScore * 100f;
        float f              = 100f / ratingScaleImageNumber;
        float colourDuration = (float)ratingScaleImages.Count / (ratingScaleImages.Count * ratingScaleImages.Count);
        float delay          = colourDuration * 1.5f;

        AnimationSequence ratingAnimation = new AnimationSequence(applicationEventRelay.RequestStartingCoroutine);

        ratingAnimation.AddDelay(1f);

        spinningAnimations.Clear();
        AnimationOperation spinningOperation1 = new AnimationOperation(null, UIAnimationType.Rotate, EaseType.Linear, 0, 4f)
        {
            rotateSettings = new AnimationOperation.RotateSettings {
                startEuler  = new Vector3(0, 0, 0),
                targetEuler = new Vector3(0, 0, 180)
            }
        };
        AnimationOperation spinningOperation2 = new AnimationOperation(null, UIAnimationType.Rotate, EaseType.Linear, 3.95f, 4f)
        {
            rotateSettings = new AnimationOperation.RotateSettings {
                startEuler  = new Vector3(0, 0, 180),
                targetEuler = new Vector3(0, 0, 360)
            }
        };

        for (int i = 0; i < ratingScaleImages.Count; i++)
        {
            if (i * f >= ratingValue)
            {
                break;
            }

            AnimationOperation colourOperation = new AnimationOperation(ratingScaleImages[i].gameObject, UIAnimationType.Colour, EaseType.CubicIn, delay, colourDuration)
            {
                colourSettings = new AnimationOperation.ColourSettings {
                    startColour  = ratingScaleImages[i].color,
                    targetColour = Color.white
                }
            };

            ratingAnimation.AddOperation(colourOperation);

            AnimationSequence spinningAnim = new AnimationSequence(applicationEventRelay.RequestStartingCoroutine);

            spinningAnim.AddOperation(
                new AnimationOperation(spinningOperation1)
            {
                targetObject = ratingScaleImages[i].gameObject
            },
                new AnimationOperation(spinningOperation2)
            {
                targetObject = ratingScaleImages[i].gameObject
            });
            spinningAnim.Loop();

            spinningAnimations.Add(spinningAnim);
        }

        foreach (TrainingSessionData session in programData.trainingSessions)
        {
            GameObject sessionResultRow = Instantiate(sessionResultRowPrefab, sessionScrollRect.content);
            if (!sessionResultRow.TryGetComponent(out SessionResultRow sessionResult))
            {
                continue;
            }

            sessionResult.Setup(session.sessionName, $"{session.Score}/{session.MaximumScore}", null);
            // TODO hook session row buttons to show respective session results => set action
        }

        ShowWindow(true, true);
        ratingAnimation.Play();

        foreach (AnimationSequence animationSequence in spinningAnimations)
        {
            animationSequence.Play();
        }
    }
Example #6
0
    private void InitAnims()
    {
        showAnim = new AnimationSequence(applicationEventRelay.RequestStartingCoroutine);

        AnimationOperation activateOperation = new AnimationOperation(panel, UIAnimationType.Activate, EaseType.None, fadeScreenDuration, 0)
        {
            activate = true
        };
        AnimationOperation fadeOperation = new AnimationOperation(panel, UIAnimationType.Fade, EaseType.SmoothStepSmoother, 0, 1f)
        {
            fadeSettings = new AnimationOperation.FadeSettings {
                startAlpha  = 0,
                targetAlpha = 1
            }
        };

        showAnim.AddOperation(activateOperation, fadeOperation);

        hideAnim = new AnimationSequence(applicationEventRelay.RequestStartingCoroutine);
        AnimationOperation deactivateOperation = new AnimationOperation(panel, UIAnimationType.Activate, EaseType.None, 1, 0)
        {
            activate = false
        };

        hideAnim.AddOperation(new AnimationOperation(fadeOperation.Reversed()), deactivateOperation);
        hideAnim.OnFinished(() => {
            progressBarFill.fillAmount = 0;
            if (progressRoutine != null)
            {
                applicationEventRelay.RequestStoppingCoroutine(progressRoutine);
            }
            barCurrentTargetValue = 0;
        });

        textBlinkAnim = new AnimationSequence(applicationEventRelay.RequestStartingCoroutine);
        AnimationOperation blinkOperation = new AnimationOperation(fadeOperation)
        {
            targetObject = loadingText.gameObject
        };

        textBlinkAnim.AddOperation(blinkOperation);
        textBlinkAnim.PingPong();
        textBlinkAnim.Loop();

        AnimationOperation spinningOperation1 = new AnimationOperation(tennisBallImage, UIAnimationType.Rotate, EaseType.Linear, 0, 4f)
        {
            rotateSettings = new AnimationOperation.RotateSettings {
                startEuler  = new Vector3(0, 0, 0),
                targetEuler = new Vector3(0, 0, 180)
            }
        };
        AnimationOperation spinningOperation2 = new AnimationOperation(tennisBallImage, UIAnimationType.Rotate, EaseType.Linear, 3.95f, 4f)
        {
            rotateSettings = new AnimationOperation.RotateSettings {
                startEuler  = new Vector3(0, 0, 180),
                targetEuler = new Vector3(0, 0, 360)
            }
        };

        spinningBallAnim = new AnimationSequence(applicationEventRelay.RequestStartingCoroutine);
        spinningBallAnim.AddOperation(spinningOperation1, spinningOperation2);
        spinningBallAnim.Loop();
    }
Example #7
0
        private unsafe object ExportAnimation(ICommandContext commandContext, ContentManager contentManager)
        {
            // Read from model file
            var           modelSkeleton  = LoadSkeleton(commandContext, contentManager); // we get model skeleton to compare it to real skeleton we need to map to
            var           animationClips = LoadAnimation(commandContext, contentManager);
            AnimationClip animationClip  = null;

            if (animationClips.Count > 0)
            {
                animationClip = new AnimationClip();

                AnimationClip rootMotionAnimationClip = null;

                // If root motion is explicitely enabled, or if there is no skeleton, try to find root node and apply animation directly on TransformComponent
                if ((AnimationRootMotion || SkeletonUrl == null) && modelSkeleton.Nodes.Length >= 1)
                {
                    // No skeleton, map root node only
                    // TODO: For now, it seems to be located on node 1 in FBX files. Need to check if always the case, and what happens with Assimp
                    var rootNode0 = modelSkeleton.Nodes.Length >= 1 ? modelSkeleton.Nodes[0].Name : null;
                    var rootNode1 = modelSkeleton.Nodes.Length >= 2 ? modelSkeleton.Nodes[1].Name : null;
                    if ((rootNode0 != null && animationClips.TryGetValue(rootNode0, out rootMotionAnimationClip)) ||
                        (rootNode1 != null && animationClips.TryGetValue(rootNode1, out rootMotionAnimationClip)))
                    {
                        foreach (var channel in rootMotionAnimationClip.Channels)
                        {
                            var curve = rootMotionAnimationClip.Curves[channel.Value.CurveIndex];

                            // Root motion
                            var channelName = channel.Key;
                            if (channelName.StartsWith("Transform."))
                            {
                                animationClip.AddCurve($"[TransformComponent.Key]." + channelName.Replace("Transform.", string.Empty), curve);
                            }

                            // Also apply Camera curves
                            // TODO: Add some other curves?
                            if (channelName.StartsWith("Camera."))
                            {
                                animationClip.AddCurve($"[CameraComponent.Key]." + channelName.Replace("Camera.", string.Empty), curve);
                            }
                        }

                        // Take max of durations
                        if (animationClip.Duration < rootMotionAnimationClip.Duration)
                        {
                            animationClip.Duration = rootMotionAnimationClip.Duration;
                        }
                    }
                }

                // Load asset reference skeleton
                if (SkeletonUrl != null)
                {
                    var skeleton        = contentManager.Load <Skeleton>(SkeletonUrl);
                    var skeletonMapping = new SkeletonMapping(skeleton, modelSkeleton);

                    // Process missing nodes
                    foreach (var nodeAnimationClipEntry in animationClips)
                    {
                        var nodeName          = nodeAnimationClipEntry.Key;
                        var nodeAnimationClip = nodeAnimationClipEntry.Value;
                        var nodeIndex         = modelSkeleton.Nodes.IndexOf(x => x.Name == nodeName);

                        // Node doesn't exist in skeleton? skip it
                        if (nodeIndex == -1 || skeletonMapping.SourceToSource[nodeIndex] != nodeIndex)
                        {
                            continue;
                        }

                        // Skip root motion node (if any)
                        if (nodeAnimationClip == rootMotionAnimationClip)
                        {
                            continue;
                        }

                        // Find parent node
                        var parentNodeIndex = modelSkeleton.Nodes[nodeIndex].ParentIndex;

                        if (parentNodeIndex != -1 && skeletonMapping.SourceToSource[parentNodeIndex] != parentNodeIndex)
                        {
                            // Some nodes were removed, we need to concat the anim curves
                            var currentNodeIndex = nodeIndex;
                            var nodesToMerge     = new List <Tuple <ModelNodeDefinition, AnimationBlender, AnimationClipEvaluator> >();
                            while (currentNodeIndex != -1 && currentNodeIndex != skeletonMapping.SourceToSource[parentNodeIndex])
                            {
                                AnimationClip          animationClipToMerge;
                                AnimationClipEvaluator animationClipEvaluator = null;
                                AnimationBlender       animationBlender       = null;
                                if (animationClips.TryGetValue(modelSkeleton.Nodes[currentNodeIndex].Name, out animationClipToMerge))
                                {
                                    animationBlender       = new AnimationBlender();
                                    animationClipEvaluator = animationBlender.CreateEvaluator(animationClipToMerge);
                                }
                                nodesToMerge.Add(Tuple.Create(modelSkeleton.Nodes[currentNodeIndex], animationBlender, animationClipEvaluator));
                                currentNodeIndex = modelSkeleton.Nodes[currentNodeIndex].ParentIndex;
                            }

                            // Put them in proper parent to children order
                            nodesToMerge.Reverse();

                            // Find all key times
                            // TODO: We should detect discontinuities and keep them
                            var animationKeysSet = new HashSet <CompressedTimeSpan>();

                            foreach (var node in nodesToMerge)
                            {
                                foreach (var curve in node.Item3.Clip.Curves)
                                {
                                    foreach (CompressedTimeSpan time in curve.Keys)
                                    {
                                        animationKeysSet.Add(time);
                                    }
                                }
                            }

                            // Sort key times
                            var animationKeys = animationKeysSet.ToList();
                            animationKeys.Sort();

                            var animationOperations = new FastList <AnimationOperation>();

                            var combinedAnimationClip = new AnimationClip();

                            var translationCurve = new AnimationCurve <Vector3>();
                            var rotationCurve    = new AnimationCurve <Quaternion>();
                            var scaleCurve       = new AnimationCurve <Vector3>();

                            // Evaluate at every key frame
                            foreach (var animationKey in animationKeys)
                            {
                                var matrix = Matrix.Identity;

                                // Evaluate node
                                foreach (var node in nodesToMerge)
                                {
                                    // Get default position
                                    var modelNodeDefinition = node.Item1;

                                    // Compute
                                    AnimationClipResult animationClipResult = null;
                                    animationOperations.Clear();
                                    animationOperations.Add(AnimationOperation.NewPush(node.Item3, animationKey));
                                    node.Item2.Compute(animationOperations, ref animationClipResult);

                                    var updateMemberInfos = new List <UpdateMemberInfo>();
                                    foreach (var channel in animationClipResult.Channels)
                                    {
                                        updateMemberInfos.Add(new UpdateMemberInfo {
                                            Name = channel.PropertyName, DataOffset = channel.Offset
                                        });
                                    }

                                    // TODO: Cache this
                                    var compiledUpdate = UpdateEngine.Compile(typeof(ModelNodeDefinition), updateMemberInfos);

                                    unsafe
                                    {
                                        fixed(byte *data = animationClipResult.Data)
                                        UpdateEngine.Run(modelNodeDefinition, compiledUpdate, (IntPtr)data, null);
                                    }

                                    Matrix localMatrix;
                                    TransformComponent.CreateMatrixTRS(ref modelNodeDefinition.Transform.Position, ref modelNodeDefinition.Transform.Rotation, ref modelNodeDefinition.Transform.Scale,
                                                                       out localMatrix);
                                    matrix = Matrix.Multiply(localMatrix, matrix);
                                }

                                // Done evaluating, let's decompose matrix
                                TransformTRS transform;
                                matrix.Decompose(out transform.Scale, out transform.Rotation, out transform.Position);

                                // Create a key
                                translationCurve.KeyFrames.Add(new KeyFrameData <Vector3>(animationKey, transform.Position));
                                rotationCurve.KeyFrames.Add(new KeyFrameData <Quaternion>(animationKey, transform.Rotation));
                                scaleCurve.KeyFrames.Add(new KeyFrameData <Vector3>(animationKey, transform.Scale));
                            }

                            combinedAnimationClip.AddCurve($"{nameof(ModelNodeTransformation.Transform)}.{nameof(TransformTRS.Position)}", translationCurve);
                            combinedAnimationClip.AddCurve($"{nameof(ModelNodeTransformation.Transform)}.{nameof(TransformTRS.Rotation)}", rotationCurve);
                            combinedAnimationClip.AddCurve($"{nameof(ModelNodeTransformation.Transform)}.{nameof(TransformTRS.Scale)}", scaleCurve);
                            nodeAnimationClip = combinedAnimationClip;
                        }

                        foreach (var channel in nodeAnimationClip.Channels)
                        {
                            var curve = nodeAnimationClip.Curves[channel.Value.CurveIndex];

                            // TODO: Root motion
                            var channelName = channel.Key;
                            if (channelName.StartsWith("Transform."))
                            {
                                animationClip.AddCurve($"[ModelComponent.Key].Skeleton.NodeTransformations[{skeletonMapping.SourceToTarget[nodeIndex]}]." + channelName, curve);
                            }
                        }

                        // Take max of durations
                        if (animationClip.Duration < nodeAnimationClip.Duration)
                        {
                            animationClip.Duration = nodeAnimationClip.Duration;
                        }
                    }
                }
            }

            if (animationClip == null)
            {
                commandContext.Logger.Info("File {0} has an empty animation.", SourcePath);
            }
            else
            {
                if (animationClip.Duration.Ticks == 0)
                {
                    commandContext.Logger.Warning("File {0} has a 0 tick long animation.", SourcePath);
                }

                // Optimize and set common parameters
                animationClip.RepeatMode = AnimationRepeatMode;
                animationClip.Optimize();
            }
            return(animationClip);
        }
Example #8
0
    private void Show(float duration, bool show)
    {
        inverseMaskImage.rectTransform.sizeDelta = rootCanvasRectTransform.rect.size;

        if (show)
        {
            AnimationSequence sequence = new AnimationSequence(e => {
                if (applicationEventRelay)
                {
                    applicationEventRelay.RequestStartingCoroutine(e);
                }
            });

            AnimationOperation activateOperation = new AnimationOperation(screenFaderPanel, UIAnimationType.Activate, EaseType.None, 0, 0)
            {
                activate = true
            };

            AnimationOperation showOperation = new AnimationOperation(faderMaskImage.gameObject, UIAnimationType.AnchoredPosition, EaseType.QuarticIn, 0, duration)
            {
                anchoredPositionSettings = new AnimationOperation.AnchoredPositionSettings {
                    startMin  = new Vector2(-1, -1),
                    startMax  = new Vector2(2, 2),
                    targetMin = new Vector2(0.5f, 0.5f),
                    targetMax = new Vector2(0.5f, 0.5f)
                }
            };

            sequence.AddOperation(activateOperation);
            sequence.AddOperation(showOperation);

            sequence.Play();
        }
        else
        {
            AnimationSequence sequence = new AnimationSequence(e => {
                if (applicationEventRelay)
                {
                    applicationEventRelay.RequestStartingCoroutine(e);
                }
            });

            AnimationOperation showOperation = new AnimationOperation(faderMaskImage.gameObject, UIAnimationType.AnchoredPosition, EaseType.QuarticIn, 0, duration)
            {
                anchoredPositionSettings = new AnimationOperation.AnchoredPositionSettings {
                    targetMin = new Vector2(-1, -1),
                    targetMax = new Vector2(2, 2),
                    startMin  = new Vector2(0.5f, 0.5f),
                    startMax  = new Vector2(0.5f, 0.5f)
                }
            };

            AnimationOperation activateOperation = new AnimationOperation(screenFaderPanel, UIAnimationType.Activate, EaseType.None, duration, 0)
            {
                activate = false
            };

            sequence.AddOperation(showOperation);
            sequence.AddOperation(activateOperation);


            sequence.Play();
        }
    }
Example #9
0
    private void SceneLoadedShow()
    {
        // Maybe do this in via the editor...
        GameObject         qMButton        = quickMatchButton.gameObject;
        AnimationOperation qMFadeOperation = new AnimationOperation(qMButton, UIAnimationType.Fade, EaseType.SmoothStepSmoother, 0, 0.5f)
        {
            fadeSettings = new AnimationOperation.FadeSettings {
                startAlpha  = 0f,
                targetAlpha = 1f
            }
        };
        AnimationOperation qMScaleOperation = new AnimationOperation(qMButton, UIAnimationType.Scale, EaseType.SmoothStepSmoother, 0, 0.5f)
        {
            scaleSettings = new AnimationOperation.ScaleSettings {
                startScale  = Vector3.zero,
                targetScale = Vector3.one
            }
        };
        AnimationOperation qMActivateOperation = new AnimationOperation(qMButton, UIAnimationType.Activate, EaseType.None, 0, 0.5f)
        {
            activate = true
        };

        AnimationSequence quickMatchButtonSequence = new AnimationSequence(RelayRoutine);

        quickMatchButtonSequence.AddOperation(qMFadeOperation, qMScaleOperation, qMActivateOperation);

        GameObject         cButton        = careerButton.gameObject;
        AnimationOperation cFadeOperation = new AnimationOperation(qMFadeOperation)
        {
            targetObject = cButton,
            delay        = 0.25f
        };
        AnimationOperation cScaleOperation = new AnimationOperation(qMScaleOperation)
        {
            targetObject = cButton
        };
        AnimationOperation cActivateOperation = new AnimationOperation(qMActivateOperation)
        {
            targetObject = cButton
        };
        AnimationSequence careerButtonSequence = new AnimationSequence(RelayRoutine);

        careerButtonSequence.AddOperation(cFadeOperation, cScaleOperation, cActivateOperation);

        GameObject         oButton        = onlineButton.gameObject;
        AnimationOperation oFadeOperation = new AnimationOperation(qMFadeOperation)
        {
            targetObject = oButton,
            delay        = 0.35f
        };
        AnimationOperation oScaleOperation = new AnimationOperation(qMScaleOperation)
        {
            targetObject = oButton
        };
        AnimationOperation oActivateOperation = new AnimationOperation(qMActivateOperation)
        {
            targetObject = oButton
        };
        AnimationSequence onlineButtonSequence = new AnimationSequence(RelayRoutine);

        onlineButtonSequence.AddOperation(oFadeOperation, oScaleOperation, oActivateOperation);

        GameObject         tButton        = trainingButton.gameObject;
        AnimationOperation tFadeOperation = new AnimationOperation(qMFadeOperation)
        {
            targetObject = tButton,
            delay        = 0.45f
        };
        AnimationOperation tScaleOperation = new AnimationOperation(qMScaleOperation)
        {
            targetObject = tButton
        };
        AnimationOperation tActivateOperation = new AnimationOperation(qMActivateOperation)
        {
            targetObject = tButton
        };
        AnimationSequence trainingButtonSequence = new AnimationSequence(RelayRoutine);

        trainingButtonSequence.AddOperation(tFadeOperation, tScaleOperation, tActivateOperation);

        GameObject         seButton        = settingsButton.gameObject;
        AnimationOperation seFadeOperation = new AnimationOperation(qMFadeOperation)
        {
            targetObject = seButton,
            delay        = 0.6f
        };
        AnimationOperation seScaleOperation = new AnimationOperation(qMScaleOperation)
        {
            targetObject = seButton
        };
        AnimationOperation seActivateOperation = new AnimationOperation(qMActivateOperation)
        {
            targetObject = seButton
        };
        AnimationSequence settingsButtonSequence = new AnimationSequence(RelayRoutine);

        settingsButtonSequence.AddOperation(seFadeOperation, seScaleOperation, seActivateOperation);

        GameObject         stButton        = statisticsButton.gameObject;
        AnimationOperation stFadeOperation = new AnimationOperation(qMFadeOperation)
        {
            targetObject = stButton,
            delay        = 0.75f
        };
        AnimationOperation stScaleOperation = new AnimationOperation(qMScaleOperation)
        {
            targetObject = stButton
        };
        AnimationOperation stActivateOperation = new AnimationOperation(qMActivateOperation)
        {
            targetObject = stButton
        };

        statisticsButtonSequence = new AnimationSequence(RelayRoutine);
        statisticsButtonSequence.AddOperation(stFadeOperation, stScaleOperation, stActivateOperation);
        statisticsButtonSequence.OnFinished(() => trainingButton.Select());

        quickMatchButtonSequence.Play();
        careerButtonSequence.Play();
        onlineButtonSequence.Play();
        trainingButtonSequence.Play();
        settingsButtonSequence.Play();
        statisticsButtonSequence.Play();

        statisticsButton.CanBePressed = true;
        quickMatchButton.CanBePressed = true;
        careerButton.CanBePressed     = true;
        onlineButton.CanBePressed     = true;
        trainingButton.CanBePressed   = true;
        settingsButton.CanBePressed   = true;

        Utility.SetCursor(true, CursorLockMode.None);
    }