source: OniSplit/Totoro/AnimationDaeWriter.cs

Last change on this file was 1114, checked in by iritscen, 5 years ago

Adding OniSplit source code (v0.9.99.0). Many thanks to Neo for all his work over the years.

File size: 9.1 KB
Line 
1using System;
2using System.Diagnostics;
3using System.Collections.Generic;
4
5namespace 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}
Note: See TracBrowser for help on using the repository browser.