public static JointAnimManager ToJointAnim(string filePath, JointMap jm) { var bke = BKE.Open(filePath); JointAnimManager anim = new JointAnimManager(); anim.FrameCount = bke.CountFrames(); for (int i = 0; i < bke.Nodes.Count; i++) { var node = new AnimNode(); node.Tracks.Add(new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_TRAX, Keys = new List <FOBJKey>() }); node.Tracks.Add(new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_TRAY, Keys = new List <FOBJKey>() }); node.Tracks.Add(new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_TRAZ, Keys = new List <FOBJKey>() }); node.Tracks.Add(new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_ROTX, Keys = new List <FOBJKey>() }); node.Tracks.Add(new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_ROTY, Keys = new List <FOBJKey>() }); node.Tracks.Add(new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_ROTZ, Keys = new List <FOBJKey>() }); node.Tracks.Add(new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_SCAX, Keys = new List <FOBJKey>() }); node.Tracks.Add(new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_SCAY, Keys = new List <FOBJKey>() }); node.Tracks.Add(new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_SCAZ, Keys = new List <FOBJKey>() }); anim.Nodes.Add(node); } foreach (var n in bke.Nodes) { if (jm.IndexOf(n.Name) == -1) { break; } var node = anim.Nodes[jm.IndexOf(n.Name)]; Console.WriteLine(n.Name + " " + n.Parent?.Name); for (int f = 0; f < anim.FrameCount; f++) { var matrix = n.GetLocal(f); var tra = matrix.ExtractTranslation(); var sca = matrix.ExtractScale(); var rot = Math3D.ToEulerAngles(matrix.ExtractRotation().Inverted()); node.Tracks.Find(e => e.JointTrackType == JointTrackType.HSD_A_J_TRAX).Keys.Add(new FOBJKey() { Frame = f, Value = tra.X, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); node.Tracks.Find(e => e.JointTrackType == JointTrackType.HSD_A_J_TRAY).Keys.Add(new FOBJKey() { Frame = f, Value = tra.Y, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); node.Tracks.Find(e => e.JointTrackType == JointTrackType.HSD_A_J_TRAZ).Keys.Add(new FOBJKey() { Frame = f, Value = tra.Z, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); node.Tracks.Find(e => e.JointTrackType == JointTrackType.HSD_A_J_ROTX).Keys.Add(new FOBJKey() { Frame = f, Value = rot.X, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); node.Tracks.Find(e => e.JointTrackType == JointTrackType.HSD_A_J_ROTY).Keys.Add(new FOBJKey() { Frame = f, Value = rot.Y, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); node.Tracks.Find(e => e.JointTrackType == JointTrackType.HSD_A_J_ROTZ).Keys.Add(new FOBJKey() { Frame = f, Value = rot.Z, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); node.Tracks.Find(e => e.JointTrackType == JointTrackType.HSD_A_J_SCAX).Keys.Add(new FOBJKey() { Frame = f, Value = sca.X, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); node.Tracks.Find(e => e.JointTrackType == JointTrackType.HSD_A_J_SCAY).Keys.Add(new FOBJKey() { Frame = f, Value = sca.Y, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); node.Tracks.Find(e => e.JointTrackType == JointTrackType.HSD_A_J_SCAZ).Keys.Add(new FOBJKey() { Frame = f, Value = sca.Z, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); } } return(anim); }
public AnimNodeData(AnimNode node, SsbhAnimTrackDecoder decoder) { Name = node.Name; Tracks = node.Tracks.Select(track => new AnimTrackData(track, decoder)).ToArray(); }
/// <summary> /// Load sprite data from an XML file /// </summary> /// <param name="XMLFile">Path and name of the XML file to load</param> /// <param name="RootPath">Path within the XML scheme to look for the sprite tags</param> public void LoadSpriteXML(string XMLFile, string RootPath) { XmlDocument SpriteXML; XmlNodeList SpriteList, AnimList, FrameList; SpriteInfo NewSprite; SpriteAnimation NewAnim; Point Pos = new Point(); cSpriteList.Clear(); try { SpriteXML = new XmlDocument(); SpriteXML.Load(XMLFile); } catch (Exception ExErr) { throw new Exception(String.Format("Failed to load XML File {1}{0}Exception {2}{0}Message {3}", Environment.NewLine, XMLFile, ExErr.GetType().ToString(), ExErr.Message)); } SpriteList = SpriteXML.DocumentElement.SelectNodes(RootPath + "/sprite"); foreach (XmlNode SpriteNode in SpriteList) { //Load all details regarding this scene if (SpriteNode.Attributes["name"] != null) { NewSprite = new SpriteInfo(SpriteNode.Attributes["name"].InnerText); } else { throw new Exception(String.Format("Failed to load XML File {1}{0}Encountered a sprite tag with no name attribute.", Environment.NewLine, XMLFile)); } if (SpriteNode.Attributes["tileset"] != null) { NewSprite.TileSet = SpriteNode.Attributes["tileset"].InnerText; } else { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a sprite tag with no tileset attribute.", Environment.NewLine, XMLFile, NewSprite.Name)); } if (SpriteNode.Attributes["height"] != null) { if (Int32.TryParse(SpriteNode.Attributes["height"].InnerText, out NewSprite.ScreenRect.Height) == false) { throw new Exception(String.Format("Failed to load XML File {1}{0}Encountered a sprite tag named {2} with an invalid height attribute.", Environment.NewLine, XMLFile, NewSprite.Name)); } } else { throw new Exception(String.Format("Failed to load XML File {1}{0}Encountered a sprite tag named {2} with no height attribute.", Environment.NewLine, XMLFile, NewSprite.Name)); } if (SpriteNode.Attributes["width"] != null) { if (Int32.TryParse(SpriteNode.Attributes["width"].InnerText, out NewSprite.ScreenRect.Width) == false) { throw new Exception(String.Format("Failed to load XML File {1}{0}Encountered a sprite tag named {2} with an invalid width attribute.", Environment.NewLine, XMLFile, NewSprite.Name)); } } else { throw new Exception(String.Format("Failed to load XML File {1}{0}Encountered a sprite tag named {2} with no width attribute.", Environment.NewLine, XMLFile, NewSprite.Name)); } FrameList = SpriteNode.SelectNodes("default"); if (FrameList.Count != 1) { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a sprite tag with no incorrect number of default tags.", Environment.NewLine, XMLFile, NewSprite.Name)); } if (FrameList[0].Attributes["down"] != null) { if (RegEx.QuickTest(FrameList[0].Attributes["down"].InnerText, "^([0-9+]),([0-9])+$") == true) { Pos.X = Int32.Parse(RegEx.GetRegExGroup(FrameList[0].Attributes["down"].InnerText, "^([0-9+]),([0-9])+$", 1)); Pos.Y = Int32.Parse(RegEx.GetRegExGroup(FrameList[0].Attributes["down"].InnerText, "^([0-9+]),([0-9])+$", 2)); NewSprite.DefaultTilePos[SpriteFacing.Down] = Pos; } else { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a default tag with invalid down attribute.", Environment.NewLine, XMLFile, NewSprite.Name)); } } if (FrameList[0].Attributes["up"] != null) { if (RegEx.QuickTest(FrameList[0].Attributes["up"].InnerText, "^([0-9+]),([0-9])+$") == true) { Pos.X = Int32.Parse(RegEx.GetRegExGroup(FrameList[0].Attributes["up"].InnerText, "^([0-9+]),([0-9])+$", 1)); Pos.Y = Int32.Parse(RegEx.GetRegExGroup(FrameList[0].Attributes["up"].InnerText, "^([0-9+]),([0-9])+$", 2)); NewSprite.DefaultTilePos[SpriteFacing.Up] = Pos; } else { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a default tag with invalid up attribute.", Environment.NewLine, XMLFile, NewSprite.Name)); } } if (FrameList[0].Attributes["left"] != null) { if (RegEx.QuickTest(FrameList[0].Attributes["left"].InnerText, "^([0-9+]),([0-9])+$") == true) { Pos.X = Int32.Parse(RegEx.GetRegExGroup(FrameList[0].Attributes["left"].InnerText, "^([0-9+]),([0-9])+$", 1)); Pos.Y = Int32.Parse(RegEx.GetRegExGroup(FrameList[0].Attributes["left"].InnerText, "^([0-9+]),([0-9])+$", 2)); NewSprite.DefaultTilePos[SpriteFacing.Left] = Pos; } else { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a default tag with invalid left attribute.", Environment.NewLine, XMLFile, NewSprite.Name)); } } if (FrameList[0].Attributes["right"] != null) { if (RegEx.QuickTest(FrameList[0].Attributes["right"].InnerText, "^([0-9+]),([0-9])+$") == true) { Pos.X = Int32.Parse(RegEx.GetRegExGroup(FrameList[0].Attributes["right"].InnerText, "^([0-9+]),([0-9])+$", 1)); Pos.Y = Int32.Parse(RegEx.GetRegExGroup(FrameList[0].Attributes["right"].InnerText, "^([0-9+]),([0-9])+$", 2)); NewSprite.DefaultTilePos[SpriteFacing.Right] = Pos; } else { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a default tag with invalid right attribute.", Environment.NewLine, XMLFile, NewSprite.Name)); } } AnimList = SpriteNode.SelectNodes("animation"); foreach (XmlNode AnimNode in AnimList) { if (AnimNode.Attributes["name"] != null) { NewAnim = new SpriteAnimation(AnimNode.Attributes["name"].InnerText); } else { throw new Exception(String.Format("Failed to load XML File {1}{0}Encountered an animation tag with no name attribute.", Environment.NewLine, XMLFile)); } if (AnimNode.Attributes["duration"] != null) { if (Int32.TryParse(AnimNode.Attributes["duration"].InnerText, out NewAnim.TimeMS) == false) { throw new Exception(String.Format("Failed to load XML File {1}{0}Encountered a animation tag named {2} with an invalid duration attribute.", Environment.NewLine, XMLFile, NewAnim.Name)); } } else { throw new Exception(String.Format("Failed to load XML File {1}{0}Encountered a animation tag named {2} with no duration attribute.", Environment.NewLine, XMLFile, NewAnim.Name)); } FrameList = AnimNode.SelectNodes("frame"); foreach (XmlNode FrameNode in FrameList) { if (FrameNode.Attributes["down"] != null) { if (RegEx.QuickTest(FrameNode.Attributes["down"].InnerText, "^([0-9+]),([0-9])+$") == true) { Pos.X = Int32.Parse(RegEx.GetRegExGroup(FrameNode.Attributes["down"].InnerText, "^([0-9+]),([0-9])+$", 1)); Pos.Y = Int32.Parse(RegEx.GetRegExGroup(FrameNode.Attributes["down"].InnerText, "^([0-9+]),([0-9])+$", 2)); NewAnim.TilePos[SpriteFacing.Down].Add(Pos); } else { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a default tag with invalid down attribute.", Environment.NewLine, XMLFile, NewAnim.Name)); } } if (FrameNode.Attributes["up"] != null) { if (RegEx.QuickTest(FrameNode.Attributes["up"].InnerText, "^([0-9+]),([0-9])+$") == true) { Pos.X = Int32.Parse(RegEx.GetRegExGroup(FrameNode.Attributes["up"].InnerText, "^([0-9+]),([0-9])+$", 1)); Pos.Y = Int32.Parse(RegEx.GetRegExGroup(FrameNode.Attributes["up"].InnerText, "^([0-9+]),([0-9])+$", 2)); NewAnim.TilePos[SpriteFacing.Up].Add(Pos); } else { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a default tag with invalid up attribute.", Environment.NewLine, XMLFile, NewAnim.Name)); } } if (FrameNode.Attributes["left"] != null) { if (RegEx.QuickTest(FrameNode.Attributes["left"].InnerText, "^([0-9+]),([0-9])+$") == true) { Pos.X = Int32.Parse(RegEx.GetRegExGroup(FrameNode.Attributes["left"].InnerText, "^([0-9+]),([0-9])+$", 1)); Pos.Y = Int32.Parse(RegEx.GetRegExGroup(FrameNode.Attributes["left"].InnerText, "^([0-9+]),([0-9])+$", 2)); NewAnim.TilePos[SpriteFacing.Left].Add(Pos); } else { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a default tag with invalid left attribute.", Environment.NewLine, XMLFile, NewAnim.Name)); } } if (FrameNode.Attributes["right"] != null) { if (RegEx.QuickTest(FrameNode.Attributes["right"].InnerText, "^([0-9+]),([0-9])+$") == true) { Pos.X = Int32.Parse(RegEx.GetRegExGroup(FrameNode.Attributes["right"].InnerText, "^([0-9+]),([0-9])+$", 1)); Pos.Y = Int32.Parse(RegEx.GetRegExGroup(FrameNode.Attributes["right"].InnerText, "^([0-9+]),([0-9])+$", 2)); NewAnim.TilePos[SpriteFacing.Right].Add(Pos); } else { throw new Exception(String.Format("Failed to load XML File {1}{0}In sprite named {2} Encountered a default tag with invalid right attribute.", Environment.NewLine, XMLFile, NewAnim.Name)); } } } NewSprite.AnimationList.Add(NewAnim.Name, NewAnim); } cSpriteList.Add(NewSprite.Name, NewSprite); } }
private static void ReadTrack(BinaryReaderExt r, int frameCount, int type, FOBJ_Player track, uint dataOffset, AnimNode node) { var offset = r.ReadUInt32() + dataOffset; var temp = r.Position; r.Seek(offset); int fCount = -1; float scale = 0; float[] frame = null, step = null, tan = null; if (type == 0x1) { fCount = r.ReadUInt16(); r.Skip(2); scale = r.ReadSingle(); float stepb = r.ReadSingle(); float base2 = r.ReadSingle(); frame = new float[fCount]; step = new float[fCount]; tan = new float[fCount]; for (int i = 0; i < fCount; i++) { var v = r.ReadInt32(); frame[i] = (v >> 24) & 0xFF; int th = v & 0xFFFFFF; step[i] = base2 + ((th >> 12) & 0xfff) * stepb; tan[i] = (Sign12Bit(th & 0xfff) / 32f); track.Keys.Add(new FOBJKey() { Frame = frame[i], Value = step[i], Tan = tan[i], InterpolationType = GXInterpolationType.HSD_A_OP_SPL }); } } if (type == 0x2) { fCount = r.ReadUInt16(); r.Skip(2); scale = r.ReadSingle(); float stepb = r.ReadSingle(); float base2 = r.ReadSingle(); frame = new float[fCount]; step = new float[fCount]; tan = new float[fCount]; for (int i = 0; i < fCount; i++) { frame[i] = r.ReadUInt16() / 32f; step[i] = base2 + r.ReadUInt16() * stepb; tan[i] = (r.ReadInt16() / 256f); track.Keys.Add(new FOBJKey() { Frame = frame[i], Value = step[i], Tan = tan[i], InterpolationType = GXInterpolationType.HSD_A_OP_SPL }); } } if (type == 0x3) { fCount = r.ReadUInt16(); r.Skip(2); scale = r.ReadSingle(); frame = new float[fCount]; step = new float[fCount]; tan = new float[fCount]; for (int i = 0; i < fCount; i++) { frame[i] = r.ReadSingle(); step[i] = r.ReadSingle(); tan[i] = r.ReadSingle(); track.Keys.Add(new FOBJKey() { Frame = frame[i], Value = step[i], Tan = tan[i], InterpolationType = GXInterpolationType.HSD_A_OP_SPL }); } } if (type == 0x4) { float stepb = r.ReadSingle(); float base2 = r.ReadSingle(); for (int i = 0; i < frameCount; i++) { float v = base2 + stepb * (r.ReadByte()); track.Keys.Add(new FOBJKey() { Frame = i, Value = v, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); } } if (type == 0x6) { for (int i = 0; i < frameCount; i++) { float v = r.ReadSingle(); track.Keys.Add(new FOBJKey() { Frame = i, Value = v, InterpolationType = GXInterpolationType.HSD_A_OP_LIN }); } } r.Seek(temp); }
/// <summary> /// /// </summary> /// <param name="filePath"></param> /// <returns></returns> public static JointAnimManager LoadCHR0(string filePath, Dictionary <int, string> BoneLabelMap) { JointAnimManager anim = new JointAnimManager(); Dictionary <string, int> nameToIndex = new Dictionary <string, int>(); foreach (var v in BoneLabelMap) { nameToIndex.Add(v.Value, v.Key); anim.Nodes.Add(new AnimNode()); } using (BinaryReaderExt r = new BinaryReaderExt(new FileStream(filePath, FileMode.Open))) { r.BigEndian = true; if (r.BaseStream.Length < 4 || new string(r.ReadChars(4)) != "CHR0") { throw new InvalidDataException("CHR0 file is not valid"); } r.Skip(4); int versionNum = r.ReadInt32(); if (versionNum != 4) { throw new InvalidDataException($"CHR0 version {versionNum} not supported"); } System.Console.WriteLine("Reading Track "); r.Seek(0x10); var indexGroupOffset = r.ReadUInt32(); var animName = r.ReadString(r.ReadInt32(), -1); r.Skip(4); anim.FrameCount = r.ReadUInt16(); int animDataCount = r.ReadUInt16(); r.Skip(8); r.Seek(indexGroupOffset); var sectionOffset = r.ReadUInt32() + indexGroupOffset; int sectionCount = r.ReadInt32(); for (uint i = 0; i < sectionCount; i++) { r.Seek(indexGroupOffset + 8 + 16 * i); r.Skip(4); // id and unknown r.Skip(2); // let r.Skip(2); // right var boneName = r.ReadString(r.ReadInt32() + (int)indexGroupOffset, -1); var dataOffset = r.ReadUInt32() + indexGroupOffset; if (dataOffset == indexGroupOffset) { sectionCount += 1; continue; } if (!nameToIndex.ContainsKey(boneName)) { continue; } r.Seek(dataOffset); var nameOff = r.Position + r.ReadUInt32(); var flags = r.ReadInt32(); int t_type = (flags >> 0x1e) & 0x3; int r_type = (flags >> 0x1b) & 0x7; int s_type = (flags >> 0x19) & 0x3; int hasT = (flags >> 0x18) & 0x1; int hasR = (flags >> 0x17) & 0x1; int hasS = (flags >> 0x16) & 0x1; int Zfixed = (flags >> 0x15) & 0x1; int Yfixed = (flags >> 0x14) & 0x1; int Xfixed = (flags >> 0x13) & 0x1; int RZfixed = (flags >> 0x12) & 0x1; int RYfixed = (flags >> 0x11) & 0x1; int RXfixed = (flags >> 0x10) & 0x1; int SZfixed = (flags >> 0xf) & 0x1; int SYfixed = (flags >> 0xe) & 0x1; int SXfixed = (flags >> 0xd) & 0x1; int Tiso = (flags >> 0x6) & 0x1; int Riso = (flags >> 0x5) & 0x1; int Siso = (flags >> 0x4) & 0x1; AnimNode node = new AnimNode(); FOBJ_Player trackX = new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_TRAX }; FOBJ_Player trackY = new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_TRAY }; FOBJ_Player trackZ = new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_TRAZ }; FOBJ_Player trackRX = new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_ROTX }; FOBJ_Player trackRY = new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_ROTY }; FOBJ_Player trackRZ = new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_ROTZ }; FOBJ_Player trackSX = new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_SCAX }; FOBJ_Player trackSY = new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_SCAY }; FOBJ_Player trackSZ = new FOBJ_Player() { JointTrackType = JointTrackType.HSD_A_J_SCAZ }; if (hasS == 1) { ReadKeys(r, node, (int)anim.FrameCount, trackSX, trackSY, trackSZ, Siso == 1, SXfixed == 1, SYfixed == 1, SZfixed == 1, s_type, dataOffset); } if (hasR == 1) { ReadKeys(r, node, (int)anim.FrameCount, trackRX, trackRY, trackRZ, Riso == 1, RXfixed == 1, RYfixed == 1, RZfixed == 1, r_type, dataOffset); } if (hasT == 1) { ReadKeys(r, node, (int)anim.FrameCount, trackX, trackY, trackZ, Tiso == 1, Xfixed == 1, Yfixed == 1, Zfixed == 1, t_type, dataOffset); } if (trackX.Keys.Count > 1) { node.Tracks.Add(trackX); } if (trackY.Keys.Count > 1) { node.Tracks.Add(trackY); } if (trackZ.Keys.Count > 1) { node.Tracks.Add(trackZ); } if (trackRX.Keys.Count > 1) { node.Tracks.Add(trackRX); } if (trackRY.Keys.Count > 1) { node.Tracks.Add(trackRY); } if (trackRZ.Keys.Count > 1) { node.Tracks.Add(trackRZ); } if (trackSX.Keys.Count > 1) { node.Tracks.Add(trackSX); } if (trackSY.Keys.Count > 1) { node.Tracks.Add(trackSY); } if (trackSZ.Keys.Count > 1) { node.Tracks.Add(trackSZ); } foreach (var k in trackRX.Keys) { k.Value = MathHelper.DegreesToRadians(k.Value); k.Tan = MathHelper.DegreesToRadians(k.Value); } foreach (var k in trackRY.Keys) { k.Value = MathHelper.DegreesToRadians(k.Value); k.Tan = MathHelper.DegreesToRadians(k.Value); } foreach (var k in trackRZ.Keys) { k.Value = MathHelper.DegreesToRadians(k.Value); k.Tan = MathHelper.DegreesToRadians(k.Value); } Console.WriteLine(boneName + " Tracks:" + node.Tracks.Count); Console.WriteLine($"{trackX.Keys.Count} {trackY.Keys.Count} {trackZ.Keys.Count}"); Console.WriteLine($"{trackRX.Keys.Count} {trackRY.Keys.Count} {trackRZ.Keys.Count}"); Console.WriteLine($"{trackSX.Keys.Count} {trackSY.Keys.Count} {trackSZ.Keys.Count}"); anim.Nodes[nameToIndex[boneName]] = node; } } return(anim); }
private static void ReadKeys(BinaryReaderExt r, AnimNode node, int frameCount, FOBJ_Player xtrack, FOBJ_Player ytrack, FOBJ_Player ztrack, bool isIsotrophic, bool isXFixed, bool isYFixed, bool isZFixed, int type, uint dataOffset) { if (isIsotrophic) { var temp = r.Position + 4; if (!isXFixed && !isYFixed && !isZFixed) { var offset = r.ReadUInt32() + r.Position; r.Seek(offset); } float iss = r.ReadSingle(); xtrack.Keys.Add(new FOBJKey() { Frame = 0, Value = iss, InterpolationType = GXInterpolationType.HSD_A_OP_KEY }); ytrack.Keys.Add(new FOBJKey() { Frame = 0, Value = iss, InterpolationType = GXInterpolationType.HSD_A_OP_KEY }); ztrack.Keys.Add(new FOBJKey() { Frame = 0, Value = iss, InterpolationType = GXInterpolationType.HSD_A_OP_KEY }); r.Seek(temp); } else { if (isXFixed) { xtrack.Keys.Add(new FOBJKey() { Frame = 0, Value = r.ReadSingle(), InterpolationType = GXInterpolationType.HSD_A_OP_KEY }); } else { ReadTrack(r, frameCount, type, xtrack, dataOffset, node); } if (isYFixed) { ytrack.Keys.Add(new FOBJKey() { Frame = 0, Value = r.ReadSingle(), InterpolationType = GXInterpolationType.HSD_A_OP_KEY }); } else { ReadTrack(r, frameCount, type, ytrack, dataOffset, node); } if (isZFixed) { ztrack.Keys.Add(new FOBJKey() { Frame = 0, Value = r.ReadSingle(), InterpolationType = GXInterpolationType.HSD_A_OP_KEY }); } else { ReadTrack(r, frameCount, type, ztrack, dataOffset, node); } } }