Beispiel #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();

            //@cartzhang if SkinnedMeshRender gameobject,but has no meshfilter,add one.
            SkinnedMeshRenderer[] meshfilterRender = gameObj.GetComponentsInChildren <SkinnedMeshRenderer>();
            for (int i = 0; i < meshfilterRender.Length; i++)
            {
                if (meshfilterRender[i].GetComponent <MeshFilter>() == null)
                {
                    meshfilterRender[i].gameObject.AddComponent <MeshFilter>();
                    meshfilterRender[i].GetComponent <MeshFilter>().sharedMesh = GameObject.Instantiate(meshfilterRender[i].sharedMesh);
                }
            }

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

            string meshName = gameObj.name;

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

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

            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}", position.x * -1, position.y, 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}", localRotation.x, localRotation.y * -1, -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}", localScale.x, localScale.y, 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 (filter != null)
            {
                Mesh mesh = filter.sharedMesh;

                // =================================
                //         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}", verticies[i].x * -1, verticies[i].y, 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},",
                                              newNormal.x * -1, // Switch normal as is tradition
                                              newNormal.y,
                                              newNormal.z);

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

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

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

                    tempObjectSb.AppendFormat("{0},{1},{2}",
                                              newNormal.x * -1, // Switch normal as is tradition
                                              newNormal.y,
                                              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}", color.Key.r, color.Key.g, color.Key.b, 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}", uvs[i].x, 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);
        }
Beispiel #2
0
        public static string MeshToString(GameObject gameObj, string newPath, bool copyMaterials = false, bool copyTextures = false)
        {
            StringBuilder sb = new StringBuilder();

            StringBuilder objectProps = new StringBuilder();

            objectProps.AppendLine("; Object properties");
            objectProps.AppendLine(";------------------------------------------------------------------");
            objectProps.AppendLine("");
            objectProps.AppendLine("Objects:  {");

            StringBuilder objectConnections = new StringBuilder();

            objectConnections.AppendLine("; Object connections");
            objectConnections.AppendLine(";------------------------------------------------------------------");
            objectConnections.AppendLine("");
            objectConnections.AppendLine("Connections:  {");
            objectConnections.AppendLine("\t");

            Material[] materials = new Material[0];

            // First finds all unique materials and compiles them (and writes to the object connections) for funzies
            string materialsObjectSerialized     = "";
            string materialConnectionsSerialized = "";

            FBXUnityMaterialGetter.GetAllMaterialsToString(gameObj, newPath, copyMaterials, copyTextures, out materials, out materialsObjectSerialized, out materialConnectionsSerialized);

            // Run recursive FBX Mesh grab over the entire gameobject
            FBXUnityMeshGetter.GetMeshToString(gameObj, materials, ref objectProps, ref objectConnections);

            // write the materials to the objectProps here. Should not do it in the above as it recursive.

            objectProps.Append(materialsObjectSerialized);
            objectConnections.Append(materialConnectionsSerialized);

            // Close up both builders;
            objectProps.AppendLine("}");
            objectConnections.AppendLine("}");


            // ========= Create header ========

            // Intro
            sb.AppendLine("; FBX 7.3.0 project file");
            sb.AppendLine("; Copyright (C) 1997-2010 Autodesk Inc. and/or its licensors.");
            sb.AppendLine("; All rights reserved.");
            sb.AppendLine("; ----------------------------------------------------");
            sb.AppendLine();

            // The header
            sb.AppendLine("FBXHeaderExtension:  {");
            sb.AppendLine("\tFBXHeaderVersion: 1003");
            sb.AppendLine("\tFBXVersion: 7300");

            // Creationg Date Stamp
            System.DateTime currentDate = System.DateTime.Now;
            sb.AppendLine("\tCreationTimeStamp:  {");
            sb.AppendLine("\t\tVersion: 1000");
            sb.AppendLine("\t\tYear: " + currentDate.Year);
            sb.AppendLine("\t\tMonth: " + currentDate.Month);
            sb.AppendLine("\t\tDay: " + currentDate.Day);
            sb.AppendLine("\t\tHour: " + currentDate.Hour);
            sb.AppendLine("\t\tMinute: " + currentDate.Minute);
            sb.AppendLine("\t\tSecond: " + currentDate.Second);
            sb.AppendLine("\t\tMillisecond: " + currentDate.Millisecond);
            sb.AppendLine("\t}");

            // Info on the Creator
            sb.AppendLine("\tCreator: \"" + VersionInformation + "\"");
            sb.AppendLine("\tSceneInfo: \"SceneInfo::GlobalInfo\", \"UserData\" {");
            sb.AppendLine("\t\tType: \"UserData\"");
            sb.AppendLine("\t\tVersion: 100");
            sb.AppendLine("\t\tMetaData:  {");
            sb.AppendLine("\t\t\tVersion: 100");
            sb.AppendLine("\t\t\tTitle: \"\"");
            sb.AppendLine("\t\t\tSubject: \"\"");
            sb.AppendLine("\t\t\tAuthor: \"\"");
            sb.AppendLine("\t\t\tKeywords: \"\"");
            sb.AppendLine("\t\t\tRevision: \"\"");
            sb.AppendLine("\t\t\tComment: \"\"");
            sb.AppendLine("\t\t}");
            sb.AppendLine("\t\tProperties70:  {");

            // Information on how this item was originally generated
            string documentInfoPaths = Application.dataPath + newPath + ".fbx";

            sb.AppendLine("\t\t\tP: \"DocumentUrl\", \"KString\", \"Url\", \"\", \"" + documentInfoPaths + "\"");
            sb.AppendLine("\t\t\tP: \"SrcDocumentUrl\", \"KString\", \"Url\", \"\", \"" + documentInfoPaths + "\"");
            sb.AppendLine("\t\t\tP: \"Original\", \"Compound\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"Original|ApplicationVendor\", \"KString\", \"\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"Original|ApplicationName\", \"KString\", \"\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"Original|ApplicationVersion\", \"KString\", \"\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"Original|DateTime_GMT\", \"DateTime\", \"\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"Original|FileName\", \"KString\", \"\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"LastSaved\", \"Compound\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"LastSaved|ApplicationVendor\", \"KString\", \"\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"LastSaved|ApplicationName\", \"KString\", \"\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"LastSaved|ApplicationVersion\", \"KString\", \"\", \"\", \"\"");
            sb.AppendLine("\t\t\tP: \"LastSaved|DateTime_GMT\", \"DateTime\", \"\", \"\", \"\"");
            sb.AppendLine("\t\t}");
            sb.AppendLine("\t}");
            sb.AppendLine("}");

            // The Global information
            sb.AppendLine("GlobalSettings:  {");
            sb.AppendLine("\tVersion: 1000");
            sb.AppendLine("\tProperties70:  {");
            sb.AppendLine("\t\tP: \"UpAxis\", \"int\", \"Integer\", \"\",1");
            sb.AppendLine("\t\tP: \"UpAxisSign\", \"int\", \"Integer\", \"\",1");
            sb.AppendLine("\t\tP: \"FrontAxis\", \"int\", \"Integer\", \"\",2");
            sb.AppendLine("\t\tP: \"FrontAxisSign\", \"int\", \"Integer\", \"\",1");
            sb.AppendLine("\t\tP: \"CoordAxis\", \"int\", \"Integer\", \"\",0");
            sb.AppendLine("\t\tP: \"CoordAxisSign\", \"int\", \"Integer\", \"\",1");
            sb.AppendLine("\t\tP: \"OriginalUpAxis\", \"int\", \"Integer\", \"\",-1");
            sb.AppendLine("\t\tP: \"OriginalUpAxisSign\", \"int\", \"Integer\", \"\",1");
            sb.AppendLine("\t\tP: \"UnitScaleFactor\", \"double\", \"Number\", \"\",100"); // NOTE: This sets the resize scale upon import
            sb.AppendLine("\t\tP: \"OriginalUnitScaleFactor\", \"double\", \"Number\", \"\",100");
            sb.AppendLine("\t\tP: \"AmbientColor\", \"ColorRGB\", \"Color\", \"\",0,0,0");
            sb.AppendLine("\t\tP: \"DefaultCamera\", \"KString\", \"\", \"\", \"Producer Perspective\"");
            sb.AppendLine("\t\tP: \"TimeMode\", \"enum\", \"\", \"\",11");
            sb.AppendLine("\t\tP: \"TimeSpanStart\", \"KTime\", \"Time\", \"\",0");
            sb.AppendLine("\t\tP: \"TimeSpanStop\", \"KTime\", \"Time\", \"\",479181389250");
            sb.AppendLine("\t\tP: \"CustomFrameRate\", \"double\", \"Number\", \"\",-1");
            sb.AppendLine("\t}");
            sb.AppendLine("}");

            // The Object definations
            sb.AppendLine("; Object definitions");
            sb.AppendLine(";------------------------------------------------------------------");
            sb.AppendLine("");
            sb.AppendLine("Definitions:  {");
            sb.AppendLine("\tVersion: 100");
            sb.AppendLine("\tCount: 4");

            sb.AppendLine("\tObjectType: \"GlobalSettings\" {");
            sb.AppendLine("\t\tCount: 1");
            sb.AppendLine("\t}");


            sb.AppendLine("\tObjectType: \"Model\" {");
            sb.AppendLine("\t\tCount: 1"); // TODO figure out if this count matters
            sb.AppendLine("\t\tPropertyTemplate: \"FbxNode\" {");
            sb.AppendLine("\t\t\tProperties70:  {");
            sb.AppendLine("\t\t\t\tP: \"QuaternionInterpolate\", \"enum\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationOffset\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"RotationPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"ScalingOffset\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"ScalingPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"TranslationActive\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"TranslationMin\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"TranslationMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"TranslationMinX\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"TranslationMinY\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"TranslationMinZ\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"TranslationMaxX\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"TranslationMaxY\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"TranslationMaxZ\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationOrder\", \"enum\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationSpaceForLimitOnly\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationStiffnessX\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationStiffnessY\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationStiffnessZ\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"AxisLen\", \"double\", \"Number\", \"\",10");
            sb.AppendLine("\t\t\t\tP: \"PreRotation\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"PostRotation\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"RotationActive\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationMin\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"RotationMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"RotationMinX\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationMinY\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationMinZ\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationMaxX\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationMaxY\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"RotationMaxZ\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"ScalingActive\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"ScalingMin\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",1,1,1");
            sb.AppendLine("\t\t\t\tP: \"ScalingMinX\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"ScalingMinY\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"ScalingMinZ\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"ScalingMaxX\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"ScalingMaxY\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"ScalingMaxZ\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"GeometricTranslation\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"GeometricRotation\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"GeometricScaling\", \"Vector3D\", \"Vector\", \"\",1,1,1");
            sb.AppendLine("\t\t\t\tP: \"MinDampRangeX\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MinDampRangeY\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MinDampRangeZ\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MaxDampRangeX\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MaxDampRangeY\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MaxDampRangeZ\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MinDampStrengthX\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MinDampStrengthY\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MinDampStrengthZ\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MaxDampStrengthX\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MaxDampStrengthY\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"MaxDampStrengthZ\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"PreferedAngleX\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"PreferedAngleY\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"PreferedAngleZ\", \"double\", \"Number\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"LookAtProperty\", \"object\", \"\", \"\"");
            sb.AppendLine("\t\t\t\tP: \"UpVectorProperty\", \"object\", \"\", \"\"");
            sb.AppendLine("\t\t\t\tP: \"Show\", \"bool\", \"\", \"\",1");
            sb.AppendLine("\t\t\t\tP: \"NegativePercentShapeSupport\", \"bool\", \"\", \"\",1");
            sb.AppendLine("\t\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",-1");
            sb.AppendLine("\t\t\t\tP: \"Freeze\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"LODBox\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"Lcl Translation\", \"Lcl Translation\", \"\", \"A\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"Lcl Rotation\", \"Lcl Rotation\", \"\", \"A\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"Lcl Scaling\", \"Lcl Scaling\", \"\", \"A\",1,1,1");
            sb.AppendLine("\t\t\t\tP: \"Visibility\", \"Visibility\", \"\", \"A\",1");
            sb.AppendLine("\t\t\t\tP: \"Visibility Inheritance\", \"Visibility Inheritance\", \"\", \"\",1");
            sb.AppendLine("\t\t\t}");
            sb.AppendLine("\t\t}");
            sb.AppendLine("\t}");

            // The geometry, this is IMPORTANT
            sb.AppendLine("\tObjectType: \"Geometry\" {");
            sb.AppendLine("\t\tCount: 1"); // TODO - this must be set by the number of items being placed.
            sb.AppendLine("\t\tPropertyTemplate: \"FbxMesh\" {");
            sb.AppendLine("\t\t\tProperties70:  {");
            sb.AppendLine("\t\t\t\tP: \"Color\", \"ColorRGB\", \"Color\", \"\",0.8,0.8,0.8");
            sb.AppendLine("\t\t\t\tP: \"BBoxMin\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"BBoxMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"Primary Visibility\", \"bool\", \"\", \"\",1");
            sb.AppendLine("\t\t\t\tP: \"Casts Shadows\", \"bool\", \"\", \"\",1");
            sb.AppendLine("\t\t\t\tP: \"Receive Shadows\", \"bool\", \"\", \"\",1");
            sb.AppendLine("\t\t\t}");
            sb.AppendLine("\t\t}");
            sb.AppendLine("\t}");

            // The materials that are being placed. Has to be simple I think
            sb.AppendLine("\tObjectType: \"Material\" {");
            sb.AppendLine("\t\tCount: 1");
            sb.AppendLine("\t\tPropertyTemplate: \"FbxSurfacePhong\" {");
            sb.AppendLine("\t\t\tProperties70:  {");
            sb.AppendLine("\t\t\t\tP: \"ShadingModel\", \"KString\", \"\", \"\", \"Phong\"");
            sb.AppendLine("\t\t\t\tP: \"MultiLayer\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"EmissiveColor\", \"Color\", \"\", \"A\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"EmissiveFactor\", \"Number\", \"\", \"A\",1");
            sb.AppendLine("\t\t\t\tP: \"AmbientColor\", \"Color\", \"\", \"A\",0.2,0.2,0.2");
            sb.AppendLine("\t\t\t\tP: \"AmbientFactor\", \"Number\", \"\", \"A\",1");
            sb.AppendLine("\t\t\t\tP: \"DiffuseColor\", \"Color\", \"\", \"A\",0.8,0.8,0.8");
            sb.AppendLine("\t\t\t\tP: \"DiffuseFactor\", \"Number\", \"\", \"A\",1");
            sb.AppendLine("\t\t\t\tP: \"Bump\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"NormalMap\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"BumpFactor\", \"double\", \"Number\", \"\",1");
            sb.AppendLine("\t\t\t\tP: \"TransparentColor\", \"Color\", \"\", \"A\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"TransparencyFactor\", \"Number\", \"\", \"A\",0");
            sb.AppendLine("\t\t\t\tP: \"DisplacementColor\", \"ColorRGB\", \"Color\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"DisplacementFactor\", \"double\", \"Number\", \"\",1");
            sb.AppendLine("\t\t\t\tP: \"VectorDisplacementColor\", \"ColorRGB\", \"Color\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"VectorDisplacementFactor\", \"double\", \"Number\", \"\",1");
            sb.AppendLine("\t\t\t\tP: \"SpecularColor\", \"Color\", \"\", \"A\",0.2,0.2,0.2");
            sb.AppendLine("\t\t\t\tP: \"SpecularFactor\", \"Number\", \"\", \"A\",1");
            sb.AppendLine("\t\t\t\tP: \"ShininessExponent\", \"Number\", \"\", \"A\",20");
            sb.AppendLine("\t\t\t\tP: \"ReflectionColor\", \"Color\", \"\", \"A\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"ReflectionFactor\", \"Number\", \"\", \"A\",1");
            sb.AppendLine("\t\t\t}");
            sb.AppendLine("\t\t}");
            sb.AppendLine("\t}");

            // Explanation of how textures work
            sb.AppendLine("\tObjectType: \"Texture\" {");
            sb.AppendLine("\t\tCount: 2"); // TODO - figure out if this texture number is important
            sb.AppendLine("\t\tPropertyTemplate: \"FbxFileTexture\" {");
            sb.AppendLine("\t\t\tProperties70:  {");
            sb.AppendLine("\t\t\t\tP: \"TextureTypeUse\", \"enum\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"Texture alpha\", \"Number\", \"\", \"A\",1");
            sb.AppendLine("\t\t\t\tP: \"CurrentMappingType\", \"enum\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"WrapModeU\", \"enum\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"WrapModeV\", \"enum\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"UVSwap\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"PremultiplyAlpha\", \"bool\", \"\", \"\",1");
            sb.AppendLine("\t\t\t\tP: \"Translation\", \"Vector\", \"\", \"A\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"Rotation\", \"Vector\", \"\", \"A\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"Scaling\", \"Vector\", \"\", \"A\",1,1,1");
            sb.AppendLine("\t\t\t\tP: \"TextureRotationPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"TextureScalingPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0");
            sb.AppendLine("\t\t\t\tP: \"CurrentTextureBlendMode\", \"enum\", \"\", \"\",1");
            sb.AppendLine("\t\t\t\tP: \"UVSet\", \"KString\", \"\", \"\", \"default\"");
            sb.AppendLine("\t\t\t\tP: \"UseMaterial\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t\tP: \"UseMipMap\", \"bool\", \"\", \"\",0");
            sb.AppendLine("\t\t\t}");
            sb.AppendLine("\t\t}");
            sb.AppendLine("\t}");

            sb.AppendLine("}");
            sb.AppendLine("");

            sb.Append(objectProps.ToString());
            sb.Append(objectConnections.ToString());

            return(sb.ToString());
        }