/// <summary> /// Reads direct information from track /// </summary> /// <param name="reader"></param> /// <param name="flags"></param> /// <returns></returns> private object ReadDirect(SSBHParser reader, uint flags) { if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Transform)) { var Transform = new AnimTrackTransform() { SX = reader.ReadSingle(), SY = reader.ReadSingle(), SZ = reader.ReadSingle(), RX = reader.ReadSingle(), RY = reader.ReadSingle(), RZ = reader.ReadSingle(), RW = reader.ReadSingle(), X = reader.ReadSingle(), Y = reader.ReadSingle(), Z = reader.ReadSingle(), CompensateScale = reader.ReadInt32() }; return(Transform); } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Texture)) { return(new AnimTrackTexture() { UnkFloat1 = reader.ReadSingle(), UnkFloat2 = reader.ReadSingle(), UnkFloat3 = reader.ReadSingle(), UnkFloat4 = reader.ReadSingle(), Unknown = reader.ReadInt32() }); } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Float)) { return(reader.ReadSingle()); } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.PatternIndex)) { return(reader.ReadInt32()); } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Boolean)) { return(reader.ReadByte() == 1); } if (CheckFlag(flags, 0x00FF, ANIM_TRACKFLAGS.Vector4)) { return(new AnimTrackCustomVector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } return(null); }
/// <summary> /// Reads direct information from track /// </summary> /// <param name="reader"></param> /// <param name="flags"></param> /// <returns></returns> private object ReadDirect(SsbhParser reader, uint flags) { if (CheckFlag(flags, 0x00FF, AnimTrackFlags.Transform)) { var transform = new AnimTrackTransform() { Sx = reader.ReadSingle(), Sy = reader.ReadSingle(), Sz = reader.ReadSingle(), Rx = reader.ReadSingle(), Ry = reader.ReadSingle(), Rz = reader.ReadSingle(), Rw = reader.ReadSingle(), X = reader.ReadSingle(), Y = reader.ReadSingle(), Z = reader.ReadSingle(), CompensateScale = reader.ReadInt32() }; return(transform); } if (CheckFlag(flags, 0x00FF, AnimTrackFlags.Texture)) { return(new AnimTrackTexture() { UnkFloat1 = reader.ReadSingle(), UnkFloat2 = reader.ReadSingle(), UnkFloat3 = reader.ReadSingle(), UnkFloat4 = reader.ReadSingle(), Unknown = reader.ReadInt32() }); } if (CheckFlag(flags, 0x00FF, AnimTrackFlags.Float)) { return(reader.ReadSingle()); } if (CheckFlag(flags, 0x00FF, AnimTrackFlags.PatternIndex)) { return(reader.ReadInt32()); } if (CheckFlag(flags, 0x00FF, AnimTrackFlags.Boolean)) { return(reader.ReadByte() == 1); } if (CheckFlag(flags, 0x00FF, AnimTrackFlags.Vector4)) { return(new AnimTrackCustomVector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); } return(null); }
/// <summary> /// Compresses <see cref="AnimTrackTransform" track and writes to writer/> /// </summary> /// <param name="w"></param> /// <param name="values"></param> private void CompressTransformTracks(BinaryWriter w, IList <object> values) { Quantanizer SX = new Quantanizer(); Quantanizer SY = new Quantanizer(); Quantanizer SZ = new Quantanizer(); Quantanizer RX = new Quantanizer(); Quantanizer RY = new Quantanizer(); Quantanizer RZ = new Quantanizer(); Quantanizer X = new Quantanizer(); Quantanizer Y = new Quantanizer(); Quantanizer Z = new Quantanizer(); // pre-process foreach (AnimTrackTransform transform in values) { SX.Add(transform.SX); SY.Add(transform.SY); SZ.Add(transform.SZ); RX.Add(transform.RX); RY.Add(transform.RY); RZ.Add(transform.RZ); X.Add(transform.X); Y.Add(transform.Y); Z.Add(transform.Z); } short Flags = 0; ushort BitsPerEntry = 0; bool hasScale = (!SX.Constant || !SY.Constant || !SZ.Constant); bool hasRotation = (!RX.Constant || !RY.Constant || !RZ.Constant); bool hasPosition = (!X.Constant || !Y.Constant || !Z.Constant); if (!hasScale) { Flags |= 0x02; } else { BitsPerEntry += (ushort)((SX.Constant ? 0 : SX.GetBitCount(Epsilon)) + (SY.Constant ? 0 : SY.GetBitCount(Epsilon)) + (SZ.Constant ? 0 : SZ.GetBitCount(Epsilon))); Flags |= 0x01; } if (hasRotation) { BitsPerEntry += (ushort)((RX.Constant ? 0 : RX.GetBitCount(Epsilon)) + (RY.Constant ? 0 : RY.GetBitCount(Epsilon)) + (RZ.Constant ? 0 : RZ.GetBitCount(Epsilon)) + 1); // the 1 is for extra w rotation bit Flags |= 0x04; } if (hasPosition) { BitsPerEntry += (ushort)((X.Constant ? 0 : X.GetBitCount(Epsilon)) + (Y.Constant ? 0 : Y.GetBitCount(Epsilon)) + (Z.Constant ? 0 : Z.GetBitCount(Epsilon))); Flags |= 0x08; } // Compressed Header w.Write((short)0x04); w.Write(Flags); w.Write((short)(0x10 + 0x10 * 9)); // default values offset w.Write(BitsPerEntry); w.Write(0x10 + 0x10 * 9 + sizeof(float) * 11); // compressed data start w.Write(values.Count); // frame count // write chunks w.Write(SX.Min); w.Write(SX.Max); w.Write((long)SX.GetBitCount(Epsilon)); w.Write(SY.Min); w.Write(SY.Max); w.Write((long)SY.GetBitCount(Epsilon)); w.Write(SZ.Min); w.Write(SZ.Max); w.Write((long)SZ.GetBitCount(Epsilon)); w.Write(RX.Min); w.Write(RX.Max); w.Write((long)RX.GetBitCount(Epsilon)); w.Write(RY.Min); w.Write(RY.Max); w.Write((long)RY.GetBitCount(Epsilon)); w.Write(RZ.Min); w.Write(RZ.Max); w.Write((long)RZ.GetBitCount(Epsilon)); w.Write(X.Min); w.Write(X.Max); w.Write((long)X.GetBitCount(Epsilon)); w.Write(Y.Min); w.Write(Y.Max); w.Write((long)Y.GetBitCount(Epsilon)); w.Write(Z.Min); w.Write(Z.Max); w.Write((long)Z.GetBitCount(Epsilon)); // write default values AnimTrackTransform defaultValue = (AnimTrackTransform)values[0]; w.Write(defaultValue.SX); w.Write(defaultValue.SY); w.Write(defaultValue.SZ); w.Write(defaultValue.RX); w.Write(defaultValue.RY); w.Write(defaultValue.RZ); w.Write(defaultValue.RW); w.Write(defaultValue.X); w.Write(defaultValue.Y); w.Write(defaultValue.Z); w.Write(0); // write compressed values BitWriter writer = new BitWriter(); foreach (AnimTrackTransform transform in values) { if (hasScale) { writer.WriteBits(SX.GetQuantanizedValue(transform.SX), SX.GetBitCount(Epsilon)); writer.WriteBits(SY.GetQuantanizedValue(transform.SY), SY.GetBitCount(Epsilon)); writer.WriteBits(SZ.GetQuantanizedValue(transform.SZ), SZ.GetBitCount(Epsilon)); } if (hasRotation) { writer.WriteBits(RX.GetQuantanizedValue(transform.RX), RX.GetBitCount(Epsilon)); writer.WriteBits(RY.GetQuantanizedValue(transform.RY), RY.GetBitCount(Epsilon)); writer.WriteBits(RZ.GetQuantanizedValue(transform.RZ), RZ.GetBitCount(Epsilon)); } if (hasPosition) { writer.WriteBits(X.GetQuantanizedValue(transform.X), X.GetBitCount(Epsilon)); writer.WriteBits(Y.GetQuantanizedValue(transform.Y), Y.GetBitCount(Epsilon)); writer.WriteBits(Z.GetQuantanizedValue(transform.Z), Z.GetBitCount(Epsilon)); } if (hasRotation) { // flip w bit float calculateW = (float)Math.Sqrt(Math.Abs(1 - ( RX.DecompressedValue(transform.RX) * RX.DecompressedValue(transform.RX) + RY.DecompressedValue(transform.RY) * RY.DecompressedValue(transform.RY) + RZ.DecompressedValue(transform.RZ) * RZ.DecompressedValue(transform.RZ)))); writer.WriteBits(Math.Sign((int)transform.RW) != Math.Sign((int)calculateW) ? 1 : 0, 1); } } w.Write(writer.GetBytes()); }
/// <summary> /// decompresses transform values from a track /// </summary> /// <param name="parser"></param> /// <param name="dataOffset"></param> /// <param name="header"></param> /// <returns></returns> private AnimTrackTransform[] DecompressTransform(SsbhParser parser, uint dataOffset, SsbhAnimCompressedHeader header) { AnimTrackTransform[] transforms = new AnimTrackTransform[header.FrameCount]; // PreProcess SsbhAnimCompressedItem[] items = parser.ByteToType <SsbhAnimCompressedItem>(9); parser.Seek(dataOffset + header.DefaultDataOffset); float xsca = parser.ReadSingle(); float ysca = parser.ReadSingle(); float zsca = parser.ReadSingle(); float xrot = parser.ReadSingle(); float yrot = parser.ReadSingle(); float zrot = parser.ReadSingle(); float wrot = parser.ReadSingle(); float xpos = parser.ReadSingle(); float ypos = parser.ReadSingle(); float zpos = parser.ReadSingle(); float csca = parser.ReadSingle(); parser.Seek(dataOffset + header.CompressedDataOffset); for (int frame = 0; frame < header.FrameCount; frame++) { AnimTrackTransform transform = new AnimTrackTransform() { X = xpos, Y = ypos, Z = zpos, Rx = xrot, Ry = yrot, Rz = zrot, Rw = wrot, Sx = xsca, Sy = ysca, Sz = zsca, CompensateScale = csca }; for (int itemIndex = 0; itemIndex < items.Length; itemIndex++) { // First check if this track should be parsed // TODO: Don't hard code these flags. if (!((itemIndex == 0 && (header.Flags & 0x3) == 0x3) || //isotropic scale (itemIndex >= 0 && itemIndex <= 2 && (header.Flags & 0x3) == 0x1) || //normal scale (itemIndex > 2 && itemIndex <= 5 && (header.Flags & 0x4) > 0) || (itemIndex > 5 && itemIndex <= 8 && (header.Flags & 0x8) > 0))) { continue; } var item = items[itemIndex]; // Decompress int valueBitCount = (int)item.Count; if (valueBitCount == 0) { continue; } int value = parser.ReadBits(valueBitCount); int scale = 0; for (int k = 0; k < valueBitCount; k++) { scale |= 0x1 << k; } float frameValue = Lerp(item.Start, item.End, 0, 1, value / (float)scale); if (float.IsNaN(frameValue)) { frameValue = 0; } // the Transform type relies a lot on flags if ((header.Flags & 0x3) == 0x3) { //Scale Compensate if (itemIndex == 0) { transform.CompensateScale = frameValue; } } if ((header.Flags & 0x3) == 0x1) { //Scale normal switch (itemIndex) { case 0: transform.Sx = frameValue; break; case 1: transform.Sy = frameValue; break; case 2: transform.Sz = frameValue; break; } } //Rotation and Position switch (itemIndex) { case 3: transform.Rx = frameValue; break; case 4: transform.Ry = frameValue; break; case 5: transform.Rz = frameValue; break; case 6: transform.X = frameValue; break; case 7: transform.Y = frameValue; break; case 8: transform.Z = frameValue; break; } } // Rotations have an extra bit at the end if ((header.Flags & 0x4) > 0) { bool wFlip = parser.ReadBits(1) == 1; // W is calculated transform.Rw = (float)Math.Sqrt(Math.Abs(1 - (transform.Rx * transform.Rx + transform.Ry * transform.Ry + transform.Rz * transform.Rz))); if (wFlip) { transform.Rw = -transform.Rw; } } transforms[frame] = transform; } return(transforms); }
/// <summary> /// Compresses <see cref="AnimTrackTransform"></see> track and writes to writer /// </summary> /// <param name="w"></param> /// <param name="values"></param> private void CompressTransformTracks(BinaryWriter w, IList <object> values) { Quantanizer sx = new Quantanizer(); Quantanizer sy = new Quantanizer(); Quantanizer sz = new Quantanizer(); Quantanizer rx = new Quantanizer(); Quantanizer ry = new Quantanizer(); Quantanizer rz = new Quantanizer(); Quantanizer x = new Quantanizer(); Quantanizer y = new Quantanizer(); Quantanizer z = new Quantanizer(); // pre-process foreach (AnimTrackTransform transform in values) { sx.Add(transform.Sx); sy.Add(transform.Sy); sz.Add(transform.Sz); rx.Add(transform.Rx); ry.Add(transform.Ry); rz.Add(transform.Rz); x.Add(transform.X); y.Add(transform.Y); z.Add(transform.Z); } short flags = 0; ushort bitsPerEntry = 0; bool hasScale = (!sx.Constant || !sy.Constant || !sz.Constant); bool hasRotation = (!rx.Constant || !ry.Constant || !rz.Constant); bool hasPosition = (!x.Constant || !y.Constant || !z.Constant); if (sx.GetBitCount(epsilon) == -1 || sy.GetBitCount(epsilon) == -1 || (sz.GetBitCount(epsilon) == -1 || rx.GetBitCount(epsilon) == -1) || (ry.GetBitCount(epsilon) == -1 || rz.GetBitCount(epsilon) == -1 || (x.GetBitCount(epsilon) == -1 || y.GetBitCount(epsilon) == -1)) || z.GetBitCount(epsilon) == -1) { throw new Exception("Compression Level is too small to compress!"); } if (!hasScale) { flags |= 0x02; } else { bitsPerEntry += (ushort)((sx.Constant ? 0 : sx.GetBitCount(epsilon)) + (sy.Constant ? 0 : sy.GetBitCount(epsilon)) + (sz.Constant ? 0 : sz.GetBitCount(epsilon))); flags |= 0x01; } if (hasRotation) { bitsPerEntry += (ushort)((rx.Constant ? 0 : rx.GetBitCount(epsilon)) + (ry.Constant ? 0 : ry.GetBitCount(epsilon)) + (rz.Constant ? 0 : rz.GetBitCount(epsilon)) + 1); // the 1 is for extra w rotation bit flags |= 0x04; } if (hasPosition) { bitsPerEntry += (ushort)((x.Constant ? 0 : x.GetBitCount(epsilon)) + (y.Constant ? 0 : y.GetBitCount(epsilon)) + (z.Constant ? 0 : z.GetBitCount(epsilon))); flags |= 0x08; } // Compressed Header w.Write((short)0x04); w.Write(flags); w.Write((short)160); w.Write(bitsPerEntry); w.Write(204); w.Write(values.Count); w.Write(sx.Min); w.Write(sx.Max); w.Write(hasScale ? sx.GetBitCount(epsilon) : 16L); w.Write(sy.Min); w.Write(sy.Max); w.Write(hasScale ? sy.GetBitCount(epsilon) : 16L); w.Write(sz.Min); w.Write(sz.Max); w.Write(hasScale ? sz.GetBitCount(epsilon) : 16L); w.Write(rx.Min); w.Write(rx.Max); w.Write(hasRotation ? rx.GetBitCount(epsilon) : 16L); w.Write(ry.Min); w.Write(ry.Max); w.Write(hasRotation ? ry.GetBitCount(epsilon) : 16L); w.Write(rz.Min); w.Write(rz.Max); w.Write(hasRotation ? rz.GetBitCount(epsilon) : 16L); w.Write(x.Min); w.Write(x.Max); w.Write(hasPosition ? x.GetBitCount(epsilon) : 16L); w.Write(y.Min); w.Write(y.Max); w.Write(hasPosition ? y.GetBitCount(epsilon) : 16L); w.Write(z.Min); w.Write(z.Max); w.Write(hasPosition ? z.GetBitCount(epsilon) : 16L); AnimTrackTransform defaultValue = (AnimTrackTransform)values[0]; w.Write(defaultValue.Sx); w.Write(defaultValue.Sy); w.Write(defaultValue.Sz); w.Write(defaultValue.Rx); w.Write(defaultValue.Ry); w.Write(defaultValue.Rz); w.Write(defaultValue.Rw); w.Write(defaultValue.X); w.Write(defaultValue.Y); w.Write(defaultValue.Z); w.Write(0); // write compressed values BitWriter writer = new BitWriter(); foreach (AnimTrackTransform transform in values) { if (hasScale) { writer.WriteBits(sx.GetQuantanizedValue(transform.Sx), sx.GetBitCount(epsilon)); writer.WriteBits(sy.GetQuantanizedValue(transform.Sy), sy.GetBitCount(epsilon)); writer.WriteBits(sz.GetQuantanizedValue(transform.Sz), sz.GetBitCount(epsilon)); } if (hasRotation) { writer.WriteBits(rx.GetQuantanizedValue(transform.Rx), rx.GetBitCount(epsilon)); writer.WriteBits(ry.GetQuantanizedValue(transform.Ry), ry.GetBitCount(epsilon)); writer.WriteBits(rz.GetQuantanizedValue(transform.Rz), rz.GetBitCount(epsilon)); } if (hasPosition) { writer.WriteBits(x.GetQuantanizedValue(transform.X), x.GetBitCount(epsilon)); writer.WriteBits(y.GetQuantanizedValue(transform.Y), y.GetBitCount(epsilon)); writer.WriteBits(z.GetQuantanizedValue(transform.Z), z.GetBitCount(epsilon)); } if (hasRotation) { // flip w bit float calculateW = (float)Math.Sqrt(Math.Abs(1 - ( rx.DecompressedValue(transform.Rx) * rx.DecompressedValue(transform.Rx) + ry.DecompressedValue(transform.Ry) * ry.DecompressedValue(transform.Ry) + rz.DecompressedValue(transform.Rz) * rz.DecompressedValue(transform.Rz)))); writer.WriteBits(Math.Sign(transform.Rw) != Math.Sign(calculateW) ? 1 : 0, 1); } } w.Write(writer.GetBytes()); }
/// <summary> /// decompresses transform values from a track /// </summary> /// <param name="parser"></param> /// <param name="dataOffset"></param> /// <param name="header"></param> /// <returns></returns> private AnimTrackTransform[] DecompressTransform(SSBHParser parser, uint dataOffset, SSBHAnimCompressedHeader header) { AnimTrackTransform[] transforms = new AnimTrackTransform[header.FrameCount]; // PreProcess SSBHAnimCompressedItem[] items = parser.ByteToType <SSBHAnimCompressedItem>(9); parser.Seek(dataOffset + header.DefaultDataOffset); float XSCA = parser.ReadSingle(); float YSCA = parser.ReadSingle(); float ZSCA = parser.ReadSingle(); float XROT = parser.ReadSingle(); float YROT = parser.ReadSingle(); float ZROT = parser.ReadSingle(); float WROT = parser.ReadSingle(); float XPOS = parser.ReadSingle(); float YPOS = parser.ReadSingle(); float ZPOS = parser.ReadSingle(); float CSCA = parser.ReadSingle(); parser.Seek(dataOffset + header.CompressedDataOffset); for (int frame = 0; frame < header.FrameCount; frame++) { AnimTrackTransform transform = new AnimTrackTransform() { X = XPOS, Y = YPOS, Z = ZPOS, RX = XROT, RY = YROT, RZ = ZROT, RW = WROT, SX = XSCA, SY = YSCA, SZ = ZSCA, CompensateScale = CSCA }; for (int itemIndex = 0; itemIndex < items.Length; itemIndex++) { // First check if this track should be parsed // TODO: Don't hard code these flags. if (!((itemIndex == 0 && (header.Flags & 0x3) == 0x3) || //isotropic scale (itemIndex >= 0 && itemIndex <= 2 && (header.Flags & 0x3) == 0x1) || //normal scale (itemIndex > 2 && itemIndex <= 5 && (header.Flags & 0x4) > 0) || (itemIndex > 5 && itemIndex <= 8 && (header.Flags & 0x8) > 0))) { continue; } var item = items[itemIndex]; // Decompress int valueBitCount = (int)item.Count; if (valueBitCount == 0) { continue; } int value = parser.ReadBits(valueBitCount); int scale = 0; for (int k = 0; k < valueBitCount; k++) { scale |= 0x1 << k; } float frameValue = Lerp(item.Start, item.End, 0, 1, value / (float)scale); if (float.IsNaN(frameValue)) { frameValue = 0; } // the Transform type relies a lot on flags if ((header.Flags & 0x3) == 0x3) { //Scale Compensate if (itemIndex == 0) { transform.CompensateScale = frameValue; } } if ((header.Flags & 0x3) == 0x1) { //Scale normal switch (itemIndex) { case 0: transform.SX = frameValue; break; case 1: transform.SY = frameValue; break; case 2: transform.SZ = frameValue; break; } } //Rotation and Position switch (itemIndex) { case 3: transform.RX = frameValue; break; case 4: transform.RY = frameValue; break; case 5: transform.RZ = frameValue; break; case 6: transform.X = frameValue; break; case 7: transform.Y = frameValue; break; case 8: transform.Z = frameValue; break; } } // Rotations have an extra bit at the end if ((header.Flags & 0x4) > 0) { bool wFlip = parser.ReadBits(1) == 1; // W is calculated transform.RW = (float)Math.Sqrt(Math.Abs(1 - (transform.RX * transform.RX + transform.RY * transform.RY + transform.RZ * transform.RZ))); if (wFlip) { transform.RW = -transform.RW; } } transforms[frame] = transform; } return(transforms); }
/// <summary> /// Compresses <see cref="AnimTrackTransform"></see> track and writes to writer /// </summary> /// <param name="w"></param> /// <param name="values"></param> private void CompressTransformTracks(BinaryWriter w, IList <object> values) { Quantanizer sx = new Quantanizer(); Quantanizer sy = new Quantanizer(); Quantanizer sz = new Quantanizer(); Quantanizer rx = new Quantanizer(); Quantanizer ry = new Quantanizer(); Quantanizer rz = new Quantanizer(); Quantanizer x = new Quantanizer(); Quantanizer y = new Quantanizer(); Quantanizer z = new Quantanizer(); // pre-process foreach (AnimTrackTransform transform in values) { sx.Add(transform.Sx); sy.Add(transform.Sy); sz.Add(transform.Sz); rx.Add(transform.Rx); ry.Add(transform.Ry); rz.Add(transform.Rz); x.Add(transform.X); y.Add(transform.Y); z.Add(transform.Z); } short flags = 0; ushort bitsPerEntry = 0; bool hasScale = (!sx.Constant || !sy.Constant || !sz.Constant); bool hasRotation = (!rx.Constant || !ry.Constant || !rz.Constant); bool hasPosition = (!x.Constant || !y.Constant || !z.Constant); if (!hasScale) { flags |= 0x02; } else { bitsPerEntry += (ushort)((sx.Constant ? 0 : sx.GetBitCount(epsilon)) + (sy.Constant ? 0 : sy.GetBitCount(epsilon)) + (sz.Constant ? 0 : sz.GetBitCount(epsilon))); flags |= 0x01; } if (hasRotation) { bitsPerEntry += (ushort)((rx.Constant ? 0 : rx.GetBitCount(epsilon)) + (ry.Constant ? 0 : ry.GetBitCount(epsilon)) + (rz.Constant ? 0 : rz.GetBitCount(epsilon)) + 1); // the 1 is for extra w rotation bit flags |= 0x04; } if (hasPosition) { bitsPerEntry += (ushort)((x.Constant ? 0 : x.GetBitCount(epsilon)) + (y.Constant ? 0 : y.GetBitCount(epsilon)) + (z.Constant ? 0 : z.GetBitCount(epsilon))); flags |= 0x08; } // Compressed Header w.Write((short)0x04); w.Write(flags); w.Write((short)(0x10 + 0x10 * 9)); // default values offset w.Write(bitsPerEntry); w.Write(0x10 + 0x10 * 9 + sizeof(float) * 11); // compressed data start w.Write(values.Count); // frame count // write chunks w.Write(sx.Min); w.Write(sx.Max); w.Write((long)sx.GetBitCount(epsilon)); w.Write(sy.Min); w.Write(sy.Max); w.Write((long)sy.GetBitCount(epsilon)); w.Write(sz.Min); w.Write(sz.Max); w.Write((long)sz.GetBitCount(epsilon)); w.Write(rx.Min); w.Write(rx.Max); w.Write((long)rx.GetBitCount(epsilon)); w.Write(ry.Min); w.Write(ry.Max); w.Write((long)ry.GetBitCount(epsilon)); w.Write(rz.Min); w.Write(rz.Max); w.Write((long)rz.GetBitCount(epsilon)); w.Write(x.Min); w.Write(x.Max); w.Write((long)x.GetBitCount(epsilon)); w.Write(y.Min); w.Write(y.Max); w.Write((long)y.GetBitCount(epsilon)); w.Write(z.Min); w.Write(z.Max); w.Write((long)z.GetBitCount(epsilon)); // write default values AnimTrackTransform defaultValue = (AnimTrackTransform)values[0]; w.Write(defaultValue.Sx); w.Write(defaultValue.Sy); w.Write(defaultValue.Sz); w.Write(defaultValue.Rx); w.Write(defaultValue.Ry); w.Write(defaultValue.Rz); w.Write(defaultValue.Rw); w.Write(defaultValue.X); w.Write(defaultValue.Y); w.Write(defaultValue.Z); w.Write(0); // write compressed values BitWriter writer = new BitWriter(); foreach (AnimTrackTransform transform in values) { if (hasScale) { writer.WriteBits(sx.GetQuantanizedValue(transform.Sx), sx.GetBitCount(epsilon)); writer.WriteBits(sy.GetQuantanizedValue(transform.Sy), sy.GetBitCount(epsilon)); writer.WriteBits(sz.GetQuantanizedValue(transform.Sz), sz.GetBitCount(epsilon)); } if (hasRotation) { writer.WriteBits(rx.GetQuantanizedValue(transform.Rx), rx.GetBitCount(epsilon)); writer.WriteBits(ry.GetQuantanizedValue(transform.Ry), ry.GetBitCount(epsilon)); writer.WriteBits(rz.GetQuantanizedValue(transform.Rz), rz.GetBitCount(epsilon)); } if (hasPosition) { writer.WriteBits(x.GetQuantanizedValue(transform.X), x.GetBitCount(epsilon)); writer.WriteBits(y.GetQuantanizedValue(transform.Y), y.GetBitCount(epsilon)); writer.WriteBits(z.GetQuantanizedValue(transform.Z), z.GetBitCount(epsilon)); } if (hasRotation) { // flip w bit float calculateW = (float)Math.Sqrt(Math.Abs(1 - ( rx.DecompressedValue(transform.Rx) * rx.DecompressedValue(transform.Rx) + ry.DecompressedValue(transform.Ry) * ry.DecompressedValue(transform.Ry) + rz.DecompressedValue(transform.Rz) * rz.DecompressedValue(transform.Rz)))); writer.WriteBits(Math.Sign((int)transform.Rw) != Math.Sign((int)calculateW) ? 1 : 0, 1); } } w.Write(writer.GetBytes()); }
public object[] ReadTrack(AnimTrack Track) { Console.WriteLine(Track.Name + " " + Track.Flags.ToString() + " " + Track.FrameCount + " " + Track.DataOffset.ToString("X")); List <object> output = new List <object>(); using (SSBHParser parser = new SSBHParser(new MemoryStream(AnimFile.Buffer))) { parser.Seek(Track.DataOffset); if (CheckFlag(Track.Flags, 0x00FF, ANIM_TRACKFLAGS.Boolean)) { if (CheckFlag(Track.Flags, 0xFF00, ANIM_TRACKFLAGS.Animated)) { int Unk_4 = parser.ReadInt16(); int TrackFlag = parser.ReadInt16(); int Unk1 = parser.ReadInt16(); int Unk2 = parser.ReadInt16(); int DataStart = parser.ReadInt32(); int FrameCount = parser.ReadInt32(); parser.Seek((int)Track.DataOffset + DataStart); for (int i = 0; i < FrameCount; i++) { output.Add(new AnimTrackBool(parser.ReadBits(1) == 1)); } } else if (CheckFlag(Track.Flags, 0xFF00, ANIM_TRACKFLAGS.Constant)) { output.Add(new AnimTrackBool(parser.ReadBits(1) == 1)); } } if (CheckFlag(Track.Flags, 0x00FF, ANIM_TRACKFLAGS.Vector4)) { if (CheckFlag(Track.Flags, 0xFF00, ANIM_TRACKFLAGS.Animated)) { int Unk_4 = parser.ReadInt16(); int TrackFlag = parser.ReadInt16(); int Unk1 = parser.ReadInt16(); int Unk2 = parser.ReadInt16(); int DataStart = parser.ReadInt32(); int FrameCount = parser.ReadInt32(); int[] ByteCounts = new int[9]; int[] BitCounts = new int[9]; float[] Start = new float[9]; float[] End = new float[9]; for (int i = 0; i < 4; i++) { Start[i] = parser.ReadSingle(); End[i] = parser.ReadSingle(); long Count = parser.ReadInt64(); long bytes = (Count >> 3); int bits = ((int)Count & 0x7); if ((i >= 0 && i <= 0 && (TrackFlag & 0x3) == 0x3) || //isotrophic scale (i >= 0 && i <= 2 && (TrackFlag & 0x3) == 0x1) || //normal scale (i > 2 && i <= 5 && (TrackFlag & 0x4) > 0) || (i > 5 && i <= 8 && (TrackFlag & 0x8) > 0)) { //reads { BitCounts[i] = bits; ByteCounts[i] = (int)bytes; } } } float X = parser.ReadSingle(); float Y = parser.ReadSingle(); float Z = parser.ReadSingle(); float W = parser.ReadSingle(); parser.Seek((int)Track.DataOffset + DataStart); for (int i = 0; i < FrameCount; i++) { AnimTrackCustomVector4 Vector = new AnimTrackCustomVector4(); for (int j = 0; j < 4; j++) { int ValueBitCount = ByteCounts[j] * 8 + BitCounts[j]; int Value = parser.ReadBits(ValueBitCount); int scale = 0; for (int k = 0; k < ValueBitCount; k++) { scale |= 0x1 << k; } float FrameValue = Lerp(Start[j], End[j], 0, 1, Value / (float)scale); if (float.IsNaN(FrameValue)) { FrameValue = 0; } switch (j) { case 0: if (ValueBitCount > 0) { Vector.X = FrameValue; } else { Vector.X = X; } break; case 1: if (ValueBitCount > 0) { Vector.Y = FrameValue; } else { Vector.Y = Y; } break; case 2: if (ValueBitCount > 0) { Vector.Z = FrameValue; } else { Vector.Z = Z; } break; case 3: if (ValueBitCount > 0) { Vector.W = FrameValue; } else { Vector.W = W; } break; } } output.Add(Vector); } } else if (CheckFlag(Track.Flags, 0xFF00, ANIM_TRACKFLAGS.Constant)) { output.Add(new AnimTrackCustomVector4() { X = parser.ReadSingle(), Y = parser.ReadSingle(), Z = parser.ReadSingle(), W = parser.ReadSingle() }); } } if (CheckFlag(Track.Flags, 0x00FF, ANIM_TRACKFLAGS.Transform)) { if (CheckFlag(Track.Flags, 0xFF00, ANIM_TRACKFLAGS.Animated)) { int Unk_4 = parser.ReadInt16(); int TrackFlag = parser.ReadInt16(); int Unk1 = parser.ReadInt16(); int Unk2 = parser.ReadInt16(); int DataStart = parser.ReadInt32(); int FrameCount = parser.ReadInt32(); int[] ByteCounts = new int[9]; int[] BitCounts = new int[9]; float[] Start = new float[9]; float[] End = new float[9]; for (int i = 0; i < 9; i++) { Start[i] = parser.ReadSingle(); End[i] = parser.ReadSingle(); long Count = parser.ReadInt64(); long bytes = (Count >> 3); int bits = ((int)Count & 0x7); if ((i >= 0 && i <= 0 && (TrackFlag & 0x3) == 0x3) || //isotrophic scale (i >= 0 && i <= 2 && (TrackFlag & 0x3) == 0x1) || //normal scale (i > 2 && i <= 5 && (TrackFlag & 0x4) > 0) || (i > 5 && i <= 8 && (TrackFlag & 0x8) > 0)) { //reads { BitCounts[i] = bits; ByteCounts[i] = (int)bytes; } } } float XSCA = parser.ReadSingle(); float YSCA = parser.ReadSingle(); float ZSCA = parser.ReadSingle(); float XROT = parser.ReadSingle(); float YROT = parser.ReadSingle(); float ZROT = parser.ReadSingle(); float WROT = parser.ReadSingle(); float XPOS = parser.ReadSingle(); float YPOS = parser.ReadSingle(); float ZPOS = parser.ReadSingle(); parser.ReadInt32(); // ???? parser.Seek((int)Track.DataOffset + DataStart); for (int i = 0; i < FrameCount; i++) { AnimTrackTransform Transform = new AnimTrackTransform(); for (int j = 0; j < 9; j++) { int ValueBitCount = ByteCounts[j] * 8 + BitCounts[j]; int Value = parser.ReadBits(ValueBitCount); int scale = 0; for (int k = 0; k < ValueBitCount; k++) { scale |= 0x1 << k; } float FrameValue = Lerp(Start[j], End[j], 0, 1, Value / (float)scale); if (float.IsNaN(FrameValue)) { FrameValue = 0; } if ((TrackFlag & 0x3) == 0x3) { //Scale Isotropic if (j == 0) { Transform.SX = FrameValue; Transform.SY = FrameValue; Transform.SZ = FrameValue; } } else if ((TrackFlag & 0x3) == 0x1) { //Scale normal switch (j) { case 0: if (ValueBitCount > 0) { Transform.SX = FrameValue; } else { Transform.SX = XSCA; } break; case 1: if (ValueBitCount > 0) { Transform.SY = FrameValue; } else { Transform.SY = YSCA; } break; case 2: if (ValueBitCount > 0) { Transform.SZ = FrameValue; } else { Transform.SZ = ZSCA; } break; } } else { Transform.SX = XSCA; Transform.SY = YSCA; Transform.SZ = ZSCA; } //Rotation if ((TrackFlag & 0x4) > 0) { switch (j) { case 3: if (ValueBitCount > 0) { Transform.RX = FrameValue; } else { Transform.RX = XROT; } break; case 4: if (ValueBitCount > 0) { Transform.RY = FrameValue; } else { Transform.RY = YROT; } break; case 5: if (ValueBitCount > 0) { Transform.RZ = FrameValue; } else { Transform.RZ = ZROT; } break; } } else { Transform.RX = XROT; Transform.RY = YROT; Transform.RZ = ZROT; Transform.RW = WROT; } // Position if ((TrackFlag & 0x8) > 0) { switch (j) { case 6: if (ValueBitCount > 0) { Transform.X = FrameValue; } else { Transform.X = XPOS; } break; case 7: if (ValueBitCount > 0) { Transform.Y = FrameValue; } else { Transform.Y = YPOS; } break; case 8: if (ValueBitCount > 0) { Transform.Z = FrameValue; } else { Transform.Z = ZPOS; } break; } } else { Transform.X = XPOS; Transform.Y = YPOS; Transform.Z = ZPOS; } } if ((TrackFlag & 0x4) > 0) { // Rotation w bool Wflip = parser.ReadBits(1) == 1;// (TrackFlag & 0x1) == 0 ? parser.ReadBits(1) == 1 : true; Transform.RW = (float)Math.Sqrt(Math.Abs(1 - (Transform.RX * Transform.RX + Transform.RY * Transform.RY + Transform.RZ * Transform.RZ))); if (Wflip) { Transform.RW = -Transform.RW; } } output.Add(Transform); } } else if (CheckFlag(Track.Flags, 0xFF00, ANIM_TRACKFLAGS.ConstTransform)) { output.Add(new AnimTrackTransform() { SX = parser.ReadSingle(), SY = parser.ReadSingle(), SZ = parser.ReadSingle(), RX = parser.ReadSingle(), RY = parser.ReadSingle(), RZ = parser.ReadSingle(), RW = parser.ReadSingle(), X = parser.ReadSingle(), Y = parser.ReadSingle(), Z = parser.ReadSingle(), }); parser.ReadInt32(); // ???? } } } return(output.ToArray()); }