public override void Start()
        {
            base.Start();

            if (AnimationComponent == null)
                throw new InvalidOperationException("The animation component is not set");

            if (AnimationIdle == null)
                throw new InvalidOperationException("Idle animation is not set");

            if (AnimationWalk == null)
                throw new InvalidOperationException("Walking animation is not set");

            if (AnimationShoot == null)
                throw new InvalidOperationException("Shooting animation is not set");

            if (AnimationReload == null)
                throw new InvalidOperationException("Reloading animation is not set");

            // By setting a custom blend tree builder we can override the default behavior of the animation system
            //  Instead, BuildBlendTree(FastList<AnimationOperation> blendStack) will be called each frame
            AnimationComponent.BlendTreeBuilder = this;

            animEvaluatorIdle   = AnimationComponent.Blender.CreateEvaluator(AnimationIdle);
            animEvaluatorWalk   = AnimationComponent.Blender.CreateEvaluator(AnimationWalk);
            animEvaluatorShoot  = AnimationComponent.Blender.CreateEvaluator(AnimationShoot);
            animEvaluatorReload = AnimationComponent.Blender.CreateEvaluator(AnimationReload);

            currentEvaluator    = animEvaluatorIdle;
            currentClip         = AnimationIdle;
        }
        public void TestAnimationClip()
        {
            var clip = new AnimationClip
            {
                Duration = TimeSpan.FromSeconds(2.0f),
                RepeatMode = AnimationRepeatMode.LoopInfinite
            };

            var testCurve = new AnimationCurve<float>();
            clip.AddCurve("posx[TestNode]", testCurve);
            testCurve.InterpolationType = AnimationCurveInterpolationType.Linear;

            var time = CompressedTimeSpan.FromSeconds(0.0f);
            var value = 0.0f;
            var frame0 = new KeyFrameData<float>(time, value);
            testCurve.KeyFrames.Add(frame0);

            time = CompressedTimeSpan.FromSeconds(1.0f);
            value = 1.0f;
            var frame1 = new KeyFrameData<float>(time, value);
            testCurve.KeyFrames.Add(frame1);

            clip.Optimize();

            var optimizedCurvesFloat = (AnimationData<float>)clip.OptimizedAnimationDatas.First();

            //we should have 3 frames at this point. the last one will be added by the optimization process...
            Assert.That(optimizedCurvesFloat.AnimationSortedValueCount, Is.EqualTo(1));
            //And 2 initial frames            
            Assert.That(optimizedCurvesFloat.AnimationInitialValues[0].Value1, Is.EqualTo(frame0));
            Assert.That(optimizedCurvesFloat.AnimationInitialValues[0].Value2, Is.EqualTo(frame1));
            Assert.That(optimizedCurvesFloat.AnimationSortedValues.Length, Is.EqualTo(1));
            Assert.That(optimizedCurvesFloat.AnimationSortedValues[0].Length, Is.EqualTo(1));
            Assert.That(optimizedCurvesFloat.AnimationSortedValues[0][0].Value, Is.EqualTo(frame1));
        }
Beispiel #3
0
        protected override async Task LoadContent()
        {
            await base.LoadContent();

            var knightModel = Content.Load<Model>("knight Model");
            knight = new Entity { new ModelComponent { Model = knightModel } };
            knight.Transform.Position = new Vector3(0, 0f, 0f);
            var animationComponent = knight.GetOrCreate<AnimationComponent>();
            animationComponent.Animations.Add("Run", Content.Load<AnimationClip>("knight Run"));
            animationComponent.Animations.Add("Idle", Content.Load<AnimationClip>("knight Idle"));

            // We will test both non-optimized and optimized clips
            megalodonClip = CreateModelChangeAnimation(new ProceduralModelDescriptor(new CubeProceduralModel { Size = Vector3.One, MaterialInstance = { Material = knightModel.Materials[0].Material } }).GenerateModel(Services));
            knightOptimizedClip = CreateModelChangeAnimation(Content.Load<Model>("knight Model"));
            knightOptimizedClip.Optimize();

            animationComponent.Animations.Add("ChangeModel1", megalodonClip);
            animationComponent.Animations.Add("ChangeModel2", knightOptimizedClip);

            Scene.Entities.Add(knight);

            camera = new TestCamera();
            CameraComponent = camera.Camera;
            Script.Add(camera);

            camera.Position = new Vector3(6.0f, 2.5f, 1.5f);
            camera.SetTarget(knight, true);
        }
        internal void Initialize(AnimationClip clip, List<AnimationBlender.Channel> channels)
        {
            this.BlenderChannels = channels;
            this.clip = clip;
            clip.Freeze();

            // If there are optimized curve data, instantiate (first time) and initialize appropriate evaluators
            if (clip.OptimizedAnimationDatas != null)
            {
                foreach (var optimizedData in clip.OptimizedAnimationDatas)
                {
                    var optimizedEvaluatorGroup = curveEvaluatorGroups.OfType<AnimationCurveEvaluatorOptimizedGroup>().FirstOrDefault(x => x.ElementType == optimizedData.ElementType);
                    if (optimizedEvaluatorGroup == null)
                    {
                        optimizedEvaluatorGroup = optimizedData.CreateEvaluator();
                        curveEvaluatorGroups.Add(optimizedEvaluatorGroup);
                    }
                    optimizedEvaluatorGroup.Initialize(optimizedData);
                }
            }

            // Add already existing channels
            for (int index = 0; index < channels.Count; index++)
            {
                var channel = channels[index];
                AddChannel(ref channel);
            }
        }
        protected override async Task LoadContent()
        {
            await base.LoadContent();

            var knightModel = Asset.Load<Model>("knight Model");
            knight = new Entity { new ModelComponent { Model = knightModel } };
            knight.Transform.Position = new Vector3(0, 0f, 0f);
            var animationComponent = knight.GetOrCreate<AnimationComponent>();
            animationComponent.Animations.Add("Run", Asset.Load<AnimationClip>("knight Run"));
            animationComponent.Animations.Add("Idle", Asset.Load<AnimationClip>("knight Idle"));

            // We will test both non-optimized and optimized clips
            megalodonClip = CreateModelChangeAnimation(new ProceduralModelDescriptor(new CubeProceduralModel { Size = Vector3.One, MaterialInstance = { Material = knightModel.Materials[0].Material } }).GenerateModel(Services));
            knightOptimizedClip = CreateModelChangeAnimation(Asset.Load<Model>("knight Model"));
            knightOptimizedClip.Optimize();

            animationComponent.Animations.Add("ChangeModel1", megalodonClip);
            animationComponent.Animations.Add("ChangeModel2", knightOptimizedClip);

            Scene.Entities.Add(knight);

            camera = new TestCamera();
            CameraComponent = camera.Camera;
            Script.Add(camera);

            LightingKeys.EnableFixedAmbientLight(GraphicsDevice.Parameters, true);
            GraphicsDevice.Parameters.Set(EnvironmentLightKeys.GetParameterKey(LightSimpleAmbientKeys.AmbientLight, 0), (Color3)Color.White);

            camera.Position = new Vector3(6.0f, 2.5f, 1.5f);
            camera.SetTarget(knight, true);
        }
Beispiel #6
0
        private AnimationClip CreateModelChangeAnimation(Model model)
        {
            var changeMegalodonAnimClip = new AnimationClip();
            var modelCurve = new AnimationCurve<object>();
            modelCurve.KeyFrames.Add(new KeyFrameData<object>(CompressedTimeSpan.Zero, model));
            changeMegalodonAnimClip.AddCurve("[ModelComponent.Key].Model", modelCurve);

            return changeMegalodonAnimClip;
        }
        internal void Cleanup()
        {
            foreach (var curveEvaluatorGroup in curveEvaluatorGroups)
            {
                curveEvaluatorGroup.Cleanup();
            }

            Channels.Clear();
            BlenderChannels = null;
            clip = null;
        }
        public override void Start()
        {
            base.Start();

            if (AnimationComponent == null)
                throw new InvalidOperationException("The animation component is not set");

            if (AnimationIdle == null)
                throw new InvalidOperationException("Idle animation is not set");

            if (AnimationWalk == null)
                throw new InvalidOperationException("Walking animation is not set");

            if (AnimationRun == null)
                throw new InvalidOperationException("Running animation is not set");

            if (AnimationJumpStart == null)
                throw new InvalidOperationException("Jumping animation is not set");

            if (AnimationJumpMid == null)
                throw new InvalidOperationException("Airborne animation is not set");

            if (AnimationJumpEnd == null)
                throw new InvalidOperationException("Landing animation is not set");

            // By setting a custom blend tree builder we can override the default behavior of the animation system
            //  Instead, BuildBlendTree(FastList<AnimationOperation> blendStack) will be called each frame
            AnimationComponent.BlendTreeBuilder = this;

            animEvaluatorIdle = AnimationComponent.Blender.CreateEvaluator(AnimationIdle);
            animEvaluatorWalk = AnimationComponent.Blender.CreateEvaluator(AnimationWalk);
            animEvaluatorRun = AnimationComponent.Blender.CreateEvaluator(AnimationRun);
            animEvaluatorJumpStart = AnimationComponent.Blender.CreateEvaluator(AnimationJumpStart);
            animEvaluatorJumpMid = AnimationComponent.Blender.CreateEvaluator(AnimationJumpMid);
            animEvaluatorJumpEnd = AnimationComponent.Blender.CreateEvaluator(AnimationJumpEnd);

            // Initial walk lerp
            walkLerpFactor = 0;
            animEvaluatorWalkLerp1 = animEvaluatorIdle;
            animEvaluatorWalkLerp2 = animEvaluatorWalk;
            animationClipWalkLerp1 = AnimationIdle;
            animationClipWalkLerp2 = AnimationWalk;
        }
        private void UpdateWalking()
        {
            if (runSpeed < WalkThreshold)
            {
                walkLerpFactor = runSpeed / WalkThreshold;
                walkLerpFactor = (float)Math.Sqrt(walkLerpFactor);  // Idle-Walk blend looks really werid, so skew the factor towards walking
                animEvaluatorWalkLerp1 = animEvaluatorIdle;
                animEvaluatorWalkLerp2 = animEvaluatorWalk;
                animationClipWalkLerp1 = AnimationIdle;
                animationClipWalkLerp2 = AnimationWalk;
            }
            else
            {
                walkLerpFactor = (runSpeed - WalkThreshold) / (1.0f - WalkThreshold);
                animEvaluatorWalkLerp1 = animEvaluatorWalk;
                animEvaluatorWalkLerp2 = animEvaluatorRun;
                animationClipWalkLerp1 = AnimationWalk;
                animationClipWalkLerp2 = AnimationRun;
            }

            // Use DrawTime rather than UpdateTime
            var time = Game.DrawTime;
            // This update function will account for animation with different durations, keeping a current time relative to the blended maximum duration
            long blendedMaxDuration = 0;
            blendedMaxDuration =
                (long)MathUtil.Lerp(animationClipWalkLerp1.Duration.Ticks, animationClipWalkLerp2.Duration.Ticks, walkLerpFactor);

            var currentTicks = TimeSpan.FromTicks((long)(currentTime * blendedMaxDuration));

            currentTicks = blendedMaxDuration == 0
                ? TimeSpan.Zero
                : TimeSpan.FromTicks((currentTicks.Ticks + (long)(time.Elapsed.Ticks * TimeFactor)) %
                                     blendedMaxDuration);

            currentTime = ((double)currentTicks.Ticks / (double)blendedMaxDuration);
        }
Beispiel #10
0
        internal bool attached; // Is it part of a AnimationComponent.PlayingAnimations collection?

        internal PlayingAnimation(string name, AnimationClip clip) : this()
        {
            Name = name;
            Clip = clip;
            RepeatMode = Clip.RepeatMode;
        }
        private unsafe object ExportAnimation(ICommandContext commandContext, AssetManager assetManager)
        {
            // Read from model file
            var modelSkeleton = LoadSkeleton(commandContext, assetManager); // we get model skeleton to compare it to real skeleton we need to map to
            var animationClips = LoadAnimation(commandContext, assetManager);
            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 = assetManager.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;
        }
        public AnimationClipEvaluator CreateEvaluator(AnimationClip clip)
        {
            // Check if this clip has already been used
            if (clips.Add(clip))
            {
                // If new clip, let's scan its channel to add new ones.
                foreach (var curve in clip.Channels)
                {
                    Channel channel;
                    if (channelsByName.TryGetValue(curve.Key, out channel))
                    {
                        // TODO: Check if channel matches
                    }
                    else
                    {
                        // New channel, add it to every evaluator

                        // Find blend type
                        BlendType blendType;
                        var elementType = curve.Value.ElementType;

                        if (elementType == typeof(Quaternion))
                        {
                            blendType = BlendType.Quaternion;
                        }
                        else if (elementType == typeof(float))
                        {
                            blendType = BlendType.Float1;
                        }
                        else if (elementType == typeof(Vector2))
                        {
                            blendType = BlendType.Float2;
                        }
                        else if (elementType == typeof(Vector3))
                        {
                            blendType = BlendType.Float3;
                        }
                        else if (elementType == typeof(Vector4))
                        {
                            blendType = BlendType.Float4;
                        }
                        else
                        {
                            blendType = BlittableHelper.IsBlittable(elementType) ? BlendType.Blit : BlendType.Object;
                        }

                        // Create channel structure
                        channel.BlendType = blendType;
                        channel.Offset = blendType == BlendType.Object ? objectsSize : structureSize;
                        channel.PropertyName = curve.Key;
                        channel.Size = curve.Value.ElementSize;

                        // Add channel
                        channelsByName.Add(channel.PropertyName, channel);
                        channels.Add(channel);

                        if (blendType == BlendType.Object)
                        {
                            objectsSize++;
                        }
                        else
                        {
                            // Update new structure size
                            // We also reserve space for a float that will specify channel existence and factor in case of subtree blending
                            structureSize += sizeof(float) + channel.Size;
                        }

                        // Add new channel update info to every evaluator
                        // TODO: Maybe it's better lazily done? (avoid need to store list of all evaluators)
                        foreach (var currentEvaluator in evaluators)
                        {
                            currentEvaluator.AddChannel(ref channel);
                        }
                    }
                }
            }

            // Update results to fit the new data size
            lock (availableResultsPool)
            {
                foreach (var result in availableResultsPool)
                {
                    if (result.DataSize < structureSize)
                    {
                        result.DataSize = structureSize;
                        result.Data = new byte[structureSize];
                    }
                }
            }

            // Create evaluator and store it in list of instantiated evaluators
            AnimationClipEvaluator evaluator;
            lock (evaluatorPool)
            {
                if (evaluatorPool.Count > 0)
                {
                    evaluator = evaluatorPool.Pop();
                }
                else
                {
                    evaluator = new AnimationClipEvaluator();
                }
            }
            
            evaluator.Initialize(clip, channels);
            evaluators.Add(evaluator);

            return evaluator;
        }
        private void SwitchToDefaultState()
        {
            currentTime = (state == defaultState) ? currentTime : 0;
            state = defaultState;

            if (state == AnimationState.Idle)
            {
                currentClip = AnimationIdle;
                currentEvaluator = animEvaluatorIdle;
            }
            else
            {
                currentClip = AnimationWalk;
                currentEvaluator = animEvaluatorWalk;
            }
        }
        public override void Update()
        {
            runSpeedEvent.TryReceive(out runSpeed);
            defaultState = (runSpeed > 0.15f) ? AnimationState.Walking : AnimationState.Idle;

            WeaponFiredResult weaponResult;
            var didFire = weaponFiredEvent.TryReceive(out weaponResult);

            bool isReloading;
            var didReload = isReloadingEvent.TryReceive(out isReloading);
            isReloading |= didReload;

            // Update current animation
            var currentTicks = TimeSpan.FromTicks((long)(currentTime * currentClip.Duration.Ticks));
            var updatedTicks = currentTicks.Ticks + (long)(Game.DrawTime.Elapsed.Ticks * TimeFactor);

            var currentClipFinished = (updatedTicks >= currentClip.Duration.Ticks);

            currentTicks = TimeSpan.FromTicks(updatedTicks % currentClip.Duration.Ticks);
            currentTime = ((double)currentTicks.Ticks / (double)currentClip.Duration.Ticks);

            // State change if necessary
            if (isReloading)
            {
                if (state != AnimationState.Reloading)
                {
                    currentTime = 0;
                    state = AnimationState.Reloading;
                    currentClip = AnimationReload;
                    currentEvaluator = animEvaluatorReload;
                }
            }
            else
            if (didFire)
            {
                if (state != AnimationState.Shooting)
                {
                    currentTime = 0;
                    state = AnimationState.Shooting;
                    currentClip = AnimationShoot;
                    currentEvaluator = animEvaluatorShoot;
                }
            }
            else
            if (currentClipFinished)
            {
                SwitchToDefaultState();
            }
            else
            if ((state == AnimationState.Idle || state == AnimationState.Walking) && state != defaultState)
            {
                SwitchToDefaultState();
            }
        }
        public AnimationClipEvaluator CreateEvaluator(AnimationClip clip)
        {
            // Check if this clip has already been used
            if (clips.Add(clip))
            {
                // If new clip, let's scan its channel to add new ones.
                foreach (var curve in clip.Channels)
                {
                    Channel channel;
                    if (channelsByName.TryGetValue(curve.Key, out channel))
                    {
                        // TODO: Check if channel matches
                    }
                    else
                    {
                        // New channel, add it to every evaluator

                        // Find blend type
                        BlendType blendType;
                        var       elementType = curve.Value.ElementType;

                        if (elementType == typeof(Quaternion))
                        {
                            blendType = BlendType.Quaternion;
                        }
                        else if (elementType == typeof(float))
                        {
                            blendType = BlendType.Float1;
                        }
                        else if (elementType == typeof(Vector2))
                        {
                            blendType = BlendType.Float2;
                        }
                        else if (elementType == typeof(Vector3))
                        {
                            blendType = BlendType.Float3;
                        }
                        else if (elementType == typeof(Vector4))
                        {
                            blendType = BlendType.Float4;
                        }
                        else
                        {
                            blendType = BlittableHelper.IsBlittable(elementType) ? BlendType.Blit : BlendType.Object;
                        }

                        // Create channel structure
                        channel.BlendType    = blendType;
                        channel.Offset       = blendType == BlendType.Object ? objectsSize : structureSize;
                        channel.PropertyName = curve.Key;
                        channel.Size         = curve.Value.ElementSize;

                        // Add channel
                        channelsByName.Add(channel.PropertyName, channel);
                        channels.Add(channel);

                        if (blendType == BlendType.Object)
                        {
                            objectsSize++;
                        }
                        else
                        {
                            // Update new structure size
                            // We also reserve space for a float that will specify channel existence and factor in case of subtree blending
                            structureSize += sizeof(float) + channel.Size;
                        }

                        // Add new channel update info to every evaluator
                        // TODO: Maybe it's better lazily done? (avoid need to store list of all evaluators)
                        foreach (var currentEvaluator in evaluators)
                        {
                            currentEvaluator.AddChannel(ref channel);
                        }
                    }
                }
            }

            // Update results to fit the new data size
            lock (availableResultsPool)
            {
                foreach (var result in availableResultsPool)
                {
                    if (result.DataSize < structureSize)
                    {
                        result.DataSize = structureSize;
                        result.Data     = new byte[structureSize];
                    }
                }
            }

            // Create evaluator and store it in list of instantiated evaluators
            AnimationClipEvaluator evaluator;

            lock (evaluatorPool)
            {
                if (evaluatorPool.Count > 0)
                {
                    evaluator = evaluatorPool.Pop();
                }
                else
                {
                    evaluator = new AnimationClipEvaluator();
                }
            }

            evaluator.Initialize(clip, channels);
            evaluators.Add(evaluator);

            return(evaluator);
        }