AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
RoomMeshExporter.cs
Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.IO;
3 using System.Text;
4 using UnityEditor;
5 using UnityEngine;
6 using HoloToolkit.Unity;
7 
8 namespace HoloToolkit.Unity.SpatialMapping
9 {
10  public static class RoomMeshExporter
11  {
12  private const string ExportDirectoryKey = "_ExportDirectory";
13  private const string ExportDirectoryDefault = "MeshExport";
14  private const string ExportDialogErrorTitle = "Export Error";
15  private const string WavefrontFileExtension = ".obj";
16 
17  public static string ExportDirectory
18  {
19  get
20  {
21  return EditorPrefsUtility.GetEditorPref(ExportDirectoryKey, ExportDirectoryDefault);
22  }
23  set
24  {
25  if (string.IsNullOrEmpty(value))
26  {
27  value = ExportDirectoryDefault;
28  }
29 
30  EditorPrefsUtility.SetEditorPref(ExportDirectoryKey, value);
31  }
32  }
33 
34  private static bool MakeExportDirectory()
35  {
36  try
37  {
38  Directory.CreateDirectory(ExportDirectory);
39  }
40  catch
41  {
42  return false;
43  }
44 
45  return true;
46  }
47 
48  [MenuItem("Mixed Reality Toolkit/Export/Export Room (.room) To Wavefront (.obj)...")]
49  public static void ExportRoomToWavefront()
50  {
51  string selectedFile = EditorUtility.OpenFilePanelWithFilters("Select Room File", MeshSaver.MeshFolderName, new string[] { "Room", "room" });
52  if (string.IsNullOrEmpty(selectedFile))
53  {
54  return;
55  }
56 
57  string fileName = Path.GetFileNameWithoutExtension(selectedFile);
58  IEnumerable<Mesh> meshes = null;
59  try
60  {
61  meshes = MeshSaver.Load(fileName);
62  }
63  catch
64  {
65  // Handling exceptions, and null returned by MeshSaver.Load, by checking if meshes
66  // is still null below.
67  }
68 
69  if (meshes == null)
70  {
71  EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Unable to parse selected file.", "Ok");
72  return;
73  }
74 
75  SaveMeshesToWavefront(fileName, meshes);
76 
77  // Open the location on where the mesh was saved.
78  System.Diagnostics.Process.Start(ExportDirectory);
79  }
80 
81  [MenuItem("Mixed Reality Toolkit/Export/Export Selection To Wavefront (.obj)")]
82  public static void ExportSelectionToWavefront()
83  {
84  Transform[] selectedTransforms = Selection.transforms;
85  if (selectedTransforms.Length <= 0)
86  {
87  EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Please select GameObject(s) within the scene that you want saved.", "OK");
88  return;
89  }
90 
91  List<MeshFilter> meshFilters = new List<MeshFilter>(selectedTransforms.Length);
92  for (int i = 0, iLength = selectedTransforms.Length; i < iLength; ++i)
93  {
94  meshFilters.AddRange(selectedTransforms[i].GetComponentsInChildren<MeshFilter>());
95  }
96 
97  if (meshFilters.Count == 0)
98  {
99  EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Nothing selected contains a MeshFilter.", "Ok");
100  return;
101  }
102 
103  SaveMeshFiltersToWavefront("Selection", meshFilters);
104 
105  // Open the location on where the mesh was saved.
106  System.Diagnostics.Process.Start(ExportDirectory);
107  }
108 
113  public static void SaveMeshesToWavefront(string fileName, IEnumerable<Mesh> meshes)
114  {
115  if (!MakeExportDirectory())
116  {
117  EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Failed to create export directory.", "Ok");
118  return;
119  }
120 
121  string filePath = Path.Combine(ExportDirectory, fileName + WavefrontFileExtension);
122  using (StreamWriter stream = new StreamWriter(filePath))
123  {
124  stream.Write(SerializeMeshes(meshes));
125  }
126  }
127 
132  public static void SaveMeshFiltersToWavefront(string fileName, IEnumerable<MeshFilter> meshes)
133  {
134  if (!MakeExportDirectory())
135  {
136  EditorUtility.DisplayDialog(ExportDialogErrorTitle, "Failed to create export directory.", "Ok");
137  return;
138  }
139 
140  string filePath = Path.Combine(ExportDirectory, fileName + WavefrontFileExtension);
141  using (StreamWriter stream = new StreamWriter(filePath))
142  {
143  stream.Write(SerializeMeshFilters(meshes));
144  }
145  }
146 
147  private static string SerializeMeshes(IEnumerable<Mesh> meshes)
148  {
149  StringWriter stream = new StringWriter();
150  int offset = 0;
151  foreach (var mesh in meshes)
152  {
153  SerializeMesh(mesh, stream, ref offset);
154  }
155  return stream.ToString();
156  }
157 
158  private static string SerializeMeshFilters(IEnumerable<MeshFilter> meshes)
159  {
160  StringWriter stream = new StringWriter();
161  int offset = 0;
162  foreach (var mesh in meshes)
163  {
164  SerializeMeshFilter(mesh, stream, ref offset);
165  }
166  return stream.ToString();
167  }
168 
175  private static void SerializeMesh(Mesh mesh, TextWriter stream, ref int offset)
176  {
177  // Write vertices to .obj file. Need to make sure the points are transformed so everything is at a single origin.
178  foreach (Vector3 vertex in mesh.vertices)
179  {
180  stream.WriteLine(string.Format("v {0} {1} {2}", -vertex.x, vertex.y, vertex.z));
181  }
182 
183  // Write normals. Need to transform the direction.
184  foreach (Vector3 normal in mesh.normals)
185  {
186  stream.WriteLine(string.Format("vn {0} {1} {2}", normal.x, normal.y, normal.z));
187  }
188 
189  // Write indices.
190  for (int s = 0, sLength = mesh.subMeshCount; s < sLength; ++s)
191  {
192  int[] indices = mesh.GetTriangles(s);
193  for (int i = 0, iLength = indices.Length - indices.Length % 3; i < iLength; i += 3)
194  {
195  // Format is "vertex index / material index / normal index"
196  stream.WriteLine(string.Format("f {0}//{0} {1}//{1} {2}//{2}",
197  indices[i + 2] + 1 + offset,
198  indices[i + 1] + 1 + offset,
199  indices[i + 0] + 1 + offset));
200  }
201  }
202 
203  offset += mesh.vertices.Length;
204  }
205 
212  private static void SerializeMeshFilter(MeshFilter meshFilter, TextWriter stream, ref int offset)
213  {
214  Mesh mesh = meshFilter.sharedMesh;
215 
216  // Write vertices to .obj file. Need to make sure the points are transformed so everything is at a single origin.
217  foreach (Vector3 vertex in mesh.vertices)
218  {
219  Vector3 pos = meshFilter.transform.TransformPoint(vertex);
220  stream.WriteLine(string.Format("v {0} {1} {2}", -pos.x, pos.y, pos.z));
221  }
222 
223  // Write normals. Need to transform the direction.
224  foreach (Vector3 meshNormal in mesh.normals)
225  {
226  Vector3 normal = meshFilter.transform.TransformDirection(meshNormal);
227  stream.WriteLine(string.Format("vn {0} {1} {2}", normal.x, normal.y, normal.z));
228  }
229 
230  // Write indices.
231  for (int s = 0, sLength = mesh.subMeshCount; s < sLength; ++s)
232  {
233  int[] indices = mesh.GetTriangles(s);
234  for (int i = 0, iLength = indices.Length - indices.Length % 3; i < iLength; i += 3)
235  {
236  // Format is "vertex index / material index / normal index"
237  stream.WriteLine(string.Format("f {0}//{0} {1}//{1} {2}//{2}",
238  indices[i + 0] + 1 + offset,
239  indices[i + 1] + 1 + offset,
240  indices[i + 2] + 1 + offset));
241  }
242  }
243 
244  offset += mesh.vertices.Length;
245  }
246  }
247 }
MeshSaver is a static class containing methods used for saving and loading meshes.
Definition: MeshSaver.cs:20
static string MeshFolderName
Read-only property which returns the folder path where mesh files are stored.
Definition: MeshSaver.cs:31
static IList< Mesh > Load(string fileName)
Loads the specified mesh file.
Definition: MeshSaver.cs:120
static void SaveMeshFiltersToWavefront(string fileName, IEnumerable< MeshFilter > meshes)
Transform all vertices and normals of the meshes into world space during serialization.
static void SetEditorPref(string key, string value)
static string GetEditorPref(string key, string defaultValue)
static void SaveMeshesToWavefront(string fileName, IEnumerable< Mesh > meshes)
Saves meshes without any modifications during serialization.