Esempio n. 1
0
        public static LTBFile Read(FileInfo info)
        {
            var bFlip = info.Name.StartsWith("PV");

            var ltbFile = new LTBFile();

            var br = new ExtendedBinaryReader(info.OpenRead());

            #region Header

            var header = br.ReadUInt16();
            if (header > 20)
            {
                br.Close();
                var lzmaStream = new LzmaDecodeStream(info.OpenRead());
                var ms         = new MemoryStream();

                lzmaStream.CopyTo(ms);

                if (ms.Length == 0)
                {
                    Console.WriteLine($"{info.Name} is not a vaild LTB file.");

                    return(null);
                }

                br = new ExtendedBinaryReader(ms);
                br.Skip(0, true);
            }

            // Skip header
            br.Skip(0x14, true);

            uint version = br.ReadUInt32();

            uint nKeyFrame        = br.ReadUInt32();
            uint nAnim            = br.ReadUInt32();
            uint numBones         = br.ReadUInt32();
            uint nPieces          = br.ReadUInt32();
            uint nChildModels     = br.ReadUInt32();
            uint nTris            = br.ReadUInt32();
            uint nVerts           = br.ReadUInt32();
            uint nVertexWeights   = br.ReadUInt32();
            uint nLODs            = br.ReadUInt32();
            uint nSockets         = br.ReadUInt32();
            uint nWeightSets      = br.ReadUInt32();
            uint nStrings         = br.ReadUInt32();
            uint StringLengths    = br.ReadUInt32();
            uint VertAnimDataSize = br.ReadUInt32();
            uint nAnimData        = br.ReadUInt32();

            string cmdString    = br.ReadStringWithUInt16Length();
            float  globalRadius = br.ReadSingle();

            uint iNumEnabledOBBs = br.ReadUInt32();

            if (iNumEnabledOBBs != 0)
            {
                throw new Exception("LTB with OBB infomations are not supported");
            }

            uint numMesh = br.ReadUInt32();

            #endregion

            var romanisation = new McCuneReischauerRomanisation {
                PreserveNonKoreanText = true
            };

            #region Mesh nodes
            for (int i = 0; i < numMesh; i++)
            {
                string meshName = br.ReadStringWithUInt16Length(ltbEncode);

                meshName = romanisation.RomaniseText(meshName);

                foreach (var kvp in replaceDictionary)
                {
                    meshName = meshName.Replace(kvp.Key, kvp.Value);
                }

                meshName = meshName.ToLower();

                uint numLod = br.ReadUInt32();

                Console.WriteLine($"{meshName} - {numLod} Lods");

                meshName = meshName.ToLower();

                br.Skip((int)numLod * 4 + 8);

                int materialIndex = -1;

                if (!ltbFile.Materials.Any(material => material.Name == meshName))
                {
                    ltbFile.Materials.Add(new SEModelMaterial
                    {
                        Name         = "mtl_" + meshName,
                        MaterialData = new SEModelSimpleMaterial
                        {
                            DiffuseMap = meshName + ".png"
                        }
                    });

                    materialIndex = ltbFile.Materials.Count - 1;
                }
                else
                {
                    materialIndex = ltbFile.Materials.FindIndex(mtl => mtl.Name == meshName);
                }

                for (int iLod = 0; iLod < numLod; iLod++)
                {
                    var mesh = new SEModelMesh();
                    mesh.AddMaterialIndex(materialIndex);

                    var       nNumTex            = br.ReadUInt32();
                    const int MAX_PIECE_TEXTURES = 4;

                    for (int iTex = 0; iTex < MAX_PIECE_TEXTURES; iTex++)
                    {
                        // Texture index
                        br.ReadUInt32();
                    }

                    var renderStyle     = br.ReadUInt32();
                    var nRenderPriority = br.ReadByte();

                    var lodType = (PieceType)br.ReadUInt32();

                    var lodSize = br.ReadUInt32();

                    if (lodSize != 0)
                    {
                        uint numVerts = br.ReadUInt32();
                        uint numTris  = br.ReadUInt32();

                        uint iMaxBonesPerTri  = br.ReadUInt32();
                        uint iMaxBonesPerVert = br.ReadUInt32();

                        Console.WriteLine($"    Lod {iLod}: \n        Vertex count: {numVerts}\n        Triangle count: {numTris}");

                        bool bReIndexBones = false, bUseMatrixPalettes = false;

                        if (lodType == PieceType.SkelMesh)
                        {
                            bReIndexBones = br.ReadBoolean();
                        }

                        DataType[] streamData = { (DataType)br.ReadUInt32(), (DataType)br.ReadUInt32(), (DataType)br.ReadUInt32(), (DataType)br.ReadUInt32() };

                        uint rigidBone = uint.MaxValue;

                        if (lodType == PieceType.RigidMesh)
                        {
                            rigidBone = br.ReadUInt32();
                        }
                        else if (lodType == PieceType.SkelMesh)
                        {
                            bUseMatrixPalettes = br.ReadBoolean();
                        }
                        else
                        {
                            throw new Exception("Unsupported lod type");
                        }

                        if (bUseMatrixPalettes)
                        {
                            uint iMinBone = br.ReadUInt32();
                            uint iMaxBone = br.ReadUInt32();
                        }

                        var boneMap = new List <uint>();

                        if (bReIndexBones)
                        {
                            uint reindexBoneMapSize = br.ReadUInt32();

                            for (int iMap = 0; iMap < reindexBoneMapSize; iMap++)
                            {
                                boneMap.Add(br.ReadUInt32());
                            }
                        }

                        for (int iStream = 0; iStream < 4; ++iStream)
                        {
                            if (!streamData[iStream].HasFlag(DataType.Position))
                            {
                                continue;
                            }

                            for (int iVert = 0; iVert < numVerts; iVert++)
                            {
                                var v = new SEModelVertex();

                                if (streamData[iStream].HasFlag(DataType.Position))
                                {
                                    v.Position = new Vector3
                                    {
                                        X = br.ReadSingle(),
                                        Y = br.ReadSingle(),
                                        Z = br.ReadSingle(),
                                    };

                                    if (bFlip)
                                    {
                                        v.Position.X *= -1;
                                    }

                                    if (rigidBone == uint.MaxValue)
                                    {
                                        var weightSum = 0.0f;

                                        var maxWeight = bUseMatrixPalettes ? iMaxBonesPerVert : iMaxBonesPerTri;

                                        for (int iWeight = 0; iWeight < maxWeight - 1; iWeight++)
                                        {
                                            var weight = br.ReadSingle();

                                            if (weight > 1)
                                            {
                                                throw new Exception("wtf");
                                            }

                                            weightSum += weight;

                                            v.Weights.Add(new SEModelWeight
                                            {
                                                BoneIndex  = uint.MaxValue,
                                                BoneWeight = weight
                                            });
                                        }

                                        if (1.0f - weightSum > float.Epsilon)
                                        {
                                            v.Weights.Add(new SEModelWeight
                                            {
                                                BoneIndex  = uint.MaxValue,
                                                BoneWeight = 1.0f - weightSum
                                            });
                                        }

                                        if (bUseMatrixPalettes)
                                        {
                                            for (int iWeight = 0; iWeight < 4; iWeight++)
                                            {
                                                var boneIndex = br.ReadByte();

                                                if (bReIndexBones)
                                                {
                                                    boneIndex = (byte)boneMap[boneIndex];
                                                }

                                                if (v.Weights.Count > iWeight)
                                                {
                                                    v.Weights[iWeight].BoneIndex = boneIndex;
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (rigidBone >= numBones || rigidBone < 0)
                                        {
                                            throw new Exception("wtf");
                                        }

                                        v.Weights.Add(new SEModelWeight
                                        {
                                            BoneIndex  = rigidBone,
                                            BoneWeight = 1.0f
                                        });
                                    }
                                }

                                if (streamData[iStream].HasFlag(DataType.Normal))
                                {
                                    v.VertexNormal = new Vector3
                                    {
                                        X = br.ReadSingle(),
                                        Y = br.ReadSingle(),
                                        Z = br.ReadSingle(),
                                    };

                                    if (bFlip)
                                    {
                                        v.VertexNormal.X *= -1;
                                    }
                                }

                                if (streamData[iStream].HasFlag(DataType.Color))
                                {
                                    br.Skip(4);
                                }

                                if (streamData[iStream].HasFlag(DataType.UVSets1))
                                {
                                    v.UVSets.Add(new Vector2
                                    {
                                        X = br.ReadSingle(),
                                        Y = br.ReadSingle()
                                    });

                                    if (v.UVSets[0].X > 1.0f)
                                    {
                                        v.UVSets[0].X -= 1.0f;
                                    }
                                }

                                if (streamData[iStream].HasFlag(DataType.UVSets2))
                                {
                                    br.Skip(8);
                                }
                                if (streamData[iStream].HasFlag(DataType.UVSets3))
                                {
                                    br.Skip(8);
                                }
                                if (streamData[iStream].HasFlag(DataType.UVSets4))
                                {
                                    br.Skip(8);
                                }
                                if (streamData[iStream].HasFlag(DataType.BasisVectors))
                                {
                                    br.Skip(24);
                                }

                                if (v.Position == null || v.WeightCount == 0)
                                {
                                    throw new Exception("wtf");
                                }

                                mesh.AddVertex(v);
                            }
                        }

                        for (uint iTriangle = 0; iTriangle < numTris; iTriangle++)
                        {
                            mesh.AddFace(br.ReadUInt16(), br.ReadUInt16(), br.ReadUInt16());
                        }

                        if (lodType == PieceType.SkelMesh && !bUseMatrixPalettes)
                        {
                            var boneComboCount = br.ReadUInt32();

                            for (int iCombo = 0; iCombo < boneComboCount; iCombo++)
                            {
                                int m_BoneIndex_Start = br.ReadUInt16();
                                int m_BoneIndex_End   = m_BoneIndex_Start + br.ReadUInt16();

                                Console.WriteLine($"        Weight Combo: {m_BoneIndex_Start} to {m_BoneIndex_End}");

                                var bones = br.ReadBytes(4);

                                uint m_iIndexIndex = br.ReadUInt32();

                                for (int iVertex = m_BoneIndex_Start; iVertex < m_BoneIndex_End; iVertex++)
                                {
                                    for (int iBone = 0; iBone < 4 && bones[iBone] != 0xFF; iBone++)
                                    {
                                        if (mesh.Verticies[iVertex].Weights.Count <= iBone)
                                        {
                                            break;
                                        }

                                        mesh.Verticies[iVertex].Weights[iBone].BoneIndex = bones[iBone];
                                    }
                                }
                            }
                        }

                        ltbFile.Meshes.Add(mesh);
                        br.Skip(br.ReadByte());
                    }
                }
            }
            #endregion

            #region Bones
            uint[] boneTree = new uint[numBones];

            for (int i = 0; i < numBones; i++)
            {
                var boneName = br.ReadStringWithUInt16Length();
                var boneId   = br.ReadByte();
                var num2     = br.ReadUInt16();

                Matrix4x4 transformMatrix = new Matrix4x4();

                for (int j = 0; j < 4; j++)
                {
                    for (int k = 0; k < 4; k++)
                    {
                        transformMatrix[j, k] = br.ReadSingle();
                    }
                }

                boneTree[i] = br.ReadUInt32();

                var bone = new SEModelBone
                {
                    BoneName       = boneName.Replace('.', '_').Replace('-', '_').Replace(' ', '_'),
                    GlobalRotation = new Quaternion(transformMatrix),
                    GlobalPosition = new Vector3(transformMatrix)
                };

                if (bFlip)
                {
                    bone.GlobalPosition.X *= -1;

                    bone.GlobalRotation.Y *= -1;
                    bone.GlobalRotation.Z *= -1;
                }

                // rotate root bone;
                if (boneId == 0)
                {
                    bone.GlobalRotation *= globalRotation;
                }

                ltbFile.Bones[boneId] = bone;
            }

            uint[] nSubbone = new uint[numBones];
            nSubbone[0] = boneTree[0];

            ltbFile.Bones[0].BoneParent = -1;

            // Build bone tree
            for (byte i = 1; i < numBones; i++)
            {
                nSubbone[i] = boneTree[i];
                for (int j = i - 1; j >= 0; j--)
                {
                    if (nSubbone[j] > 0)
                    {
                        nSubbone[j]--;
                        ltbFile.Bones[i].BoneParent = j;
                        break;
                    }
                }
            }

            #endregion

            #region Random stuff
            Console.WriteLine("\nInternal filenames:");
            var childModelCount = br.ReadUInt32();

            for (int i = 0; i < childModelCount; i++)
            {
                Console.WriteLine(br.ReadStringWithUInt16Length());

                br.Skip((int)br.ReadUInt32() * 4);
            }

            br.Skip(4);
            #endregion

            #region Animations
            if (nAnim > 0)
            {
                var animationCount = br.ReadUInt32();

                Console.WriteLine($"\nAnimation count: {animationCount}\n");

                for (int i = 0; i < animationCount; i++)
                {
                    var seanim = new SEAnim();

                    var dim = new Vector3
                    {
                        X = br.ReadSingle(),
                        Y = br.ReadSingle(),
                        Z = br.ReadSingle(),
                    };

                    var animName = br.ReadStringWithUInt16Length();
                    Console.Write(animName);

                    var compressionType = (AnimCompressionType)br.ReadUInt32();
                    var interpolationMS = br.ReadUInt32();

                    var keyFrameCount = br.ReadUInt32();
                    Console.WriteLine($" has {keyFrameCount} keyframes");

                    for (int iKeyFrame = 0; iKeyFrame < keyFrameCount; iKeyFrame++)
                    {
                        var time       = br.ReadUInt32();
                        var animString = br.ReadStringWithUInt16Length();

                        if (!string.IsNullOrEmpty(animString))
                        {
                            seanim.AddNoteTrack(animString, iKeyFrame);
                        }
                    }

                    for (byte iBone = 0; iBone < numBones; iBone++)
                    {
                        if (compressionType != AnimCompressionType.None)
                        {
                            uint pFrames = br.ReadUInt32();

                            for (int iKeyFrame = 0; iKeyFrame < pFrames; iKeyFrame++)
                            {
                                var v = new Vector3(br.ReadInt16() / 16.0, br.ReadInt16() / 16.0, br.ReadInt16() / 16.0);

                                if (bFlip)
                                {
                                    v.X *= -1;
                                }

                                seanim.AddTranslationKey(ltbFile.Bones[iBone].BoneName, iKeyFrame, v.X, v.Y, v.Z);
                            }

                            uint rFrames = br.ReadUInt32();

                            for (int iKeyFrame = 0; iKeyFrame < rFrames; iKeyFrame++)
                            {
                                var q = new Quaternion(br.ReadInt16() / 16.0, -br.ReadInt16() / 16.0, -br.ReadInt16() / 16.0, br.ReadInt16() / 16.0);

                                if (bFlip)
                                {
                                    q.Y *= -1;
                                    q.Z *= -1;
                                }

                                // rotate root bone;
                                if (iBone == 0)
                                {
                                    q *= globalRotation;
                                }

                                seanim.AddRotationKey(ltbFile.Bones[iBone].BoneName, iKeyFrame, q.X, q.Y, q.Z, q.W);
                            }
                        }
                        else if (compressionType == AnimCompressionType.None)
                        {
                            bool isVertexAnim = br.ReadBoolean();

                            if (isVertexAnim)
                            {
                                throw new Exception("Vertex animation not supported!");
                            }
                            else
                            {
                                for (int iKeyFrame = 0; iKeyFrame < keyFrameCount; iKeyFrame++)
                                {
                                    var v = new Vector3(-br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                                    seanim.AddTranslationKey(ltbFile.Bones[iBone].BoneName, iKeyFrame, v.X, v.Y, v.Z);
                                }

                                for (int iKeyFrame = 0; iKeyFrame < keyFrameCount; iKeyFrame++)
                                {
                                    var q = new Quaternion(br.ReadSingle(), -br.ReadSingle(), -br.ReadSingle(), br.ReadSingle());

                                    // rotate root bone;
                                    if (iBone == 0)
                                    {
                                        q *= globalRotation;
                                    }

                                    seanim.AddRotationKey(ltbFile.Bones[iBone].BoneName, iKeyFrame, q.X, q.Y, q.Z, q.W);
                                }
                            }
                        }
                    }

                    ltbFile.Animations.Add(animName + ".seanim", seanim);
                }
            }

            #endregion

            return(ltbFile);
        }
Esempio n. 2
0
        static void Main(string[] args)
        {
            // SELib Unit Test
            Console.Title = "SELib Unit Tests";
            Console.WriteLine("SELib Unit Tests\n");

            Console.WriteLine("- SEAnims\n");

            #region SEAnim

            {
                // Log
                Console.Write("-- Test 1   ");
                // Make it
                var anim = new SEAnim();
                // Add some keys
                anim.AddTranslationKey("shoulder", 0, 0, 0, 0);
                anim.AddTranslationKey("shoulder", 5, 1, 1, 1);
                anim.AddTranslationKey("shoulder", 10, 10, 10, 10);
                anim.AddTranslationKey("shoulder", 30, 20, 20, 20);
                anim.AddTranslationKey("shoulder", 40, 30, 30, 30);

                // Save it
                anim.Write("test1.seanim");
                // Done
                Console.WriteLine("DONE!");
            }

            {
                // Log
                Console.Write("-- Test 2   ");
                // Make it
                var anim = new SEAnim();
                // Add some keys
                anim.AddTranslationKey("shoulder", 0, 0, 0, 0);
                anim.AddTranslationKey("shoulder", 5, 1, 1, 1);
                anim.AddTranslationKey("shoulder", 10, 10, 10, 10);
                anim.AddTranslationKey("shoulder", 30, 20, 20, 20);
                anim.AddTranslationKey("shoulder", 40, 30, 30, 30);
                // Add some scale
                anim.AddScaleKey("shoulder", 0, 1, 1, 1);
                anim.AddScaleKey("shoulder", 50, 3, 3, 3);

                // Save it
                anim.Write("test2.seanim");
                // Done
                Console.WriteLine("DONE!");
            }

            {
                // Log
                Console.Write("-- Test 3   ");
                // Make it
                var anim = new SEAnim();
                // Add some keys
                anim.AddTranslationKey("shoulder", 0, 0, 0, 0);
                anim.AddTranslationKey("shoulder", 5, 1, 1, 1);
                anim.AddTranslationKey("shoulder", 10, 10, 10, 10);
                anim.AddTranslationKey("shoulder", 30, 20, 20, 20);
                anim.AddTranslationKey("shoulder", 40, 30, 30, 30);
                // Add some scale
                anim.AddScaleKey("shoulder", 0, 1, 1, 1);
                anim.AddScaleKey("shoulder", 50, 3, 3, 3);
                // Add some note
                anim.AddNoteTrack("hello_world", 3);
                anim.AddNoteTrack("bye", 50);

                // Save it
                anim.Write("test3.seanim");
                // Done
                Console.WriteLine("DONE!");
            }

            {
                // Log
                Console.Write("-- Test 4   ");
                // Make it
                var anim = new SEAnim();
                // Add some keys
                anim.AddTranslationKey("shoulder", 0, 0, 0, 0);
                anim.AddTranslationKey("shoulder", 5, 1, 1, 1);
                anim.AddTranslationKey("shoulder", 10, 10, 10, 10);
                anim.AddTranslationKey("shoulder", 30, 20, 20, 20);
                anim.AddTranslationKey("shoulder", 40, 30, 30, 30);
                // Add some scale
                anim.AddScaleKey("shoulder", 0, 1, 1, 1);
                anim.AddScaleKey("shoulder", 50, 3, 3, 3);
                // Add some note
                anim.AddNoteTrack("hello_world", 3);
                anim.AddNoteTrack("bye", 50);

                // Save it (Really, we don't need doubles!!)
                anim.Write("test4.seanim", true);
                // Done
                Console.WriteLine("DONE!");
            }

            {
                // Log
                Console.Write("-- Test 5   ");
                // Make it
                var anim = new SEAnim();
                // Add some keys
                anim.AddTranslationKey("shoulder", 0, 0, 0, 0);
                anim.AddTranslationKey("shoulder", 5, 1, 1, 1);
                anim.AddTranslationKey("shoulder", 10, 10, 10, 10);
                anim.AddTranslationKey("shoulder", 30, 20, 20, 20);
                anim.AddTranslationKey("shoulder", 40, 30, 30, 30);
                // Add some scale
                anim.AddScaleKey("shoulder", 0, 1, 1, 1);
                anim.AddScaleKey("shoulder", 50, 3, 3, 3);
                // Add some rot
                anim.AddRotationKey("shoulder", 0, 0, 0, 0, 1);
                anim.AddRotationKey("shoulder", 50, 0.3, 0.2, 0.5, 1); // Random quat for test
                // Add some note
                anim.AddNoteTrack("hello_world", 3);
                anim.AddNoteTrack("bye", 50);

                // Save it
                anim.Write("test5.seanim");
                // Done
                Console.WriteLine("DONE!");
            }

            {
                // Log
                Console.Write("-- Test 6   ");
                // Read from test 5
                var anim = SEAnim.Read("test5.seanim");

                // Check data
                System.Diagnostics.Debug.Assert(anim.AnimationNotetracks.Count == 2);
                System.Diagnostics.Debug.Assert(anim.AnimationPositionKeys.Count == 1);
                System.Diagnostics.Debug.Assert(anim.AnimationRotationKeys.Count == 1);
                System.Diagnostics.Debug.Assert(anim.AnimationScaleKeys.Count == 1);
                System.Diagnostics.Debug.Assert(anim.BoneCount == 1);
                System.Diagnostics.Debug.Assert(anim.FrameCount == 51);
                System.Diagnostics.Debug.Assert(anim.FrameRate == 30.0);

                // Version
                System.Diagnostics.Debug.Assert(anim.APIVersion == "v1.0.1");

                // Check functions
                System.Diagnostics.Debug.Assert(anim.RenameBone("shoulder", "shoulder") == false);
                System.Diagnostics.Debug.Assert(anim.RenameBone("shoulder", "new_shoulder") == true);

                // Done
                Console.WriteLine("DONE!");
            }

            #endregion

            Console.WriteLine("\n- SEModels\n");

            #region SEModel

            {
                // Log
                Console.Write("-- Test 1   ");
                // Make it
                var model = new SEModel();
                // Add some bones
                model.AddBone("bone_0001", -1, Vector3.Zero, Quaternion.Identity, Vector3.Zero, Quaternion.Identity, Vector3.One);
                model.AddBone("bone_0002", 0, Vector3.Zero, Quaternion.Identity, Vector3.Zero, Quaternion.Identity, Vector3.One);
                model.AddBone("bone_0003", 0, Vector3.Zero, Quaternion.Identity, Vector3.Zero, Quaternion.Identity, Vector3.One);
                model.AddBone("bone_0004", 2, Vector3.Zero, Quaternion.Identity, new Vector3(22, 22, 22), Quaternion.Identity, Vector3.One);

                // Save it
                model.Write("test1.semodel");
                // Done
                Console.WriteLine("DONE!");
            }

            {
                // Log
                Console.Write("-- Test 2   ");
                // Make it
                var model = new SEModel();
                // Allow globals too
                model.ModelBoneSupport = ModelBoneSupportTypes.SupportsBoth;

                // Add some bones
                model.AddBone("bone_0001", -1, Vector3.Zero, Quaternion.Identity, Vector3.Zero, Quaternion.Identity, Vector3.One);
                model.AddBone("bone_0002", 0, Vector3.Zero, Quaternion.Identity, Vector3.Zero, Quaternion.Identity, Vector3.One);
                model.AddBone("bone_0003", 0, Vector3.Zero, Quaternion.Identity, Vector3.Zero, Quaternion.Identity, Vector3.One);
                model.AddBone("bone_0004", 2, Vector3.Zero, Quaternion.Identity, new Vector3(22, 22, 22), Quaternion.Identity, Vector3.One);

                // Save it
                model.Write("test2.semodel");
                // Done
                Console.WriteLine("DONE!");
            }

            #endregion

            // Pause
            Console.Write("\nPress any key to continue...");
            Console.ReadKey();
        }