| 1 | using System; | 
 
 
 
 
 | 2 | using System.Diagnostics; | 
 
 
 
 
 | 3 | using System.Collections.Generic; | 
 
 
 
 
 | 4 |  | 
 
 
 
 
 | 5 | namespace Oni.Totoro | 
 
 
 
 
 | 6 | { | 
 
 
 
 
 | 7 | internal static class AnimationDaeWriter | 
 
 
 
 
 | 8 | { | 
 
 
 
 
 | 9 | public static void AppendFrames(Animation anim1, Animation anim2) | 
 
 
 
 
 | 10 | { | 
 
 
 
 
 | 11 | var isOverlay = (anim2.Flags & AnimationFlags.Overlay) != 0; | 
 
 
 
 
 | 12 |  | 
 
 
 
 
 | 13 | if (isOverlay) | 
 
 
 
 
 | 14 | { | 
 
 
 
 
 | 15 | Console.Error.WriteLine("Cannot merge {0} because it's an overlay animation", anim2.Name); | 
 
 
 
 
 | 16 | return; | 
 
 
 
 
 | 17 | } | 
 
 
 
 
 | 18 |  | 
 
 
 
 
 | 19 | if (anim1.FrameSize == 0) | 
 
 
 
 
 | 20 | { | 
 
 
 
 
 | 21 | anim1.FrameSize = anim2.FrameSize; | 
 
 
 
 
 | 22 | } | 
 
 
 
 
 | 23 | else if (anim1.FrameSize != anim2.FrameSize) | 
 
 
 
 
 | 24 | { | 
 
 
 
 
 | 25 | Console.Error.WriteLine("Cannot merge {0} because its frame size doesn't match the frame size of the previous animation", anim2.Name); | 
 
 
 
 
 | 26 | return; | 
 
 
 
 
 | 27 | } | 
 
 
 
 
 | 28 |  | 
 
 
 
 
 | 29 | anim1.Velocities.AddRange(anim2.Velocities); | 
 
 
 
 
 | 30 | anim1.Heights.AddRange(anim2.Heights); | 
 
 
 
 
 | 31 |  | 
 
 
 
 
 | 32 | if (anim1.Rotations.Count == 0) | 
 
 
 
 
 | 33 | { | 
 
 
 
 
 | 34 | anim1.Rotations.AddRange(anim2.Rotations); | 
 
 
 
 
 | 35 | } | 
 
 
 
 
 | 36 | else | 
 
 
 
 
 | 37 | { | 
 
 
 
 
 | 38 | for (int i = 0; i < anim1.Rotations.Count; i++) | 
 
 
 
 
 | 39 | anim1.Rotations[i].AddRange(anim2.Rotations[i]); | 
 
 
 
 
 | 40 | } | 
 
 
 
 
 | 41 | } | 
 
 
 
 
 | 42 |  | 
 
 
 
 
 | 43 | public static void Write(Dae.Node root, Animation animation, int startFrame = 0) | 
 
 
 
 
 | 44 | { | 
 
 
 
 
 | 45 | var velocities = animation.Velocities; | 
 
 
 
 
 | 46 | var heights = animation.Heights; | 
 
 
 
 
 | 47 | var rotations = animation.Rotations; | 
 
 
 
 
 | 48 | var isCompressed = animation.FrameSize == 6; | 
 
 
 
 
 | 49 | var isOverlay = (animation.Flags & AnimationFlags.Overlay) != 0; | 
 
 
 
 
 | 50 | var isRealWorld = (animation.Flags & AnimationFlags.RealWorld) != 0; | 
 
 
 
 
 | 51 | var activeBones = (uint)(animation.OverlayUsedBones | animation.OverlayReplacedBones); | 
 
 
 
 
 | 52 |  | 
 
 
 
 
 | 53 | var nodes = FindNodes(root); | 
 
 
 
 
 | 54 |  | 
 
 
 
 
 | 55 | if (!isOverlay && !isRealWorld) | 
 
 
 
 
 | 56 | { | 
 
 
 
 
 | 57 | var pelvis = nodes[0]; | 
 
 
 
 
 | 58 |  | 
 
 
 
 
 | 59 | // | 
 
 
 
 
 | 60 | // Write pelvis position animation | 
 
 
 
 
 | 61 | // | 
 
 
 
 
 | 62 |  | 
 
 
 
 
 | 63 | var positions = new Vector2[velocities.Count + 1]; | 
 
 
 
 
 | 64 |  | 
 
 
 
 
 | 65 | for (int i = 1; i < positions.Length; i++) | 
 
 
 
 
 | 66 | positions[i] = positions[i - 1] + velocities[i - 1]; | 
 
 
 
 
 | 67 |  | 
 
 
 
 
 | 68 | CreateAnimationCurve(startFrame, positions.Select(p => p.X).ToList(), pelvis, "pos", "X"); | 
 
 
 
 
 | 69 | CreateAnimationCurve(startFrame, positions.Select(p => p.Y).ToList(), pelvis, "pos", "Z"); | 
 
 
 
 
 | 70 |  | 
 
 
 
 
 | 71 | // | 
 
 
 
 
 | 72 | // Write pelvis height animation | 
 
 
 
 
 | 73 | // | 
 
 
 
 
 | 74 |  | 
 
 
 
 
 | 75 | CreateAnimationCurve(startFrame, heights.ToList(), pelvis, "pos", "Y"); | 
 
 
 
 
 | 76 | } | 
 
 
 
 
 | 77 |  | 
 
 
 
 
 | 78 | // | 
 
 
 
 
 | 79 | // Write rotation animations for all bones | 
 
 
 
 
 | 80 | // | 
 
 
 
 
 | 81 |  | 
 
 
 
 
 | 82 | bool plot = true; | 
 
 
 
 
 | 83 |  | 
 
 
 
 
 | 84 | for (int i = 0; i < rotations.Count; i++) | 
 
 
 
 
 | 85 | { | 
 
 
 
 
 | 86 | if (isOverlay && (activeBones & (1u << i)) == 0) | 
 
 
 
 
 | 87 | continue; | 
 
 
 
 
 | 88 |  | 
 
 
 
 
 | 89 | var node = nodes[i]; | 
 
 
 
 
 | 90 | var keys = rotations[i]; | 
 
 
 
 
 | 91 |  | 
 
 
 
 
 | 92 | int length; | 
 
 
 
 
 | 93 |  | 
 
 
 
 
 | 94 | if (plot) | 
 
 
 
 
 | 95 | length = keys.Sum(k => k.Duration); | 
 
 
 
 
 | 96 | else | 
 
 
 
 
 | 97 | length = keys.Count; | 
 
 
 
 
 | 98 |  | 
 
 
 
 
 | 99 | var times = new float[length]; | 
 
 
 
 
 | 100 | var xAngles = new float[length]; | 
 
 
 
 
 | 101 | var yAngles = new float[length]; | 
 
 
 
 
 | 102 | var zAngles = new float[length]; | 
 
 
 
 
 | 103 |  | 
 
 
 
 
 | 104 | if (plot) | 
 
 
 
 
 | 105 | { | 
 
 
 
 
 | 106 | // | 
 
 
 
 
 | 107 | // Transform key frames to quaternions. | 
 
 
 
 
 | 108 | // | 
 
 
 
 
 | 109 |  | 
 
 
 
 
 | 110 | var quats = new Quaternion[keys.Count]; | 
 
 
 
 
 | 111 |  | 
 
 
 
 
 | 112 | for (int k = 0; k < keys.Count; k++) | 
 
 
 
 
 | 113 | { | 
 
 
 
 
 | 114 | var key = keys[k]; | 
 
 
 
 
 | 115 |  | 
 
 
 
 
 | 116 | if (isCompressed) | 
 
 
 
 
 | 117 | { | 
 
 
 
 
 | 118 | quats[k] = Quaternion.CreateFromAxisAngle(Vector3.UnitX, MathHelper.ToRadians(key.Rotation.X)) | 
 
 
 
 
 | 119 | * Quaternion.CreateFromAxisAngle(Vector3.UnitY, MathHelper.ToRadians(key.Rotation.Y)) | 
 
 
 
 
 | 120 | * Quaternion.CreateFromAxisAngle(Vector3.UnitZ, MathHelper.ToRadians(key.Rotation.Z)); | 
 
 
 
 
 | 121 | } | 
 
 
 
 
 | 122 | else | 
 
 
 
 
 | 123 | { | 
 
 
 
 
 | 124 | quats[k] = new Quaternion(key.Rotation); | 
 
 
 
 
 | 125 | } | 
 
 
 
 
 | 126 | } | 
 
 
 
 
 | 127 |  | 
 
 
 
 
 | 128 | // | 
 
 
 
 
 | 129 | // Interpolate the quaternions. | 
 
 
 
 
 | 130 | // | 
 
 
 
 
 | 131 |  | 
 
 
 
 
 | 132 | int frame = 0; | 
 
 
 
 
 | 133 |  | 
 
 
 
 
 | 134 | for (int k = 0; k < keys.Count; k++) | 
 
 
 
 
 | 135 | { | 
 
 
 
 
 | 136 | var duration = keys[k].Duration; | 
 
 
 
 
 | 137 |  | 
 
 
 
 
 | 138 | var q1 = quats[k]; | 
 
 
 
 
 | 139 | var q2 = (k == keys.Count - 1) ? quats[k] : quats[k + 1]; | 
 
 
 
 
 | 140 |  | 
 
 
 
 
 | 141 | for (int t = 0; t < duration; t++) | 
 
 
 
 
 | 142 | { | 
 
 
 
 
 | 143 | var q = Quaternion.Lerp(q1, q2, (float)t / (float)duration); | 
 
 
 
 
 | 144 | var euler = q.ToEulerXYZ(); | 
 
 
 
 
 | 145 |  | 
 
 
 
 
 | 146 | times[frame] = (frame + startFrame) * (1.0f / 60.0f); | 
 
 
 
 
 | 147 |  | 
 
 
 
 
 | 148 | xAngles[frame] = euler.X; | 
 
 
 
 
 | 149 | yAngles[frame] = euler.Y; | 
 
 
 
 
 | 150 | zAngles[frame] = euler.Z; | 
 
 
 
 
 | 151 |  | 
 
 
 
 
 | 152 | frame++; | 
 
 
 
 
 | 153 | } | 
 
 
 
 
 | 154 | } | 
 
 
 
 
 | 155 |  | 
 
 
 
 
 | 156 | MakeRotationCurveContinuous(xAngles); | 
 
 
 
 
 | 157 | MakeRotationCurveContinuous(yAngles); | 
 
 
 
 
 | 158 | MakeRotationCurveContinuous(zAngles); | 
 
 
 
 
 | 159 | } | 
 
 
 
 
 | 160 | else | 
 
 
 
 
 | 161 | { | 
 
 
 
 
 | 162 | int frame = 0; | 
 
 
 
 
 | 163 |  | 
 
 
 
 
 | 164 | for (int k = 0; k < keys.Count; k++) | 
 
 
 
 
 | 165 | { | 
 
 
 
 
 | 166 | var key = keys[k]; | 
 
 
 
 
 | 167 |  | 
 
 
 
 
 | 168 | times[k] = (frame + startFrame) * (1.0f / 60.0f); | 
 
 
 
 
 | 169 | frame += key.Duration; | 
 
 
 
 
 | 170 |  | 
 
 
 
 
 | 171 | if (isCompressed) | 
 
 
 
 
 | 172 | { | 
 
 
 
 
 | 173 | xAngles[k] = key.Rotation.X; | 
 
 
 
 
 | 174 | yAngles[k] = key.Rotation.Y; | 
 
 
 
 
 | 175 | zAngles[k] = key.Rotation.Z; | 
 
 
 
 
 | 176 | } | 
 
 
 
 
 | 177 | else | 
 
 
 
 
 | 178 | { | 
 
 
 
 
 | 179 | var euler = new Quaternion(key.Rotation).ToEulerXYZ(); | 
 
 
 
 
 | 180 |  | 
 
 
 
 
 | 181 | xAngles[k] = euler.X; | 
 
 
 
 
 | 182 | yAngles[k] = euler.Y; | 
 
 
 
 
 | 183 | zAngles[k] = euler.Z; | 
 
 
 
 
 | 184 | } | 
 
 
 
 
 | 185 | } | 
 
 
 
 
 | 186 | } | 
 
 
 
 
 | 187 |  | 
 
 
 
 
 | 188 | CreateAnimationCurve(times, xAngles, node, "rotX", "ANGLE"); | 
 
 
 
 
 | 189 | CreateAnimationCurve(times, yAngles, node, "rotY", "ANGLE"); | 
 
 
 
 
 | 190 | CreateAnimationCurve(times, zAngles, node, "rotZ", "ANGLE"); | 
 
 
 
 
 | 191 | } | 
 
 
 
 
 | 192 | } | 
 
 
 
 
 | 193 |  | 
 
 
 
 
 | 194 | private static void MakeRotationCurveContinuous(float[] curve) | 
 
 
 
 
 | 195 | { | 
 
 
 
 
 | 196 | for (int i = 1; i < curve.Length; i++) | 
 
 
 
 
 | 197 | { | 
 
 
 
 
 | 198 | float v1 = curve[i - 1]; | 
 
 
 
 
 | 199 | float v2 = curve[i]; | 
 
 
 
 
 | 200 |  | 
 
 
 
 
 | 201 | if (Math.Abs(v2 - v1) > 180.0f) | 
 
 
 
 
 | 202 | { | 
 
 
 
 
 | 203 | if (v2 > v1) | 
 
 
 
 
 | 204 | v2 -= 360.0f; | 
 
 
 
 
 | 205 | else | 
 
 
 
 
 | 206 | v2 += 360.0f; | 
 
 
 
 
 | 207 |  | 
 
 
 
 
 | 208 | curve[i] = v2; | 
 
 
 
 
 | 209 | } | 
 
 
 
 
 | 210 | } | 
 
 
 
 
 | 211 | } | 
 
 
 
 
 | 212 |  | 
 
 
 
 
 | 213 | private static void CreateAnimationCurve(int startFrame, IList<float> values, Dae.Node targetNode, string targetSid, string targetValue) | 
 
 
 
 
 | 214 | { | 
 
 
 
 
 | 215 | if (values.Count == 0) | 
 
 
 
 
 | 216 | return; | 
 
 
 
 
 | 217 |  | 
 
 
 
 
 | 218 | var times = new float[values.Count]; | 
 
 
 
 
 | 219 |  | 
 
 
 
 
 | 220 | for (int i = 0; i < times.Length; i++) | 
 
 
 
 
 | 221 | times[i] = (i + startFrame) * (1.0f / 60.0f); | 
 
 
 
 
 | 222 |  | 
 
 
 
 
 | 223 | CreateAnimationCurve(times, values, targetNode, targetSid, targetValue); | 
 
 
 
 
 | 224 | } | 
 
 
 
 
 | 225 |  | 
 
 
 
 
 | 226 | private static void CreateAnimationCurve(IList<float> times, IList<float> values, Dae.Node targetNode, string targetSid, string targetValue) | 
 
 
 
 
 | 227 | { | 
 
 
 
 
 | 228 | Debug.Assert(times.Count > 0); | 
 
 
 
 
 | 229 | Debug.Assert(times.Count == values.Count); | 
 
 
 
 
 | 230 |  | 
 
 
 
 
 | 231 | var interpolations = new string[times.Count]; | 
 
 
 
 
 | 232 |  | 
 
 
 
 
 | 233 | for (int i = 0; i < interpolations.Length; i++) | 
 
 
 
 
 | 234 | interpolations[i] = "LINEAR"; | 
 
 
 
 
 | 235 |  | 
 
 
 
 
 | 236 | var targetTransform = targetNode.Transforms.Find(x => x.Sid == targetSid); | 
 
 
 
 
 | 237 |  | 
 
 
 
 
 | 238 | targetTransform.BindAnimation(targetValue, new Dae.Sampler { | 
 
 
 
 
 | 239 | Inputs = { | 
 
 
 
 
 | 240 | new Dae.Input(Dae.Semantic.Input, new Dae.Source(times, 1)), | 
 
 
 
 
 | 241 | new Dae.Input(Dae.Semantic.Output, new Dae.Source(values, 1)), | 
 
 
 
 
 | 242 | new Dae.Input(Dae.Semantic.Interpolation, new Dae.Source(interpolations, 1)) | 
 
 
 
 
 | 243 | } | 
 
 
 
 
 | 244 | }); | 
 
 
 
 
 | 245 | } | 
 
 
 
 
 | 246 |  | 
 
 
 
 
 | 247 | private static List<Dae.Node> FindNodes(Dae.Node root) | 
 
 
 
 
 | 248 | { | 
 
 
 
 
 | 249 | var nodes = new List<Dae.Node>(19); | 
 
 
 
 
 | 250 | FindNodesRecursive(root, nodes); | 
 
 
 
 
 | 251 | return nodes; | 
 
 
 
 
 | 252 | } | 
 
 
 
 
 | 253 |  | 
 
 
 
 
 | 254 | private static void FindNodesRecursive(Dae.Node node, List<Dae.Node> result) | 
 
 
 
 
 | 255 | { | 
 
 
 
 
 | 256 | result.Add(node); | 
 
 
 
 
 | 257 |  | 
 
 
 
 
 | 258 | foreach (var child in node.Nodes) | 
 
 
 
 
 | 259 | FindNodesRecursive(child, result); | 
 
 
 
 
 | 260 | } | 
 
 
 
 
 | 261 | } | 
 
 
 
 
 | 262 | } |