Example #1
0
    void OnGUI()
    {
        if (!terrain)
        {
            GUILayout.Label("No terrain found");
            if (GUILayout.Button("Cancel"))
            {
                EditorWindow.GetWindow <ExportTerrain>().Close();
            }
            return;
        }
        saveFormat = (SaveFormat)EditorGUILayout.EnumPopup("Export Format", saveFormat);

        saveResolution = (SaveResolution)EditorGUILayout.EnumPopup("Resolution", saveResolution);

        if (GUILayout.Button("Export"))
        {
            Export();
        }
    }
Example #2
0
	void OnGUI()
	{
		if (!terrain)
		{
			GUILayout.Label("No terrain found");
			if (GUILayout.Button("Cancel"))
			{
				EditorWindow.GetWindow<ExportTerrain>().Close();
			}
			return;
		}
		saveFormat = (SaveFormat) EditorGUILayout.EnumPopup("Export Format", saveFormat);
		
		saveResolution = (SaveResolution) EditorGUILayout.EnumPopup("Resolution", saveResolution);
		
		if (GUILayout.Button("Export"))
		{
			Export();
		}
	}
    void OnGUI()
    {
        EditorGUI.DrawPreviewTexture(new Rect(10, 10, 280, 60), WorldIcon);


        if (Selection.gameObjects.Length > 0 && Selection.gameObjects[0].GetComponent(typeof(Terrain)))
        {
            terrainObject = (Terrain)Selection.gameObjects[0].GetComponent(typeof(Terrain));
            terrain       = terrainObject.terrainData;
            terrainPos    = terrainObject.transform.position;
            GUILayout.BeginArea(new Rect(10, 70, 140, 100));
            GUILayout.Label("Mesh Type:");
            saveFormat = (SaveFormat)EditorGUILayout.EnumPopup("", saveFormat);
            GUILayout.Label("Resolution:");
            saveResolution = (SaveResolution)EditorGUILayout.EnumPopup("", saveResolution);
            TerrainCollider[] tcs = UnityEngine.Object.FindObjectsOfType <TerrainCollider>();

            foreach (TerrainCollider t in tcs)
            {
                //if the two terrain datas match but we aren't looking at the same gameobject..
                if (t.terrainData.name.Equals(terrain.name) && t.gameObject != terrainObject.gameObject)
                {
                    targetGO = t.gameObject;
                    break;
                }
                else
                {
                    targetGO = null;
                }
            }
            //if there is a target gameobject in the scene
            if (targetGO)
            {
                usePrefab = GUILayout.Toggle(usePrefab, "Use Associated Mesh");//EditorGUILayout.Toggle("Use Associated Mesh", usePrefab);
            }
            else
            {
                usePrefab = false;
            }
            GUILayout.EndArea();


            //Generate Terrain
            if (GUI.Button(new Rect(160, 72, 130, 90), GenerateIcon))//GUILayout.Button(exportText))
            {
                bool badTerrain = false;
                if (terrain.size.x % terrain.size.z > 0)
                {
                    badTerrain = true;
                }
                if (badTerrain)
                {
                    EditorUtility.DisplayDialog("Incompatible Terrain", "The terrain conversion has one requirement:\n\n1. " + terrainObject.name + " needs to have its length and width to be equal.", "Cancel Export");
                }
                else if (targetGO == null && usePrefab)
                {
                    EditorUtility.DisplayDialog("No Associated Mesh", "This terrain does not have an associated mesh generated yet. Uncheck 'Use Associated Mesh' and regenerate.", "OK");
                }
                else if (terrainObject.name.Equals("Terrain"))
                {
                    EditorUtility.DisplayDialog("Change Terrain Name", "Please give the Unity Terrain object a unique name other than 'Terrain.' This will make asset management easier for you.", "OK");
                }
                else if (saveResolution == SaveResolution.Full && shownWarning == false)
                {
                    if (EditorUtility.DisplayDialog("Warning", "Warning: Exporting a full resolution terrain carries some significant risks:\n\n1. It will take some significant time to generate depending on the speed of your cpu.\n\n2. It will be more expensive to render it on your target platform as there is much more geometry to render.\n\n3. It will take longer to lightmap.\n\n Only choose 'Full Resolution' if you know what you are doing.\n\nAre you sure you want to render at Full Resolution?", "Yes", "Cancel"))
                    {
                        shownWarning = true;
                        // AssetDatabase.Refresh();

                        Export();
                    }
                }
                else
                {
                    //AssetDatabase.Refresh();
                    Export();
                }
            }
            if (usePrefab)
            {
                if (targetGO != null)
                {
                    MeshFilter[] mf = targetGO.GetComponentsInChildren <MeshFilter>(true);

                    GUILayout.BeginArea(new Rect(10, 170, 280, 30));
                    GUILayout.Label("Editing: " + targetGO.name);
                    GUILayout.EndArea();
                    GUILayout.BeginArea(new Rect(10, 190, 140, 80));
                    meshHidden      = GUILayout.Toggle(meshHidden, "Hide Associated Mesh");
                    terrainHidden   = GUILayout.Toggle(terrainHidden, "Hide Terrain");
                    autoUpdateVC    = GUILayout.Toggle(autoUpdateVC, "Auto Bake VC");
                    smoothVertColor = GUILayout.Toggle(smoothVertColor, "Smooth Vertex Color");
                    GUILayout.EndArea();
                    GUILayout.BeginArea(new Rect(160, 187, 130, 60));
                    if (GUILayout.Button("Use Custom Shaders"))
                    {
                        Shader s = Shader.Find("QuantumTheory/VertexColors/IBL/Diffuse");
                        if (s != null)
                        {
                            MeshRenderer[] meshRenderers = targetGO.GetComponentsInChildren <MeshRenderer>();

                            foreach (MeshRenderer m in meshRenderers)
                            {
                                m.sharedMaterial.shader = s;
                            }
                            Debug.Log("Shaders applied successfully.");
                        }
                        else
                        {
                            Debug.LogWarning("Custom shaders not found! Please manually assign a shader that supports vertex colors.");
                        }
                    }

                    if (GUILayout.Button("Bake Vertex Colors"))
                    {
                        RenderVertexColors(targetGO);
                    }
                    GUILayout.EndArea();
                    // EditorGUI.DrawPreviewTexture(new Rect(10, 265, 280, DividerIcon.height), DividerIcon);



                    //if there are no chunk meshes in the prefab
                    if (!mf[0].sharedMesh.name.Contains("-Chunk"))
                    {
                        GUILayout.BeginArea(new Rect(10, 275, 280, 30));
                        chunkValUI = EditorGUILayout.IntSlider("Chunk Density:", chunkValUI, 1, 5, null);
                        GUILayout.EndArea();
                        float faceSize     = GetFaceSize(mf[0].sharedMesh);
                        float newBoundSize = (float)(faceSize * chunkVal[chunkValUI - 1]);
                        int   numMeshes    = Mathf.CeilToInt(terrain.size.x / newBoundSize);
                        EditorGUI.HelpBox(new Rect(10, 295, 280, 40), "Chunk Size:  " + newBoundSize + "m\n" + "Terrain Size:  " + terrain.size.x + "m x " + terrain.size.z + "m\n" + "Approximate Mesh Count: " + numMeshes * numMeshes, MessageType.None);
                        GUILayout.BeginArea(new Rect(10, 340, 280, 30));
                        //DIVIDE MESH
                        if (GUILayout.Button("Divide Mesh into Chunks"))
                        {
                            //add a check here to see if there is a folder with chunks inside. Delete them since we'll regenerate them.
                            string basePath = Application.dataPath;
                            string pathB    = AssetDatabase.GetAssetPath(PrefabUtility.GetPrefabParent(targetGO));
                            pathB    = pathB.Replace(targetGO.name + ".prefab", "");
                            basePath = basePath.Replace("Assets", pathB + terrainObject.name + "-Chunks");
                            if (Directory.Exists(basePath))
                            {
                                FileUtil.DeleteFileOrDirectory(basePath);
                            }
                            AssetDatabase.Refresh();
                            DivideMesh();
                            mf = null;
                        }
                        GUILayout.EndArea();
                    }



                    else
                    {
                        EditorGUI.HelpBox(new Rect(10, 295, 280, 40), "Regenerate the terrain to use different Chunk sizes.", MessageType.None);
                    }
                    if (mf != null)
                    {
                        if (meshHidden == false && mf[0].GetComponent <Renderer>().enabled != true)
                        {
                            ShowTerrainMesh(mf);
                        }
                        else if (meshHidden == true && mf[0].GetComponent <Renderer>().enabled != false)
                        {
                            HideTerrainMesh(mf);
                            if (terrainHidden)
                            {
                                terrainObject.enabled = true;
                                terrainHidden         = false;
                            }
                        }
                    }
                    if (terrainHidden == false && terrainObject.enabled != true)
                    {
                        terrainObject.enabled = true;
                    }
                    else if (terrainHidden == true && terrainObject.enabled != false)
                    {
                        terrainObject.enabled = false;
                        if (meshHidden == true)
                        {
                            ShowTerrainMesh(mf);
                            meshHidden = false;
                        }
                    }
                }
            }
            //else we are generating a new mesh
            else
            {
                usePrefab = false;
                targetGO  = null;
                EditorGUI.HelpBox(new Rect(10, 295, 280, 60), "Select your mesh type, then pick the resolution. Be careful though, if you have a large terrain, some resolutions might be too much.\nTurn on 'use associated mesh' to apply the changes to the mesh if one is available.", MessageType.Warning);
            }
        }
        else //nothing is selected
        {
            targetGO = null;
            EditorGUI.HelpBox(new Rect(10, 295, 280, 40), "Select a Unity Terrain to get started!", MessageType.Warning);
        }
    }
Example #4
0
    void OnGUI()
    {
        EditorGUI.DrawPreviewTexture(new Rect(10, 10, 280, 60), WorldIcon);

        if (Selection.gameObjects.Length > 0 && Selection.gameObjects[0].GetComponent(typeof(Terrain)))
        {
            terrainObject = (Terrain)Selection.gameObjects[0].GetComponent(typeof(Terrain));
            terrain = terrainObject.terrainData;
            terrainPos = terrainObject.transform.position;
            GUILayout.BeginArea(new Rect(10, 70, 140, 100));
            GUILayout.Label("Mesh Type:");
            saveFormat = (SaveFormat)EditorGUILayout.EnumPopup("", saveFormat);
            GUILayout.Label("Resolution:");
            saveResolution = (SaveResolution)EditorGUILayout.EnumPopup("", saveResolution);
            TerrainCollider[] tcs = UnityEngine.Object.FindObjectsOfType<TerrainCollider>();

            //check to see if the terrain is using the standard shader. if so, revert it to legacy diffuse so we render VC properly.
            if (terrainObject.materialType != Terrain.MaterialType.BuiltInLegacyDiffuse)
            {
                terrainObject.materialType = Terrain.MaterialType.BuiltInLegacyDiffuse;
                Debug.Log("Changing terrain material type to Legacy Diffuse so the vertex colors will render properly for PolyWorld Terrains.");
            }

            foreach (TerrainCollider t in tcs)
            {
                //if the two terrain datas match but we aren't looking at the same gameobject..
                if (t.terrainData.name.Equals(terrain.name) && t.gameObject != terrainObject.gameObject)
                {
                    targetGO = t.gameObject;
                    break;
                }
                else
                    targetGO = null;
            }
            //if there is a target gameobject in the scene
            if (targetGO)
            {
                usePrefab = GUILayout.Toggle(usePrefab, "Use Associated Mesh");//EditorGUILayout.Toggle("Use Associated Mesh", usePrefab);
            }
            else
                usePrefab = false;
            GUILayout.EndArea();

            //Generate Terrain
            if (GUI.Button(new Rect(160, 72, 130, 90), GenerateIcon))//GUILayout.Button(exportText))
            {
                bool badTerrain = false;
                if (terrain.size.x % terrain.size.z > 0)
                    badTerrain = true;
                if (badTerrain)
                    EditorUtility.DisplayDialog("Incompatible Terrain", "The terrain conversion has one requirement:\n\n1. " + terrainObject.name + " needs to have its length and width to be equal.", "Cancel Export");
                else if (targetGO == null && usePrefab)
                    EditorUtility.DisplayDialog("No Associated Mesh", "This terrain does not have an associated mesh generated yet. Uncheck 'Use Associated Mesh' and regenerate.", "OK");
                else if (terrainObject.name.Equals("Terrain"))
                    EditorUtility.DisplayDialog("Change Terrain Name", "Please give the Unity Terrain object a unique name other than 'Terrain.' This will make asset management easier for you.", "OK");
                else if (saveResolution == SaveResolution.Full && shownWarning==false)
                {
                    if (EditorUtility.DisplayDialog("Warning", "Warning: Exporting a full resolution terrain carries some significant risks:\n\n1. It will take some significant time to generate depending on the speed of your cpu.\n\n2. It will be more expensive to render it on your target platform as there is much more geometry to render.\n\n3. It will take longer to lightmap.\n\n Only choose 'Full Resolution' if you know what you are doing.\n\nAre you sure you want to render at Full Resolution?", "Yes", "Cancel"))
                    {
                        shownWarning = true;
                       // AssetDatabase.Refresh();

                        Export();
                    }
                }
                else
                {
                    //AssetDatabase.Refresh();
                    Export();
                }
            }
            if (usePrefab)
            {
                if (targetGO!=null)
                {
                    MeshFilter[] mf = targetGO.GetComponentsInChildren<MeshFilter>(true);

                    GUILayout.BeginArea(new Rect(10, 170, 280, 30));
                    GUILayout.Label("Editing: " + targetGO.name);
                    GUILayout.EndArea();
                    GUILayout.BeginArea(new Rect(10, 190, 140, 80));
                    meshHidden = GUILayout.Toggle(meshHidden,"Hide Associated Mesh");
                    terrainHidden = GUILayout.Toggle(terrainHidden, "Hide Terrain");
                    autoUpdateVC = GUILayout.Toggle(autoUpdateVC,"Auto Bake VC");
                    smoothVertColor = GUILayout.Toggle(smoothVertColor,"Smooth Vertex Color");
                    GUILayout.EndArea();
                    GUILayout.BeginArea(new Rect(160, 187, 130, 60));
                    if (GUILayout.Button("Use Custom Shaders"))
                    {
                        Shader s = Shader.Find("QuantumTheory/VertexColors/Unity5/Diffuse");
                        if (s != null)
                        {
                            MeshRenderer[] meshRenderers = targetGO.GetComponentsInChildren<MeshRenderer>();

                            foreach (MeshRenderer m in meshRenderers)
                                m.sharedMaterial.shader = s;
                            Debug.Log("Shaders applied successfully.");
                        }
                        else
                            Debug.LogWarning("Custom shaders not found! Please manually assign a shader that supports vertex colors.");

                    }

                    if (GUILayout.Button("Bake Vertex Colors"))
                            RenderVertexColors(targetGO);
                    GUILayout.EndArea();
                   // EditorGUI.DrawPreviewTexture(new Rect(10, 265, 280, DividerIcon.height), DividerIcon);

                    //if there are no chunk meshes in the prefab
                    if (!mf[0].sharedMesh.name.Contains("-Chunk"))
                    {
                        GUILayout.BeginArea(new Rect(10, 275, 280, 30));
                        chunkValUI = EditorGUILayout.IntSlider("Chunk Density:", chunkValUI, 1, 5, null);
                        GUILayout.EndArea();
                        float faceSize = GetFaceSize(mf[0].sharedMesh);
                        float newBoundSize = (float)(faceSize * chunkVal[chunkValUI - 1]);
                        int numMeshes = Mathf.CeilToInt(terrain.size.x / newBoundSize);
                        EditorGUI.HelpBox(new Rect(10, 295, 280, 40), "Chunk Size:  " + newBoundSize + "m\n" + "Terrain Size:  " + terrain.size.x + "m x " + terrain.size.z + "m\n" + "Approximate Mesh Count: " + numMeshes * numMeshes, MessageType.None);
                        GUILayout.BeginArea(new Rect(10, 340, 280, 30));
                        //DIVIDE MESH
                        if (GUILayout.Button("Divide Mesh into Chunks"))
                        {
                            //add a check here to see if there is a folder with chunks inside. Delete them since we'll regenerate them.
                            string basePath = Application.dataPath;
                            string pathB = AssetDatabase.GetAssetPath(PrefabUtility.GetPrefabParent(targetGO));
                            pathB = pathB.Replace(targetGO.name + ".prefab", "");
                            basePath = basePath.Replace("Assets", pathB + terrainObject.name + "-Chunks");
                            if (Directory.Exists(basePath))
                                FileUtil.DeleteFileOrDirectory(basePath);
                            AssetDatabase.Refresh();
                            DivideMesh();
                            mf = null;
                        }
                        GUILayout.EndArea();
                    }

                    else
                        EditorGUI.HelpBox(new Rect(10, 295, 280, 40), "Regenerate the terrain to use different Chunk sizes.", MessageType.None);
                    if (mf!=null)
                    {
                        if (meshHidden == false && mf[0].GetComponent<Renderer>().enabled != true)
                            ShowTerrainMesh(mf);
                        else if (meshHidden == true && mf[0].GetComponent<Renderer>().enabled != false)
                        {
                            HideTerrainMesh(mf);
                            if (terrainHidden)
                            {
                                terrainObject.enabled = true;
                                terrainHidden = false;
                            }
                        }
                    }
                    if (terrainHidden == false && terrainObject.enabled!=true)
                    {
                        terrainObject.enabled = true;
                    }
                    else if (terrainHidden == true && terrainObject.enabled != false)
                    {
                        terrainObject.enabled = false;
                        if (meshHidden == true)
                        {
                            ShowTerrainMesh(mf);
                            meshHidden = false;
                        }
                    }

                }
            }
            //else we are generating a new mesh
            else
            {
                usePrefab = false;
                targetGO = null;
                EditorGUI.HelpBox(new Rect(10, 295, 280, 60), "Select your mesh type, then pick the resolution. Be careful though, if you have a large terrain, some resolutions might be too much.\nTurn on 'use associated mesh' to apply the changes to the mesh if one is available.", MessageType.Warning);
            }

        }
        else //nothing is selected
        {
            targetGO = null;
            EditorGUI.HelpBox(new Rect(10, 295, 280, 40), "Select a Unity Terrain to get started!", MessageType.Warning);
        }
    }
Example #5
0
 public static void ExportAll()
 {
     saveResolution = SaveResolution.Eighth;
     //saveFormat = SaveFormat.Quads;
     foreach (Transform t in GameObject.Find ("Terrains").transform) {
         if (t.name != "Terrain27") {
             continue;
         }
         terrainObject = t.GetComponent<Terrain> () as Terrain;
         terrain = terrainObject.terrainData;
         terrainPos = Vector3.zero;//terrainObject.transform.position;
         terrainWorldPos = new Vector3 (terrainObject.transform.position.x, terrainObject.transform.position.y, terrainObject.transform.position.z);
         Export ();
     }
 }
Example #6
0
        /*
         * Convert Terrain Data to Mesh
         * */
        static void ExportTerrain(TerrainData terrain, string fileName, SaveResolution saveResolution, SaveFormat saveFormat)
        {
            int     w         = terrain.heightmapWidth;
            int     h         = terrain.heightmapHeight;
            Vector3 meshScale = terrain.size;
            int     tRes      = (int)Mathf.Pow(2, (int)saveResolution);

            meshScale = new Vector3(meshScale.x / (w - 1) * tRes, meshScale.y, meshScale.z / (h - 1) * tRes);
            Vector2 uvScale = new Vector2(1.0f / (w - 1), 1.0f / (h - 1));

            float[,] tData = terrain.GetHeights(0, 0, w, h);

            w = (w - 1) / tRes + 1;
            h = (h - 1) / tRes + 1;
            Vector3[] tVertices = new Vector3[w * h];
            Vector2[] tUV       = new Vector2[w * h];

            int[] tPolys;

            if (saveFormat == SaveFormat.Triangles)
            {
                tPolys = new int[(w - 1) * (h - 1) * 6];
            }
            else
            {
                tPolys = new int[(w - 1) * (h - 1) * 4];
            }

            // Build vertices and UVs
            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    tVertices[y * w + x] = Vector3.Scale(meshScale, new Vector3(-y, tData[x * tRes, y * tRes], x)) + terrainPos;
                    tUV[y * w + x]       = Vector2.Scale(new Vector2(x * tRes, y * tRes), uvScale);
                }
            }

            int index = 0;

            if (saveFormat == SaveFormat.Triangles)
            {
                // Build triangle indices: 3 indices into vertex array for each triangle
                for (int y = 0; y < h - 1; y++)
                {
                    for (int x = 0; x < w - 1; x++)
                    {
                        // For each grid cell output two triangles
                        tPolys[index++] = (y * w) + x;
                        tPolys[index++] = ((y + 1) * w) + x;
                        tPolys[index++] = (y * w) + x + 1;

                        tPolys[index++] = ((y + 1) * w) + x;
                        tPolys[index++] = ((y + 1) * w) + x + 1;
                        tPolys[index++] = (y * w) + x + 1;
                    }
                }
            }
            else
            {
                // Build quad indices: 4 indices into vertex array for each quad
                for (int y = 0; y < h - 1; y++)
                {
                    for (int x = 0; x < w - 1; x++)
                    {
                        // For each grid cell output one quad
                        tPolys[index++] = (y * w) + x;
                        tPolys[index++] = ((y + 1) * w) + x;
                        tPolys[index++] = ((y + 1) * w) + x + 1;
                        tPolys[index++] = (y * w) + x + 1;
                    }
                }
            }

            // Export to .obj
            StreamWriter sw = new StreamWriter(fileName);

            try
            {
                sw.WriteLine("# Unity terrain OBJ File");

                // Write vertices
                System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
                counter    = tCount = 0;
                totalCount = (tVertices.Length * 2 + (saveFormat == SaveFormat.Triangles ? tPolys.Length / 3 : tPolys.Length / 4)) / progressUpdateInterval;
                for (int i = 0; i < tVertices.Length; i++)
                {
                    UpdateProgress();
                    StringBuilder sb = new StringBuilder("v ", 20);
                    // StringBuilder stuff is done this way because it's faster than using the "{0} {1} {2}"etc. format
                    // Which is important when you're exporting huge terrains.
                    sb.Append(tVertices[i].x.ToString()).Append(" ").
                    Append(tVertices[i].y.ToString()).Append(" ").
                    Append(tVertices[i].z.ToString());
                    sw.WriteLine(sb);
                }
                // Write UVs
                for (int i = 0; i < tUV.Length; i++)
                {
                    UpdateProgress();
                    StringBuilder sb = new StringBuilder("vt ", 22);
                    sb.Append(tUV[i].y.ToString()).Append(" ").
                    Append(tUV[i].x.ToString()); ///
                    sw.WriteLine(sb);
                }
                if (saveFormat == SaveFormat.Triangles)
                {
                    // Write triangles
                    for (int i = 0; i < tPolys.Length; i += 3)
                    {
                        UpdateProgress();
                        StringBuilder sb = new StringBuilder("f ", 43);
                        sb.Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append(" ").
                        Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1).Append(" ").
                        Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1);
                        sw.WriteLine(sb);
                    }
                }
                else
                {
                    // Write quads
                    for (int i = 0; i < tPolys.Length; i += 4)
                    {
                        UpdateProgress();
                        StringBuilder sb = new StringBuilder("f ", 57);
                        sb.Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append(" ").
                        Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1).Append(" ").
                        Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1).Append(" ").
                        Append(tPolys[i + 3] + 1).Append("/").Append(tPolys[i + 3] + 1);
                        sw.WriteLine(sb);
                    }
                }
            }
            catch (Exception err)
            {
                Debug.Log("Error saving file: " + err.Message);
            }
            sw.Close();

            terrain = null;
            EditorUtility.DisplayProgressBar("Saving file to disc.", "This might take a while...", 1f);
            EditorWindow.GetWindow <ExportOBJ>().Close();
            EditorUtility.ClearProgressBar();
        }
Example #7
0
        /**
         * Given an RGB encoded (Massive Format) heightmap, convert it to terrain data and generate a mesh
         * DOUBLEPRECISION
         */
        static void ExportTerrainFromHeightMap(Texture2D HeightMap, string fileName, SaveResolution saveResolution, SaveFormat saveFormat, Vector3 TileSize)
        {
            EditorUtility.ClearProgressBar();
            //TerrainData td = new TerrainData();
            //td.baseMapResolution = 256;
            //td.heightmapResolution = 256;

            int w = HeightMap.width + 2;
            int h = HeightMap.height + 2;

            DVector3 size = new DVector3(TileSize.x, (float)EditorGlobals.MaxTerrainHeight, TileSize.z);

            double[,] dheights = new double[w, h];

            for (int xx = 0; xx < h; xx++)
            {
                for (int zz = 0; zz < w; zz++)
                {
                    int   xxx = Math.Min(xx, HeightMap.width - 1);
                    int   zzz = Math.Min(zz, HeightMap.height - 1);
                    Color c   = HeightMap.GetPixel(xxx, zzz);
                    //double height = BitmapUtils.MassivePixelToDouble(c) - EditorGlobals.HeightOffset;
                    double height = TextureTools.MassivePixelToDouble(c);
                    if (height < EditorGlobals.HeightOffset)
                    {
                        height = -20;
                    }
                    //range 0 - 1
                    float fh = (float)(height / EditorGlobals.MaxTerrainHeight);
                    dheights[xx, zz] = fh; //height values are reversed in unity terrain
                }
            }

            //td.SetHeights(0, 0, fheights);

            //ExportOBJ.ExportTerrain(td, fileName, saveResolution, saveFormat);


            Vector3 meshScale = size.ToVector3();
            int     tRes      = (int)Mathf.Pow(2, (int)saveResolution);

            meshScale = new Vector3(meshScale.x / (w - 1) * tRes, meshScale.y, meshScale.z / (h - 1) * tRes);
            Vector2 uvScale = new Vector2(1.0f / (w - 1), 1.0f / (h - 1));

            //float[,] tData = terrain.GetHeights(0, 0, w, h);

            w = (w - 1) / tRes + 1;
            h = (h - 1) / tRes + 1;
            Vector3[] tVertices = new Vector3[w * h];
            Vector2[] tUV       = new Vector2[w * h];

            int[] tPolys;

            if (saveFormat == SaveFormat.Triangles)
            {
                tPolys = new int[(w - 1) * (h - 1) * 6];
            }
            else
            {
                tPolys = new int[(w - 1) * (h - 1) * 4];
            }

            // Build vertices and UVs
            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    tVertices[y * w + x] = Vector3.Scale(meshScale, new Vector3(-y, (float)dheights[x * tRes, y * tRes], x)) + terrainPos;
                    tUV[y * w + x]       = Vector2.Scale(new Vector2(x * tRes, y * tRes), uvScale);
                }
            }

            int index = 0;

            if (saveFormat == SaveFormat.Triangles)
            {
                // Build triangle indices: 3 indices into vertex array for each triangle
                for (int y = 0; y < h - 1; y++)
                {
                    for (int x = 0; x < w - 1; x++)
                    {
                        // For each grid cell output two triangles
                        tPolys[index++] = (y * w) + x;
                        tPolys[index++] = ((y + 1) * w) + x;
                        tPolys[index++] = (y * w) + x + 1;

                        tPolys[index++] = ((y + 1) * w) + x;
                        tPolys[index++] = ((y + 1) * w) + x + 1;
                        tPolys[index++] = (y * w) + x + 1;
                    }
                }
            }
            else
            {
                // Build quad indices: 4 indices into vertex array for each quad
                for (int y = 0; y < h - 1; y++)
                {
                    for (int x = 0; x < w - 1; x++)
                    {
                        // For each grid cell output one quad
                        tPolys[index++] = (y * w) + x;
                        tPolys[index++] = ((y + 1) * w) + x;
                        tPolys[index++] = ((y + 1) * w) + x + 1;
                        tPolys[index++] = (y * w) + x + 1;
                    }
                }
            }

            // Export to .obj
            StreamWriter sw = new StreamWriter(fileName);

            try
            {
                sw.WriteLine("# Unity terrain OBJ File");

                // Write vertices
                System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
                counter    = tCount = 0;
                totalCount = (tVertices.Length * 2 + (saveFormat == SaveFormat.Triangles ? tPolys.Length / 3 : tPolys.Length / 4)) / progressUpdateInterval;
                for (int i = 0; i < tVertices.Length; i++)
                {
                    UpdateProgress();
                    StringBuilder sb = new StringBuilder("v ", 20);
                    // StringBuilder stuff is done this way because it's faster than using the "{0} {1} {2}"etc. format
                    // Which is important when you're exporting huge terrains.
                    sb.Append(tVertices[i].x.ToString()).Append(" ").
                    Append(tVertices[i].y.ToString()).Append(" ").
                    Append(tVertices[i].z.ToString());
                    sw.WriteLine(sb);
                }
                // Write UVs
                for (int i = 0; i < tUV.Length; i++)
                {
                    UpdateProgress();
                    StringBuilder sb = new StringBuilder("vt ", 22);
                    sb.Append(tUV[i].x.ToString()).Append(" ").
                    Append(tUV[i].y.ToString()); ///
                    sw.WriteLine(sb);
                }
                if (saveFormat == SaveFormat.Triangles)
                {
                    // Write triangles
                    for (int i = 0; i < tPolys.Length; i += 3)
                    {
                        UpdateProgress();
                        StringBuilder sb = new StringBuilder("f ", 43);
                        sb.Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append(" ").
                        Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1).Append(" ").
                        Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1);
                        sw.WriteLine(sb);
                    }
                }
                else
                {
                    // Write quads
                    for (int i = 0; i < tPolys.Length; i += 4)
                    {
                        UpdateProgress();
                        StringBuilder sb = new StringBuilder("f ", 57);
                        sb.Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append(" ").
                        Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1).Append(" ").
                        Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1).Append(" ").
                        Append(tPolys[i + 3] + 1).Append("/").Append(tPolys[i + 3] + 1);
                        sw.WriteLine(sb);
                    }
                }
            }
            catch (Exception err)
            {
                Debug.Log("Error saving file: " + err.Message);
            }
            sw.Close();

            terrain = null;
            EditorUtility.DisplayProgressBar("Saving file to disc.", "This might take a while...", 1f);
            EditorWindow.GetWindow <ExportOBJ>().Close();
            EditorUtility.ClearProgressBar();
        }