示例#1
0
        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);
        }
示例#2
0
 public AnimNodeData(AnimNode node, SsbhAnimTrackDecoder decoder)
 {
     Name   = node.Name;
     Tracks = node.Tracks.Select(track => new AnimTrackData(track, decoder)).ToArray();
 }
示例#3
0
        /// <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);
            }
        }
示例#4
0
        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);
        }
示例#5
0
        /// <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);
                }
            }
        }