| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- using UnityEngine;
- using UnityEditor;
- using System;
- using System.Collections;
- using System.IO;
- using System.Text;
- enum SaveFormat { Triangles, Quads }
- enum SaveResolution { Full = 0, Half, Quarter, Eighth, Sixteenth }
- public class ExportTerrain : EditorWindow
- {
- SaveFormat saveFormat = SaveFormat.Triangles;
- SaveResolution saveResolution = SaveResolution.Half;
- static TerrainData terrain;
- static Vector3 terrainPos;
- int tCount;
- int counter;
- int totalCount;
- int progressUpdateInterval = 10000;
- [MenuItem("Terrain/Export To Obj...")]
- static void Init()
- {
- terrain = null;
- Terrain terrainObject = Selection.activeObject as Terrain;
- if (!terrainObject)
- {
- terrainObject = Terrain.activeTerrain;
- }
- if (terrainObject)
- {
- terrain = terrainObject.terrainData;
- terrainPos = terrainObject.transform.position;
- }
- EditorWindow.GetWindow<ExportTerrain>().Show();
- }
- 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 Export()
- {
- string fileName = EditorUtility.SaveFilePanel("Export .obj file", "", "Terrain", "obj");
- int w = terrain.heightmapResolution;
- int h = terrain.heightmapResolution;
- 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].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<ExportTerrain>().Close();
- EditorUtility.ClearProgressBar();
- }
- void UpdateProgress()
- {
- if (counter++ == progressUpdateInterval)
- {
- counter = 0;
- EditorUtility.DisplayProgressBar("Saving...", "", Mathf.InverseLerp(0, totalCount, ++tCount));
- }
- }
- }
|