Пример #1
0
        /// <summary>
        /// Gets all the meshes and outputs to a string (even grabbing the child of each gameObject)
        /// </summary>
        /// <returns>The mesh to string.</returns>
        /// <param name="gameObj">GameObject Parent.</param>
        /// <param name="materials">Every Material in the parent that can be accessed.</param>
        /// <param name="objects">The StringBuidler to create objects for the FBX file.</param>
        /// <param name="connections">The StringBuidler to create connections for the FBX file.</param>
        /// <param name="parentObject">Parent object, if left null this is the top parent.</param>
        /// <param name="parentModelId">Parent model id, 0 if top parent.</param>
        public static long GetMeshToString(GameObject gameObj,
                                           Material[] materials,
                                           ref StringBuilder objects,
                                           ref StringBuilder connections,
                                           GameObject parentObject = null,
                                           long parentModelId      = 0)
        {
            StringBuilder tempObjectSb      = new StringBuilder();
            StringBuilder tempConnectionsSb = new StringBuilder();

            long geometryId = FBXExporter.GetRandomFBXId();
            long modelId    = FBXExporter.GetRandomFBXId();

            // Sees if there is a mesh to export and add to the system
            MeshFilter          filter      = gameObj.GetComponent <MeshFilter>();
            SkinnedMeshRenderer skinnedMesh = gameObj.GetComponent <SkinnedMeshRenderer>();

            // The mesh to export is this level's mesh that is going to be exported
            Mesh meshToExport = new Mesh();

            if (filter != null)
            {
                meshToExport = filter.sharedMesh;
            }
            else if (skinnedMesh != null) // If this object has a skinned mesh on it, bake that mesh into whatever pose it is at and add it as a new mesh to export
            {
                meshToExport = new Mesh();
                skinnedMesh.BakeMesh(meshToExport);
            }

            if (meshToExport == null)
            {
                Debug.LogError("Couldn't find a mesh to export");
            }

            string meshName = gameObj.name;

            // A NULL parent means that the gameObject is at the top
            string isMesh = "Null";

            if (meshToExport != null)
            {
                meshName = meshToExport.name;
                isMesh   = "Mesh";
            }

            if (filter != null)
            {
                if (filter.sharedMesh == null)
                {
                    // The MeshFilter has no mesh assigned, so treat it like an FBX Null node.
                    filter = null;
                }
                else
                {
                    meshName = filter.sharedMesh.name;
                    isMesh   = "Mesh";
                }
            }

            // If we've got a skinned mesh without a name, give it a random name
            if (meshName == "" && skinnedMesh != null)
            {
                meshName = "Skinned Mesh " + Random.Range(0, 1000000);
            }

            if (parentModelId == 0)
            {
                tempConnectionsSb.AppendLine("\t;Model::" + meshName + ", Model::RootNode");
            }
            else
            {
                tempConnectionsSb.AppendLine("\t;Model::" + meshName + ", Model::USING PARENT");
            }
            tempConnectionsSb.AppendLine("\tC: \"OO\"," + modelId + "," + parentModelId);
            tempConnectionsSb.AppendLine();
            tempObjectSb.AppendLine("\tModel: " + modelId + ", \"Model::" + gameObj.name + "\", \"" + isMesh + "\" {");
            tempObjectSb.AppendLine("\t\tVersion: 232");
            tempObjectSb.AppendLine("\t\tProperties70:  {");
            tempObjectSb.AppendLine("\t\t\tP: \"RotationOrder\", \"enum\", \"\", \"\",4");
            tempObjectSb.AppendLine("\t\t\tP: \"RotationActive\", \"bool\", \"\", \"\",1");
            tempObjectSb.AppendLine("\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",1");
            tempObjectSb.AppendLine("\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            tempObjectSb.AppendLine("\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",0");
            // ===== Local Translation Offset =========
            Vector3 position = gameObj.transform.localPosition;

            tempObjectSb.Append("\t\t\tP: \"Lcl Translation\", \"Lcl Translation\", \"\", \"A+\",");

            // Append the X Y Z coords to the system
            tempObjectSb.AppendFormat("{0},{1},{2}", FE.FBXFormat(position.x * -1), FE.FBXFormat(position.y), FE.FBXFormat(position.z));
            tempObjectSb.AppendLine();

            // Rotates the object correctly from Unity space
            Vector3 localRotation = gameObj.transform.localEulerAngles;

            tempObjectSb.AppendFormat("\t\t\tP: \"Lcl Rotation\", \"Lcl Rotation\", \"\", \"A+\",{0},{1},{2}", FE.FBXFormat(localRotation.x), FE.FBXFormat(localRotation.y * -1), FE.FBXFormat(-1 * localRotation.z));
            tempObjectSb.AppendLine();

            // Adds the local scale of this object
            Vector3 localScale = gameObj.transform.localScale;

            tempObjectSb.AppendFormat("\t\t\tP: \"Lcl Scaling\", \"Lcl Scaling\", \"\", \"A\",{0},{1},{2}", FE.FBXFormat(localScale.x), FE.FBXFormat(localScale.y), FE.FBXFormat(localScale.z));
            tempObjectSb.AppendLine();

            tempObjectSb.AppendLine("\t\t\tP: \"currentUVSet\", \"KString\", \"\", \"U\", \"map1\"");
            tempObjectSb.AppendLine("\t\t}");
            tempObjectSb.AppendLine("\t\tShading: T");
            tempObjectSb.AppendLine("\t\tCulling: \"CullingOff\"");
            tempObjectSb.AppendLine("\t}");

            // Adds in geometry if it exists, if it it does not exist, this is a empty gameObject file and skips over this
            if (meshToExport != null)
            {
                Mesh mesh = meshToExport;

                // =================================
                //         General Geometry Info
                // =================================
                // Generate the geometry information for the mesh created

                tempObjectSb.AppendLine("\tGeometry: " + geometryId + ", \"Geometry::\", \"Mesh\" {");

                // ===== WRITE THE VERTICIES =====
                Vector3[] verticies = mesh.vertices;
                int       vertCount = mesh.vertexCount * 3; // <= because the list of points is just a list of comma seperated values, we need to multiply by three

                tempObjectSb.AppendLine("\t\tVertices: *" + vertCount + " {");
                tempObjectSb.Append("\t\t\ta: ");
                for (int i = 0; i < verticies.Length; i++)
                {
                    if (i > 0)
                    {
                        tempObjectSb.Append(",");
                    }

                    // Points in the verticies. We also reverse the x value because Unity has a reverse X coordinate
                    tempObjectSb.AppendFormat("{0},{1},{2}", FE.FBXFormat(verticies[i].x * -1), FE.FBXFormat(verticies[i].y), FE.FBXFormat(verticies[i].z));
                }

                tempObjectSb.AppendLine();
                tempObjectSb.AppendLine("\t\t} ");

                // ======= WRITE THE TRIANGLES ========
                int   triangleCount = mesh.triangles.Length;
                int[] triangles     = mesh.triangles;

                tempObjectSb.AppendLine("\t\tPolygonVertexIndex: *" + triangleCount + " {");

                // Write triangle indexes
                tempObjectSb.Append("\t\t\ta: ");
                for (int i = 0; i < triangleCount; i += 3)
                {
                    if (i > 0)
                    {
                        tempObjectSb.Append(",");
                    }

                    // To get the correct normals, must rewind the triangles since we flipped the x direction
                    tempObjectSb.AppendFormat("{0},{1},{2}",
                                              triangles[i],
                                              triangles[i + 2],
                                              (triangles[i + 1] * -1) - 1); // <= Tells the poly is ended
                }

                tempObjectSb.AppendLine();

                tempObjectSb.AppendLine("\t\t} ");
                tempObjectSb.AppendLine("\t\tGeometryVersion: 124");
                tempObjectSb.AppendLine("\t\tLayerElementNormal: 0 {");
                tempObjectSb.AppendLine("\t\t\tVersion: 101");
                tempObjectSb.AppendLine("\t\t\tName: \"\"");
                tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
                tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"Direct\"");

                // ===== WRITE THE NORMALS ==========
                Vector3[] normals = mesh.normals;

                tempObjectSb.AppendLine("\t\t\tNormals: *" + (triangleCount * 3) + " {");
                tempObjectSb.Append("\t\t\t\ta: ");

                for (int i = 0; i < triangleCount; i += 3)
                {
                    if (i > 0)
                    {
                        tempObjectSb.Append(",");
                    }

                    // To get the correct normals, must rewind the normal triangles like the triangles above since x was flipped
                    Vector3 newNormal = normals[triangles[i]];

                    tempObjectSb.AppendFormat("{0},{1},{2},",
                                              FE.FBXFormat(newNormal.x * -1),                        // Switch normal as is tradition
                                              FE.FBXFormat(newNormal.y),
                                              FE.FBXFormat(newNormal.z));

                    newNormal = normals[triangles[i + 2]];

                    tempObjectSb.AppendFormat("{0},{1},{2},",
                                              FE.FBXFormat(newNormal.x * -1),                        // Switch normal as is tradition
                                              FE.FBXFormat(newNormal.y),
                                              FE.FBXFormat(newNormal.z));

                    newNormal = normals[triangles[i + 1]];

                    tempObjectSb.AppendFormat("{0},{1},{2}",
                                              FE.FBXFormat(newNormal.x * -1),                        // Switch normal as is tradition
                                              FE.FBXFormat(newNormal.y),
                                              FE.FBXFormat(newNormal.z));
                }

                tempObjectSb.AppendLine();
                tempObjectSb.AppendLine("\t\t\t}");
                tempObjectSb.AppendLine("\t\t}");

                // ===== WRITE THE COLORS =====
                bool containsColors = mesh.colors.Length == verticies.Length;

                if (containsColors)
                {
                    Color[] colors = mesh.colors;

                    Dictionary <Color, int> colorTable = new Dictionary <Color, int>(); // reducing amount of data by only keeping unique colors.
                    int idx = 0;

                    // build index table of all the different colors present in the mesh
                    for (int i = 0; i < colors.Length; i++)
                    {
                        if (!colorTable.ContainsKey(colors[i]))
                        {
                            colorTable[colors[i]] = idx;
                            idx++;
                        }
                    }

                    tempObjectSb.AppendLine("\t\tLayerElementColor: 0 {");
                    tempObjectSb.AppendLine("\t\t\tVersion: 101");
                    tempObjectSb.AppendLine("\t\t\tName: \"Col\"");
                    tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
                    tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
                    tempObjectSb.AppendLine("\t\t\tColors: *" + colorTable.Count * 4 + " {");
                    tempObjectSb.Append("\t\t\t\ta: ");

                    bool first = true;
                    foreach (KeyValuePair <Color, int> color in colorTable)
                    {
                        if (!first)
                        {
                            tempObjectSb.Append(",");
                        }

                        tempObjectSb.AppendFormat("{0},{1},{2},{3}", FE.FBXFormat(color.Key.r), FE.FBXFormat(color.Key.g), FE.FBXFormat(color.Key.b), FE.FBXFormat(color.Key.a));
                        first = false;
                    }
                    tempObjectSb.AppendLine();

                    tempObjectSb.AppendLine("\t\t\t\t}");

                    // Color index
                    tempObjectSb.AppendLine("\t\t\tColorIndex: *" + triangles.Length + " {");
                    tempObjectSb.Append("\t\t\t\ta: ");

                    for (int i = 0; i < triangles.Length; i += 3)
                    {
                        if (i > 0)
                        {
                            tempObjectSb.Append(",");
                        }

                        // Triangles need to be fliped for the x flip
                        int index1 = triangles[i];
                        int index2 = triangles[i + 2];
                        int index3 = triangles[i + 1];

                        // Find the color index related to that vertice index
                        index1 = colorTable[colors[index1]];
                        index2 = colorTable[colors[index2]];
                        index3 = colorTable[colors[index3]];

                        tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
                    }

                    tempObjectSb.AppendLine();

                    tempObjectSb.AppendLine("\t\t\t}");
                    tempObjectSb.AppendLine("\t\t}");
                }
                else
                {
                    Debug.LogWarning("Mesh contains " + mesh.vertices.Length + " vertices for " + mesh.colors.Length + " colors. Skip color export");
                }



                // ================ UV CREATION =========================

                // -- UV 1 Creation
                int       uvLength = mesh.uv.Length;
                Vector2[] uvs      = mesh.uv;

                tempObjectSb.AppendLine("\t\tLayerElementUV: 0 {"); // the Zero here is for the first UV map
                tempObjectSb.AppendLine("\t\t\tVersion: 101");
                tempObjectSb.AppendLine("\t\t\tName: \"map1\"");
                tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
                tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
                tempObjectSb.AppendLine("\t\t\tUV: *" + uvLength * 2 + " {");
                tempObjectSb.Append("\t\t\t\ta: ");

                for (int i = 0; i < uvLength; i++)
                {
                    if (i > 0)
                    {
                        tempObjectSb.Append(",");
                    }

                    tempObjectSb.AppendFormat("{0},{1}", FE.FBXFormat(uvs[i].x), FE.FBXFormat(uvs[i].y));
                }
                tempObjectSb.AppendLine();

                tempObjectSb.AppendLine("\t\t\t\t}");

                // UV tile index coords
                tempObjectSb.AppendLine("\t\t\tUVIndex: *" + triangleCount + " {");
                tempObjectSb.Append("\t\t\t\ta: ");

                for (int i = 0; i < triangleCount; i += 3)
                {
                    if (i > 0)
                    {
                        tempObjectSb.Append(",");
                    }

                    // Triangles need to be fliped for the x flip
                    int index1 = triangles[i];
                    int index2 = triangles[i + 2];
                    int index3 = triangles[i + 1];

                    tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
                }

                tempObjectSb.AppendLine();

                tempObjectSb.AppendLine("\t\t\t}");
                tempObjectSb.AppendLine("\t\t}");

                // -- UV 2 Creation
                // TODO: Add UV2 Creation here

                // -- Smoothing
                // TODO: Smoothing doesn't seem to do anything when importing. This maybe should be added. -KBH

                // ============ MATERIALS =============

                tempObjectSb.AppendLine("\t\tLayerElementMaterial: 0 {");
                tempObjectSb.AppendLine("\t\t\tVersion: 101");
                tempObjectSb.AppendLine("\t\t\tName: \"\"");
                tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygon\"");
                tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");

                int totalFaceCount = 0;

                // So by polygon means that we need 1/3rd of how many indicies we wrote.
                int numberOfSubmeshes = mesh.subMeshCount;

                StringBuilder submeshesSb = new StringBuilder();

                // For just one submesh, we set them all to zero
                if (numberOfSubmeshes == 1)
                {
                    int numFaces = triangles.Length / 3;

                    for (int i = 0; i < numFaces; i++)
                    {
                        submeshesSb.Append("0,");
                        totalFaceCount++;
                    }
                }
                else
                {
                    List <int[]> allSubmeshes = new List <int[]>();

                    // Load all submeshes into a space
                    for (int i = 0; i < numberOfSubmeshes; i++)
                    {
                        allSubmeshes.Add(mesh.GetIndices(i));
                    }

                    // TODO: Optimize this search pattern
                    for (int i = 0; i < triangles.Length; i += 3)
                    {
                        for (int subMeshIndex = 0; subMeshIndex < allSubmeshes.Count; subMeshIndex++)
                        {
                            bool breaker = false;

                            for (int n = 0; n < allSubmeshes[subMeshIndex].Length; n += 3)
                            {
                                if (triangles[i] == allSubmeshes[subMeshIndex][n] &&
                                    triangles[i + 1] == allSubmeshes[subMeshIndex][n + 1] &&
                                    triangles[i + 2] == allSubmeshes[subMeshIndex][n + 2])
                                {
                                    submeshesSb.Append(subMeshIndex.ToString());
                                    submeshesSb.Append(",");
                                    totalFaceCount++;
                                    break;
                                }

                                if (breaker)
                                {
                                    break;
                                }
                            }
                        }
                    }
                }

                tempObjectSb.AppendLine("\t\t\tMaterials: *" + totalFaceCount + " {");
                tempObjectSb.Append("\t\t\t\ta: ");
                tempObjectSb.AppendLine(submeshesSb.ToString());
                tempObjectSb.AppendLine("\t\t\t} ");
                tempObjectSb.AppendLine("\t\t}");

                // ============= INFORMS WHAT TYPE OF LATER ELEMENTS ARE IN THIS GEOMETRY =================
                tempObjectSb.AppendLine("\t\tLayer: 0 {");
                tempObjectSb.AppendLine("\t\t\tVersion: 100");
                tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
                tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementNormal\"");
                tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
                tempObjectSb.AppendLine("\t\t\t}");
                tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
                tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementMaterial\"");
                tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
                tempObjectSb.AppendLine("\t\t\t}");
                tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
                tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementTexture\"");
                tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
                tempObjectSb.AppendLine("\t\t\t}");
                if (containsColors)
                {
                    tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
                    tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementColor\"");
                    tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
                    tempObjectSb.AppendLine("\t\t\t}");
                }
                tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
                tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementUV\"");
                tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
                tempObjectSb.AppendLine("\t\t\t}");
                // TODO: Here we would add UV layer 1 for ambient occlusion UV file
                //			tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
                //			tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementUV\"");
                //			tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 1");
                //			tempObjectSb.AppendLine("\t\t\t}");
                tempObjectSb.AppendLine("\t\t}");
                tempObjectSb.AppendLine("\t}");

                // Add the connection for the model to the geometry so it is attached the right mesh
                tempConnectionsSb.AppendLine("\t;Geometry::, Model::" + mesh.name);
                tempConnectionsSb.AppendLine("\tC: \"OO\"," + geometryId + "," + modelId);
                tempConnectionsSb.AppendLine();

                // Add the connection of all the materials in order of submesh
                MeshRenderer meshRenderer = gameObj.GetComponent <MeshRenderer>();
                if (meshRenderer != null)
                {
                    Material[] allMaterialsInThisMesh = meshRenderer.sharedMaterials;

                    for (int i = 0; i < allMaterialsInThisMesh.Length; i++)
                    {
                        Material mat         = allMaterialsInThisMesh[i];
                        int      referenceId = Mathf.Abs(mat.GetInstanceID());

                        if (mat == null)
                        {
                            Debug.LogError("ERROR: the game object " + gameObj.name + " has an empty material on it. This will export problematic files. Please fix and reexport");
                            continue;
                        }

                        tempConnectionsSb.AppendLine("\t;Material::" + mat.name + ", Model::" + mesh.name);
                        tempConnectionsSb.AppendLine("\tC: \"OO\"," + referenceId + "," + modelId);
                        tempConnectionsSb.AppendLine();
                    }
                }
            }

            // Recursively add all the other objects to the string that has been built.
            for (int i = 0; i < gameObj.transform.childCount; i++)
            {
                GameObject childObject = gameObj.transform.GetChild(i).gameObject;

                FBXUnityMeshGetter.GetMeshToString(childObject, materials, ref tempObjectSb, ref tempConnectionsSb, gameObj, modelId);
            }

            objects.Append(tempObjectSb.ToString());
            connections.Append(tempConnectionsSb.ToString());

            return(modelId);
        }
Пример #2
0
        /// <summary>
        /// Finds all materials in a gameobject and writes them to a string that can be read by the FBX writer
        /// </summary>
        /// <param name="gameObj">Parent GameObject being exported.</param>
        /// <param name="newPath">The path to export to.</param>
        /// <param name="materials">Materials which were written to this fbx file.</param>
        /// <param name="matObjects">The material objects to write to the file.</param>
        /// <param name="connections">The connections to write to the file.</param>
        public static void GetAllMaterialsToString(GameObject gameObj, string newPath, bool copyMaterials, bool copyTextures, out Material[] materials, out string matObjects, out string connections)
        {
            StringBuilder tempObjectSb      = new StringBuilder();
            StringBuilder tempConnectionsSb = new StringBuilder();

            // Need to get all unique materials for the submesh here and then write them in
            //@cartzhang modify.As meshrender and skinnedrender is same level in inherit relation shape.
            // if not check,skinned render ,may lost some materials.
            Renderer[] meshRenders = gameObj.GetComponentsInChildren <Renderer>();

            List <Material> uniqueMaterials = new List <Material>();

            // Gets all the unique materials within this GameObject Hierarchy
            for (int i = 0; i < meshRenders.Length; i++)
            {
                for (int n = 0; n < meshRenders[i].sharedMaterials.Length; n++)
                {
                    Material mat = meshRenders[i].sharedMaterials[n];

                    if (uniqueMaterials.Contains(mat) == false && mat != null)
                    {
                        uniqueMaterials.Add(mat);
                    }
                }
            }

            for (int i = 0; i < uniqueMaterials.Count; i++)
            {
                Material mat = uniqueMaterials[i];

                // We rename the material if it is being copied
                string materialName = mat.name;
                if (copyMaterials)
                {
                    materialName = gameObj.name + "_" + mat.name;
                }

                int referenceId = Mathf.Abs(mat.GetInstanceID());

                tempObjectSb.AppendLine();
                tempObjectSb.AppendLine("\tMaterial: " + referenceId + ", \"Material::" + materialName + "\", \"\" {");
                tempObjectSb.AppendLine("\t\tVersion: 102");
                tempObjectSb.AppendLine("\t\tShadingModel: \"phong\"");
                tempObjectSb.AppendLine("\t\tMultiLayer: 0");
                tempObjectSb.AppendLine("\t\tProperties70:  {");
                tempObjectSb.AppendFormat("\t\t\tP: \"Diffuse\", \"Vector3D\", \"Vector\", \"\",{0},{1},{2}", FE.FBXFormat(mat.color.r), FE.FBXFormat(mat.color.g), FE.FBXFormat(mat.color.b));
                tempObjectSb.AppendLine();
                tempObjectSb.AppendFormat("\t\t\tP: \"DiffuseColor\", \"Color\", \"\", \"A\",{0},{1},{2}", FE.FBXFormat(mat.color.r), FE.FBXFormat(mat.color.g), FE.FBXFormat(mat.color.b));
                tempObjectSb.AppendLine();

                // TODO: Figure out if this property can be written to the FBX file
                //			if(mat.HasProperty("_MetallicGlossMap"))
                //			{
                //				Debug.Log("has metallic gloss map");
                //				Color color = mat.GetColor("_Color");
                //				tempObjectSb.AppendFormat("\t\t\tP: \"Specular\", \"Vector3D\", \"Vector\", \"\",{0},{1},{2}", color.r, color.g, color.r);
                //				tempObjectSb.AppendLine();
                //				tempObjectSb.AppendFormat("\t\t\tP: \"SpecularColor\", \"ColorRGB\", \"Color\", \" \",{0},{1},{2}", color.r, color.g, color.b);
                //				tempObjectSb.AppendLine();
                //			}

                if (mat.HasProperty("_SpecColor"))
                {
                    Color color = mat.GetColor("_SpecColor");
                    tempObjectSb.AppendFormat("\t\t\tP: \"Specular\", \"Vector3D\", \"Vector\", \"\",{0},{1},{2}", FE.FBXFormat(color.r), FE.FBXFormat(color.g), FE.FBXFormat(color.r));
                    tempObjectSb.AppendLine();
                    tempObjectSb.AppendFormat("\t\t\tP: \"SpecularColor\", \"ColorRGB\", \"Color\", \" \",{0},{1},{2}", FE.FBXFormat(color.r), FE.FBXFormat(color.g), FE.FBXFormat(color.b));
                    tempObjectSb.AppendLine();
                }

                if (mat.HasProperty("_Mode"))
                {
                    Color color = Color.white;

                    switch ((int)mat.GetFloat("_Mode"))
                    {
                    case 0:                     // Map is opaque

                        break;

                    case 1:                     // Map is a cutout
                                                //  TODO: Add option if it is a cutout
                        break;

                    case 2:                     // Map is a fade
                        color = mat.GetColor("_Color");

                        tempObjectSb.AppendFormat("\t\t\tP: \"TransparentColor\", \"Color\", \"\", \"A\",{0},{1},{2}", FE.FBXFormat(color.r), FE.FBXFormat(color.g), FE.FBXFormat(color.b));
                        tempObjectSb.AppendLine();
                        tempObjectSb.AppendFormat("\t\t\tP: \"Opacity\", \"double\", \"Number\", \"\",{0}", FE.FBXFormat(color.a));
                        tempObjectSb.AppendLine();
                        break;

                    case 3:                     // Map is transparent
                        color = mat.GetColor("_Color");

                        tempObjectSb.AppendFormat("\t\t\tP: \"TransparentColor\", \"Color\", \"\", \"A\",{0},{1},{2}", FE.FBXFormat(color.r), FE.FBXFormat(color.g), FE.FBXFormat(color.b));
                        tempObjectSb.AppendLine();
                        tempObjectSb.AppendFormat("\t\t\tP: \"Opacity\", \"double\", \"Number\", \"\",{0}", FE.FBXFormat(color.a));
                        tempObjectSb.AppendLine();
                        break;
                    }
                }

                // NOTE: Unity doesn't currently import this information (I think) from an FBX file.
                if (mat.HasProperty("_EmissionColor"))
                {
                    Color color = mat.GetColor("_EmissionColor");

                    tempObjectSb.AppendFormat("\t\t\tP: \"Emissive\", \"Vector3D\", \"Vector\", \"\",{0},{1},{2}", FE.FBXFormat(color.r), FE.FBXFormat(color.g), FE.FBXFormat(color.b));
                    tempObjectSb.AppendLine();

                    float averageColor = (color.r + color.g + color.b) / 3f;

                    tempObjectSb.AppendFormat("\t\t\tP: \"EmissiveFactor\", \"Number\", \"\", \"A\",{0}", FE.FBXFormat(averageColor));
                    tempObjectSb.AppendLine();
                }

                // TODO: Add these to the file based on their relation to the PBR files
//				tempObjectSb.AppendLine("\t\t\tP: \"AmbientColor\", \"Color\", \"\", \"A\",0,0,0");
//				tempObjectSb.AppendLine("\t\t\tP: \"ShininessExponent\", \"Number\", \"\", \"A\",6.31179285049438");
//				tempObjectSb.AppendLine("\t\t\tP: \"Ambient\", \"Vector3D\", \"Vector\", \"\",0,0,0");
//				tempObjectSb.AppendLine("\t\t\tP: \"Shininess\", \"double\", \"Number\", \"\",6.31179285049438");
//				tempObjectSb.AppendLine("\t\t\tP: \"Reflectivity\", \"double\", \"Number\", \"\",0");

                tempObjectSb.AppendLine("\t\t}");
                tempObjectSb.AppendLine("\t}");

                string textureObjects;
                string textureConnections;

                SerializedTextures(gameObj, newPath, mat, materialName, copyTextures, out textureObjects, out textureConnections);

                tempObjectSb.Append(textureObjects);
                tempConnectionsSb.Append(textureConnections);
            }

            materials = uniqueMaterials.ToArray <Material>();

            matObjects  = tempObjectSb.ToString();
            connections = tempConnectionsSb.ToString();
        }