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