1 |
using System; |
2 |
using System.Collections.Generic; |
3 |
using Oni.Physics; |
4 |
|
5 |
namespace Oni.Totoro |
6 |
{ |
7 |
internal class Animation |
8 |
{ |
9 |
public string Name; |
10 |
public AnimationFlags Flags; |
11 |
public readonly string[] DirectAnimations = new string[2]; |
12 |
public float FinalRotation; |
13 |
public Direction Direction = Direction.Forward; |
14 |
public int Vocalization = 65535; |
15 |
public string Impact; |
16 |
public int HardPause; |
17 |
public int SoftPause; |
18 |
public AnimationType Type; |
19 |
public AnimationType AimingType; |
20 |
public AnimationState FromState; |
21 |
public AnimationState ToState; |
22 |
public AnimationVarient Varient; |
23 |
public int ActionFrame = 65535; |
24 |
public int FirstLevelAvailable; |
25 |
public BoneMask OverlayUsedBones; |
26 |
public BoneMask OverlayReplacedBones; |
27 |
public int AtomicStart; |
28 |
public int AtomicEnd; |
29 |
public int InvulnerableStart; |
30 |
public int InvulnerableEnd; |
31 |
public int InterpolationMax; |
32 |
public int InterpolationEnd; |
33 |
public int FrameSize; |
34 |
|
35 |
public readonly List<float> Heights = new List<float>(); |
36 |
public readonly List<Vector2> Velocities = new List<Vector2>(); |
37 |
public readonly List<List<KeyFrame>> Rotations = new List<List<KeyFrame>>(); |
38 |
public readonly List<Shortcut> Shortcuts = new List<Shortcut>(); |
39 |
public readonly List<Position> Positions = new List<Position>(); |
40 |
public readonly List<Damage> SelfDamage = new List<Damage>(); |
41 |
public ThrowInfo ThrowSource; |
42 |
public readonly List<Sound> Sounds = new List<Sound>(); |
43 |
public readonly List<Footstep> Footsteps = new List<Footstep>(); |
44 |
public readonly List<Particle> Particles = new List<Particle>(); |
45 |
public readonly List<MotionBlur> MotionBlur = new List<MotionBlur>(); |
46 |
public readonly List<Attack> Attacks = new List<Attack>(); |
47 |
public readonly float[] AttackRing = new float[36]; |
48 |
public readonly List<List<Vector3>> AllPoints = new List<List<Vector3>>(); |
49 |
|
50 |
public void ValidateFrames() |
51 |
{ |
52 |
var error = Console.Error; |
53 |
int frameCount = Heights.Count; |
54 |
|
55 |
foreach (var sound in Sounds.FindAll(s => s.Start >= frameCount)) |
56 |
{ |
57 |
error.WriteLine("Warning: sound start {0} is beyond the last animation frame", sound.Start); |
58 |
Sounds.Remove(sound); |
59 |
} |
60 |
|
61 |
foreach (var footstep in Footsteps.FindAll(f => f.Frame >= frameCount)) |
62 |
{ |
63 |
error.WriteLine("Warning: footstep frame {0} is beyond the last animation frame", footstep.Frame); |
64 |
Footsteps.Remove(footstep); |
65 |
} |
66 |
|
67 |
foreach (var damage in SelfDamage.FindAll(d => d.Frame > frameCount)) |
68 |
{ |
69 |
error.WriteLine("Warning: damage frame {0} is beyond the last animation frame", damage.Frame); |
70 |
SelfDamage.Remove(damage); |
71 |
} |
72 |
|
73 |
foreach (var attack in Attacks.FindAll(a => a.Start >= frameCount)) |
74 |
{ |
75 |
error.WriteLine("Warning: attack start frame {0} is beyond the last animation frame", attack.Start); |
76 |
Attacks.Remove(attack); |
77 |
} |
78 |
|
79 |
foreach (var particle in Particles.FindAll(p => p.Start >= frameCount)) |
80 |
{ |
81 |
error.WriteLine("Warning: particle start frame {0} is beyond the last animation frame", particle.Start); |
82 |
Particles.Remove(particle); |
83 |
} |
84 |
} |
85 |
|
86 |
public void ComputeExtents(Body body) |
87 |
{ |
88 |
Positions.Clear(); |
89 |
AllPoints.Clear(); |
90 |
|
91 |
int frameCount = Heights.Count; |
92 |
int boneCount = Rotations.Count; |
93 |
|
94 |
var rotations = new Quaternion[frameCount, boneCount]; |
95 |
|
96 |
// |
97 |
// Compute the quaternions for each bone and frame |
98 |
// |
99 |
|
100 |
for (int bone = 0; bone < boneCount; bone++) |
101 |
{ |
102 |
var keys = Rotations[bone]; |
103 |
|
104 |
for (int frame = 0; frame < keys.Count; frame++) |
105 |
rotations[frame, bone] = new Quaternion(keys[frame].Rotation); |
106 |
} |
107 |
|
108 |
var transforms = new Matrix[boneCount]; |
109 |
var offset = Vector2.Zero; |
110 |
|
111 |
for (int frame = 0; frame < frameCount; frame++) |
112 |
{ |
113 |
// |
114 |
// Create transforms |
115 |
// |
116 |
|
117 |
for (int bone = 0; bone < boneCount; bone++) |
118 |
{ |
119 |
transforms[bone] = Matrix.CreateFromQuaternion(rotations[frame, bone]); |
120 |
transforms[bone].Translation = body.Nodes[bone].Translation; |
121 |
} |
122 |
|
123 |
// |
124 |
// Propagate transforms through the hierarchy |
125 |
// |
126 |
|
127 |
PropagateTransforms(body.Root, transforms); |
128 |
|
129 |
// |
130 |
// Apply the root translation |
131 |
// |
132 |
|
133 |
for (int bone = 0; bone < boneCount; bone++) |
134 |
{ |
135 |
transforms[bone] *= Matrix.CreateTranslation(offset.X, Heights[frame], offset.Y); |
136 |
} |
137 |
|
138 |
// |
139 |
// Compute the vertical extent for this frame |
140 |
// |
141 |
|
142 |
var minY = 1e09f; |
143 |
var maxY = -1e09f; |
144 |
var allFramePoints = new List<Vector3>(8 * boneCount); |
145 |
|
146 |
for (int bone = 0; bone < boneCount; bone++) |
147 |
{ |
148 |
var points = body.Nodes[bone].Geometry.Points; |
149 |
var box = BoundingBox.CreateFromPoints(points); |
150 |
var sphere = BoundingSphere.CreateFromBoundingBox(box); |
151 |
var worldCorners = Vector3.Transform(box.GetCorners(), ref transforms[bone]); |
152 |
var worldCenter = Vector3.Transform(sphere.Center, ref transforms[bone]); |
153 |
|
154 |
minY = Math.Min(minY, worldCenter.Y - sphere.Radius); |
155 |
maxY = Math.Max(maxY, worldCenter.Y + sphere.Radius); |
156 |
allFramePoints.AddRange(worldCorners); |
157 |
} |
158 |
|
159 |
Positions.Add(new Position { |
160 |
Height = maxY - minY, |
161 |
YOffset = minY, |
162 |
X = offset.X, |
163 |
Z = offset.Y |
164 |
}); |
165 |
|
166 |
AllPoints.Add(allFramePoints); |
167 |
|
168 |
offset += Velocities[frame]; |
169 |
} |
170 |
} |
171 |
|
172 |
private static void PropagateTransforms(BodyNode bodyNode, Matrix[] transforms) |
173 |
{ |
174 |
foreach (var child in bodyNode.Nodes) |
175 |
{ |
176 |
transforms[child.Index] *= transforms[bodyNode.Index]; |
177 |
PropagateTransforms(child, transforms); |
178 |
} |
179 |
} |
180 |
|
181 |
public ObjectAnimation[] ToObjectAnimation(Body body) |
182 |
{ |
183 |
var anims = new ObjectAnimation[body.Nodes.Count]; |
184 |
|
185 |
foreach (var node in body.Nodes) |
186 |
{ |
187 |
anims[node.Index] = new ObjectAnimation { |
188 |
Name = Name + "_" + node.Name, |
189 |
Length = Heights.Count, |
190 |
}; |
191 |
} |
192 |
|
193 |
FillObjectAnimationFrames(anims, body.Root, null); |
194 |
|
195 |
return anims; |
196 |
} |
197 |
|
198 |
private void FillObjectAnimationFrames(ObjectAnimation[] anims, BodyNode node, BodyNode parentNode) |
199 |
{ |
200 |
var frames = new ObjectAnimationKey[Velocities.Count]; |
201 |
|
202 |
// |
203 |
// Scale is always 1. Frame length is always 1 too. |
204 |
// |
205 |
|
206 |
for (int i = 0; i < frames.Length; i++) |
207 |
{ |
208 |
frames[i] = new ObjectAnimationKey { |
209 |
Time = i, |
210 |
Scale = Vector3.One |
211 |
}; |
212 |
} |
213 |
|
214 |
// |
215 |
// Transform key frames to quaternions. |
216 |
// |
217 |
|
218 |
var keys = Rotations[node.Index]; |
219 |
var quats = new Quaternion[keys.Count]; |
220 |
var isCompressed = FrameSize == 6; |
221 |
|
222 |
for (int k = 0; k < keys.Count; k++) |
223 |
{ |
224 |
var key = keys[k]; |
225 |
|
226 |
if (isCompressed) |
227 |
{ |
228 |
quats[k] = Quaternion.CreateFromAxisAngle(Vector3.UnitX, MathHelper.ToRadians(key.Rotation.X)) |
229 |
* Quaternion.CreateFromAxisAngle(Vector3.UnitY, MathHelper.ToRadians(key.Rotation.Y)) |
230 |
* Quaternion.CreateFromAxisAngle(Vector3.UnitZ, MathHelper.ToRadians(key.Rotation.Z)); |
231 |
} |
232 |
else |
233 |
{ |
234 |
quats[k] = new Quaternion(key.Rotation); |
235 |
} |
236 |
} |
237 |
|
238 |
// |
239 |
// Interpolate the quaternions. |
240 |
// |
241 |
|
242 |
int frame = 0; |
243 |
|
244 |
for (int k = 0; k < keys.Count; k++) |
245 |
{ |
246 |
var duration = keys[k].Duration; |
247 |
|
248 |
var q1 = quats[k]; |
249 |
var q2 = (k == keys.Count - 1) ? quats[k] : quats[k + 1]; |
250 |
|
251 |
for (int t = 0; t < duration; t++) |
252 |
frames[frame++].Rotation = Quaternion.Lerp(q1, q2, (float)t / (float)duration); |
253 |
} |
254 |
|
255 |
// |
256 |
// Build translation and merge with parent anim. |
257 |
// |
258 |
|
259 |
if (parentNode == null) |
260 |
{ |
261 |
Vector2 offset = Vector2.Zero; |
262 |
|
263 |
for (int i = 0; i < frames.Length; i++) |
264 |
{ |
265 |
//frames[i].Translation = new Vector3(offset.X, 0.0f, offset.Y); |
266 |
offset += Velocities[i]; |
267 |
} |
268 |
} |
269 |
else |
270 |
{ |
271 |
for (int i = 0; i < frames.Length; i++) |
272 |
{ |
273 |
frames[i].Translation = node.Translation; |
274 |
} |
275 |
|
276 |
var parentFrames = anims[parentNode.Index].Keys; |
277 |
|
278 |
for (int i = 0; i < frames.Length; i++) |
279 |
{ |
280 |
frames[i].Rotation = parentFrames[i].Rotation * frames[i].Rotation; |
281 |
frames[i].Translation = parentFrames[i].Translation + Vector3.Transform(frames[i].Translation, parentFrames[i].Rotation); |
282 |
} |
283 |
} |
284 |
|
285 |
anims[node.Index].Keys = frames; |
286 |
|
287 |
foreach (var child in node.Nodes) |
288 |
FillObjectAnimationFrames(anims, child, node); |
289 |
} |
290 |
} |
291 |
} |