- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using UnityEditor;
- using UnityEngine;
- using HoloToolkit.Unity;
-
- namespace HoloToolkit.Unity.SpatialMapping
- {
- public static class RoomMeshExporter
- {
- private const string ExportDirectoryKey = "_ExportDirectory";
- private const string ExportDirectoryDefault = "MeshExport";
- private const string ExportDialogErrorTitle = "Export Error";
- private const string WavefrontFileExtension = ".obj";
-
- public static string ExportDirectory
- {
- get
- {
- return EditorPrefsUtility.GetEditorPref(ExportDirectoryKey, ExportDirectoryDefault);
- }
- set
- {
- if (string.IsNullOrEmpty(value))
- {
- value = ExportDirectoryDefault;
- }
-
- EditorPrefsUtility.SetEditorPref(ExportDirectoryKey, value);
- }
- }
-
- private static bool MakeExportDirectory()
- {
- try
- {
- Directory.CreateDirectory(ExportDirectory);
- }
- catch
- {
- return false;
- }
-
- return true;
- }
-
- [MenuItem("Mixed Reality Toolkit/Export/Export Room (.room) To Wavefront (.obj)...")]
- public static void ExportRoomToWavefront()
- {
- string selectedFile = EditorUtility.OpenFilePanelWithFilters("Select Room File", MeshSaver.MeshFolderName, new string[] { "Room", "room" });
- if (string.IsNullOrEmpty(selectedFile))
- {
- return;
- }
-
- string fileName = Path.GetFileNameWithoutExtension(selectedFile);
- IEnumerable<Mesh> meshes = null;
- try
- {
- meshes = MeshSaver.Load(fileName);
- }
- catch
- {
- // Handling exceptions, and null returned by MeshSaver.Load, by checking if meshes
- // is still null below.
- }
-
- if (meshes == null)
- {
- EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Unable to parse selected file.", "Ok");
- return;
- }
-
- SaveMeshesToWavefront(fileName, meshes);
-
- // Open the location on where the mesh was saved.
- System.Diagnostics.Process.Start(ExportDirectory);
- }
-
- [MenuItem("Mixed Reality Toolkit/Export/Export Selection To Wavefront (.obj)")]
- public static void ExportSelectionToWavefront()
- {
- Transform[] selectedTransforms = Selection.transforms;
- if (selectedTransforms.Length <= 0)
- {
- EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Please select GameObject(s) within the scene that you want saved.", "OK");
- return;
- }
-
- List<MeshFilter> meshFilters = new List<MeshFilter>(selectedTransforms.Length);
- for (int i = 0, iLength = selectedTransforms.Length; i < iLength; ++i)
- {
- meshFilters.AddRange(selectedTransforms[i].GetComponentsInChildren<MeshFilter>());
- }
-
- if (meshFilters.Count == 0)
- {
- EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Nothing selected contains a MeshFilter.", "Ok");
- return;
- }
-
- SaveMeshFiltersToWavefront("Selection", meshFilters);
-
- // Open the location on where the mesh was saved.
- System.Diagnostics.Process.Start(ExportDirectory);
- }
-
- /// <summary>
- /// Saves meshes without any modifications during serialization.
- /// </summary>
- /// <param name="fileName">Name of the file, without path and extension.</param>
- public static void SaveMeshesToWavefront(string fileName, IEnumerable<Mesh> meshes)
- {
- if (!MakeExportDirectory())
- {
- EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Failed to create export directory.", "Ok");
- return;
- }
-
- string filePath = Path.Combine(ExportDirectory, fileName + WavefrontFileExtension);
- using (StreamWriter stream = new StreamWriter(filePath))
- {
- stream.Write(SerializeMeshes(meshes));
- }
- }
-
- /// <summary>
- /// Transform all vertices and normals of the meshes into world space during serialization.
- /// </summary>
- /// <param name="fileName">Name of the file, without path and extension.</param>
- public static void SaveMeshFiltersToWavefront(string fileName, IEnumerable<MeshFilter> meshes)
- {
- if (!MakeExportDirectory())
- {
- EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Failed to create export directory.", "Ok");
- return;
- }
-
- string filePath = Path.Combine(ExportDirectory, fileName + WavefrontFileExtension);
- using (StreamWriter stream = new StreamWriter(filePath))
- {
- stream.Write(SerializeMeshFilters(meshes));
- }
- }
-
- private static string SerializeMeshes(IEnumerable<Mesh> meshes)
- {
- StringWriter stream = new StringWriter();
- int offset = 0;
- foreach (var mesh in meshes)
- {
- SerializeMesh(mesh, stream, ref offset);
- }
- return stream.ToString();
- }
-
- private static string SerializeMeshFilters(IEnumerable<MeshFilter> meshes)
- {
- StringWriter stream = new StringWriter();
- int offset = 0;
- foreach (var mesh in meshes)
- {
- SerializeMeshFilter(mesh, stream, ref offset);
- }
- return stream.ToString();
- }
-
- /// <summary>
- /// Write single mesh to the stream passed in.
- /// </summary>
- /// <param name="meshFilter">Mesh to be serialized.</param>
- /// <param name="stream">Stream to write the mesh into.</param>
- /// <param name="offset">Index offset for handling multiple meshes in a single stream.</param>
- private static void SerializeMesh(Mesh mesh, TextWriter stream, ref int offset)
- {
- // Write vertices to .obj file. Need to make sure the points are transformed so everything is at a single origin.
- foreach (Vector3 vertex in mesh.vertices)
- {
- stream.WriteLine(string.Format("v {0} {1} {2}", -vertex.x, vertex.y, vertex.z));
- }
-
- // Write normals. Need to transform the direction.
- foreach (Vector3 normal in mesh.normals)
- {
- stream.WriteLine(string.Format("vn {0} {1} {2}", normal.x, normal.y, normal.z));
- }
-
- // Write indices.
- for (int s = 0, sLength = mesh.subMeshCount; s < sLength; ++s)
- {
- int[] indices = mesh.GetTriangles(s);
- for (int i = 0, iLength = indices.Length - indices.Length % 3; i < iLength; i += 3)
- {
- // Format is "vertex index / material index / normal index"
- stream.WriteLine(string.Format("f {0}//{0} {1}//{1} {2}//{2}",
- indices[i + 2] + 1 + offset,
- indices[i + 1] + 1 + offset,
- indices[i + 0] + 1 + offset));
- }
- }
-
- offset += mesh.vertices.Length;
- }
-
- /// <summary>
- /// Write single, transformed, mesh to the stream passed in.
- /// </summary>
- /// <param name="meshFilter">Contains the mesh to be transformed and serialized.</param>
- /// <param name="stream">Stream to write the transformed mesh into.</param>
- /// <param name="offset">Index offset for handling multiple meshes in a single stream.</param>
- private static void SerializeMeshFilter(MeshFilter meshFilter, TextWriter stream, ref int offset)
- {
- Mesh mesh = meshFilter.sharedMesh;
-
- // Write vertices to .obj file. Need to make sure the points are transformed so everything is at a single origin.
- foreach (Vector3 vertex in mesh.vertices)
- {
- Vector3 pos = meshFilter.transform.TransformPoint(vertex);
- stream.WriteLine(string.Format("v {0} {1} {2}", -pos.x, pos.y, pos.z));
- }
-
- // Write normals. Need to transform the direction.
- foreach (Vector3 meshNormal in mesh.normals)
- {
- Vector3 normal = meshFilter.transform.TransformDirection(meshNormal);
- stream.WriteLine(string.Format("vn {0} {1} {2}", normal.x, normal.y, normal.z));
- }
-
- // Write indices.
- for (int s = 0, sLength = mesh.subMeshCount; s < sLength; ++s)
- {
- int[] indices = mesh.GetTriangles(s);
- for (int i = 0, iLength = indices.Length - indices.Length % 3; i < iLength; i += 3)
- {
- // Format is "vertex index / material index / normal index"
- stream.WriteLine(string.Format("f {0}//{0} {1}//{1} {2}//{2}",
- indices[i + 0] + 1 + offset,
- indices[i + 1] + 1 + offset,
- indices[i + 2] + 1 + offset));
- }
- }
-
- offset += mesh.vertices.Length;
- }
- }
- }