1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.IO;
|
---|
4 | using System.Xml;
|
---|
5 | using Oni.Metadata;
|
---|
6 | using Oni.Xml;
|
---|
7 |
|
---|
8 | namespace Oni.Totoro
|
---|
9 | {
|
---|
10 | internal class AnimationXmlReader
|
---|
11 | {
|
---|
12 | private const string ns = "";
|
---|
13 | private static readonly char[] emptyChars = new char[0];
|
---|
14 | private XmlReader xml;
|
---|
15 | private string basePath;
|
---|
16 | private Animation animation;
|
---|
17 | private AnimationDaeReader daeReader;
|
---|
18 |
|
---|
19 | private AnimationXmlReader()
|
---|
20 | {
|
---|
21 | }
|
---|
22 |
|
---|
23 | public static Animation Read(XmlReader xml, string baseDir)
|
---|
24 | {
|
---|
25 | var reader = new AnimationXmlReader
|
---|
26 | {
|
---|
27 | xml = xml,
|
---|
28 | basePath = baseDir,
|
---|
29 | animation = new Animation()
|
---|
30 | };
|
---|
31 |
|
---|
32 | var animation = reader.Read();
|
---|
33 | animation.ValidateFrames();
|
---|
34 | return animation;
|
---|
35 | }
|
---|
36 |
|
---|
37 | private Animation Read()
|
---|
38 | {
|
---|
39 | animation.Name = xml.GetAttribute("Name");
|
---|
40 | xml.ReadStartElement("Animation", ns);
|
---|
41 |
|
---|
42 | if (xml.IsStartElement("DaeImport") || xml.IsStartElement("Import"))
|
---|
43 | ImportDaeAnimation();
|
---|
44 |
|
---|
45 | xml.ReadStartElement("Lookup");
|
---|
46 | animation.Type = MetaEnum.Parse<AnimationType>(xml.ReadElementContentAsString("Type", ns));
|
---|
47 | animation.AimingType = MetaEnum.Parse<AnimationType>(xml.ReadElementContentAsString("AimingType", ns));
|
---|
48 | animation.FromState = MetaEnum.Parse<AnimationState>(xml.ReadElementContentAsString("FromState", ns));
|
---|
49 | animation.ToState = MetaEnum.Parse<AnimationState>(xml.ReadElementContentAsString("ToState", ns));
|
---|
50 | animation.Varient = MetaEnum.Parse<AnimationVarient>(xml.ReadElementContentAsString("Varient", ns));
|
---|
51 | animation.FirstLevelAvailable = xml.ReadElementContentAsInt("FirstLevel", ns);
|
---|
52 | ReadRawArray("Shortcuts", animation.Shortcuts, Read);
|
---|
53 | xml.ReadEndElement();
|
---|
54 |
|
---|
55 | animation.Flags = MetaEnum.Parse<AnimationFlags>(xml.ReadElementContentAsString("Flags", ns));
|
---|
56 | xml.ReadStartElement("Atomic", ns);
|
---|
57 | animation.AtomicStart = xml.ReadElementContentAsInt("Start", ns);
|
---|
58 | animation.AtomicEnd = xml.ReadElementContentAsInt("End", ns);
|
---|
59 | xml.ReadEndElement();
|
---|
60 | xml.ReadStartElement("Invulnerable", ns);
|
---|
61 | animation.InvulnerableStart = xml.ReadElementContentAsInt("Start", ns);
|
---|
62 | animation.InvulnerableEnd = xml.ReadElementContentAsInt("End", ns);
|
---|
63 | xml.ReadEndElement();
|
---|
64 | xml.ReadStartElement("Overlay", ns);
|
---|
65 | animation.OverlayUsedBones = MetaEnum.Parse<BoneMask>(xml.ReadElementContentAsString("UsedBones", ns));
|
---|
66 | animation.OverlayReplacedBones = MetaEnum.Parse<BoneMask>(xml.ReadElementContentAsString("ReplacedBones", ns));
|
---|
67 | xml.ReadEndElement();
|
---|
68 |
|
---|
69 | xml.ReadStartElement("DirectAnimations", ns);
|
---|
70 | animation.DirectAnimations[0] = xml.ReadElementContentAsString("Link", ns);
|
---|
71 | animation.DirectAnimations[1] = xml.ReadElementContentAsString("Link", ns);
|
---|
72 | xml.ReadEndElement();
|
---|
73 | xml.ReadStartElement("Pause");
|
---|
74 | animation.HardPause = xml.ReadElementContentAsInt("Hard", ns);
|
---|
75 | animation.SoftPause = xml.ReadElementContentAsInt("Soft", ns);
|
---|
76 | xml.ReadEndElement();
|
---|
77 | xml.ReadStartElement("Interpolation", ns);
|
---|
78 | animation.InterpolationEnd = xml.ReadElementContentAsInt("End", ns);
|
---|
79 | animation.InterpolationMax = xml.ReadElementContentAsInt("Max", ns);
|
---|
80 | xml.ReadEndElement();
|
---|
81 |
|
---|
82 | animation.FinalRotation = MathHelper.ToRadians(xml.ReadElementContentAsFloat("FinalRotation", ns));
|
---|
83 | animation.Direction = MetaEnum.Parse<Direction>(xml.ReadElementContentAsString("Direction", ns));
|
---|
84 | animation.Vocalization = xml.ReadElementContentAsInt("Vocalization", ns);
|
---|
85 | animation.ActionFrame = xml.ReadElementContentAsInt("ActionFrame", ns);
|
---|
86 | animation.Impact = xml.ReadElementContentAsString("Impact", ns);
|
---|
87 |
|
---|
88 | ReadRawArray("Particle", animation.Particles, Read);
|
---|
89 | ReadRawArray("MotionBlur", animation.MotionBlur, Read);
|
---|
90 | ReadRawArray("Footsteps", animation.Footsteps, Read);
|
---|
91 | ReadRawArray("Sounds", animation.Sounds, Read);
|
---|
92 |
|
---|
93 | if (daeReader == null)
|
---|
94 | {
|
---|
95 | ReadHeights();
|
---|
96 | ReadVelocities();
|
---|
97 | ReadRotations();
|
---|
98 | ReadPositions();
|
---|
99 | }
|
---|
100 |
|
---|
101 | ReadThrowInfo();
|
---|
102 | ReadRawArray("SelfDamage", animation.SelfDamage, Read);
|
---|
103 |
|
---|
104 | if (xml.IsStartElement("Attacks"))
|
---|
105 | {
|
---|
106 | ReadRawArray("Attacks", animation.Attacks, Read);
|
---|
107 | ReadAttackRing();
|
---|
108 | }
|
---|
109 |
|
---|
110 | xml.ReadEndElement();
|
---|
111 |
|
---|
112 | if (daeReader != null)
|
---|
113 | {
|
---|
114 | daeReader.Read(animation);
|
---|
115 | }
|
---|
116 |
|
---|
117 | return animation;
|
---|
118 | }
|
---|
119 |
|
---|
120 | private void ReadVelocities()
|
---|
121 | {
|
---|
122 | if (!xml.IsStartElement("Velocities"))
|
---|
123 | return;
|
---|
124 |
|
---|
125 | if (xml.SkipEmpty())
|
---|
126 | return;
|
---|
127 |
|
---|
128 | xml.ReadStartElement();
|
---|
129 |
|
---|
130 | while (xml.IsStartElement())
|
---|
131 | animation.Velocities.Add(xml.ReadElementContentAsVector2());
|
---|
132 |
|
---|
133 | xml.ReadEndElement();
|
---|
134 | }
|
---|
135 |
|
---|
136 | private void ReadPositions()
|
---|
137 | {
|
---|
138 | var xz = new Vector2();
|
---|
139 |
|
---|
140 | if (xml.IsStartElement("PositionOffset"))
|
---|
141 | {
|
---|
142 | xml.ReadStartElement();
|
---|
143 | xz.X = xml.ReadElementContentAsFloat("X", ns);
|
---|
144 | xz.Y = xml.ReadElementContentAsFloat("Z", ns);
|
---|
145 | xml.ReadEndElement();
|
---|
146 | }
|
---|
147 |
|
---|
148 | ReadRawArray("Positions", animation.Positions, ReadPosition);
|
---|
149 |
|
---|
150 | for (int i = 0; i < animation.Positions.Count; i++)
|
---|
151 | {
|
---|
152 | var position = animation.Positions[i];
|
---|
153 | position.X = xz.X;
|
---|
154 | position.Z = xz.Y;
|
---|
155 | xz += animation.Velocities[i];
|
---|
156 | }
|
---|
157 | }
|
---|
158 |
|
---|
159 | private void ReadRotations()
|
---|
160 | {
|
---|
161 | var rotations = animation.Rotations;
|
---|
162 |
|
---|
163 | xml.ReadStartElement("Rotations");
|
---|
164 |
|
---|
165 | while (xml.IsStartElement())
|
---|
166 | {
|
---|
167 | xml.ReadStartElement("Bone");
|
---|
168 |
|
---|
169 | var keys = new List<KeyFrame>();
|
---|
170 |
|
---|
171 | int count = 0;
|
---|
172 |
|
---|
173 | while (xml.IsStartElement())
|
---|
174 | {
|
---|
175 | string name = xml.LocalName;
|
---|
176 | string[] tokens = xml.ReadElementContentAsString().Split(emptyChars, StringSplitOptions.RemoveEmptyEntries);
|
---|
177 |
|
---|
178 | var key = new KeyFrame();
|
---|
179 | key.Duration = XmlConvert.ToByte(tokens[0]);
|
---|
180 |
|
---|
181 | switch (name)
|
---|
182 | {
|
---|
183 | case "EKey":
|
---|
184 | animation.FrameSize = 6;
|
---|
185 | key.Rotation.X = XmlConvert.ToSingle(tokens[1]);
|
---|
186 | key.Rotation.Y = XmlConvert.ToSingle(tokens[2]);
|
---|
187 | key.Rotation.Z = XmlConvert.ToSingle(tokens[3]);
|
---|
188 | break;
|
---|
189 |
|
---|
190 | case "QKey":
|
---|
191 | animation.FrameSize = 16;
|
---|
192 | key.Rotation.X = XmlConvert.ToSingle(tokens[1]);
|
---|
193 | key.Rotation.Y = XmlConvert.ToSingle(tokens[2]);
|
---|
194 | key.Rotation.Z = XmlConvert.ToSingle(tokens[3]);
|
---|
195 | key.Rotation.W = -XmlConvert.ToSingle(tokens[4]);
|
---|
196 | break;
|
---|
197 |
|
---|
198 | default:
|
---|
199 | throw new InvalidDataException(string.Format("Unknonw animation key type '{0}'", name));
|
---|
200 | }
|
---|
201 |
|
---|
202 | count += key.Duration;
|
---|
203 | keys.Add(key);
|
---|
204 | }
|
---|
205 |
|
---|
206 | if (count != animation.Velocities.Count)
|
---|
207 | throw new InvalidDataException("bad number of frames");
|
---|
208 |
|
---|
209 | rotations.Add(keys);
|
---|
210 | xml.ReadEndElement();
|
---|
211 | }
|
---|
212 |
|
---|
213 | xml.ReadEndElement();
|
---|
214 | }
|
---|
215 |
|
---|
216 | private void ReadHeights()
|
---|
217 | {
|
---|
218 | if (!xml.IsStartElement("Heights"))
|
---|
219 | return;
|
---|
220 |
|
---|
221 | if (xml.SkipEmpty())
|
---|
222 | return;
|
---|
223 |
|
---|
224 | xml.ReadStartElement();
|
---|
225 |
|
---|
226 | while (xml.IsStartElement())
|
---|
227 | animation.Heights.Add(xml.ReadElementContentAsFloat("Height", ns));
|
---|
228 |
|
---|
229 | xml.ReadEndElement();
|
---|
230 | }
|
---|
231 |
|
---|
232 | private void ReadThrowInfo()
|
---|
233 | {
|
---|
234 | if (!xml.IsStartElement("ThrowSource"))
|
---|
235 | return;
|
---|
236 |
|
---|
237 | if (xml.SkipEmpty())
|
---|
238 | return;
|
---|
239 |
|
---|
240 | animation.ThrowSource = new ThrowInfo();
|
---|
241 | xml.ReadStartElement("ThrowSource");
|
---|
242 | xml.ReadStartElement("TargetAdjustment");
|
---|
243 | animation.ThrowSource.Position = xml.ReadElementContentAsVector3("Position");
|
---|
244 | animation.ThrowSource.Angle = xml.ReadElementContentAsFloat("Angle", ns);
|
---|
245 | xml.ReadEndElement();
|
---|
246 | animation.ThrowSource.Distance = xml.ReadElementContentAsFloat("Distance", ns);
|
---|
247 | animation.ThrowSource.Type = MetaEnum.Parse<AnimationType>(xml.ReadElementContentAsString("TargetType", ns));
|
---|
248 | xml.ReadEndElement();
|
---|
249 | }
|
---|
250 |
|
---|
251 | private void ReadAttackRing()
|
---|
252 | {
|
---|
253 | if (!xml.IsStartElement("AttackRing") && !xml.IsStartElement("HorizontalExtents"))
|
---|
254 | return;
|
---|
255 |
|
---|
256 | if (animation.Attacks.Count == 0)
|
---|
257 | {
|
---|
258 | Console.Error.WriteLine("Warning: AttackRing found but no attacks are present, ignoring");
|
---|
259 | xml.Skip();
|
---|
260 | return;
|
---|
261 | }
|
---|
262 |
|
---|
263 | xml.ReadStartElement();
|
---|
264 |
|
---|
265 | for (int i = 0; i < 36; i++)
|
---|
266 | animation.AttackRing[i] = xml.ReadElementContentAsFloat();
|
---|
267 |
|
---|
268 | xml.ReadEndElement();
|
---|
269 | }
|
---|
270 |
|
---|
271 | private void ReadPosition(Position position)
|
---|
272 | {
|
---|
273 | xml.ReadStartElement("Position");
|
---|
274 | position.Height = xml.ReadElementContentAsFloat("Height", ns);
|
---|
275 | position.YOffset = xml.ReadElementContentAsFloat("YOffset", ns);
|
---|
276 | xml.ReadEndElement();
|
---|
277 | }
|
---|
278 |
|
---|
279 | private void Read(Particle particle)
|
---|
280 | {
|
---|
281 | xml.ReadStartElement("Particle");
|
---|
282 | particle.Start = xml.ReadElementContentAsInt("Start", ns);
|
---|
283 | particle.End = xml.ReadElementContentAsInt("End", ns);
|
---|
284 | particle.Bone = MetaEnum.Parse<Bone>(xml.ReadElementContentAsString("Bone", ns));
|
---|
285 | particle.Name = xml.ReadElementContentAsString("Name", ns);
|
---|
286 | xml.ReadEndElement();
|
---|
287 | }
|
---|
288 |
|
---|
289 | private void Read(Sound sound)
|
---|
290 | {
|
---|
291 | xml.ReadStartElement("Sound");
|
---|
292 | sound.Name = xml.ReadElementContentAsString("Name", ns);
|
---|
293 | sound.Start = xml.ReadElementContentAsInt("Start", ns);
|
---|
294 | xml.ReadEndElement();
|
---|
295 | }
|
---|
296 |
|
---|
297 | private void Read(Shortcut shortcut)
|
---|
298 | {
|
---|
299 | xml.ReadStartElement("Shortcut");
|
---|
300 | shortcut.FromState = MetaEnum.Parse<AnimationState>(xml.ReadElementContentAsString("FromState", ns));
|
---|
301 | shortcut.Length = xml.ReadElementContentAsInt("Length", ns);
|
---|
302 | shortcut.ReplaceAtomic = (xml.ReadElementContentAsString("ReplaceAtomic", ns) == "yes");
|
---|
303 | xml.ReadEndElement();
|
---|
304 | }
|
---|
305 |
|
---|
306 | private void Read(Footstep footstep)
|
---|
307 | {
|
---|
308 | xml.ReadStartElement("Footstep");
|
---|
309 |
|
---|
310 | string frame = xml.GetAttribute("Frame");
|
---|
311 |
|
---|
312 | if (frame != null)
|
---|
313 | {
|
---|
314 | footstep.Frame = XmlConvert.ToInt32(frame);
|
---|
315 | footstep.Type = MetaEnum.Parse<FootstepType>(xml.GetAttribute("Type"));
|
---|
316 | }
|
---|
317 | else
|
---|
318 | {
|
---|
319 | footstep.Frame = xml.ReadElementContentAsInt("Frame", ns);
|
---|
320 | footstep.Type = MetaEnum.Parse<FootstepType>(xml.ReadElementContentAsString("Type", ns));
|
---|
321 | }
|
---|
322 |
|
---|
323 | xml.ReadEndElement();
|
---|
324 | }
|
---|
325 |
|
---|
326 | private void Read(Damage damage)
|
---|
327 | {
|
---|
328 | xml.ReadStartElement("Damage");
|
---|
329 | damage.Points = xml.ReadElementContentAsInt("Points", ns);
|
---|
330 | damage.Frame = xml.ReadElementContentAsInt("Frame", ns);
|
---|
331 | xml.ReadEndElement();
|
---|
332 | }
|
---|
333 |
|
---|
334 | private void Read(MotionBlur d)
|
---|
335 | {
|
---|
336 | xml.ReadStartElement("MotionBlur");
|
---|
337 | d.Bones = MetaEnum.Parse<BoneMask>(xml.ReadElementContentAsString("Bones", ns));
|
---|
338 | d.Start = xml.ReadElementContentAsInt("Start", ns);
|
---|
339 | d.End = xml.ReadElementContentAsInt("End", ns);
|
---|
340 | d.Lifetime = xml.ReadElementContentAsInt("Lifetime", ns);
|
---|
341 | d.Alpha = xml.ReadElementContentAsInt("Alpha", ns);
|
---|
342 | d.Interval = xml.ReadElementContentAsInt("Interval", ns);
|
---|
343 | xml.ReadEndElement();
|
---|
344 | }
|
---|
345 |
|
---|
346 | private void Read(Attack attack)
|
---|
347 | {
|
---|
348 | xml.ReadStartElement("Attack");
|
---|
349 | attack.Start = xml.ReadElementContentAsInt("Start", ns);
|
---|
350 | attack.End = xml.ReadElementContentAsInt("End", ns);
|
---|
351 | attack.Bones = MetaEnum.Parse<BoneMask>(xml.ReadElementContentAsString("Bones", ns));
|
---|
352 | attack.Flags = MetaEnum.Parse<AttackFlags>(xml.ReadElementContentAsString("Flags", ns));
|
---|
353 | attack.Knockback = xml.ReadElementContentAsFloat("Knockback", ns);
|
---|
354 | attack.HitPoints = xml.ReadElementContentAsInt("HitPoints", ns);
|
---|
355 | attack.HitType = MetaEnum.Parse<AnimationType>(xml.ReadElementContentAsString("HitType", ns));
|
---|
356 | attack.HitLength = xml.ReadElementContentAsInt("HitLength", ns);
|
---|
357 | attack.StunLength = xml.ReadElementContentAsInt("StunLength", ns);
|
---|
358 | attack.StaggerLength = xml.ReadElementContentAsInt("StaggerLength", ns);
|
---|
359 |
|
---|
360 | if (xml.IsStartElement("Extents"))
|
---|
361 | {
|
---|
362 | ReadRawArray("Extents", attack.Extents, Read);
|
---|
363 |
|
---|
364 | if (attack.Extents.Count != attack.End - attack.Start + 1)
|
---|
365 | Console.Error.WriteLine("Error: Attack starting at frame {0} has an incorrect number of extents ({1})", attack.Start, attack.Extents.Count);
|
---|
366 | }
|
---|
367 |
|
---|
368 | xml.ReadEndElement();
|
---|
369 | }
|
---|
370 |
|
---|
371 | private void Read(AttackExtent extent)
|
---|
372 | {
|
---|
373 | xml.ReadStartElement("Extent");
|
---|
374 | extent.Angle = xml.ReadElementContentAsFloat("Angle", ns);
|
---|
375 | extent.Length = xml.ReadElementContentAsFloat("Length", ns);
|
---|
376 | extent.MinY = xml.ReadElementContentAsFloat("MinY", ns);
|
---|
377 | extent.MaxY = xml.ReadElementContentAsFloat("MaxY", ns);
|
---|
378 | xml.ReadEndElement();
|
---|
379 | }
|
---|
380 |
|
---|
381 | private void ReadRawArray<T>(string name, List<T> list, Action<T> elementReader)
|
---|
382 | where T : new()
|
---|
383 | {
|
---|
384 | if (xml.SkipEmpty())
|
---|
385 | return;
|
---|
386 |
|
---|
387 | xml.ReadStartElement();
|
---|
388 |
|
---|
389 | while (xml.IsStartElement())
|
---|
390 | {
|
---|
391 | T t = new T();
|
---|
392 | elementReader(t);
|
---|
393 | list.Add(t);
|
---|
394 | }
|
---|
395 |
|
---|
396 | xml.ReadEndElement();
|
---|
397 | }
|
---|
398 |
|
---|
399 | private void ImportDaeAnimation()
|
---|
400 | {
|
---|
401 | string filePath = xml.GetAttribute("Path");
|
---|
402 | bool empty = xml.SkipEmpty();
|
---|
403 |
|
---|
404 | if (!empty)
|
---|
405 | {
|
---|
406 | xml.ReadStartElement();
|
---|
407 |
|
---|
408 | if (filePath == null)
|
---|
409 | filePath = xml.ReadElementContentAsString("Path", ns);
|
---|
410 | }
|
---|
411 |
|
---|
412 | filePath = Path.Combine(basePath, filePath);
|
---|
413 |
|
---|
414 | if (!File.Exists(filePath))
|
---|
415 | {
|
---|
416 | Console.Error.WriteLine("Could not find animation import source file '{0}'", filePath);
|
---|
417 | return;
|
---|
418 | }
|
---|
419 |
|
---|
420 | Console.WriteLine("Importing {0}", filePath);
|
---|
421 |
|
---|
422 | daeReader = new AnimationDaeReader();
|
---|
423 | daeReader.Scene = Dae.Reader.ReadFile(filePath);
|
---|
424 |
|
---|
425 | if (!empty)
|
---|
426 | {
|
---|
427 | if (xml.IsStartElement("Start"))
|
---|
428 | daeReader.StartFrame = xml.ReadElementContentAsInt("Start", ns);
|
---|
429 |
|
---|
430 | if (xml.IsStartElement("End"))
|
---|
431 | daeReader.EndFrame = xml.ReadElementContentAsInt("End", ns);
|
---|
432 |
|
---|
433 | xml.ReadEndElement();
|
---|
434 | }
|
---|
435 | }
|
---|
436 | }
|
---|
437 | }
|
---|