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 | }
|
---|