/// <summary>
        /// Serialize animation data into a stream.
        /// </summary>
        public void ToStream(Stream stream, float startTime)
        {
            PoseCurves defaultCurves = new PoseCurves();

            var writer = new BinaryWriter(stream);

            InputAnimationSerializationUtils.WriteHeader(writer);

            PoseCurvesToStream(writer, cameraCurves, startTime);

            InputAnimationSerializationUtils.WriteBoolCurve(writer, handTrackedCurveLeft, startTime);
            InputAnimationSerializationUtils.WriteBoolCurve(writer, handTrackedCurveRight, startTime);
            InputAnimationSerializationUtils.WriteBoolCurve(writer, handPinchCurveLeft, startTime);
            InputAnimationSerializationUtils.WriteBoolCurve(writer, handPinchCurveRight, startTime);

            for (int i = 0; i < jointCount; ++i)
            {
                if (!handJointCurvesLeft.TryGetValue((TrackedHandJoint)i, out PoseCurves curves))
                {
                    curves = defaultCurves;
                }
                PoseCurvesToStream(writer, curves, startTime);
            }
            for (int i = 0; i < jointCount; ++i)
            {
                if (!handJointCurvesRight.TryGetValue((TrackedHandJoint)i, out PoseCurves curves))
                {
                    curves = defaultCurves;
                }
                PoseCurvesToStream(writer, curves, startTime);
            }

            InputAnimationSerializationUtils.WriteMarkerList(writer, markers, startTime);
        }
        private static void PoseCurvesFromStream(BinaryReader reader, PoseCurves curves)
        {
            InputAnimationSerializationUtils.ReadFloatCurve(reader, curves.PositionX);
            InputAnimationSerializationUtils.ReadFloatCurve(reader, curves.PositionY);
            InputAnimationSerializationUtils.ReadFloatCurve(reader, curves.PositionZ);

            InputAnimationSerializationUtils.ReadFloatCurve(reader, curves.RotationX);
            InputAnimationSerializationUtils.ReadFloatCurve(reader, curves.RotationY);
            InputAnimationSerializationUtils.ReadFloatCurve(reader, curves.RotationZ);
            InputAnimationSerializationUtils.ReadFloatCurve(reader, curves.RotationW);
        }
        private static void PoseCurvesToStream(BinaryWriter writer, PoseCurves curves, float startTime)
        {
            InputAnimationSerializationUtils.WriteFloatCurve(writer, curves.PositionX, startTime);
            InputAnimationSerializationUtils.WriteFloatCurve(writer, curves.PositionY, startTime);
            InputAnimationSerializationUtils.WriteFloatCurve(writer, curves.PositionZ, startTime);

            InputAnimationSerializationUtils.WriteFloatCurve(writer, curves.RotationX, startTime);
            InputAnimationSerializationUtils.WriteFloatCurve(writer, curves.RotationY, startTime);
            InputAnimationSerializationUtils.WriteFloatCurve(writer, curves.RotationZ, startTime);
            InputAnimationSerializationUtils.WriteFloatCurve(writer, curves.RotationW, startTime);
        }
        private static void AddPoseKey(PoseCurves curves, float time, MixedRealityPose pose)
        {
            AddFloatKey(curves.PositionX, time, pose.Position.x);
            AddFloatKey(curves.PositionY, time, pose.Position.y);
            AddFloatKey(curves.PositionZ, time, pose.Position.z);

            AddFloatKey(curves.RotationX, time, pose.Rotation.x);
            AddFloatKey(curves.RotationY, time, pose.Rotation.y);
            AddFloatKey(curves.RotationZ, time, pose.Rotation.z);
            AddFloatKey(curves.RotationW, time, pose.Rotation.w);
        }
        public InputAnimation()
        {
            handTrackedCurveLeft  = new AnimationCurve();
            handTrackedCurveRight = new AnimationCurve();
            handPinchCurveLeft    = new AnimationCurve();
            handPinchCurveRight   = new AnimationCurve();
            handJointCurvesLeft   = new Dictionary <TrackedHandJoint, PoseCurves>();
            handJointCurvesRight  = new Dictionary <TrackedHandJoint, PoseCurves>();
            cameraCurves          = new PoseCurves();

            markers = new List <InputAnimationMarker>();
        }
        /// Add a keyframe for one hand joint.
        private void AddHandJointKey(float time, TrackedHandJoint joint, MixedRealityPose jointPose, Dictionary <TrackedHandJoint, PoseCurves> jointCurves, float positionThreshold, float rotationThreshold)
        {
            if (!jointCurves.TryGetValue(joint, out PoseCurves curves))
            {
                curves = new PoseCurves();
                jointCurves.Add(joint, curves);
            }

            AddPoseKeyFiltered(curves, time, jointPose, positionThreshold, rotationThreshold);

            duration = Mathf.Max(duration, time);
        }
        private static MixedRealityPose EvaluatePose(PoseCurves curves, float time)
        {
            float px = curves.PositionX.Evaluate(time);
            float py = curves.PositionY.Evaluate(time);
            float pz = curves.PositionZ.Evaluate(time);
            float rx = curves.RotationX.Evaluate(time);
            float ry = curves.RotationY.Evaluate(time);
            float rz = curves.RotationZ.Evaluate(time);
            float rw = curves.RotationW.Evaluate(time);

            var pose = new MixedRealityPose();

            pose.Position = new Vector3(px, py, pz);
            pose.Rotation = new Quaternion(rx, ry, rz, rw);
            pose.Rotation.Normalize();
            return(pose);
        }
        /// <summary>
        /// Deserialize animation data from a stream.
        /// </summary>
        public void FromStream(Stream stream)
        {
            var reader = new BinaryReader(stream);

            InputAnimationSerializationUtils.ReadHeader(reader, out int versionMajor, out int versionMinor);
            if (versionMajor != 1 || versionMinor != 0)
            {
                Debug.LogError("Only version 1.0 of input animation file format is supported.");
                return;
            }

            PoseCurvesFromStream(reader, cameraCurves);

            InputAnimationSerializationUtils.ReadBoolCurve(reader, handTrackedCurveLeft);
            InputAnimationSerializationUtils.ReadBoolCurve(reader, handTrackedCurveRight);
            InputAnimationSerializationUtils.ReadBoolCurve(reader, handPinchCurveLeft);
            InputAnimationSerializationUtils.ReadBoolCurve(reader, handPinchCurveRight);

            for (int i = 0; i < jointCount; ++i)
            {
                if (!handJointCurvesLeft.TryGetValue((TrackedHandJoint)i, out PoseCurves curves))
                {
                    curves = new PoseCurves();
                    handJointCurvesLeft.Add((TrackedHandJoint)i, curves);
                }
                PoseCurvesFromStream(reader, curves);
            }
            for (int i = 0; i < jointCount; ++i)
            {
                if (!handJointCurvesRight.TryGetValue((TrackedHandJoint)i, out PoseCurves curves))
                {
                    curves = new PoseCurves();
                    handJointCurvesRight.Add((TrackedHandJoint)i, curves);
                }
                PoseCurvesFromStream(reader, curves);
            }

            InputAnimationSerializationUtils.ReadMarkerList(reader, markers);

            ComputeDuration();
        }
 /// <summary>
 /// Make sure the pose animation curves for the given hand joint exist.
 /// </summary>
 public PoseCurves CreateHandJointCurves(Handedness handedness, TrackedHandJoint joint)
 {
     if (handedness == Handedness.Left)
     {
         if (!handJointCurvesLeft.TryGetValue(joint, out PoseCurves curves))
         {
             curves = new PoseCurves();
             handJointCurvesLeft.Add(joint, curves);
         }
         return(curves);
     }
     else if (handedness == Handedness.Right)
     {
         if (!handJointCurvesRight.TryGetValue(joint, out PoseCurves curves))
         {
             curves = new PoseCurves();
             handJointCurvesRight.Add(joint, curves);
         }
         return(curves);
     }
     return(null);
 }
 /// Add a pose keyframe to an animation curve.
 /// Keys are only added if the value changes sufficiently.
 private static void AddPoseKeyFiltered(PoseCurves curves, float time, MixedRealityPose pose, float positionThreshold, float rotationThreshold)
 {
     AddPositionKeyFiltered(curves.PositionX, curves.PositionY, curves.PositionZ, time, pose.Position, positionThreshold);
     AddRotationKeyFiltered(curves.RotationX, curves.RotationY, curves.RotationZ, curves.RotationW, time, pose.Rotation, rotationThreshold);
 }
 /// <summary>
 /// Get animation curves for the pose of the given hand joint, if they exist.
 /// </summary>
 public bool TryGetHandJointCurves(Handedness handedness, TrackedHandJoint joint, out PoseCurves curves)
 {
     if (handedness == Handedness.Left)
     {
         return(handJointCurvesLeft.TryGetValue(joint, out curves));
     }
     else if (handedness == Handedness.Right)
     {
         return(handJointCurvesRight.TryGetValue(joint, out curves));
     }
     curves = null;
     return(false);
 }