private static void CreateDispSurface()
    {
        GameObject BSP_DispSurface = new GameObject(WorldController.MapName + "_disp");
        Dictionary <int, List <int> > subMeshData = new Dictionary <int, List <int> >();

        for (int i = 0; i < BSP_Faces.Count; i++)
        {
            if (!subMeshData.ContainsKey(BSP_Texdata[BSP_Texinfo[BSP_Faces[i].texinfo].texdata].nameStringTableID))
            {
                subMeshData.Add(BSP_Texdata[BSP_Texinfo[BSP_Faces[i].texinfo].texdata].nameStringTableID, new List <int>());
            }

            subMeshData[BSP_Texdata[BSP_Texinfo[BSP_Faces[i].texinfo].texdata].nameStringTableID].Add(i);
        }

        for (int i = 0; i < BSP_TexStrData.Count; i++)
        {
            if (!subMeshData.ContainsKey(i))
            {
                continue;
            }

            List <Vector3> vertices  = new List <Vector3>();
            List <Vector2> uv        = new List <Vector2>();
            List <int>     triangles = new List <int>();

            for (int k = 0; k < subMeshData[i].Count; k++)
            {
                if (BSP_Faces[subMeshData[i][k]].dispinfo != -1)
                {
                    face f           = CreateDispSurface(BSP_Faces[subMeshData[i][k]].dispinfo);
                    int  pointOffset = vertices.Count;

                    for (int j = 0; j < f.triangles.Length; j++)
                    {
                        triangles.Add(f.triangles[j] + pointOffset);
                    }

                    vertices.AddRange(f.points);
                    uv.AddRange(f.uv);
                }
            }

            if (vertices.Count > 0)
            {
                GameObject submesh = new GameObject(BSP_TexStrData[i]);
                submesh.transform.localScale = new Vector3(1, 1, -1);
                submesh.transform.parent     = BSP_DispSurface.transform;

                MeshRenderer meshRenderer = submesh.AddComponent <MeshRenderer>();
                MeshFilter   meshFilter   = submesh.AddComponent <MeshFilter>();

                WorldController.CurrentTexPath = WorldController.DefaultTexPath;
                if (BSP_TexStrData[i].Contains(WorldController.MapName))
                {
                    WorldController.CurrentTexPath = WorldController.PakTexPath;
                }

                meshRenderer.sharedMaterial = ValveTextureLoader.LoadMaterial(BSP_TexStrData[i]);

                meshFilter.sharedMesh           = new Mesh();
                meshFilter.sharedMesh.vertices  = vertices.ToArray();
                meshFilter.sharedMesh.triangles = triangles.ToArray();
                meshFilter.sharedMesh.uv        = uv.ToArray();

                meshFilter.sharedMesh.RecalculateNormals();
                meshFilter.sharedMesh.Optimize();
            }
        }
    }
    private static void ParseVtxFile()
    {
        List <BoneWeight> pBoneWeight = new List <BoneWeight>();
        List <Vector3>    pVertices   = new List <Vector3>();
        List <Vector3>    pNormals    = new List <Vector3>();
        List <Vector2>    pUvBuffer   = new List <Vector2>();

        // Load necessary information
        mstudiomodel_t   pModel = MDL_Models[0]; mstudiomesh_t pStudioMesh;
        BodyPartHeader_t vBodypart = CRead.ReadType <BodyPartHeader_t>(VTX_Header.bodyPartOffset);

        int           ModelInputFilePosition = VTX_Header.bodyPartOffset + vBodypart.modelOffset;
        ModelHeader_t vModel = CRead.ReadType <ModelHeader_t>(ModelInputFilePosition);

        int ModelLODInputFilePosition = ModelInputFilePosition + vModel.lodOffset;
        ModelLODHeader_t vLod         = CRead.ReadType <ModelLODHeader_t>(ModelLODInputFilePosition);

        int MeshInputFilePosition = ModelLODInputFilePosition + vLod.meshOffset;

        VTX_Meshes.AddRange(CRead.ReadType <MeshHeader_t>(MeshInputFilePosition, vLod.numMeshes));

        // Get bone weight's, vertices, normals, uv
        for (int i = 0; i < pModel.numvertices; i++)
        {
            pBoneWeight.Add(GetBoneWeight(VVD_Vertexes[pModel.vertexindex + i].m_BoneWeights));

            pVertices.Add(WorldController.SwapZY(VVD_Vertexes[pModel.vertexindex + i].m_vecPosition * WorldController.WorldScale));
            pNormals.Add(WorldController.SwapZY(VVD_Vertexes[pModel.vertexindex + i].m_vecNormal));
            pUvBuffer.Add(VVD_Vertexes[pModel.vertexindex + i].m_vecTexCoord);
        }

        GameObject meshObject = new GameObject(new string(pModel.name));

        meshObject.transform.parent = ModelObject.transform;

        SkinnedMeshRenderer smr = meshObject.AddComponent <SkinnedMeshRenderer>();

        smr.materials = new Material[vLod.numMeshes];

        // Calculate bindposes
        Matrix4x4[] bindPoses = new Matrix4x4[MDL_Bones.Count];
        for (int i = 0; i < bindPoses.Length; i++)
        {
            MDL_Bones[i].localPosition = Vector3.zero;
            bindPoses[i] = MDL_Bones[i].worldToLocalMatrix * ModelObject.transform.localToWorldMatrix;
        }

        // Generate skin mesh
        smr.sharedMesh      = new Mesh();
        smr.sharedMesh.name = new string(pModel.name);

        smr.sharedMesh.subMeshCount = vLod.numMeshes;
        smr.sharedMesh.vertices     = pVertices.ToArray();
        smr.sharedMesh.normals      = pNormals.ToArray();
        smr.sharedMesh.uv           = pUvBuffer.ToArray();

        smr.sharedMesh.boneWeights = pBoneWeight.ToArray();
        smr.sharedMesh.bindposes   = bindPoses;

        smr.sharedMesh.Optimize();

        smr.bones = MDL_Bones.ToArray();
        smr.updateWhenOffscreen = true;

        for (int i = 0; i < vLod.numMeshes; i++)
        {
            List <int> pIndices = new List <int>();

            List <StripGroupHeader_t> StripGroups = new List <StripGroupHeader_t>();
            int StripGroupFilePosition            = MeshInputFilePosition + (Marshal.SizeOf(typeof(MeshHeader_t)) * i) + VTX_Meshes[i].stripGroupHeaderOffset;
            StripGroups.AddRange(CRead.ReadType <StripGroupHeader_t>(StripGroupFilePosition, VTX_Meshes[i].numStripGroups)); pStudioMesh = MDL_Meshes[i];

            // Get indices for model
            for (int l = 0; l < VTX_Meshes[i].numStripGroups; l++)
            {
                List <Vertex_t> pVertexBuffer = new List <Vertex_t>();
                pVertexBuffer.AddRange(CRead.ReadType <Vertex_t>(StripGroupFilePosition + (Marshal.SizeOf(typeof(StripGroupHeader_t)) * l) + StripGroups[l].vertOffset, StripGroups[l].numVerts));

                List <ushort> Indices = new List <ushort>();
                Indices.AddRange(CRead.ReadType <ushort>(StripGroupFilePosition + (Marshal.SizeOf(typeof(StripGroupHeader_t)) * l) + StripGroups[l].indexOffset, StripGroups[l].numIndices));

                for (int n = 0; n < Indices.Count; n++)
                {
                    pIndices.Add(pVertexBuffer[Indices[n]].origMeshVertID + pStudioMesh.vertexoffset);
                }
            }

            smr.sharedMesh.SetTriangles(pIndices.ToArray(), i);
            smr.materials[i].name = MDL_Textures[pStudioMesh.material];

            string pathToTex = null; Material material = null;

            // Find path to mesh material
            for (int l = 0; l < MDL_TDirectories.Count; l++)
            {
                if (File.Exists(WorldController.DefaultTexPath + MDL_TDirectories[l] + MDL_Textures[pStudioMesh.material] + ".vmt"))
                {
                    pathToTex = WorldController.DefaultTexPath + MDL_TDirectories[l] + MDL_Textures[pStudioMesh.material] + ".vmt";
                }
            }

            if (pathToTex != null)
            {
                material = ValveTextureLoader.LoadMaterial(pathToTex.Replace(WorldController.DefaultTexPath, ""));
            }

            // Apply loaded material to mesh
            if (material != null)
            {
                smr.materials[i].CopyPropertiesFromMaterial(material);
                smr.materials[i].shader = material.shader;
            }
        }
    }
    private static void CreateModel(int index)
    {
        Dictionary <int, List <int> > subMeshData = new Dictionary <int, List <int> >();
        GameObject model = new GameObject("*" + index);

        model.transform.parent = BSP_WorldSpawn.transform;

        int firstFace = BSP_Models[index].firstface;
        int faces     = BSP_Models[index].numfaces;

        for (int i = firstFace; i < firstFace + faces; i++)
        {
            if (!subMeshData.ContainsKey(BSP_Texdata[BSP_Texinfo[BSP_Faces[i].texinfo].texdata].nameStringTableID))
            {
                subMeshData.Add(BSP_Texdata[BSP_Texinfo[BSP_Faces[i].texinfo].texdata].nameStringTableID, new List <int>());
            }

            subMeshData[BSP_Texdata[BSP_Texinfo[BSP_Faces[i].texinfo].texdata].nameStringTableID].Add(i);
        }

        for (int i = 0; i < BSP_TexStrData.Count; i++)
        {
            if (!subMeshData.ContainsKey(i))
            {
                continue;
            }

            List <Vector3> vertices  = new List <Vector3>();
            List <Vector2> uv        = new List <Vector2>();
            List <int>     triangles = new List <int>();
            List <face>    faceList  = new List <face>();

            for (int k = 0; k < subMeshData[i].Count; k++)
            {
                // Get points, indices, UV's
                if (BSP_Faces[subMeshData[i][k]].dispinfo == -1)
                {
                    face f           = CreateFace(subMeshData[i][k]);
                    int  pointOffset = vertices.Count;

                    for (int j = 0; j < f.triangles.Length; j++)
                    {
                        triangles.Add(f.triangles[j] + pointOffset);
                    }

                    vertices.AddRange(f.points);
                    uv.AddRange(f.uv);
                    faceList.Add(f);
                }
            }

            GameObject submesh = new GameObject(BSP_TexStrData[i]);
            submesh.transform.parent = model.transform;

            MeshRenderer meshRenderer = submesh.AddComponent <MeshRenderer>();
            MeshFilter   meshFilter   = submesh.AddComponent <MeshFilter>();

            List <Vector2> uv2      = new List <Vector2>();
            Texture2D      lightMap = new Texture2D(0, 0);

            // Load lightmaps and generate atlas
            CreateLightMap(faceList, ref lightMap, ref uv2);

            WorldController.CurrentTexPath = WorldController.DefaultTexPath;
            if (BSP_TexStrData[i].Contains(WorldController.MapName))
            {
                WorldController.CurrentTexPath = WorldController.PakTexPath;
            }

            // Load material for this object
            meshRenderer.sharedMaterial = ValveTextureLoader.LoadMaterial(BSP_TexStrData[i]);
            meshRenderer.sharedMaterial.SetTexture("_LightMap", lightMap);

            if (BSP_TexStrData[i].Contains("TOOLS/"))
            {
                meshRenderer.enabled = false;
            }

            // Generate submesh
            meshFilter.sharedMesh           = new Mesh();
            meshFilter.sharedMesh.vertices  = vertices.ToArray();
            meshFilter.sharedMesh.triangles = triangles.ToArray();

            meshFilter.sharedMesh.uv  = uv.ToArray();
            meshFilter.sharedMesh.uv2 = uv2.ToArray();

            meshFilter.sharedMesh.RecalculateNormals();
            meshFilter.sharedMesh.Optimize();
        }
    }