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