public unsafe void Compute(CompressedTimeSpan newTime, AnimationClipResult result)
        {
            fixed (byte* structures = result.Data)
            {
                // Update factors
                for (int index = 0; index < Channels.Count; index++)
                {
                    // For now, objects are not supported, so treat everything as a blittable struct.
                    var channel = Channels.Items[index];

                    if (channel.BlendType == AnimationBlender.BlendType.Object)
                    {
                        // Non-blittable (note: 0.0f representation is 0 and that's what UpdateEngine checks against)
                        result.Objects[channel.Offset].Condition = *(int*)&channel.Factor;
                    }
                    else
                    {
                        // Blittable
                        var structureStart = (float*)(structures + channel.Offset);

                        // Write a float specifying channel factor (1 if exists, 0 if doesn't exist)
                        *structureStart = channel.Factor;
                    }
                }

                // Update values
                foreach (var curveEvaluatorGroup in curveEvaluatorGroups)
                {
                    curveEvaluatorGroup.Evaluate(newTime, (IntPtr)structures, result.Objects);
                }
            }
        }
Пример #2
0
        public void TestDiscontinuity()
        {
            var animationChannel = new AnimationChannel();

            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.Zero, Value = 0.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(1.0), Value = 0.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(1.0), Value = 0.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(1.0), Value = 1.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(1.0), Value = 1.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(2.0), Value = 1.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(2.0), Value = 1.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(2.0), Value = 0.0f
            });
            animationChannel.KeyFrames.Add(new KeyFrameData <float> {
                Time = CompressedTimeSpan.FromSeconds(2.0), Value = 0.0f
            });

            var evaluator = new AnimationChannel.Evaluator(animationChannel.KeyFrames);

            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(0.0)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(0.999999)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(1.0)), Is.EqualTo(1.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(1.000001)), Is.EqualTo(1.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(1.999999)), Is.EqualTo(1.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(2.0)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(2.000001)), Is.EqualTo(0.0f));
            Assert.That(evaluator.Evaluate(CompressedTimeSpan.FromSeconds(2.5)), Is.EqualTo(0.0f));
        }
Пример #3
0
        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));
        }
Пример #4
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);
        }
Пример #5
0
            protected override Task <ResultStatus> DoCommandOverride(ICommandContext commandContext)
            {
                var    nodes = new List <SpriteStudioNode>();
                string modelName;

                if (!SpriteStudioXmlImport.ParseModel(AssetParameters.Source, nodes, out modelName))
                {
                    return(null);
                }

                var anims = new List <SpriteStudioAnim>();

                if (!SpriteStudioXmlImport.ParseAnimations(AssetParameters.Source, anims))
                {
                    return(null);
                }

                var assetManager = new AssetManager();

                var anim = anims.First(x => x.Name == AssetParameters.AnimationName);

                //Compile the animations
                var animation = new AnimationClip
                {
                    Duration   = TimeSpan.FromSeconds((1.0 / anim.Fps) * anim.FrameCount),
                    RepeatMode = AssetParameters.RepeatMode
                };

                foreach (var pair in anim.NodesData)
                {
                    var data     = pair.Value;
                    var nodeName = pair.Key;
                    if (data.Data.Count == 0)
                    {
                        continue;
                    }

                    if (data.Data.ContainsKey("POSX"))
                    {
                        var posxCurve = new AnimationCurve <float>();
                        animation.AddCurve("posx[" + nodeName + "]", posxCurve);
                        posxCurve.InterpolationType = data.Data["POSX"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["POSX"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            posxCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("POSY"))
                    {
                        var posyCurve = new AnimationCurve <float>();
                        animation.AddCurve("posy[" + nodeName + "]", posyCurve);
                        posyCurve.InterpolationType = data.Data["POSY"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["POSY"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            posyCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("ROTZ"))
                    {
                        var anglCurve = new AnimationCurve <float>();
                        animation.AddCurve("rotz[" + nodeName + "]", anglCurve);
                        anglCurve.InterpolationType = data.Data["ROTZ"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["ROTZ"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = MathUtil.DegreesToRadians(float.Parse(nodeData["value"], CultureInfo.InvariantCulture));
                            anglCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("PRIO"))
                    {
                        var prioCurve = new AnimationCurve <int>();
                        animation.AddCurve("prio[" + nodeName + "]", prioCurve);
                        prioCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["PRIO"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            prioCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("SCLX"))
                    {
                        var scaxCurve = new AnimationCurve <float>();
                        animation.AddCurve("sclx[" + nodeName + "]", scaxCurve);
                        scaxCurve.InterpolationType = data.Data["SCLX"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["SCLX"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            scaxCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("SCLY"))
                    {
                        var scayCurve = new AnimationCurve <float>();
                        animation.AddCurve("scly[" + nodeName + "]", scayCurve);
                        scayCurve.InterpolationType = data.Data["SCLY"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["SCLY"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            scayCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("ALPH"))
                    {
                        var tranCurve = new AnimationCurve <float>();
                        animation.AddCurve("alph[" + nodeName + "]", tranCurve);
                        tranCurve.InterpolationType = data.Data["ALPH"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["ALPH"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            tranCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("HIDE"))
                    {
                        var hideCurve = new AnimationCurve <int>();
                        animation.AddCurve("hide[" + nodeName + "]", hideCurve);
                        hideCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["HIDE"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            hideCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("FLPH"))
                    {
                        var flphCurve = new AnimationCurve <int>();
                        animation.AddCurve("flph[" + nodeName + "]", flphCurve);
                        flphCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["FLPH"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            flphCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("FLPV"))
                    {
                        var flpvCurve = new AnimationCurve <int>();
                        animation.AddCurve("flpv[" + nodeName + "]", flpvCurve);
                        flpvCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["FLPV"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            flpvCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("CELL"))
                    {
                        var cellCurve = new AnimationCurve <int>();
                        animation.AddCurve("cell[" + nodeName + "]", cellCurve);
                        cellCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["CELL"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            cellCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("COLV"))
                    {
                        var colvCurve = new AnimationCurve <Vector4>();
                        animation.AddCurve("colv[" + nodeName + "]", colvCurve);
                        colvCurve.InterpolationType = AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["COLV"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var color = new Color4(Color.FromBgra(int.Parse(nodeData["value"], CultureInfo.InvariantCulture)));
                            color = colorSpace == ColorSpace.Linear ? color.ToLinear() : color;
                            colvCurve.KeyFrames.Add(new KeyFrameData <Vector4>(time, color.ToVector4()));
                        }
                    }

                    if (data.Data.ContainsKey("COLB"))
                    {
                        var colbCurve = new AnimationCurve <int>();
                        animation.AddCurve("colb[" + nodeName + "]", colbCurve);
                        colbCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["COLB"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            colbCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("COLF"))
                    {
                        var colfCurve = new AnimationCurve <float>();
                        animation.AddCurve("colf[" + nodeName + "]", colfCurve);
                        colfCurve.InterpolationType = data.Data["COLF"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["COLF"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            colfCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }
                }

                animation.Optimize();

                assetManager.Save(Url, animation);

                return(Task.FromResult(ResultStatus.Successful));
            }
Пример #6
0
            protected override Task <ResultStatus> DoCommandOverride(ICommandContext commandContext)
            {
                var    nodes = new List <SpriteStudioNode>();
                string modelName;

                if (!SpriteStudioXmlImport.ParseModel(Parameters.Source, nodes, out modelName))
                {
                    return(null);
                }

                var anims = new List <SpriteStudioAnim>();

                if (!SpriteStudioXmlImport.ParseAnimations(Parameters.Source, anims))
                {
                    return(null);
                }

                var assetManager = new ContentManager();

                var anim = anims.First(x => x.Name == Parameters.AnimationName);

                //Compile the animations
                var animation = new AnimationClip
                {
                    Duration   = TimeSpan.FromSeconds((1.0 / anim.Fps) * anim.FrameCount),
                    RepeatMode = Parameters.RepeatMode
                };

                var nodeMapping = nodes.Select((x, i) => new { Name = x.Name, Index = i }).ToDictionary(x => x.Name, x => x.Index);

                foreach (var pair in anim.NodesData)
                {
                    int nodeIndex;
                    if (!nodeMapping.TryGetValue(pair.Key, out nodeIndex))
                    {
                        continue;
                    }

                    var data = pair.Value;
                    if (data.Data.Count == 0)
                    {
                        continue;
                    }

                    var keyPrefix = $"[SpriteStudioComponent.Key].Nodes[{nodeIndex}]";

                    if (data.Data.ContainsKey("POSX"))
                    {
                        var posxCurve = new AnimationCurve <float>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.Position)}.{nameof(Vector2.X)}", posxCurve);
                        posxCurve.InterpolationType = data.Data["POSX"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["POSX"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            posxCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("POSY"))
                    {
                        var posyCurve = new AnimationCurve <float>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.Position)}.{nameof(Vector2.Y)}", posyCurve);
                        posyCurve.InterpolationType = data.Data["POSY"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["POSY"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            posyCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("ROTZ"))
                    {
                        var anglCurve = new AnimationCurve <float>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.RotationZ)}", anglCurve);
                        anglCurve.InterpolationType = data.Data["ROTZ"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["ROTZ"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = MathUtil.DegreesToRadians(float.Parse(nodeData["value"], CultureInfo.InvariantCulture));
                            anglCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("PRIO"))
                    {
                        var prioCurve = new AnimationCurve <int>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.Priority)}", prioCurve);
                        prioCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["PRIO"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            prioCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("SCLX"))
                    {
                        var scaxCurve = new AnimationCurve <float>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.Scale)}.{nameof(Vector2.X)}", scaxCurve);
                        scaxCurve.InterpolationType = data.Data["SCLX"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["SCLX"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            scaxCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("SCLY"))
                    {
                        var scayCurve = new AnimationCurve <float>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.Scale)}.{nameof(Vector2.Y)}", scayCurve);
                        scayCurve.InterpolationType = data.Data["SCLY"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["SCLY"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            scayCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("ALPH"))
                    {
                        var tranCurve = new AnimationCurve <float>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.Transparency)}", tranCurve);
                        tranCurve.InterpolationType = data.Data["ALPH"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["ALPH"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            tranCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("HIDE"))
                    {
                        var hideCurve = new AnimationCurve <int>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.Hide)}", hideCurve);
                        hideCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["HIDE"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            hideCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("FLPH"))
                    {
                        var flphCurve = new AnimationCurve <int>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.HFlipped)}", flphCurve);
                        flphCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["FLPH"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            flphCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("FLPV"))
                    {
                        var flpvCurve = new AnimationCurve <int>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.VFlipped)}", flpvCurve);
                        flpvCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["FLPV"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            flpvCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("CELL"))
                    {
                        var cellCurve = new AnimationCurve <int>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.SpriteId)}", cellCurve);
                        cellCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["CELL"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            cellCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("COLV"))
                    {
                        var colvCurve = new AnimationCurve <Vector4>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.BlendColor)}", colvCurve);
                        colvCurve.InterpolationType = AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["COLV"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var color = new Color4(Color.FromBgra(int.Parse(nodeData["value"], CultureInfo.InvariantCulture)));
                            color = colorSpace == ColorSpace.Linear ? color.ToLinear() : color;
                            colvCurve.KeyFrames.Add(new KeyFrameData <Vector4>(time, color.ToVector4()));
                        }
                    }

                    if (data.Data.ContainsKey("COLB"))
                    {
                        var colbCurve = new AnimationCurve <int>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.BlendType)}", colbCurve);
                        colbCurve.InterpolationType = AnimationCurveInterpolationType.Constant;

                        foreach (var nodeData in data.Data["COLB"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = int.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            colbCurve.KeyFrames.Add(new KeyFrameData <int>(time, value));
                        }
                    }

                    if (data.Data.ContainsKey("COLF"))
                    {
                        var colfCurve = new AnimationCurve <float>();
                        animation.AddCurve($"{keyPrefix}.{nameof(SpriteStudioNodeState.BlendFactor)}", colfCurve);
                        colfCurve.InterpolationType = data.Data["COLF"].Any(x => x["curve"] != "linear") ? AnimationCurveInterpolationType.Cubic : AnimationCurveInterpolationType.Linear;

                        foreach (var nodeData in data.Data["COLF"])
                        {
                            var time  = CompressedTimeSpan.FromSeconds((1.0 / anim.Fps) * int.Parse(nodeData["time"], CultureInfo.InvariantCulture));
                            var value = float.Parse(nodeData["value"], CultureInfo.InvariantCulture);
                            colfCurve.KeyFrames.Add(new KeyFrameData <float>(time, value));
                        }
                    }
                }

                animation.Optimize();

                assetManager.Save(Url, animation);

                return(Task.FromResult(ResultStatus.Successful));
            }
Пример #7
0
 public abstract void Evaluate(CompressedTimeSpan newTime, IntPtr data, UpdateObjectData[] objects);
        public unsafe void AddCurveValues(CompressedTimeSpan newTime, AnimationClipResult result)
        {
            fixed (byte* structures = result.Data)
            {
                for (int index = 0; index < Channels.Count; index++)
                {
                    var channel = Channels.Items[index];

                    // For now, objects are not supported, so treat everything as a blittable struct.
                    channel.Curve.AddValue(newTime, (IntPtr)(structures + channel.Offset + sizeof(float)));
                }
            }
        }
Пример #9
0
        public unsafe void Compute(CompressedTimeSpan newTime, AnimationClipResult result)
        {
            fixed (byte* structures = result.Data)
            {
                // Update factors
                for (int index = 0; index < Channels.Count; index++)
                {
                    // For now, objects are not supported, so treat everything as a blittable struct.
                    var channel = Channels.Items[index];

                    var structureStart = (float*)(structures + channel.Offset);

                    // Write a float specifying channel factor (1 if exists, 0 if doesn't exist)
                    *structureStart = channel.Factor;
                }

                if (curveEvaluatorOptimizedFloat != null)
                {
                    curveEvaluatorOptimizedFloat.Evaluate(newTime, (IntPtr)structures);
                }
                
                if (curveEvaluatorOptimizedVector3 != null)
                {
                    curveEvaluatorOptimizedVector3.Evaluate(newTime, (IntPtr)structures);
                }

                if (curveEvaluatorOptimizedQuaternion != null)
                {
                    curveEvaluatorOptimizedQuaternion.Evaluate(newTime, (IntPtr)structures);
                }

                // Write interpolated data
                curveEvaluatorFloat.Evaluate(newTime, (IntPtr)structures);
                curveEvaluatorVector3.Evaluate(newTime, (IntPtr)structures);
                curveEvaluatorQuaternion.Evaluate(newTime, (IntPtr)structures);
            }
        }
Пример #10
0
 /// <summary>
 /// Writes a new value at the end of the curve (used for building curves).
 /// It should be done in increasing order as it will simply add a new key at the end of <see cref="AnimationCurve{T}.KeyFrames"/>.
 /// </summary>
 /// <param name="newTime">The new time.</param>
 /// <param name="location">The location.</param>
 public abstract void AddValue(CompressedTimeSpan newTime, IntPtr location);