// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.IO; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Input { /// /// Functions for serializing input animation data to and from binary files. /// public static class InputAnimationSerializationUtils { public const string Extension = "bin"; const long Magic = 0x6a8faf6e0f9e42c6; public const int VersionMajor = 1; public const int VersionMinor = 1; /// /// Generate a file name for export. /// public static string GetOutputFilename(string baseName = "InputAnimation", bool appendTimestamp = true) { string filename; if (appendTimestamp) { filename = string.Format("{0}-{1}.{2}", baseName, DateTime.UtcNow.ToString("yyyyMMdd-HHmmss"), Extension); } else { filename = baseName; } return filename; } /// /// Write a header for the input animation file format into the stream. /// public static void WriteHeader(BinaryWriter writer) { writer.Write(Magic); writer.Write(VersionMajor); writer.Write(VersionMinor); } /// /// Write a header for the input animation file format into the stream. /// public static void ReadHeader(BinaryReader reader, out int fileVersionMajor, out int fileVersionMinor) { long fileMagic = reader.ReadInt64(); if (fileMagic != Magic) { throw new Exception("File is not an input animation file"); } fileVersionMajor = reader.ReadInt32(); fileVersionMinor = reader.ReadInt32(); } /// /// Serialize an animation curve with tangents as binary data. /// public static void WriteFloatCurve(BinaryWriter writer, AnimationCurve curve, float startTime) { writer.Write((int)curve.preWrapMode); writer.Write((int)curve.postWrapMode); writer.Write(curve.length); for (int i = 0; i < curve.length; ++i) { var keyframe = curve.keys[i]; writer.Write(keyframe.time - startTime); writer.Write(keyframe.value); writer.Write(keyframe.inTangent); writer.Write(keyframe.outTangent); writer.Write(keyframe.inWeight); writer.Write(keyframe.outWeight); writer.Write((int)keyframe.weightedMode); } } /// /// Deserialize an animation curve with tangents from binary data. /// public static void ReadFloatCurve(BinaryReader reader, AnimationCurve curve) { curve.preWrapMode = (WrapMode)reader.ReadInt32(); curve.postWrapMode = (WrapMode)reader.ReadInt32(); int keyframeCount = reader.ReadInt32(); Keyframe[] keys = new Keyframe[keyframeCount]; for (int i = 0; i < keyframeCount; ++i) { keys[i].time = reader.ReadSingle(); keys[i].value = reader.ReadSingle(); keys[i].inTangent = reader.ReadSingle(); keys[i].outTangent = reader.ReadSingle(); keys[i].inWeight = reader.ReadSingle(); keys[i].outWeight = reader.ReadSingle(); keys[i].weightedMode = (WeightedMode)reader.ReadInt32(); } curve.keys = keys; } /// /// Serialize an animation curve as binary data, ignoring tangents. /// public static void WriteBoolCurve(BinaryWriter writer, AnimationCurve curve, float startTime) { writer.Write((int)curve.preWrapMode); writer.Write((int)curve.postWrapMode); writer.Write(curve.length); for (int i = 0; i < curve.length; ++i) { var keyframe = curve.keys[i]; writer.Write(keyframe.time - startTime); writer.Write(keyframe.value); } } /// /// Deserialize an animation curve from binary data, ignoring tangents. /// public static void ReadBoolCurve(BinaryReader reader, AnimationCurve curve) { curve.preWrapMode = (WrapMode)reader.ReadInt32(); curve.postWrapMode = (WrapMode)reader.ReadInt32(); int keyframeCount = reader.ReadInt32(); Keyframe[] keys = new Keyframe[keyframeCount]; for (int i = 0; i < keyframeCount; ++i) { keys[i].time = reader.ReadSingle(); keys[i].value = reader.ReadSingle(); keys[i].outWeight = 1.0e6f; keys[i].weightedMode = WeightedMode.Both; } curve.keys = keys; } /// /// Serialize an animation curve with tangents as binary data. Only encodes keyframe position and time. /// public static void WriteFloatCurveSimple(BinaryWriter writer, AnimationCurve curve, float startTime) { writer.Write((int)curve.preWrapMode); writer.Write((int)curve.postWrapMode); writer.Write(curve.length); for (int i = 0; i < curve.length; ++i) { var keyframe = curve.keys[i]; writer.Write(keyframe.time - startTime); writer.Write(keyframe.value); } } /// /// Deserialize an animation curve with tangents from binary data. Only decodes keyframe position and time. /// /// Only use for curves serialized using WriteFloatCurvesSimple public static void ReadFloatCurveSimple(BinaryReader reader, AnimationCurve curve) { curve.preWrapMode = (WrapMode)reader.ReadInt32(); curve.postWrapMode = (WrapMode)reader.ReadInt32(); int keyframeCount = reader.ReadInt32(); Keyframe[] keys = new Keyframe[keyframeCount]; for (int i = 0; i < keyframeCount; ++i) { keys[i].time = reader.ReadSingle(); keys[i].value = reader.ReadSingle(); keys[i].weightedMode = WeightedMode.Both; } curve.keys = keys; } /// /// Serialize an array of animation curves with tangents as binary data. /// public static void WriteFloatCurveArray(BinaryWriter writer, AnimationCurve[] curves, float startTime) { foreach (AnimationCurve curve in curves) { InputAnimationSerializationUtils.WriteFloatCurve(writer, curve, startTime); } } /// /// Deserialize an array of animation curves with tangents from binary data. /// public static void ReadFloatCurveArray(BinaryReader reader, AnimationCurve[] curves) { foreach (AnimationCurve curve in curves) { InputAnimationSerializationUtils.ReadFloatCurve(reader, curve); } } /// /// Serialize an array of animation curves as binary data, ignoring tangents. /// public static void WriteBoolCurveArray(BinaryWriter writer, AnimationCurve[] curves, float startTime) { foreach (AnimationCurve curve in curves) { InputAnimationSerializationUtils.WriteBoolCurve(writer, curve, startTime); } } /// /// Deserialize an array of animation curves from binary data, ignoring tangents. /// public static void ReadBoolCurveArray(BinaryReader reader, AnimationCurve[] curves) { foreach (AnimationCurve curve in curves) { InputAnimationSerializationUtils.ReadBoolCurve(reader, curve); } } /// /// Serialize a list of markers. /// public static void WriteMarkerList(BinaryWriter writer, List markers, float startTime) { writer.Write(markers.Count); foreach (var marker in markers) { writer.Write(marker.time - startTime); writer.Write(marker.name); } } /// /// Deserialize a list of markers. /// public static void ReadMarkerList(BinaryReader reader, List markers) { markers.Clear(); int count = reader.ReadInt32(); markers.Capacity = count; for (int i = 0; i < count; ++i) { var marker = new InputAnimationMarker(); marker.time = reader.ReadSingle(); marker.name = reader.ReadString(); markers.Add(marker); } } } }