[1114] | 1 | using System;
|
---|
| 2 | using System.Collections.Generic;
|
---|
| 3 | using Oni.Xml;
|
---|
| 4 | using Oni.Metadata;
|
---|
| 5 |
|
---|
| 6 | namespace Oni.Totoro
|
---|
| 7 | {
|
---|
| 8 | internal class AnimationDatWriter
|
---|
| 9 | {
|
---|
| 10 | private Animation animation;
|
---|
| 11 | private List<DatExtent> extents;
|
---|
| 12 | private DatExtentInfo extentInfo;
|
---|
| 13 | private Importer importer;
|
---|
| 14 | private BinaryWriter dat;
|
---|
| 15 | private BinaryWriter raw;
|
---|
| 16 |
|
---|
| 17 | #region private class DatExtent
|
---|
| 18 |
|
---|
| 19 | private class DatExtent
|
---|
| 20 | {
|
---|
| 21 | public readonly int Frame;
|
---|
| 22 | public readonly AttackExtent Extent;
|
---|
| 23 |
|
---|
| 24 | public DatExtent(int frame, AttackExtent extent)
|
---|
| 25 | {
|
---|
| 26 | this.Frame = frame;
|
---|
| 27 | this.Extent = extent;
|
---|
| 28 | }
|
---|
| 29 | }
|
---|
| 30 |
|
---|
| 31 | #endregion
|
---|
| 32 | #region private class DatExtentInfo
|
---|
| 33 |
|
---|
| 34 | private class DatExtentInfo
|
---|
| 35 | {
|
---|
| 36 | public float MaxDistance;
|
---|
| 37 | public float MinY = 1e09f;
|
---|
| 38 | public float MaxY = -1e09f;
|
---|
| 39 | public readonly DatExtentInfoFrame FirstExtent = new DatExtentInfoFrame();
|
---|
| 40 | public readonly DatExtentInfoFrame MaxExtent = new DatExtentInfoFrame();
|
---|
| 41 | }
|
---|
| 42 |
|
---|
| 43 | #endregion
|
---|
| 44 | #region private class DatExtentInfoFrame
|
---|
| 45 |
|
---|
| 46 | private class DatExtentInfoFrame
|
---|
| 47 | {
|
---|
| 48 | public int Frame = -1;
|
---|
| 49 | public int Attack;
|
---|
| 50 | public int AttackOffset;
|
---|
| 51 | public Vector2 Location;
|
---|
| 52 | public float Height;
|
---|
| 53 | public float Length;
|
---|
| 54 | public float MinY;
|
---|
| 55 | public float MaxY;
|
---|
| 56 | public float Angle;
|
---|
| 57 | }
|
---|
| 58 |
|
---|
| 59 | #endregion
|
---|
| 60 |
|
---|
| 61 | private AnimationDatWriter()
|
---|
| 62 | {
|
---|
| 63 | }
|
---|
| 64 |
|
---|
| 65 | public static void Write(Animation animation, Importer importer, BinaryWriter dat)
|
---|
| 66 | {
|
---|
| 67 | var writer = new AnimationDatWriter
|
---|
| 68 | {
|
---|
| 69 | animation = animation,
|
---|
| 70 | importer = importer,
|
---|
| 71 | dat = dat,
|
---|
| 72 | raw = importer.RawWriter
|
---|
| 73 | };
|
---|
| 74 |
|
---|
| 75 | writer.WriteAnimation();
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | private void WriteAnimation()
|
---|
| 79 | {
|
---|
| 80 | extentInfo = new DatExtentInfo();
|
---|
| 81 | extents = new List<DatExtent>();
|
---|
| 82 |
|
---|
| 83 | if (animation.Attacks.Count > 0)
|
---|
| 84 | {
|
---|
| 85 | if (animation.Attacks[0].Extents.Count == 0)
|
---|
| 86 | GenerateExtentInfo();
|
---|
| 87 |
|
---|
| 88 | foreach (var attack in animation.Attacks)
|
---|
| 89 | {
|
---|
| 90 | int frame = attack.Start;
|
---|
| 91 |
|
---|
| 92 | foreach (var extent in attack.Extents)
|
---|
| 93 | extents.Add(new DatExtent(frame++, extent));
|
---|
| 94 | }
|
---|
| 95 |
|
---|
| 96 | GenerateExtentSummary();
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | var rotations = animation.Rotations;
|
---|
| 100 | int frameSize = animation.FrameSize;
|
---|
| 101 |
|
---|
| 102 | if (frameSize == 16 && (animation.Flags & AnimationFlags.Overlay) == 0)
|
---|
| 103 | {
|
---|
| 104 | rotations = CompressFrames(rotations);
|
---|
| 105 | frameSize = 6;
|
---|
| 106 | }
|
---|
| 107 |
|
---|
| 108 | dat.Write(0);
|
---|
| 109 | WriteRawArray(animation.Heights, x => raw.Write(x));
|
---|
| 110 | WriteRawArray(animation.Velocities, x => raw.Write(x));
|
---|
| 111 | WriteRawArray(animation.Attacks, Write);
|
---|
| 112 | WriteRawArray(animation.SelfDamage, Write);
|
---|
| 113 | WriteRawArray(animation.MotionBlur, Write);
|
---|
| 114 | WriteRawArray(animation.Shortcuts, Write);
|
---|
| 115 | WriteThrowInfo();
|
---|
| 116 | WriteRawArray(animation.Footsteps, Write);
|
---|
| 117 | WriteRawArray(animation.Particles, Write);
|
---|
| 118 | WriteRawArray(animation.Positions, Write);
|
---|
| 119 | WriteRotations(rotations, frameSize);
|
---|
| 120 | WriteRawArray(animation.Sounds, Write);
|
---|
| 121 | dat.Write((int)animation.Flags);
|
---|
| 122 |
|
---|
| 123 | if (!string.IsNullOrEmpty(animation.DirectAnimations[0]))
|
---|
| 124 | dat.Write(importer.CreateInstance(TemplateTag.TRAM, animation.DirectAnimations[0]));
|
---|
| 125 | else
|
---|
| 126 | dat.Write(0);
|
---|
| 127 |
|
---|
| 128 | if (!string.IsNullOrEmpty(animation.DirectAnimations[1]))
|
---|
| 129 | dat.Write(importer.CreateInstance(TemplateTag.TRAM, animation.DirectAnimations[1]));
|
---|
| 130 | else
|
---|
| 131 | dat.Write(0);
|
---|
| 132 |
|
---|
| 133 | dat.Write((int)animation.OverlayUsedBones);
|
---|
| 134 | dat.Write((int)animation.OverlayReplacedBones);
|
---|
| 135 | dat.Write(animation.FinalRotation);
|
---|
| 136 | dat.Write((ushort)animation.Direction);
|
---|
| 137 | dat.WriteUInt16(animation.Vocalization);
|
---|
| 138 | WriteExtentInfo();
|
---|
| 139 | dat.Write(animation.Impact, 16);
|
---|
| 140 | dat.WriteUInt16(animation.HardPause);
|
---|
| 141 | dat.WriteUInt16(animation.SoftPause);
|
---|
| 142 | dat.Write(animation.Sounds.Count);
|
---|
| 143 | dat.Skip(6);
|
---|
| 144 | dat.WriteUInt16(60);
|
---|
| 145 | dat.WriteUInt16(frameSize);
|
---|
| 146 | dat.WriteUInt16((ushort)animation.Type);
|
---|
| 147 | dat.WriteUInt16((ushort)animation.AimingType);
|
---|
| 148 | dat.WriteUInt16((ushort)animation.FromState);
|
---|
| 149 | dat.WriteUInt16((ushort)animation.ToState);
|
---|
| 150 | dat.WriteUInt16(rotations.Count);
|
---|
| 151 | dat.WriteUInt16(animation.Velocities.Count);
|
---|
| 152 | dat.WriteUInt16(animation.Velocities.Count);
|
---|
| 153 | dat.WriteUInt16((ushort)animation.Varient);
|
---|
| 154 | dat.Skip(2);
|
---|
| 155 | dat.WriteUInt16(animation.AtomicStart);
|
---|
| 156 | dat.WriteUInt16(animation.AtomicEnd);
|
---|
| 157 | dat.WriteUInt16(animation.InterpolationEnd);
|
---|
| 158 | dat.WriteUInt16(animation.InterpolationMax);
|
---|
| 159 | dat.WriteUInt16(animation.ActionFrame);
|
---|
| 160 | dat.WriteUInt16(animation.FirstLevelAvailable);
|
---|
| 161 | dat.WriteByte(animation.InvulnerableStart);
|
---|
| 162 | dat.WriteByte(animation.InvulnerableEnd);
|
---|
| 163 | dat.WriteByte(animation.Attacks.Count);
|
---|
| 164 | dat.WriteByte(animation.SelfDamage.Count);
|
---|
| 165 | dat.WriteByte(animation.MotionBlur.Count);
|
---|
| 166 | dat.WriteByte(animation.Shortcuts.Count);
|
---|
| 167 | dat.WriteByte(animation.Footsteps.Count);
|
---|
| 168 | dat.WriteByte(animation.Particles.Count);
|
---|
| 169 | }
|
---|
| 170 |
|
---|
| 171 | private void WriteRotations(List<List<KeyFrame>> rotations, int frameSize)
|
---|
| 172 | {
|
---|
| 173 | dat.Write(raw.Align32());
|
---|
| 174 |
|
---|
| 175 | var offsets = new ushort[rotations.Count];
|
---|
| 176 |
|
---|
| 177 | offsets[0] = (ushort)(rotations.Count * 2);
|
---|
| 178 |
|
---|
| 179 | for (int i = 1; i < offsets.Length; i++)
|
---|
| 180 | offsets[i] = (ushort)(offsets[i - 1] + rotations[i - 1].Count * (frameSize + 1) - 1);
|
---|
| 181 |
|
---|
| 182 | raw.Write(offsets);
|
---|
| 183 |
|
---|
| 184 | foreach (var keys in rotations)
|
---|
| 185 | {
|
---|
| 186 | foreach (var key in keys)
|
---|
| 187 | {
|
---|
| 188 | switch (frameSize)
|
---|
| 189 | {
|
---|
| 190 | case 6:
|
---|
| 191 | raw.WriteInt16((short)(Math.Round(key.Rotation.X / 180.0f * 32767.5f)));
|
---|
| 192 | raw.WriteInt16((short)(Math.Round(key.Rotation.Y / 180.0f * 32767.5f)));
|
---|
| 193 | raw.WriteInt16((short)(Math.Round(key.Rotation.Z / 180.0f * 32767.5f)));
|
---|
| 194 | break;
|
---|
| 195 |
|
---|
| 196 | case 16:
|
---|
| 197 | raw.Write(new Quaternion(key.Rotation));
|
---|
| 198 | break;
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | if (key != keys.Last())
|
---|
| 202 | raw.WriteByte(key.Duration);
|
---|
| 203 | }
|
---|
| 204 | }
|
---|
| 205 | }
|
---|
| 206 |
|
---|
| 207 | private void WriteThrowInfo()
|
---|
| 208 | {
|
---|
| 209 | if (animation.ThrowSource == null)
|
---|
| 210 | {
|
---|
| 211 | dat.Write(0);
|
---|
| 212 | return;
|
---|
| 213 | }
|
---|
| 214 |
|
---|
| 215 | dat.Write(raw.Align32());
|
---|
| 216 |
|
---|
| 217 | raw.Write(animation.ThrowSource.Position);
|
---|
| 218 | raw.Write(animation.ThrowSource.Angle);
|
---|
| 219 | raw.Write(animation.ThrowSource.Distance);
|
---|
| 220 | raw.WriteUInt16((ushort)animation.ThrowSource.Type);
|
---|
| 221 | }
|
---|
| 222 |
|
---|
| 223 | private void WriteExtentInfo()
|
---|
| 224 | {
|
---|
| 225 | dat.Write(extentInfo.MaxDistance);
|
---|
| 226 | dat.Write(extentInfo.MinY);
|
---|
| 227 | dat.Write(extentInfo.MaxY);
|
---|
| 228 | dat.Write(animation.AttackRing);
|
---|
| 229 | Write(extentInfo.FirstExtent);
|
---|
| 230 | Write(extentInfo.MaxExtent);
|
---|
| 231 | dat.Write(0);
|
---|
| 232 | dat.Write(extents.Count);
|
---|
| 233 | WriteRawArray(extents, Write);
|
---|
| 234 | }
|
---|
| 235 |
|
---|
| 236 | private void Write(DatExtentInfoFrame info)
|
---|
| 237 | {
|
---|
| 238 | dat.WriteInt16(info.Frame);
|
---|
| 239 | dat.WriteByte(info.Attack);
|
---|
| 240 | dat.WriteByte(info.AttackOffset);
|
---|
| 241 | dat.Write(info.Location);
|
---|
| 242 | dat.Write(info.Height);
|
---|
| 243 | dat.Write(info.Length);
|
---|
| 244 | dat.Write(info.MinY);
|
---|
| 245 | dat.Write(info.MaxY);
|
---|
| 246 | dat.Write(info.Angle);
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 | private void Write(Position position)
|
---|
| 250 | {
|
---|
| 251 | raw.Write((short)Math.Round(position.X * 100.0f));
|
---|
| 252 | raw.Write((short)Math.Round(position.Z * 100.0f));
|
---|
| 253 | raw.Write((ushort)Math.Round(position.Height * 100.0f));
|
---|
| 254 | raw.Write((short)Math.Round(position.YOffset * 100.0f));
|
---|
| 255 | }
|
---|
| 256 |
|
---|
| 257 | private void Write(Damage damage)
|
---|
| 258 | {
|
---|
| 259 | raw.WriteUInt16(damage.Points);
|
---|
| 260 | raw.WriteUInt16(damage.Frame);
|
---|
| 261 | }
|
---|
| 262 |
|
---|
| 263 | private void Write(Shortcut shortcut)
|
---|
| 264 | {
|
---|
| 265 | raw.WriteUInt16((ushort)shortcut.FromState);
|
---|
| 266 | raw.WriteUInt16(shortcut.Length);
|
---|
| 267 | raw.Write(shortcut.ReplaceAtomic ? 1 : 0);
|
---|
| 268 | }
|
---|
| 269 |
|
---|
| 270 | private void Write(Footstep footstep)
|
---|
| 271 | {
|
---|
| 272 | raw.WriteUInt16(footstep.Frame);
|
---|
| 273 | raw.WriteUInt16((ushort)footstep.Type);
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | private void Write(Sound sound)
|
---|
| 277 | {
|
---|
| 278 | raw.Write(sound.Name, 32);
|
---|
| 279 | raw.WriteUInt16(sound.Start);
|
---|
| 280 | }
|
---|
| 281 |
|
---|
| 282 | private void Write(Particle particle)
|
---|
| 283 | {
|
---|
| 284 | raw.WriteUInt16(particle.Start);
|
---|
| 285 | raw.WriteUInt16(particle.End);
|
---|
| 286 | raw.Write((int)particle.Bone);
|
---|
| 287 | raw.Write(particle.Name, 16);
|
---|
| 288 | }
|
---|
| 289 |
|
---|
| 290 | private void Write(MotionBlur m)
|
---|
| 291 | {
|
---|
| 292 | raw.Write((int)m.Bones);
|
---|
| 293 | raw.WriteUInt16(m.Start);
|
---|
| 294 | raw.WriteUInt16(m.End);
|
---|
| 295 | raw.WriteByte(m.Lifetime);
|
---|
| 296 | raw.WriteByte(m.Alpha);
|
---|
| 297 | raw.WriteByte(m.Interval);
|
---|
| 298 | raw.WriteByte(0);
|
---|
| 299 | }
|
---|
| 300 |
|
---|
| 301 | private void Write(DatExtent extent)
|
---|
| 302 | {
|
---|
| 303 | raw.WriteInt16(extent.Frame);
|
---|
| 304 | raw.Write((short)Math.Round(extent.Extent.Angle * 65535.0f / 360.0f));
|
---|
| 305 | raw.Write((ushort)Math.Round(extent.Extent.Length * 100.0f));
|
---|
| 306 | raw.WriteInt16(0);
|
---|
| 307 | raw.Write((short)Math.Round(extent.Extent.MinY * 100.0f));
|
---|
| 308 | raw.Write((short)Math.Round(extent.Extent.MaxY * 100.0f));
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | private void Write(Attack attack)
|
---|
| 312 | {
|
---|
| 313 | raw.Write((int)attack.Bones);
|
---|
| 314 | raw.Write(attack.Knockback);
|
---|
| 315 | raw.Write((int)attack.Flags);
|
---|
| 316 | raw.WriteInt16(attack.HitPoints);
|
---|
| 317 | raw.WriteInt16(attack.Start);
|
---|
| 318 | raw.WriteInt16(attack.End);
|
---|
| 319 | raw.WriteInt16((short)attack.HitType);
|
---|
| 320 | raw.WriteInt16(attack.HitLength);
|
---|
| 321 | raw.WriteInt16(attack.StunLength);
|
---|
| 322 | raw.WriteInt16(attack.StaggerLength);
|
---|
| 323 | raw.WriteInt16(0);
|
---|
| 324 | raw.Write(0);
|
---|
| 325 | }
|
---|
| 326 |
|
---|
| 327 | private void WriteRawArray<T>(List<T> list, Action<T> writeElement)
|
---|
| 328 | {
|
---|
| 329 | if (list.Count == 0)
|
---|
| 330 | {
|
---|
| 331 | dat.Write(0);
|
---|
| 332 | return;
|
---|
| 333 | }
|
---|
| 334 |
|
---|
| 335 | dat.Write(raw.Align32());
|
---|
| 336 |
|
---|
| 337 | foreach (T t in list)
|
---|
| 338 | writeElement(t);
|
---|
| 339 | }
|
---|
| 340 |
|
---|
| 341 | private void GenerateExtentInfo()
|
---|
| 342 | {
|
---|
| 343 | float[] attackRing = animation.AttackRing;
|
---|
| 344 |
|
---|
| 345 | Array.Clear(attackRing, 0, attackRing.Length);
|
---|
| 346 |
|
---|
| 347 | foreach (var attack in animation.Attacks)
|
---|
| 348 | {
|
---|
| 349 | attack.Extents.Clear();
|
---|
| 350 |
|
---|
| 351 | for (int frame = attack.Start; frame <= attack.End; frame++)
|
---|
| 352 | {
|
---|
| 353 | var position = animation.Positions[frame].XZ;
|
---|
| 354 | var framePoints = animation.AllPoints[frame];
|
---|
| 355 |
|
---|
| 356 | for (int j = 0; j < framePoints.Count / 8; j++)
|
---|
| 357 | {
|
---|
| 358 | if ((attack.Bones & (BoneMask)(1 << j)) == 0)
|
---|
| 359 | continue;
|
---|
| 360 |
|
---|
| 361 | for (int k = j * 8; k < (j + 1) * 8; k++)
|
---|
| 362 | {
|
---|
| 363 | var point = framePoints[k];
|
---|
| 364 | var delta = point.XZ - animation.Positions[0].XZ;
|
---|
| 365 |
|
---|
| 366 | float distance = delta.Length();
|
---|
| 367 | float angle = FMath.Atan2(delta.X, delta.Y);
|
---|
| 368 |
|
---|
| 369 | if (angle < 0.0f)
|
---|
| 370 | angle += MathHelper.TwoPi;
|
---|
| 371 |
|
---|
| 372 | for (int r = 0; r < attackRing.Length; r++)
|
---|
| 373 | {
|
---|
| 374 | float ringAngle = r * MathHelper.TwoPi / attackRing.Length;
|
---|
| 375 |
|
---|
| 376 | if (Math.Abs(ringAngle - angle) < MathHelper.ToRadians(30.0f))
|
---|
| 377 | attackRing[r] = Math.Max(attackRing[r], distance);
|
---|
| 378 | }
|
---|
| 379 | }
|
---|
| 380 | }
|
---|
| 381 |
|
---|
| 382 | float minHeight = +1e09f;
|
---|
| 383 | float maxHeight = -1e09f;
|
---|
| 384 | float maxDistance = -1e09f;
|
---|
| 385 | float maxAngle = 0.0f;
|
---|
| 386 |
|
---|
| 387 | for (int j = 0; j < framePoints.Count / 8; j++)
|
---|
| 388 | {
|
---|
| 389 | if ((attack.Bones & (BoneMask)(1 << j)) == 0)
|
---|
| 390 | continue;
|
---|
| 391 |
|
---|
| 392 | for (int k = j * 8; k < (j + 1) * 8; k++)
|
---|
| 393 | {
|
---|
| 394 | var point = framePoints[k];
|
---|
| 395 | var delta = point.XZ - position;
|
---|
| 396 |
|
---|
| 397 | float distance;
|
---|
| 398 |
|
---|
| 399 | switch (animation.Direction)
|
---|
| 400 | {
|
---|
| 401 | case Direction.Forward:
|
---|
| 402 | distance = delta.Y;
|
---|
| 403 | break;
|
---|
| 404 | case Direction.Left:
|
---|
| 405 | distance = delta.X;
|
---|
| 406 | break;
|
---|
| 407 | case Direction.Right:
|
---|
| 408 | distance = -delta.X;
|
---|
| 409 | break;
|
---|
| 410 | case Direction.Backward:
|
---|
| 411 | distance = -delta.Y;
|
---|
| 412 | break;
|
---|
| 413 | default:
|
---|
| 414 | distance = delta.Length();
|
---|
| 415 | break;
|
---|
| 416 | }
|
---|
| 417 |
|
---|
| 418 | if (distance > maxDistance)
|
---|
| 419 | {
|
---|
| 420 | maxDistance = distance;
|
---|
| 421 | maxAngle = FMath.Atan2(delta.X, delta.Y);
|
---|
| 422 | }
|
---|
| 423 |
|
---|
| 424 | minHeight = Math.Min(minHeight, point.Y);
|
---|
| 425 | maxHeight = Math.Max(maxHeight, point.Y);
|
---|
| 426 | }
|
---|
| 427 | }
|
---|
| 428 |
|
---|
| 429 | maxDistance = Math.Max(maxDistance, 0.0f);
|
---|
| 430 |
|
---|
| 431 | if (maxAngle < 0)
|
---|
| 432 | maxAngle += MathHelper.TwoPi;
|
---|
| 433 |
|
---|
| 434 | attack.Extents.Add(new AttackExtent
|
---|
| 435 | {
|
---|
| 436 | Angle = MathHelper.ToDegrees(maxAngle),
|
---|
| 437 | Length = maxDistance,
|
---|
| 438 | MinY = minHeight,
|
---|
| 439 | MaxY = maxHeight
|
---|
| 440 | });
|
---|
| 441 | }
|
---|
| 442 | }
|
---|
| 443 | }
|
---|
| 444 |
|
---|
| 445 | private void GenerateExtentSummary()
|
---|
| 446 | {
|
---|
| 447 | if (extents.Count == 0)
|
---|
| 448 | return;
|
---|
| 449 |
|
---|
| 450 | var positions = animation.Positions;
|
---|
| 451 | var attacks = animation.Attacks;
|
---|
| 452 | var heights = animation.Heights;
|
---|
| 453 |
|
---|
| 454 | float minY = float.MaxValue, maxY = float.MinValue;
|
---|
| 455 |
|
---|
| 456 | foreach (var datExtent in extents)
|
---|
| 457 | {
|
---|
| 458 | minY = Math.Min(minY, datExtent.Extent.MinY);
|
---|
| 459 | maxY = Math.Max(maxY, datExtent.Extent.MaxY);
|
---|
| 460 | }
|
---|
| 461 |
|
---|
| 462 | var firstExtent = extents[0];
|
---|
| 463 | var maxExtent = firstExtent;
|
---|
| 464 |
|
---|
| 465 | foreach (var datExtent in extents)
|
---|
| 466 | {
|
---|
| 467 | if (datExtent.Extent.Length + positions[datExtent.Frame].Z > maxExtent.Extent.Length + positions[maxExtent.Frame].Z)
|
---|
| 468 | maxExtent = datExtent;
|
---|
| 469 | }
|
---|
| 470 |
|
---|
| 471 | int maxAttackIndex = 0, maxAttackOffset = 0;
|
---|
| 472 |
|
---|
| 473 | for (int i = 0; i < attacks.Count; i++)
|
---|
| 474 | {
|
---|
| 475 | var attack = attacks[i];
|
---|
| 476 |
|
---|
| 477 | if (attack.Start <= maxExtent.Frame && maxExtent.Frame <= attack.End)
|
---|
| 478 | {
|
---|
| 479 | maxAttackIndex = i;
|
---|
| 480 | maxAttackOffset = maxExtent.Frame - attack.Start;
|
---|
| 481 | break;
|
---|
| 482 | }
|
---|
| 483 | }
|
---|
| 484 |
|
---|
| 485 | extentInfo.MaxDistance = animation.AttackRing.Max();
|
---|
| 486 | extentInfo.MinY = minY;
|
---|
| 487 | extentInfo.MaxY = maxY;
|
---|
| 488 |
|
---|
| 489 | extentInfo.FirstExtent.Frame = firstExtent.Frame;
|
---|
| 490 | extentInfo.FirstExtent.Attack = 0;
|
---|
| 491 | extentInfo.FirstExtent.AttackOffset = 0;
|
---|
| 492 | extentInfo.FirstExtent.Location.X = positions[firstExtent.Frame].X;
|
---|
| 493 | extentInfo.FirstExtent.Location.Y = -positions[firstExtent.Frame].Z;
|
---|
| 494 | extentInfo.FirstExtent.Height = heights[firstExtent.Frame];
|
---|
| 495 | extentInfo.FirstExtent.Angle = MathHelper.ToRadians(firstExtent.Extent.Angle);
|
---|
| 496 | extentInfo.FirstExtent.Length = firstExtent.Extent.Length;
|
---|
| 497 | extentInfo.FirstExtent.MinY = FMath.Round(firstExtent.Extent.MinY, 2);
|
---|
| 498 | extentInfo.FirstExtent.MaxY = firstExtent.Extent.MaxY;
|
---|
| 499 |
|
---|
| 500 | if ((animation.Flags & AnimationFlags.ThrowTarget) == 0)
|
---|
| 501 | {
|
---|
| 502 | extentInfo.MaxExtent.Frame = maxExtent.Frame;
|
---|
| 503 | extentInfo.MaxExtent.Attack = maxAttackIndex;
|
---|
| 504 | extentInfo.MaxExtent.AttackOffset = maxAttackOffset;
|
---|
| 505 | extentInfo.MaxExtent.Location.X = positions[maxExtent.Frame].X;
|
---|
| 506 | extentInfo.MaxExtent.Location.Y = -positions[maxExtent.Frame].Z;
|
---|
| 507 | extentInfo.MaxExtent.Height = heights[maxExtent.Frame];
|
---|
| 508 | extentInfo.MaxExtent.Angle = MathHelper.ToRadians(maxExtent.Extent.Angle);
|
---|
| 509 | extentInfo.MaxExtent.Length = maxExtent.Extent.Length;
|
---|
| 510 | extentInfo.MaxExtent.MinY = maxExtent.Extent.MinY;
|
---|
| 511 | extentInfo.MaxExtent.MaxY = FMath.Round(maxExtent.Extent.MaxY, 2);
|
---|
| 512 | }
|
---|
| 513 | }
|
---|
| 514 |
|
---|
| 515 | private List<List<KeyFrame>> CompressFrames(List<List<KeyFrame>> tracks)
|
---|
| 516 | {
|
---|
| 517 | float tolerance = 0.5f;
|
---|
| 518 | float cosTolerance = FMath.Cos(MathHelper.ToRadians(tolerance) * 0.5f);
|
---|
| 519 | var newTracks = new List<List<KeyFrame>>();
|
---|
| 520 |
|
---|
| 521 | foreach (var keys in tracks)
|
---|
| 522 | {
|
---|
| 523 | var newFrames = new List<KeyFrame>(keys.Count);
|
---|
| 524 |
|
---|
| 525 | for (int i = 0; i < keys.Count;)
|
---|
| 526 | {
|
---|
| 527 | var key = keys[i];
|
---|
| 528 |
|
---|
| 529 | int duration = key.Duration;
|
---|
| 530 | var q0 = new Quaternion(key.Rotation);
|
---|
| 531 |
|
---|
| 532 | if (duration == 1)
|
---|
| 533 | {
|
---|
| 534 | for (int j = i + 2; j < keys.Count; j++)
|
---|
| 535 | {
|
---|
| 536 | if (!IsLinearRange(keys, i, j, cosTolerance))
|
---|
| 537 | break;
|
---|
| 538 |
|
---|
| 539 | duration = j - i;
|
---|
| 540 | }
|
---|
| 541 | }
|
---|
| 542 |
|
---|
| 543 | var eulerXYZ = q0.ToEulerXYZ();
|
---|
| 544 |
|
---|
| 545 | newFrames.Add(new KeyFrame
|
---|
| 546 | {
|
---|
| 547 | Duration = duration,
|
---|
| 548 | Rotation = {
|
---|
| 549 | X = eulerXYZ.X,
|
---|
| 550 | Y = eulerXYZ.Y,
|
---|
| 551 | Z = eulerXYZ.Z
|
---|
| 552 | }
|
---|
| 553 | });
|
---|
| 554 |
|
---|
| 555 | i += duration;
|
---|
| 556 | }
|
---|
| 557 |
|
---|
| 558 | newTracks.Add(newFrames);
|
---|
| 559 | }
|
---|
| 560 |
|
---|
| 561 | return newTracks;
|
---|
| 562 | }
|
---|
| 563 |
|
---|
| 564 | private static bool IsLinearRange(List<KeyFrame> frames, int first, int last, float tolerance)
|
---|
| 565 | {
|
---|
| 566 | var q0 = new Quaternion(frames[first].Rotation);
|
---|
| 567 | var q1 = new Quaternion(frames[last].Rotation);
|
---|
| 568 | float length = last - first;
|
---|
| 569 |
|
---|
| 570 | for (int i = first + 1; i < last; ++i)
|
---|
| 571 | {
|
---|
| 572 | float t = (i - first) / length;
|
---|
| 573 |
|
---|
| 574 | var linear = Quaternion.Lerp(q0, q1, t);
|
---|
| 575 | var real = new Quaternion(frames[i].Rotation);
|
---|
| 576 | var error = Quaternion.Conjugate(linear) * real;
|
---|
| 577 |
|
---|
| 578 | if (Math.Abs(error.W) < tolerance)
|
---|
| 579 | return false;
|
---|
| 580 | }
|
---|
| 581 |
|
---|
| 582 | return true;
|
---|
| 583 | }
|
---|
| 584 | }
|
---|
| 585 | }
|
---|