public void OnEnable()
 {
     FoxModel foxModel = target as FoxModel;
     meshGroupsLength = foxModel.meshGroups.Length;
     meshDefinitionsLength = foxModel.meshDefinitions.Length;
     currentControlID = GUIUtility.keyboardControl;
 } //OnEnable
Example #2
0
        } //OnImportAsset

        public void ReadWithoutAssetImportContext(string filePath)
        {
            Fmdl fmdl = new Fmdl(Path.GetFileNameWithoutExtension(filePath));
            fmdl.Read(filePath);

            int boneCount = fmdl.fmdlBones != null ? fmdl.fmdlBones.Length : 0;
            int materialCount = fmdl.fmdlMaterialInstances.Length;
            int textureCount = fmdl.fmdlTextures != null ? fmdl.fmdlTextures.Length : 0;
            int meshCount = fmdl.fmdlMeshInfos.Length;
            int meshGroupCount = fmdl.fmdlMeshGroups.Length;

            GameObject mainObject = new GameObject(fmdl.name);
            FoxModel foxModel = mainObject.AddComponent<FoxModel>();
            foxModel.meshGroups = new FoxMeshGroup[meshGroupCount];
            foxModel.meshDefinitions = new FoxMeshDefinition[meshCount];
            Transform[] bones = new Transform[boneCount];
            Texture[] textures = new Texture[textureCount];
            Material[] materials = new Material[materialCount];
            Mesh[] meshes = new Mesh[meshCount];

            Transform rootBone = new GameObject().transform;
            rootBone.name = "[Root]";
            rootBone.parent = mainObject.transform;

            Read(fmdl, mainObject, foxModel, bones, textures, materials, meshes, rootBone);
        } //ReadWithoutAssetImportContext
Example #3
0
        } //LoadTextureDXT

        private void ReadWithAssetImportContext(AssetImportContext ctx, string filePath)
        {
            Fmdl fmdl = new Fmdl(Path.GetFileNameWithoutExtension(filePath));
            fmdl.Read(filePath);

            int boneCount = fmdl.fmdlBones != null ? fmdl.fmdlBones.Length : 0;
            int materialCount = fmdl.fmdlMaterialInstances.Length;
            int textureCount = fmdl.fmdlTextures != null ? fmdl.fmdlTextures.Length : 0;
            int meshCount = fmdl.fmdlMeshInfos.Length;
            int meshGroupCount = fmdl.fmdlMeshGroups.Length;

            GameObject mainObject = new GameObject(fmdl.name);
            FoxModel foxModel = mainObject.AddComponent<FoxModel>();
            foxModel.meshGroups = new FoxMeshGroup[meshGroupCount];
            foxModel.meshDefinitions = new FoxMeshDefinition[meshCount];
            Transform[] bones = new Transform[boneCount];
            Texture[] textures = new Texture[textureCount];
            Material[] materials = new Material[materialCount];
            Mesh[] meshes = new Mesh[meshCount];

            Transform rootBone = new GameObject().transform;
            rootBone.name = "[Root]";
            rootBone.parent = mainObject.transform;

            Read(fmdl, mainObject, foxModel, bones, textures, materials, meshes, rootBone);

            ctx.AddObjectToAsset("mainObject", mainObject);
            ctx.SetMainObject(mainObject);

            for (int i = 0; i < textureCount; i++)
                ctx.AddObjectToAsset($"Texture {i}", textures[i]);

            for (int i = 0; i < materialCount; i++)
                ctx.AddObjectToAsset($"Material {i}", materials[i]);

            for (int i = 0; i < meshCount; i++)
                ctx.AddObjectToAsset($"Mesh {i}", meshes[i]);
        } //ReadWithAssetImportContext
Example #4
0
        } //ReadWithAssetImportContext

        private void Read(Fmdl fmdl, GameObject mainObject, FoxModel foxModel, Transform[] bones, Texture[] textures, Material[] materials, Mesh[] meshes, Transform rootBone)
        {
            bool isGZFormat = fmdl.version == 2.03f;
            int boneCount = fmdl.fmdlBones != null ? fmdl.fmdlBones.Length : 0;
            int materialCount = fmdl.fmdlMaterialInstances.Length;
            int textureCount = fmdl.fmdlTextures != null ? fmdl.fmdlTextures.Length : 0;
            int meshCount = fmdl.fmdlMeshInfos.Length;
            int meshGroupCount = fmdl.fmdlMeshGroups.Length;

            {
                Fmdl.FmdlBoundingBox fmdlBoundingBox = fmdl.fmdlBoundingBoxes[0];

                Bounds bounds = new Bounds();
                bounds.SetMinMax(new Vector3(-fmdlBoundingBox.min.x, fmdlBoundingBox.min.y, fmdlBoundingBox.min.z), new Vector3(-fmdlBoundingBox.max.x, fmdlBoundingBox.max.y, fmdlBoundingBox.max.z));
                BoxCollider boxCollider = rootBone.gameObject.AddComponent<BoxCollider>();
                boxCollider.center = rootBone.InverseTransformPoint(bounds.center);
                boxCollider.size = bounds.size;
            } //code block

            for (int i = 0; i < boneCount; i++)
            {
                bones[i] = new GameObject().transform;

                Fmdl.FmdlBone fmdlBone = fmdl.fmdlBones[i];
                Fmdl.FmdlBoundingBox fmdlBoundingBox = fmdl.fmdlBoundingBoxes[fmdlBone.boundingBoxIndex];

                //Get the name.
                if (isGZFormat)
                    bones[i].gameObject.name = fmdl.fmdlStrings[fmdlBone.nameIndex];
                else
                    bones[i].gameObject.name = Hashing.TryGetStringName(fmdl.fmdlStrCode64s[fmdlBone.nameIndex]);

                try
                {
                    UInt64.Parse(bones[i].gameObject.name, System.Globalization.NumberStyles.HexNumber);
                    bones[i].name = $"SKL_{(Int32.Parse(bones[i - 1].name.Substring(4, 3)) + 1).ToString("000")}_UNKNOWN ({bones[i].name})";
                } //try
                catch { }

                //Assign the parent.
                if (fmdlBone.parentIndex != -1)
                    bones[i].parent = bones[fmdlBone.parentIndex];
                else
                    bones[i].parent = rootBone.transform;

                bones[i].position = new Vector3(-fmdlBone.worldPosition.x, fmdlBone.worldPosition.y, fmdlBone.worldPosition.z);

                //Set up the bounding box.
                Bounds bounds = new Bounds();

                //The x min and max are swapped here on purpose! The max value will end up as the smallest value and the min value will end up as the largest value if they aren't swapped here!
                bounds.SetMinMax(new Vector3(-fmdlBoundingBox.max.x, fmdlBoundingBox.min.y, fmdlBoundingBox.min.z), new Vector3(-fmdlBoundingBox.min.x, fmdlBoundingBox.max.y, fmdlBoundingBox.max.z));
                BoxCollider boxCollider = bones[i].gameObject.AddComponent<BoxCollider>();
                boxCollider.center = bones[i].InverseTransformPoint(bounds.center);
                boxCollider.size = bounds.size;
            } //for

            for (int i = 0; i < textureCount; i++)
            {
                string name;

                Fmdl.FmdlTexture fmdlTexture = fmdl.fmdlTextures[i];

                //Get the name.
                if (isGZFormat)
                {
                    name = fmdl.fmdlStrings[fmdlTexture.pathIndex] + fmdl.fmdlStrings[fmdlTexture.nameIndex];
                    name = name.Substring(0, name.IndexOf(".")) + ".dds";
                } //if
                else
                    name = Hashing.TryGetPathName(fmdl.fmdlPathCode64s[fmdlTexture.pathIndex]) + ".dds";

                //Read the file.
                if (File.Exists($"{Globals.GetTexturePath()}\\{name}"))
                    textures[i] = LoadTextureDXT($"{Globals.GetTexturePath()}\\{name}");
                else
                {
                    Debug.LogWarning($"Could not find {Globals.GetTexturePath()}\\{name}");

                    Texture2D texture = new Texture2D(512, 512);

                    for (int j = 0; j < 512; j++)
                        for (int h = 0; h < 512; h++)
                        {
                            Color c = new Color(0.25f, 0.25f, 0.25f);

                            if (((j / 32) % 2 == 0 && (h / 32) % 2 != 0) || ((j / 32) % 2 != 0 && (h / 32) % 2 == 0))
                                c = new Color(0.5f, 0.5f, 0.5f);

                            texture.SetPixel(j, h, c);
                        } //for

                    texture.Apply();

                    textures[i] = texture;
                } //else
                
                textures[i].name = name;
            } //for

            for (int i = 0; i < materialCount; i++)
            {
                string shaderName;
                string materialName;

                Fmdl.FmdlMaterialInstance fmdlMaterialInstance = fmdl.fmdlMaterialInstances[i];

                //Get the shader and material name.
                if (isGZFormat)
                {
                    shaderName = fmdl.fmdlStrings[fmdl.fmdlMaterials[fmdlMaterialInstance.materialIndex].typeIndex];
                    materialName = fmdl.fmdlStrings[fmdlMaterialInstance.nameIndex];
                } //if
                else
                {
                    shaderName = Hashing.TryGetStringName(fmdl.fmdlStrCode64s[fmdl.fmdlMaterials[fmdlMaterialInstance.materialIndex].typeIndex]);
                    materialName = Hashing.TryGetStringName(fmdl.fmdlStrCode64s[fmdlMaterialInstance.nameIndex]);
                } //else

                //Some ombs models have broken, unused, materials in them. Try catch here is necessary to handle them.
                try
                {
                    materials[i] = new Material(Shader.Find($"FoxShaders/{shaderName}"));
                } //try
                catch
                {
                    materials[i] = new Material(Shader.Find($"FoxShaders/fox3DDF_Blin_LNM"));
                    Debug.LogWarning($"Material {materialName} trying to use missing shader: {shaderName}! Defaulting to fox3DDF_Blin_LNM!");
                } //catch
                
                materials[i].name = materialName;

                //Link textures.
                for (int j = fmdlMaterialInstance.firstTextureIndex; j < fmdlMaterialInstance.firstTextureIndex + fmdlMaterialInstance.textureCount; j++)
                {
                    string textureType;

                    Fmdl.FmdlMaterialParameter fmdlMaterialParameter = fmdl.fmdlMaterialParameters[j];

                    if (isGZFormat)
                        textureType = fmdl.fmdlStrings[fmdlMaterialParameter.nameIndex];
                    else
                        textureType = Hashing.TryGetStringName(fmdl.fmdlStrCode64s[fmdlMaterialParameter.nameIndex]);

                    materials[i].SetTexture($"_{textureType}", textures[fmdlMaterialParameter.referenceIndex]);
                    materials[i].SetTextureScale($"_{textureType}", new Vector2(1, -1)); //Have to flip textures here because Texture2D.LoadRawData is bugged an imports DDS files upside down.
                } //for

                //Set parameters.
                for (int j = fmdlMaterialInstance.firstParameterIndex; j < fmdlMaterialInstance.firstParameterIndex + fmdlMaterialInstance.parameterCount; j++)
                {
                    string parameterName;

                    Fmdl.FmdlMaterialParameter fmdlMaterialParameter = fmdl.fmdlMaterialParameters[j];

                    if (isGZFormat)
                        parameterName = fmdl.fmdlStrings[fmdlMaterialParameter.nameIndex];
                    else
                        parameterName = Hashing.TryGetStringName(fmdl.fmdlStrCode64s[fmdlMaterialParameter.nameIndex]);

                    materials[i].SetVector($"_{parameterName}", fmdl.fmdlMaterialParameterVectors[fmdlMaterialParameter.referenceIndex]);
                } //for
            } //for

            for (int i = 0; i < meshGroupCount; i++)
            {
                foxModel.meshGroups[i] = new FoxMeshGroup();
                string name;

                Fmdl.FmdlMeshGroup fmdlMeshGroup = fmdl.fmdlMeshGroups[i];

                if (isGZFormat)
                    name = fmdl.fmdlStrings[fmdlMeshGroup.nameIndex];
                else
                    name = Hashing.TryGetStringName(fmdl.fmdlStrCode64s[fmdlMeshGroup.nameIndex]);

                foxModel.meshGroups[i].name = name;
                foxModel.meshGroups[i].parent = fmdlMeshGroup.parentIndex;

                if (fmdlMeshGroup.invisibilityFlag == 0)
                    foxModel.meshGroups[i].visible = true;
                else
                    foxModel.meshGroups[i].visible = false;
            } //for

            for (int i = 0; i < meshCount; i++)
            {
                meshes[i] = new Mesh();
                GameObject gameObject = new GameObject();
                gameObject.transform.parent = mainObject.transform;
                SkinnedMeshRenderer skinnedMeshRenderer = gameObject.AddComponent<SkinnedMeshRenderer>();
                foxModel.meshDefinitions[i] = new FoxMeshDefinition();

                Fmdl.FmdlMesh fmdlMesh = fmdl.fmdlMeshes[i];
                Fmdl.FmdlMeshInfo fmdlMeshInfo = fmdl.fmdlMeshInfos[i];
                Fmdl.FmdlBoneGroup fmdlBoneGroup = fmdl.fmdlBoneGroups != null ? fmdl.fmdlBoneGroups[fmdlMeshInfo.boneGroupIndex] : new Fmdl.FmdlBoneGroup();

                int vertexLength = fmdlMesh.vertices.Length;
                int faceLength = fmdlMesh.triangles.Length;
                string name;

                Vector3[] vertices = new Vector3[vertexLength];
                Vector3[] normals = new Vector3[vertexLength];
                Vector4[] tangents = fmdlMesh.tangents != null ? new Vector4[vertexLength] : new Vector4[0];
                Color[] colors = fmdlMesh.colors != null ? new Color[vertexLength] : new Color[0];
                BoneWeight[] boneWeights = fmdlMesh.boneWeights != null ? new BoneWeight[vertexLength] : new BoneWeight[0];
                Vector2[] uv = fmdlMesh.uv != null ? new Vector2[vertexLength] : new Vector2[0];
                Vector2[] uv2 = fmdlMesh.uv2 != null ? new Vector2[vertexLength] : new Vector2[0];
                Vector2[] uv3 = fmdlMesh.uv3 != null ? new Vector2[vertexLength] : new Vector2[0];
                Vector2[] uv4 = fmdlMesh.uv4 != null ? new Vector2[vertexLength] : new Vector2[0];
                int[] triangles = new int[faceLength];
                Matrix4x4[] bindPoses;

                List<Transform> usedBones = new List<Transform>(0);
                Transform[] usedBonesArray;

                for (int j = 0; j < vertexLength; j++)
                {
                    vertices[j] = new Vector3(-fmdlMesh.vertices[j].x, fmdlMesh.vertices[j].y, fmdlMesh.vertices[j].z);
                    normals[j] = new Vector3(-fmdlMesh.normals[j].x, fmdlMesh.normals[j].y, fmdlMesh.normals[j].z);
                    if (tangents.Length > 0)
                        tangents[j] = new Vector4(-fmdlMesh.tangents[j].x, fmdlMesh.tangents[j].y, fmdlMesh.tangents[j].z, fmdlMesh.tangents[j].w);
                    if (colors.Length > 0)
                        colors[j] = new Color(fmdlMesh.colors[j].x / 255f, fmdlMesh.colors[j].y / 255f, fmdlMesh.colors[j].z / 255f, fmdlMesh.colors[j].w / 255f);
                    if (boneWeights.Length > 0)
                    {
                        boneWeights[j].weight0 = fmdlMesh.boneWeights[j].x / 255f;
                        boneWeights[j].weight1 = fmdlMesh.boneWeights[j].y / 255f;
                        boneWeights[j].weight2 = fmdlMesh.boneWeights[j].z / 255f;
                        boneWeights[j].weight3 = fmdlMesh.boneWeights[j].w / 255f;
                        boneWeights[j].boneIndex0 = fmdlBoneGroup.boneIndices[(int)fmdlMesh.boneIndices[j].x];
                        boneWeights[j].boneIndex1 = fmdlBoneGroup.boneIndices[(int)fmdlMesh.boneIndices[j].y];
                        boneWeights[j].boneIndex2 = fmdlBoneGroup.boneIndices[(int)fmdlMesh.boneIndices[j].z];
                        boneWeights[j].boneIndex3 = fmdlBoneGroup.boneIndices[(int)fmdlMesh.boneIndices[j].w];
                    } //if
                    if (uv.Length > 0)
                        uv[j] = new Vector2(fmdlMesh.uv[j].x, 1 - fmdlMesh.uv[j].y);
                    if (uv2.Length > 0)
                        uv2[j] = new Vector2(fmdlMesh.uv2[j].x, 1 - fmdlMesh.uv2[j].y);
                    if (uv3.Length > 0)
                        uv3[j] = new Vector2(fmdlMesh.uv3[j].x, 1 - fmdlMesh.uv3[j].y);
                    if (uv4.Length > 0)
                        uv4[j] = new Vector2(fmdlMesh.uv4[j].x, 1 - fmdlMesh.uv4[j].y);
                } //for

                for (int j = 0; j < faceLength; j++)
                    triangles[j] = fmdlMesh.triangles[j];

                for (int j = 0; j < boneWeights.Length; j++)
                {
                    if (boneWeights[j].weight0 > 0f)
                    {
                        if (usedBones.Contains(bones[boneWeights[j].boneIndex0]))
                            boneWeights[j].boneIndex0 = usedBones.IndexOf(bones[boneWeights[j].boneIndex0]);
                        else
                        {
                            usedBones.Add(bones[boneWeights[j].boneIndex0]);
                            boneWeights[j].boneIndex0 = usedBones.IndexOf(bones[boneWeights[j].boneIndex0]);
                        } //else
                    } //if
                    else
                        boneWeights[j].boneIndex0 = 0;

                    if (boneWeights[j].weight1 > 0f)
                    {
                        if (usedBones.Contains(bones[boneWeights[j].boneIndex1]))
                            boneWeights[j].boneIndex1 = usedBones.IndexOf(bones[boneWeights[j].boneIndex1]);
                        else
                        {
                            usedBones.Add(bones[boneWeights[j].boneIndex1]);
                            boneWeights[j].boneIndex1 = usedBones.IndexOf(bones[boneWeights[j].boneIndex1]);
                        } //else
                    } //if
                    else
                        boneWeights[j].boneIndex1 = 0;

                    if (boneWeights[j].weight2 > 0f)
                    {
                        if (usedBones.Contains(bones[boneWeights[j].boneIndex2]))
                            boneWeights[j].boneIndex2 = usedBones.IndexOf(bones[boneWeights[j].boneIndex2]);
                        else
                        {
                            usedBones.Add(bones[boneWeights[j].boneIndex2]);
                            boneWeights[j].boneIndex2 = usedBones.IndexOf(bones[boneWeights[j].boneIndex2]);
                        } //else
                    } //if
                    else
                        boneWeights[j].boneIndex2 = 0;

                    if (boneWeights[j].weight3 > 0f)
                    {
                        if (usedBones.Contains(bones[boneWeights[j].boneIndex3]))
                            boneWeights[j].boneIndex3 = usedBones.IndexOf(bones[boneWeights[j].boneIndex3]);
                        else
                        {
                            usedBones.Add(bones[boneWeights[j].boneIndex3]);
                            boneWeights[j].boneIndex3 = usedBones.IndexOf(bones[boneWeights[j].boneIndex3]);
                        } //else
                    } //if
                    else
                        boneWeights[j].boneIndex3 = 0;
                } //for

                usedBonesArray = usedBones.ToArray();
                bindPoses = new Matrix4x4[usedBonesArray.Length];

                for (int j = 0; j < usedBonesArray.Length; j++)
                    bindPoses[j] = usedBones[j].worldToLocalMatrix * gameObject.transform.localToWorldMatrix;

                meshes[i].vertices = vertices;
                meshes[i].normals = normals;
                meshes[i].tangents = tangents;
                meshes[i].colors = colors;
                meshes[i].boneWeights = boneWeights;
                meshes[i].uv = uv;
                meshes[i].uv2 = uv2;
                meshes[i].uv3 = uv3;
                meshes[i].uv4 = uv4;
                meshes[i].triangles = triangles;
                meshes[i].bindposes = bindPoses;

                skinnedMeshRenderer.bones = usedBonesArray;
                skinnedMeshRenderer.sharedMaterial = materials[fmdlMeshInfo.materialInstanceIndex];
                skinnedMeshRenderer.sharedMesh = meshes[i];
                skinnedMeshRenderer.rootBone = mainObject.transform;

                foxModel.meshDefinitions[i].mesh = meshes[i];
                foxModel.meshDefinitions[i].meshGroup = Array.Find(fmdl.fmdlMeshGroupEntries, x => x.firstMeshIndex <= i && x.firstMeshIndex + x.meshCount > i).meshGroupIndex;
                foxModel.meshDefinitions[i].alpha = (FoxMeshDefinition.Alpha)fmdlMeshInfo.alphaEnum;
                foxModel.meshDefinitions[i].shadow = (FoxMeshDefinition.Shadow)fmdlMeshInfo.shadowEnum;

                if (isGZFormat)
                    name = fmdl.fmdlStrings[fmdl.fmdlMeshGroups[foxModel.meshDefinitions[i].meshGroup].nameIndex];
                else
                    name = Hashing.TryGetStringName(fmdl.fmdlStrCode64s[fmdl.fmdlMeshGroups[foxModel.meshDefinitions[i].meshGroup].nameIndex]);

                gameObject.name = $"{i} - {name}";
            } //for
        } //Read
        public GameObject GetDataFromFmdl(OldFmdl fmdl)
        {
            meshes = new UnityMesh[fmdl.section0Block3Entries.Count];

            GameObject fmdlGameObject = new GameObject();
            fmdlGameObject.name = fmdl.name;
            GameObject[] subFmdlGameObjects = new GameObject[fmdl.objects.Count];
            Transform[] bones;
            //Matrix4x4[] bindPoses;
            Bounds[] bounds = new Bounds[fmdl.section0BlockDEntries.Count];

            FoxModel foxModel = fmdlGameObject.AddComponent<FoxModel>();
            foxModel.meshGroups = new FoxMeshGroup[fmdl.section0Block1Entries.Count];
            foxModel.meshDefinitions = new FoxMeshDefinition[fmdl.objects.Count];

            Material[] materials = new Material[fmdl.section0Block4Entries.Count];

            if (fmdl.bonesIndex != -1)
            {
                bones = new Transform[fmdl.section0Block0Entries.Count];
                //bindPoses = new Matrix4x4[fmdl.section0Block0Entries.Count];
            } //if
            else
            {
                bones = new Transform[0];
                //bindPoses = new Matrix4x4[0];
            } //else

            //Mesh Groups
            for (int i = 0; i < fmdl.section0Block1Entries.Count; i++)
            {
                FoxMeshGroup foxMeshGroup = new FoxMeshGroup();

                if (fmdl.stringsIndex != -1)
                    foxMeshGroup.name = fmdl.strings[fmdl.section0Block1Entries[i].stringId];
                else
                    foxMeshGroup.name = Hashing.TryGetStringName(fmdl.section0Block16Entries[fmdl.section0Block1Entries[i].stringId]);

                foxMeshGroup.parent = (short)fmdl.section0Block1Entries[i].parentId;

                if (fmdl.section0Block1Entries[i].invisibilityFlag == 0)
                    foxMeshGroup.visible = true;
                else
                    foxMeshGroup.visible = false;

                foxModel.meshGroups[i] = foxMeshGroup;
            } //for

            //Materials
            for (int i = 0; i < fmdl.section0Block4Entries.Count; i++)
            {
                if (fmdl.stringsIndex != -1)
                    materials[i] = new Material(Shader.Find($"FoxShaders/{fmdl.strings[fmdl.section0Block8Entries[fmdl.section0Block4Entries[i].materialId].typeId]}"));
                else
                    materials[i] = new Material(Shader.Find($"FoxShaders/{Hashing.TryGetStringName(fmdl.section0Block16Entries[fmdl.section0Block8Entries[fmdl.section0Block4Entries[i].materialId].typeId])}"));
                //materials[i].material = new Material(Shader.Find("Legacy Shaders/Transparent/Cutout/Bumped Diffuse"));
                //materials[i].material = new Material(Shader.Find("Standard"));

                if (fmdl.stringsIndex != -1)
                {
                    materials[i].name = fmdl.strings[fmdl.section0Block4Entries[i].stringId];

                    for (int j = fmdl.section0Block4Entries[i].firstTextureId; j < fmdl.section0Block4Entries[i].firstTextureId + fmdl.section0Block4Entries[i].numTextures; j++)
                    {
                        string textureName = "";
                        string textureType = fmdl.strings[fmdl.section0Block7Entries[j].stringId];
                        int extensionLocation;

                        textureName = fmdl.strings[fmdl.section0Block6Entries[fmdl.section0Block7Entries[j].referenceId].pathId] + fmdl.strings[fmdl.section0Block6Entries[fmdl.section0Block7Entries[j].referenceId].stringId];
                        //materials[i].textureType = fmdl.strings[fmdl.section0Block7Entries[fmdl.section0Block4Entries[i].firstTextureId].stringId];

                        extensionLocation = textureName.IndexOf('.');
                        textureName = textureName.Substring(0, extensionLocation);

                        if (File.Exists(Globals.texturePath + "\\" + textureName + ".dds"))
                        {
                            Texture2D texture = LoadTextureDXT(Globals.texturePath + "\\" + textureName + ".dds");
                            texture.name = textureName + ".dds";

                            materials[i].SetTexture(textureType, texture);
                        } //if
                        else
                        {
                            Debug.Log("Could not find: " + Globals.texturePath + "\\" + textureName + ".dds");
                        } //else
                    } //for

                    for (int j = fmdl.section0Block4Entries[i].firstParameterId; j < fmdl.section0Block4Entries[i].firstParameterId + fmdl.section0Block4Entries[i].numParameters; j++)
                        materials[i].SetVector(fmdl.strings[fmdl.section0Block7Entries[j].stringId], new Vector4(fmdl.materialParameters[fmdl.section0Block7Entries[j].referenceId].values.x, fmdl.materialParameters[fmdl.section0Block7Entries[j].referenceId].values.y, fmdl.materialParameters[fmdl.section0Block7Entries[j].referenceId].values.z, fmdl.materialParameters[fmdl.section0Block7Entries[j].referenceId].values.w));
                } //if
                else
                {
                    materials[i].name = Hashing.TryGetStringName(fmdl.section0Block16Entries[fmdl.section0Block4Entries[i].stringId]);

                    if (fmdl.texturePathsIndex != -1)
                    {
                        for (int j = fmdl.section0Block4Entries[i].firstTextureId; j < fmdl.section0Block4Entries[i].firstTextureId + fmdl.section0Block4Entries[i].numTextures; j++)
                        {
                            string textureName = Hashing.TryGetPathName(fmdl.section0Block15Entries[fmdl.section0Block7Entries[j].referenceId]);
                            string textureType = Hashing.TryGetStringName(fmdl.section0Block16Entries[fmdl.section0Block7Entries[j].stringId]);

                            if (File.Exists(Globals.texturePath + "\\" + textureName + ".dds"))
                            {
                                Texture2D texture = LoadTextureDXT(Globals.texturePath + "\\" + textureName + ".dds");
                                texture.name = textureName + ".dds";

                                materials[i].SetTexture(textureType, texture);
                            } //if
                            else
                            {
                                Debug.Log("Could not find: " + Globals.texturePath + "\\" + textureName + ".dds");
                            } //else
                        } //for
                    } //if

                    for (int j = fmdl.section0Block4Entries[i].firstParameterId; j < fmdl.section0Block4Entries[i].firstParameterId + fmdl.section0Block4Entries[i].numParameters; j++)
                        materials[i].SetVector(Hashing.TryGetStringName(fmdl.section0Block16Entries[fmdl.section0Block7Entries[j].stringId]), new Vector4(fmdl.materialParameters[fmdl.section0Block7Entries[j].referenceId].values[0], fmdl.materialParameters[fmdl.section0Block7Entries[j].referenceId].values[1], fmdl.materialParameters[fmdl.section0Block7Entries[j].referenceId].values[2], fmdl.materialParameters[fmdl.section0Block7Entries[j].referenceId].values[3]));
                } //else

                //Have to flip textures here because Texture2D.LoadRawData is bugged an imports DDS files upside down.
                for (int j = 0; j < ShaderUtil.GetPropertyCount(materials[i].shader); j++)
                    if (ShaderUtil.GetPropertyType(materials[i].shader, j) == ShaderUtil.ShaderPropertyType.TexEnv)
                        materials[i].SetTextureScale(ShaderUtil.GetPropertyName(materials[i].shader, j), new Vector2(1, -1));
            } //for

            for (int i = 0; i < bounds.Length; i++)
            {
                bounds[i].SetMinMax(new Vector3(-fmdl.section0BlockDEntries[i].minX, fmdl.section0BlockDEntries[i].minY, fmdl.section0BlockDEntries[i].minZ), new Vector3(-fmdl.section0BlockDEntries[i].maxX, fmdl.section0BlockDEntries[i].maxY, fmdl.section0BlockDEntries[i].maxZ));
            } //for

            Transform rootBone = new GameObject("[Root]").transform; //From what I can tell, the real name is "". But it looks kinda dumb having "" as its name; so using "[Root]" as a placeholder seems better.
            rootBone.parent = fmdlGameObject.transform;

            {
                BoxCollider collider = rootBone.gameObject.AddComponent<BoxCollider>();
                collider.center = rootBone.InverseTransformPoint(bounds[0].center); //Have to convert these to local positions. They're stored as world positions.
                collider.size = bounds[0].size;
            } //code block

            for (int i = 0; i < bones.Length; i++)
            {
                bones[i] = new GameObject().transform;
                bones[i].position = new Vector3(-fmdl.section0Block0Entries[i].worldPositionX, fmdl.section0Block0Entries[i].worldPositionY, fmdl.section0Block0Entries[i].worldPositionZ);

                BoxCollider collider = bones[i].gameObject.AddComponent<BoxCollider>();
                collider.center = bones[i].InverseTransformPoint(bounds[fmdl.section0Block0Entries[i].boundingBoxId].center); //Have to convert these to local positions. They're stored as world positions.
                collider.size = bounds[fmdl.section0Block0Entries[i].boundingBoxId].size;

                if (fmdl.stringsIndex != -1)
                    bones[i].name = fmdl.strings[fmdl.section0Block0Entries[i].stringId];
                else
                    bones[i].name = Hashing.TryGetStringName(fmdl.section0Block16Entries[fmdl.section0Block0Entries[i].stringId]);

                if (fmdl.section0Block0Entries[i].parentId != 0xFFFF)
                    bones[i].parent = bones[fmdl.section0Block0Entries[i].parentId];
                else
                    bones[i].parent = rootBone;
            } //for

            for (int i = 0; i < fmdl.objects.Count; i++)
            {
                int lod = 0; //Temporary solution. 0 loads the normal faces. 1 loads the first set of LOD faces. 2 loads the next set. Etc....

                meshes[i].vertices = new Vector3[fmdl.objects[i].vertices.Length];
                meshes[i].normals = new Vector3[fmdl.objects[i].additionalVertexData.Length];
                meshes[i].tangents = new Vector4[fmdl.objects[i].additionalVertexData.Length];
                meshes[i].colour = new Color[fmdl.objects[i].additionalVertexData.Length];
                meshes[i].uv = new Vector2[fmdl.objects[i].additionalVertexData.Length];
                meshes[i].uv2 = new Vector2[fmdl.objects[i].additionalVertexData.Length];
                meshes[i].uv3 = new Vector2[fmdl.objects[i].additionalVertexData.Length];
                meshes[i].uv4 = new Vector2[fmdl.objects[i].additionalVertexData.Length];
                //meshes[i].faces = new int[fmdl.objects[i].faces.Length * 3];
                meshes[i].faces = new int[fmdl.objects[i].lodFaces[lod].Length * 3];
                meshes[i].boneWeights = new BoneWeight[fmdl.objects[i].additionalVertexData.Length];

                //Position
                for (int j = 0; j < fmdl.objects[i].vertices.Length; j++)
                    meshes[i].vertices[j] = new Vector3(-fmdl.objects[i].vertices[j].x, fmdl.objects[i].vertices[j].y, fmdl.objects[i].vertices[j].z);

                //Normals, Bone Weights, Bone Group Ids and UVs
                for (int j = 0; j < fmdl.objects[i].additionalVertexData.Length; j++)
                {
                    meshes[i].normals[j] = new Vector3(-fmdl.objects[i].additionalVertexData[j].normalX, fmdl.objects[i].additionalVertexData[j].normalY, fmdl.objects[i].additionalVertexData[j].normalZ);
                    meshes[i].tangents[j] = new Vector4(-fmdl.objects[i].additionalVertexData[j].tangentX, fmdl.objects[i].additionalVertexData[j].tangentY, fmdl.objects[i].additionalVertexData[j].tangentZ, fmdl.objects[i].additionalVertexData[j].tangentW);
                    meshes[i].colour[j] = new Color(fmdl.objects[i].additionalVertexData[j].colourR, fmdl.objects[i].additionalVertexData[j].colourG, fmdl.objects[i].additionalVertexData[j].colourB, fmdl.objects[i].additionalVertexData[j].colourA);

                    if (fmdl.bonesIndex != -1)
                    {
                        meshes[i].boneWeights[j].weight0 = fmdl.objects[i].additionalVertexData[j].boneWeightX;
                        meshes[i].boneWeights[j].weight1 = fmdl.objects[i].additionalVertexData[j].boneWeightY;
                        meshes[i].boneWeights[j].weight2 = fmdl.objects[i].additionalVertexData[j].boneWeightZ;
                        meshes[i].boneWeights[j].weight3 = fmdl.objects[i].additionalVertexData[j].boneWeightW;
                        meshes[i].boneWeights[j].boneIndex0 = fmdl.section0Block5Entries[fmdl.section0Block3Entries[i].boneGroupId].entries[fmdl.objects[i].additionalVertexData[j].boneGroup0Id];
                        meshes[i].boneWeights[j].boneIndex1 = fmdl.section0Block5Entries[fmdl.section0Block3Entries[i].boneGroupId].entries[fmdl.objects[i].additionalVertexData[j].boneGroup1Id];
                        meshes[i].boneWeights[j].boneIndex2 = fmdl.section0Block5Entries[fmdl.section0Block3Entries[i].boneGroupId].entries[fmdl.objects[i].additionalVertexData[j].boneGroup2Id];
                        meshes[i].boneWeights[j].boneIndex3 = fmdl.section0Block5Entries[fmdl.section0Block3Entries[i].boneGroupId].entries[fmdl.objects[i].additionalVertexData[j].boneGroup3Id];
                    } //if

                    meshes[i].uv[j] = new Vector2(fmdl.objects[i].additionalVertexData[j].textureU, -fmdl.objects[i].additionalVertexData[j].textureV);
                    meshes[i].uv2[j] = new Vector2(fmdl.objects[i].additionalVertexData[j].unknownU0, -fmdl.objects[i].additionalVertexData[j].unknownV0);
                    meshes[i].uv3[j] = new Vector2(fmdl.objects[i].additionalVertexData[j].unknownU1, -fmdl.objects[i].additionalVertexData[j].unknownV1);
                    meshes[i].uv4[j] = new Vector2(fmdl.objects[i].additionalVertexData[j].unknownU2, -fmdl.objects[i].additionalVertexData[j].unknownV2);
                } //for

                //Faces
                /*for (int j = 0, h = 0; j < fmdl.objects[i].faces.Length; j++, h += 3)
                {
                    meshes[i].faces[h] = fmdl.objects[i].faces[j].vertex1Id;
                    meshes[i].faces[h + 1] = fmdl.objects[i].faces[j].vertex2Id;
                    meshes[i].faces[h + 2] = fmdl.objects[i].faces[j].vertex3Id;
                } //for*/

                for (int j = 0, h = 0; j < fmdl.objects[i].lodFaces[lod].Length; j++, h += 3)
                {
                    meshes[i].faces[h] = fmdl.objects[i].lodFaces[lod][j].vertex1Id;
                    meshes[i].faces[h + 1] = fmdl.objects[i].lodFaces[lod][j].vertex2Id;
                    meshes[i].faces[h + 2] = fmdl.objects[i].lodFaces[lod][j].vertex3Id;
                } //for

                //Render the mesh in Unity.
                subFmdlGameObjects[i] = new GameObject();
                FoxMeshDefinition foxMeshDefinition = new FoxMeshDefinition();
                foxMeshDefinition.alpha = (FoxMeshDefinition.Alpha)fmdl.section0Block3Entries[i].alphaEnum;
                foxMeshDefinition.shadow = (FoxMeshDefinition.Shadow)fmdl.section0Block3Entries[i].shadowEnum;

                //Get the mesh name.
                for (int j = 0; j < fmdl.section0Block2Entries.Count; j++)
                {
                    if (i >= fmdl.section0Block2Entries[j].firstObjectId && i < fmdl.section0Block2Entries[j].firstObjectId + fmdl.section0Block2Entries[j].numObjects)
                    {
                        if (fmdl.stringsIndex != -1)
                            subFmdlGameObjects[i].name = i + " - " + fmdl.strings[fmdl.section0Block1Entries[fmdl.section0Block2Entries[j].meshGroupId].stringId];
                        else
                            subFmdlGameObjects[i].name = i + " - " + Hashing.TryGetStringName(fmdl.section0Block16Entries[fmdl.section0Block1Entries[fmdl.section0Block2Entries[j].meshGroupId].stringId]);

                        foxMeshDefinition.meshGroup = fmdl.section0Block2Entries[j].meshGroupId;

                        break;
                    } //if
                } //for

                subFmdlGameObjects[i].transform.parent = fmdlGameObject.transform;
                SkinnedMeshRenderer meshRenderer = subFmdlGameObjects[i].AddComponent<SkinnedMeshRenderer>();

                meshRenderer.sharedMaterial = materials[fmdl.section0Block3Entries[i].materialInstanceId];

                Mesh mesh = new Mesh();
                mesh.vertices = meshes[i].vertices;
                mesh.uv = meshes[i].uv;
                mesh.uv2 = meshes[i].uv2;
                mesh.uv3 = meshes[i].uv3;
                mesh.uv4 = meshes[i].uv4;
                mesh.normals = meshes[i].normals;
                mesh.tangents = meshes[i].tangents;
                mesh.triangles = meshes[i].faces;
                mesh.boneWeights = meshes[i].boneWeights;

                //Sets up the model to only use weighted bones.
                Transform[] boneArray = bones;
                List<Transform> usedBones = new List<Transform>(0);
                BoneWeight[] weightArray = meshes[i].boneWeights;
                int boneCount = boneArray.Length;
                int weightCount = weightArray.Length;

                for (int j = 0; j < boneCount; j++)
                {
                    bool found = false;

                    for (int h = 0; h < weightCount; h++)
                    {
                        if (weightArray[h].boneIndex0 == j)
                        {
                            found = true;

                            if (!usedBones.Contains(boneArray[j]))
                                weightArray[h].boneIndex0 = usedBones.Count;
                            else
                                weightArray[h].boneIndex0 = usedBones.IndexOf(boneArray[j]);
                        } //if
                        if (weightArray[h].boneIndex1 == j)
                        {
                            found = true;

                            if (!usedBones.Contains(boneArray[j]))
                                weightArray[h].boneIndex1 = usedBones.Count;
                            else
                                weightArray[h].boneIndex1 = usedBones.IndexOf(boneArray[j]);
                        } //if
                        if (weightArray[h].boneIndex2 == j)
                        {
                            found = true;

                            if (!usedBones.Contains(boneArray[j]))
                                weightArray[h].boneIndex2 = usedBones.Count;
                            else
                                weightArray[h].boneIndex2 = usedBones.IndexOf(boneArray[j]);
                        } //if
                        if (weightArray[h].boneIndex3 == j)
                        {
                            found = true;

                            if (!usedBones.Contains(boneArray[j]))
                                weightArray[h].boneIndex3 = usedBones.Count;
                            else
                                weightArray[h].boneIndex3 = usedBones.IndexOf(boneArray[j]);
                        } //if
                    } //for

                    if (found)
                        if (!usedBones.Contains(boneArray[j]))
                            usedBones.Add(boneArray[j]);
                } //for

                mesh.boneWeights = weightArray;

                meshRenderer.bones = usedBones.ToArray();

                boneCount = usedBones.Count;
                List<Matrix4x4> bindPoses = new List<Matrix4x4>(0);

                for (int j = 0; j < boneCount; j++)
                {
                    bindPoses.Add(usedBones[j].worldToLocalMatrix * subFmdlGameObjects[i].transform.localToWorldMatrix);
                } //for

                mesh.bindposes = bindPoses.ToArray();
                meshRenderer.sharedMesh = mesh;
                foxMeshDefinition.mesh = meshRenderer.sharedMesh;
                foxModel.meshDefinitions[i] = foxMeshDefinition;
                subFmdlGameObjects[i].AddComponent<MeshCollider>();
            } //for

            return fmdlGameObject;
        } //GetDataFromFmdl
 /// <summary>
 /// Returns true if the given model is ignored.
 /// </summary>
 /// <param name="model">The model to check.</param>
 /// <returns>True if the given model is ignored.</returns>
 public bool IsModelIgnored(FoxModel model)
 {
     return(this.ignoredModels.Contains(model));
 }
 /// <summary>
 /// Ignores a model. An ignored model will never have a SceneProxy created for it.
 /// </summary>
 /// <param name="model">The model to ignore.</param>
 public void IgnoreModel(FoxModel model)
 {
     this.ignoredModels.Add(model);
 }
        } //OnEnable

        public override void OnInspectorGUI()
        {
            FoxModel foxModel = target as FoxModel;
            serializedObject.Update();

            serializedObject.FindProperty("meshGroups").isExpanded = EditorGUILayout.Foldout(serializedObject.FindProperty("meshGroups").isExpanded, "Mesh Groups", true);

            if (serializedObject.FindProperty("meshGroups").isExpanded)
            {
                meshGroupsLength = EditorGUILayout.IntField("Size", meshGroupsLength);

                if (currentControlID != GUIUtility.keyboardControl)
                {
                    foxModel.meshGroups = SetArraySize(foxModel.meshGroups, meshGroupsLength);
                    currentControlID = GUIUtility.keyboardControl;
                    serializedObject.Update();
                } //if

                for (int i = 0; i < foxModel.meshGroups.Length; i++)
                {
                    serializedObject.FindProperty("meshGroups").GetArrayElementAtIndex(i).isExpanded = EditorGUILayout.Foldout(serializedObject.FindProperty("meshGroups").GetArrayElementAtIndex(i).isExpanded, $"Element {i}", true);

                    if (serializedObject.FindProperty("meshGroups").GetArrayElementAtIndex(i).isExpanded)
                    {
                        string[] meshGroupStrings = new string[i + 1];

                        meshGroupStrings[0] = "No Parent";

                        for (int j = 0; j < i; j++)
                            meshGroupStrings[j + 1] = foxModel.meshGroups[j].name;

                        foxModel.meshGroups[i].name = EditorGUILayout.TextField("Name", foxModel.meshGroups[i].name);
                        foxModel.meshGroups[i].parent = (short)(EditorGUILayout.Popup("Parent", foxModel.meshGroups[i].parent + 1, meshGroupStrings) - 1);
                        foxModel.meshGroups[i].visible = EditorGUILayout.Toggle("Is Visible", foxModel.meshGroups[i].visible);
                    } //if
                } //for
            } //if

            serializedObject.FindProperty("meshDefinitions").isExpanded = EditorGUILayout.Foldout(serializedObject.FindProperty("meshDefinitions").isExpanded, "Mesh Definitions", true);

            if (serializedObject.FindProperty("meshDefinitions").isExpanded)
            {
                meshDefinitionsLength = EditorGUILayout.IntField("Size", meshDefinitionsLength);

                if (currentControlID != GUIUtility.keyboardControl)
                {
                    foxModel.meshDefinitions = SetArraySize(foxModel.meshDefinitions, meshDefinitionsLength);
                    currentControlID = GUIUtility.keyboardControl;
                    serializedObject.Update();
                } //if

                for (int i = 0; i < foxModel.meshDefinitions.Length; i++)
                {
                    serializedObject.FindProperty("meshDefinitions").GetArrayElementAtIndex(i).isExpanded = EditorGUILayout.Foldout(serializedObject.FindProperty("meshDefinitions").GetArrayElementAtIndex(i).isExpanded, $"Element {i}", true);

                    if (serializedObject.FindProperty("meshDefinitions").GetArrayElementAtIndex(i).isExpanded)
                    {
                        string[] meshGroupStrings = new string[foxModel.meshGroups.Length];

                        for (int j = 0; j < foxModel.meshGroups.Length; j++)
                            meshGroupStrings[j] = foxModel.meshGroups[j].name;

                        foxModel.meshDefinitions[i].mesh = (Mesh)EditorGUILayout.ObjectField("Mesh", foxModel.meshDefinitions[i].mesh, typeof(Mesh), true);
                        foxModel.meshDefinitions[i].meshGroup = EditorGUILayout.Popup("Mesh Group", foxModel.meshDefinitions[i].meshGroup, meshGroupStrings);
                        foxModel.meshDefinitions[i].alpha = (FoxMeshDefinition.Alpha)EditorGUILayout.EnumPopup("Alpha", foxModel.meshDefinitions[i].alpha);
                        foxModel.meshDefinitions[i].shadow = (FoxMeshDefinition.Shadow)EditorGUILayout.EnumPopup("Shadow", foxModel.meshDefinitions[i].shadow);
                    } //if
                } //for
            } //if
        } //OnInspectorGUI