Tanoda
FBXExporter.cs
Go to the documentation of this file.
1// ===============================================================================================
2// The MIT License (MIT) for UnityFBXExporter
3//
4// UnityFBXExporter was created for Building Crafter (http://u3d.as/ovC) a tool to rapidly
5// create high quality buildings right in Unity with no need to use 3D modeling programs.
6//
7// Copyright (c) 2016 | 8Bit Goose Games, Inc.
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
13// of the Software, and to permit persons to whom the Software is furnished to do so,
14// subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in all
17// copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
20// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
21// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
24// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25//
26// ===============================================================================================
27
28using UnityEngine;
29using System.Text;
30using System.Collections.Generic;
31using System.IO;
32#if UNITY_EDITOR
33using UnityEditor;
34#endif
35using System.Linq;
36
37namespace UnityFBXExporter
38{
39 public class FBXExporter
40 {
41 public static bool ExportGameObjToFBX(GameObject gameObj, string newPath, bool copyMaterials = false, bool copyTextures = false)
42 {
43 // Check to see if the extension is right
44 if (Path.GetExtension(newPath).ToLower() != ".fbx")
45 {
46 Debug.LogError("The end of the path wasn't \".fbx\"");
47 return false;
48 }
49
50 if(copyMaterials)
51 CopyComplexMaterialsToPath(gameObj, newPath, copyTextures);
52
53 string buildMesh = MeshToString(gameObj, newPath, copyMaterials, copyTextures);
54
55 if(System.IO.File.Exists(newPath))
56 System.IO.File.Delete(newPath);
57
58 System.IO.File.WriteAllText(newPath, buildMesh);
59
60#if UNITY_EDITOR && false
61 // Import the model properly so it looks for the material instead of by the texture name
62 // TODO: By calling refresh, it imports the model with the wrong materials, but we can't find the model to import without
63 // refreshing the database. A chicken and the egg issue
64 AssetDatabase.Refresh();
65 string stringLocalPath = newPath.Remove(0, newPath.LastIndexOf("/Assets") + 1);
66 ModelImporter modelImporter = ModelImporter.GetAtPath(stringLocalPath) as ModelImporter;
67 if(modelImporter != null)
68 {
69 ModelImporterMaterialName modelImportOld = modelImporter.materialName;
70 modelImporter.materialName = ModelImporterMaterialName.BasedOnMaterialName;
71#if UNITY_5_1
72 modelImporter.normalImportMode = ModelImporterTangentSpaceMode.Import;
73#else
74 modelImporter.importNormals = ModelImporterNormals.Import;
75#endif
76 if (copyMaterials == false)
77 modelImporter.materialSearch = ModelImporterMaterialSearch.Everywhere;
78
79 AssetDatabase.ImportAsset(stringLocalPath, ImportAssetOptions.ForceUpdate);
80 }
81 else
82 {
83 Debug.Log("Model Importer is null and can't import");
84 }
85
86 AssetDatabase.Refresh();
87#endif
88 return true;
89 }
90 public static string ExportGameObjToFBXString(GameObject gameObj)
91 {
92 return MeshToString(gameObj, "", false, false);
93 }
94
95 public static string VersionInformation
96 {
97 get { return "FBX Unity Export version 1.1.1 (Originally created for the Unity Asset, Building Crafter)"; }
98 }
99
100 public static long GetRandomFBXId()
101 {
102 return System.BitConverter.ToInt64(System.Guid.NewGuid().ToByteArray(), 0);
103 }
104
105 public static string MeshToString (GameObject gameObj, string newPath, bool copyMaterials = false, bool copyTextures = false)
106 {
107 StringBuilder sb = new StringBuilder();
108
109 StringBuilder objectProps = new StringBuilder();
110 objectProps.AppendLine("; Object properties");
111 objectProps.AppendLine(";------------------------------------------------------------------");
112 objectProps.AppendLine("");
113 objectProps.AppendLine("Objects: {");
114
115 StringBuilder objectConnections = new StringBuilder();
116 objectConnections.AppendLine("; Object connections");
117 objectConnections.AppendLine(";------------------------------------------------------------------");
118 objectConnections.AppendLine("");
119 objectConnections.AppendLine("Connections: {");
120 objectConnections.AppendLine("\t");
121
122 Material[] materials = new Material[0];
123
124 // First finds all unique materials and compiles them (and writes to the object connections) for funzies
125 string materialsObjectSerialized = "";
126 string materialConnectionsSerialized = "";
127
128 FBXUnityMaterialGetter.GetAllMaterialsToString(gameObj, newPath, copyMaterials, copyTextures, out materials, out materialsObjectSerialized, out materialConnectionsSerialized);
129
130 // Run recursive FBX Mesh grab over the entire gameobject
131 FBXUnityMeshGetter.GetMeshToString(gameObj, materials, ref objectProps, ref objectConnections);
132
133 // write the materials to the objectProps here. Should not do it in the above as it recursive.
134
135 objectProps.Append(materialsObjectSerialized);
136 objectConnections.Append(materialConnectionsSerialized);
137
138 // Close up both builders;
139 objectProps.AppendLine("}");
140 objectConnections.AppendLine("}");
141
142 // ========= Create header ========
143
144 // Intro
145 sb.AppendLine("; FBX 7.3.0 project file");
146 sb.AppendLine("; Copyright (C) 1997-2010 Autodesk Inc. and/or its licensors.");
147 sb.AppendLine("; All rights reserved.");
148 sb.AppendLine("; ----------------------------------------------------");
149 sb.AppendLine();
150
151 // The header
152 sb.AppendLine("FBXHeaderExtension: {");
153 sb.AppendLine("\tFBXHeaderVersion: 1003");
154 sb.AppendLine("\tFBXVersion: 7300");
155
156 // Creationg Date Stamp
157 System.DateTime currentDate = System.DateTime.Now;
158 sb.AppendLine("\tCreationTimeStamp: {");
159 sb.AppendLine("\t\tVersion: 1000");
160 sb.AppendLine("\t\tYear: " + currentDate.Year);
161 sb.AppendLine("\t\tMonth: " + currentDate.Month);
162 sb.AppendLine("\t\tDay: " + currentDate.Day);
163 sb.AppendLine("\t\tHour: " + currentDate.Hour);
164 sb.AppendLine("\t\tMinute: " + currentDate.Minute);
165 sb.AppendLine("\t\tSecond: " + currentDate.Second);
166 sb.AppendLine("\t\tMillisecond: " + currentDate.Millisecond);
167 sb.AppendLine("\t}");
168
169 // Info on the Creator
170 sb.AppendLine("\tCreator: \"" + VersionInformation + "\"");
171 sb.AppendLine("\tSceneInfo: \"SceneInfo::GlobalInfo\", \"UserData\" {");
172 sb.AppendLine("\t\tType: \"UserData\"");
173 sb.AppendLine("\t\tVersion: 100");
174 sb.AppendLine("\t\tMetaData: {");
175 sb.AppendLine("\t\t\tVersion: 100");
176 sb.AppendLine("\t\t\tTitle: \"\"");
177 sb.AppendLine("\t\t\tSubject: \"\"");
178 sb.AppendLine("\t\t\tAuthor: \"\"");
179 sb.AppendLine("\t\t\tKeywords: \"\"");
180 sb.AppendLine("\t\t\tRevision: \"\"");
181 sb.AppendLine("\t\t\tComment: \"\"");
182 sb.AppendLine("\t\t}");
183 sb.AppendLine("\t\tProperties70: {");
184
185 // Information on how this item was originally generated
186 string documentInfoPaths = Application.dataPath + newPath + ".fbx";
187 sb.AppendLine("\t\t\tP: \"DocumentUrl\", \"KString\", \"Url\", \"\", \"" + documentInfoPaths + "\"");
188 sb.AppendLine("\t\t\tP: \"SrcDocumentUrl\", \"KString\", \"Url\", \"\", \"" + documentInfoPaths + "\"");
189 sb.AppendLine("\t\t\tP: \"Original\", \"Compound\", \"\", \"\"");
190 sb.AppendLine("\t\t\tP: \"Original|ApplicationVendor\", \"KString\", \"\", \"\", \"\"");
191 sb.AppendLine("\t\t\tP: \"Original|ApplicationName\", \"KString\", \"\", \"\", \"\"");
192 sb.AppendLine("\t\t\tP: \"Original|ApplicationVersion\", \"KString\", \"\", \"\", \"\"");
193 sb.AppendLine("\t\t\tP: \"Original|DateTime_GMT\", \"DateTime\", \"\", \"\", \"\"");
194 sb.AppendLine("\t\t\tP: \"Original|FileName\", \"KString\", \"\", \"\", \"\"");
195 sb.AppendLine("\t\t\tP: \"LastSaved\", \"Compound\", \"\", \"\"");
196 sb.AppendLine("\t\t\tP: \"LastSaved|ApplicationVendor\", \"KString\", \"\", \"\", \"\"");
197 sb.AppendLine("\t\t\tP: \"LastSaved|ApplicationName\", \"KString\", \"\", \"\", \"\"");
198 sb.AppendLine("\t\t\tP: \"LastSaved|ApplicationVersion\", \"KString\", \"\", \"\", \"\"");
199 sb.AppendLine("\t\t\tP: \"LastSaved|DateTime_GMT\", \"DateTime\", \"\", \"\", \"\"");
200 sb.AppendLine("\t\t}");
201 sb.AppendLine("\t}");
202 sb.AppendLine("}");
203
204 // The Global information
205 sb.AppendLine("GlobalSettings: {");
206 sb.AppendLine("\tVersion: 1000");
207 sb.AppendLine("\tProperties70: {");
208 sb.AppendLine("\t\tP: \"UpAxis\", \"int\", \"Integer\", \"\",1");
209 sb.AppendLine("\t\tP: \"UpAxisSign\", \"int\", \"Integer\", \"\",1");
210 sb.AppendLine("\t\tP: \"FrontAxis\", \"int\", \"Integer\", \"\",2");
211 sb.AppendLine("\t\tP: \"FrontAxisSign\", \"int\", \"Integer\", \"\",1");
212 sb.AppendLine("\t\tP: \"CoordAxis\", \"int\", \"Integer\", \"\",0");
213 sb.AppendLine("\t\tP: \"CoordAxisSign\", \"int\", \"Integer\", \"\",1");
214 sb.AppendLine("\t\tP: \"OriginalUpAxis\", \"int\", \"Integer\", \"\",-1");
215 sb.AppendLine("\t\tP: \"OriginalUpAxisSign\", \"int\", \"Integer\", \"\",1");
216 sb.AppendLine("\t\tP: \"UnitScaleFactor\", \"double\", \"Number\", \"\",100"); // NOTE: This sets the resize scale upon import
217 sb.AppendLine("\t\tP: \"OriginalUnitScaleFactor\", \"double\", \"Number\", \"\",100");
218 sb.AppendLine("\t\tP: \"AmbientColor\", \"ColorRGB\", \"Color\", \"\",0,0,0");
219 sb.AppendLine("\t\tP: \"DefaultCamera\", \"KString\", \"\", \"\", \"Producer Perspective\"");
220 sb.AppendLine("\t\tP: \"TimeMode\", \"enum\", \"\", \"\",11");
221 sb.AppendLine("\t\tP: \"TimeSpanStart\", \"KTime\", \"Time\", \"\",0");
222 sb.AppendLine("\t\tP: \"TimeSpanStop\", \"KTime\", \"Time\", \"\",479181389250");
223 sb.AppendLine("\t\tP: \"CustomFrameRate\", \"double\", \"Number\", \"\",-1");
224 sb.AppendLine("\t}");
225 sb.AppendLine("}");
226
227 // The Object definations
228 sb.AppendLine("; Object definitions");
229 sb.AppendLine(";------------------------------------------------------------------");
230 sb.AppendLine("");
231 sb.AppendLine("Definitions: {");
232 sb.AppendLine("\tVersion: 100");
233 sb.AppendLine("\tCount: 4");
234
235 sb.AppendLine("\tObjectType: \"GlobalSettings\" {");
236 sb.AppendLine("\t\tCount: 1");
237 sb.AppendLine("\t}");
238
239
240 sb.AppendLine("\tObjectType: \"Model\" {");
241 sb.AppendLine("\t\tCount: 1"); // TODO figure out if this count matters
242 sb.AppendLine("\t\tPropertyTemplate: \"FbxNode\" {");
243 sb.AppendLine("\t\t\tProperties70: {");
244 sb.AppendLine("\t\t\t\tP: \"QuaternionInterpolate\", \"enum\", \"\", \"\",0");
245 sb.AppendLine("\t\t\t\tP: \"RotationOffset\", \"Vector3D\", \"Vector\", \"\",0,0,0");
246 sb.AppendLine("\t\t\t\tP: \"RotationPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0");
247 sb.AppendLine("\t\t\t\tP: \"ScalingOffset\", \"Vector3D\", \"Vector\", \"\",0,0,0");
248 sb.AppendLine("\t\t\t\tP: \"ScalingPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0");
249 sb.AppendLine("\t\t\t\tP: \"TranslationActive\", \"bool\", \"\", \"\",0");
250 sb.AppendLine("\t\t\t\tP: \"TranslationMin\", \"Vector3D\", \"Vector\", \"\",0,0,0");
251 sb.AppendLine("\t\t\t\tP: \"TranslationMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
252 sb.AppendLine("\t\t\t\tP: \"TranslationMinX\", \"bool\", \"\", \"\",0");
253 sb.AppendLine("\t\t\t\tP: \"TranslationMinY\", \"bool\", \"\", \"\",0");
254 sb.AppendLine("\t\t\t\tP: \"TranslationMinZ\", \"bool\", \"\", \"\",0");
255 sb.AppendLine("\t\t\t\tP: \"TranslationMaxX\", \"bool\", \"\", \"\",0");
256 sb.AppendLine("\t\t\t\tP: \"TranslationMaxY\", \"bool\", \"\", \"\",0");
257 sb.AppendLine("\t\t\t\tP: \"TranslationMaxZ\", \"bool\", \"\", \"\",0");
258 sb.AppendLine("\t\t\t\tP: \"RotationOrder\", \"enum\", \"\", \"\",0");
259 sb.AppendLine("\t\t\t\tP: \"RotationSpaceForLimitOnly\", \"bool\", \"\", \"\",0");
260 sb.AppendLine("\t\t\t\tP: \"RotationStiffnessX\", \"double\", \"Number\", \"\",0");
261 sb.AppendLine("\t\t\t\tP: \"RotationStiffnessY\", \"double\", \"Number\", \"\",0");
262 sb.AppendLine("\t\t\t\tP: \"RotationStiffnessZ\", \"double\", \"Number\", \"\",0");
263 sb.AppendLine("\t\t\t\tP: \"AxisLen\", \"double\", \"Number\", \"\",10");
264 sb.AppendLine("\t\t\t\tP: \"PreRotation\", \"Vector3D\", \"Vector\", \"\",0,0,0");
265 sb.AppendLine("\t\t\t\tP: \"PostRotation\", \"Vector3D\", \"Vector\", \"\",0,0,0");
266 sb.AppendLine("\t\t\t\tP: \"RotationActive\", \"bool\", \"\", \"\",0");
267 sb.AppendLine("\t\t\t\tP: \"RotationMin\", \"Vector3D\", \"Vector\", \"\",0,0,0");
268 sb.AppendLine("\t\t\t\tP: \"RotationMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
269 sb.AppendLine("\t\t\t\tP: \"RotationMinX\", \"bool\", \"\", \"\",0");
270 sb.AppendLine("\t\t\t\tP: \"RotationMinY\", \"bool\", \"\", \"\",0");
271 sb.AppendLine("\t\t\t\tP: \"RotationMinZ\", \"bool\", \"\", \"\",0");
272 sb.AppendLine("\t\t\t\tP: \"RotationMaxX\", \"bool\", \"\", \"\",0");
273 sb.AppendLine("\t\t\t\tP: \"RotationMaxY\", \"bool\", \"\", \"\",0");
274 sb.AppendLine("\t\t\t\tP: \"RotationMaxZ\", \"bool\", \"\", \"\",0");
275 sb.AppendLine("\t\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",0");
276 sb.AppendLine("\t\t\t\tP: \"ScalingActive\", \"bool\", \"\", \"\",0");
277 sb.AppendLine("\t\t\t\tP: \"ScalingMin\", \"Vector3D\", \"Vector\", \"\",0,0,0");
278 sb.AppendLine("\t\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",1,1,1");
279 sb.AppendLine("\t\t\t\tP: \"ScalingMinX\", \"bool\", \"\", \"\",0");
280 sb.AppendLine("\t\t\t\tP: \"ScalingMinY\", \"bool\", \"\", \"\",0");
281 sb.AppendLine("\t\t\t\tP: \"ScalingMinZ\", \"bool\", \"\", \"\",0");
282 sb.AppendLine("\t\t\t\tP: \"ScalingMaxX\", \"bool\", \"\", \"\",0");
283 sb.AppendLine("\t\t\t\tP: \"ScalingMaxY\", \"bool\", \"\", \"\",0");
284 sb.AppendLine("\t\t\t\tP: \"ScalingMaxZ\", \"bool\", \"\", \"\",0");
285 sb.AppendLine("\t\t\t\tP: \"GeometricTranslation\", \"Vector3D\", \"Vector\", \"\",0,0,0");
286 sb.AppendLine("\t\t\t\tP: \"GeometricRotation\", \"Vector3D\", \"Vector\", \"\",0,0,0");
287 sb.AppendLine("\t\t\t\tP: \"GeometricScaling\", \"Vector3D\", \"Vector\", \"\",1,1,1");
288 sb.AppendLine("\t\t\t\tP: \"MinDampRangeX\", \"double\", \"Number\", \"\",0");
289 sb.AppendLine("\t\t\t\tP: \"MinDampRangeY\", \"double\", \"Number\", \"\",0");
290 sb.AppendLine("\t\t\t\tP: \"MinDampRangeZ\", \"double\", \"Number\", \"\",0");
291 sb.AppendLine("\t\t\t\tP: \"MaxDampRangeX\", \"double\", \"Number\", \"\",0");
292 sb.AppendLine("\t\t\t\tP: \"MaxDampRangeY\", \"double\", \"Number\", \"\",0");
293 sb.AppendLine("\t\t\t\tP: \"MaxDampRangeZ\", \"double\", \"Number\", \"\",0");
294 sb.AppendLine("\t\t\t\tP: \"MinDampStrengthX\", \"double\", \"Number\", \"\",0");
295 sb.AppendLine("\t\t\t\tP: \"MinDampStrengthY\", \"double\", \"Number\", \"\",0");
296 sb.AppendLine("\t\t\t\tP: \"MinDampStrengthZ\", \"double\", \"Number\", \"\",0");
297 sb.AppendLine("\t\t\t\tP: \"MaxDampStrengthX\", \"double\", \"Number\", \"\",0");
298 sb.AppendLine("\t\t\t\tP: \"MaxDampStrengthY\", \"double\", \"Number\", \"\",0");
299 sb.AppendLine("\t\t\t\tP: \"MaxDampStrengthZ\", \"double\", \"Number\", \"\",0");
300 sb.AppendLine("\t\t\t\tP: \"PreferedAngleX\", \"double\", \"Number\", \"\",0");
301 sb.AppendLine("\t\t\t\tP: \"PreferedAngleY\", \"double\", \"Number\", \"\",0");
302 sb.AppendLine("\t\t\t\tP: \"PreferedAngleZ\", \"double\", \"Number\", \"\",0");
303 sb.AppendLine("\t\t\t\tP: \"LookAtProperty\", \"object\", \"\", \"\"");
304 sb.AppendLine("\t\t\t\tP: \"UpVectorProperty\", \"object\", \"\", \"\"");
305 sb.AppendLine("\t\t\t\tP: \"Show\", \"bool\", \"\", \"\",1");
306 sb.AppendLine("\t\t\t\tP: \"NegativePercentShapeSupport\", \"bool\", \"\", \"\",1");
307 sb.AppendLine("\t\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",-1");
308 sb.AppendLine("\t\t\t\tP: \"Freeze\", \"bool\", \"\", \"\",0");
309 sb.AppendLine("\t\t\t\tP: \"LODBox\", \"bool\", \"\", \"\",0");
310 sb.AppendLine("\t\t\t\tP: \"Lcl Translation\", \"Lcl Translation\", \"\", \"A\",0,0,0");
311 sb.AppendLine("\t\t\t\tP: \"Lcl Rotation\", \"Lcl Rotation\", \"\", \"A\",0,0,0");
312 sb.AppendLine("\t\t\t\tP: \"Lcl Scaling\", \"Lcl Scaling\", \"\", \"A\",1,1,1");
313 sb.AppendLine("\t\t\t\tP: \"Visibility\", \"Visibility\", \"\", \"A\",1");
314 sb.AppendLine("\t\t\t\tP: \"Visibility Inheritance\", \"Visibility Inheritance\", \"\", \"\",1");
315 sb.AppendLine("\t\t\t}");
316 sb.AppendLine("\t\t}");
317 sb.AppendLine("\t}");
318
319 // The geometry, this is IMPORTANT
320 sb.AppendLine("\tObjectType: \"Geometry\" {");
321 sb.AppendLine("\t\tCount: 1"); // TODO - this must be set by the number of items being placed.
322 sb.AppendLine("\t\tPropertyTemplate: \"FbxMesh\" {");
323 sb.AppendLine("\t\t\tProperties70: {");
324 sb.AppendLine("\t\t\t\tP: \"Color\", \"ColorRGB\", \"Color\", \"\",0.8,0.8,0.8");
325 sb.AppendLine("\t\t\t\tP: \"BBoxMin\", \"Vector3D\", \"Vector\", \"\",0,0,0");
326 sb.AppendLine("\t\t\t\tP: \"BBoxMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
327 sb.AppendLine("\t\t\t\tP: \"Primary Visibility\", \"bool\", \"\", \"\",1");
328 sb.AppendLine("\t\t\t\tP: \"Casts Shadows\", \"bool\", \"\", \"\",1");
329 sb.AppendLine("\t\t\t\tP: \"Receive Shadows\", \"bool\", \"\", \"\",1");
330 sb.AppendLine("\t\t\t}");
331 sb.AppendLine("\t\t}");
332 sb.AppendLine("\t}");
333
334 // The materials that are being placed. Has to be simple I think
335 sb.AppendLine("\tObjectType: \"Material\" {");
336 sb.AppendLine("\t\tCount: 1");
337 sb.AppendLine("\t\tPropertyTemplate: \"FbxSurfacePhong\" {");
338 sb.AppendLine("\t\t\tProperties70: {");
339 sb.AppendLine("\t\t\t\tP: \"ShadingModel\", \"KString\", \"\", \"\", \"Phong\"");
340 sb.AppendLine("\t\t\t\tP: \"MultiLayer\", \"bool\", \"\", \"\",0");
341 sb.AppendLine("\t\t\t\tP: \"EmissiveColor\", \"Color\", \"\", \"A\",0,0,0");
342 sb.AppendLine("\t\t\t\tP: \"EmissiveFactor\", \"Number\", \"\", \"A\",1");
343 sb.AppendLine("\t\t\t\tP: \"AmbientColor\", \"Color\", \"\", \"A\",0.2,0.2,0.2");
344 sb.AppendLine("\t\t\t\tP: \"AmbientFactor\", \"Number\", \"\", \"A\",1");
345 sb.AppendLine("\t\t\t\tP: \"DiffuseColor\", \"Color\", \"\", \"A\",0.8,0.8,0.8");
346 sb.AppendLine("\t\t\t\tP: \"DiffuseFactor\", \"Number\", \"\", \"A\",1");
347 sb.AppendLine("\t\t\t\tP: \"Bump\", \"Vector3D\", \"Vector\", \"\",0,0,0");
348 sb.AppendLine("\t\t\t\tP: \"NormalMap\", \"Vector3D\", \"Vector\", \"\",0,0,0");
349 sb.AppendLine("\t\t\t\tP: \"BumpFactor\", \"double\", \"Number\", \"\",1");
350 sb.AppendLine("\t\t\t\tP: \"TransparentColor\", \"Color\", \"\", \"A\",0,0,0");
351 sb.AppendLine("\t\t\t\tP: \"TransparencyFactor\", \"Number\", \"\", \"A\",0");
352 sb.AppendLine("\t\t\t\tP: \"DisplacementColor\", \"ColorRGB\", \"Color\", \"\",0,0,0");
353 sb.AppendLine("\t\t\t\tP: \"DisplacementFactor\", \"double\", \"Number\", \"\",1");
354 sb.AppendLine("\t\t\t\tP: \"VectorDisplacementColor\", \"ColorRGB\", \"Color\", \"\",0,0,0");
355 sb.AppendLine("\t\t\t\tP: \"VectorDisplacementFactor\", \"double\", \"Number\", \"\",1");
356 sb.AppendLine("\t\t\t\tP: \"SpecularColor\", \"Color\", \"\", \"A\",0.2,0.2,0.2");
357 sb.AppendLine("\t\t\t\tP: \"SpecularFactor\", \"Number\", \"\", \"A\",1");
358 sb.AppendLine("\t\t\t\tP: \"ShininessExponent\", \"Number\", \"\", \"A\",20");
359 sb.AppendLine("\t\t\t\tP: \"ReflectionColor\", \"Color\", \"\", \"A\",0,0,0");
360 sb.AppendLine("\t\t\t\tP: \"ReflectionFactor\", \"Number\", \"\", \"A\",1");
361 sb.AppendLine("\t\t\t}");
362 sb.AppendLine("\t\t}");
363 sb.AppendLine("\t}");
364
365 // Explanation of how textures work
366 sb.AppendLine("\tObjectType: \"Texture\" {");
367 sb.AppendLine("\t\tCount: 2"); // TODO - figure out if this texture number is important
368 sb.AppendLine("\t\tPropertyTemplate: \"FbxFileTexture\" {");
369 sb.AppendLine("\t\t\tProperties70: {");
370 sb.AppendLine("\t\t\t\tP: \"TextureTypeUse\", \"enum\", \"\", \"\",0");
371 sb.AppendLine("\t\t\t\tP: \"Texture alpha\", \"Number\", \"\", \"A\",1");
372 sb.AppendLine("\t\t\t\tP: \"CurrentMappingType\", \"enum\", \"\", \"\",0");
373 sb.AppendLine("\t\t\t\tP: \"WrapModeU\", \"enum\", \"\", \"\",0");
374 sb.AppendLine("\t\t\t\tP: \"WrapModeV\", \"enum\", \"\", \"\",0");
375 sb.AppendLine("\t\t\t\tP: \"UVSwap\", \"bool\", \"\", \"\",0");
376 sb.AppendLine("\t\t\t\tP: \"PremultiplyAlpha\", \"bool\", \"\", \"\",1");
377 sb.AppendLine("\t\t\t\tP: \"Translation\", \"Vector\", \"\", \"A\",0,0,0");
378 sb.AppendLine("\t\t\t\tP: \"Rotation\", \"Vector\", \"\", \"A\",0,0,0");
379 sb.AppendLine("\t\t\t\tP: \"Scaling\", \"Vector\", \"\", \"A\",1,1,1");
380 sb.AppendLine("\t\t\t\tP: \"TextureRotationPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0");
381 sb.AppendLine("\t\t\t\tP: \"TextureScalingPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0");
382 sb.AppendLine("\t\t\t\tP: \"CurrentTextureBlendMode\", \"enum\", \"\", \"\",1");
383 sb.AppendLine("\t\t\t\tP: \"UVSet\", \"KString\", \"\", \"\", \"default\"");
384 sb.AppendLine("\t\t\t\tP: \"UseMaterial\", \"bool\", \"\", \"\",0");
385 sb.AppendLine("\t\t\t\tP: \"UseMipMap\", \"bool\", \"\", \"\",0");
386 sb.AppendLine("\t\t\t}");
387 sb.AppendLine("\t\t}");
388 sb.AppendLine("\t}");
389
390 sb.AppendLine("}");
391 sb.AppendLine("");
392
393 sb.Append(objectProps.ToString());
394 sb.Append(objectConnections.ToString());
395
396 return sb.ToString();
397 }
398
399 public static void CopyComplexMaterialsToPath(GameObject gameObj, string path, bool copyTextures, string texturesFolder = "/Textures", string materialsFolder = "/Materials")
400 {
401#if UNITY_EDITOR
402 int folderIndex = path.LastIndexOf('/');
403 path = path.Remove(folderIndex, path.Length - folderIndex);
404
405 // 1. First create the directories that are needed
406 string texturesPath = path + texturesFolder;
407 string materialsPath = path + materialsFolder;
408
409 if(Directory.Exists(path) == false)
410 Directory.CreateDirectory(path);
411 if(Directory.Exists(materialsPath) == false)
412 Directory.CreateDirectory(materialsPath);
413
414 // 2. Copy every distinct Material into the Materials folder
415 //@cartzhang modify.As meshrender and skinnedrender is same level in inherit relation shape.
416 // if not check,skinned render ,may lost some materials.
417 Renderer[] meshRenderers = gameObj.GetComponentsInChildren<Renderer>();
418 List<Material> everyMaterial = new List<Material>();
419 for(int i = 0; i < meshRenderers.Length; i++)
420 {
421 for(int n = 0; n < meshRenderers[i].sharedMaterials.Length; n++)
422 {
423 everyMaterial.Add(meshRenderers[i].sharedMaterials[n]);
424 }
425 //Debug.Log(meshRenderers[i].gameObject.name);
426 }
427
428 Material[] everyDistinctMaterial = everyMaterial.Distinct().ToArray<Material>();
429 everyDistinctMaterial = everyDistinctMaterial.OrderBy(o => o.name).ToArray<Material>();
430
431 // Log warning if there are multiple assets with the same name
432 for(int i = 0; i < everyDistinctMaterial.Length; i++)
433 {
434 for(int n = 0; n < everyDistinctMaterial.Length; n++)
435 {
436 if(i == n)
437 continue;
438
439 if(everyDistinctMaterial[i].name == everyDistinctMaterial[n].name)
440 {
441 Debug.LogErrorFormat("Two distinct materials {0} and {1} have the same name, this will not work with the FBX Exporter", everyDistinctMaterial[i], everyDistinctMaterial[n]);
442 return;
443 }
444 }
445 }
446
447 List<string> everyMaterialName = new List<string>();
448 // Structure of materials naming, is used when packaging up the package
449 // PARENTNAME_ORIGINALMATNAME.mat
450 for(int i = 0; i < everyDistinctMaterial.Length; i++)
451 {
452 string newName = gameObj.name + "_" + everyDistinctMaterial[i].name;
453 string fullPath = materialsPath + "/" + newName + ".mat";
454
455 if(File.Exists(fullPath))
456 File.Delete(fullPath);
457
458 if(CopyAndRenameAsset(everyDistinctMaterial[i], newName, materialsPath))
459 everyMaterialName.Add(newName);
460 }
461
462 // 3. Go through newly moved materials and copy every texture and update the material
463 AssetDatabase.Refresh();
464
465 List<Material> allNewMaterials = new List<Material>();
466
467 for (int i = 0; i < everyMaterialName.Count; i++)
468 {
469 string assetPath = materialsPath;
470 if(assetPath[assetPath.Length - 1] != '/')
471 assetPath += "/";
472
473 assetPath += everyMaterialName[i] + ".mat";
474
475 Material sourceMat = (Material)AssetDatabase.LoadAssetAtPath(assetPath, typeof(Material));
476
477 if(sourceMat != null)
478 allNewMaterials.Add(sourceMat);
479 }
480
481 // Get all the textures from the mesh renderer
482
483 if(copyTextures)
484 {
485 if(Directory.Exists(texturesPath) == false)
486 Directory.CreateDirectory(texturesPath);
487
488 AssetDatabase.Refresh();
489
490 for(int i = 0; i < allNewMaterials.Count; i++)
491 {
492 allNewMaterials[i] = CopyTexturesAndAssignCopiesToMaterial(allNewMaterials[i], texturesPath);
493 }
494 }
495
496 AssetDatabase.Refresh();
497#endif
498 }
499
500 public static bool CopyAndRenameAsset(Object obj, string newName, string newFolderPath)
501 {
502#if UNITY_EDITOR
503 string path = newFolderPath;
504
505 if(path[path.Length - 1] != '/')
506 path += "/";
507
508// string testPath = path.Remove(path.Length - 1);
509// if(AssetDatabase.IsValidFolder(testPath) == false)
510// {
511// Debug.LogError("This folder does not exist " + testPath);
512// return false;
513// }
514
515 string assetPath = AssetDatabase.GetAssetPath(obj);
516 string extension = Path.GetExtension(assetPath);
517
518 string newFileName = path + newName + extension;
519
520 if(File.Exists(newFileName))
521 return false;
522
523 return AssetDatabase.CopyAsset(assetPath, newFileName);
524#else
525 return false;
526
527#endif
528 }
529
535 private static string GetFileName(string path)
536 {
537 string fileName = path.ToString();
538 fileName = fileName.Remove(0, fileName.LastIndexOf('/') + 1);
539
540 return fileName;
541 }
542
543 private static Material CopyTexturesAndAssignCopiesToMaterial(Material material, string newPath)
544 {
545 if(material.shader.name == "Standard" || material.shader.name == "Standard (Specular setup)")
546 {
547 GetTextureUpdateMaterialWithPath(material, "_MainTex", newPath);
548
549 if(material.shader.name == "Standard")
550 GetTextureUpdateMaterialWithPath(material, "_MetallicGlossMap", newPath);
551
552 if(material.shader.name == "Standard (Specular setup)")
553 GetTextureUpdateMaterialWithPath(material, "_SpecGlossMap", newPath);
554
555 GetTextureUpdateMaterialWithPath(material, "_BumpMap", newPath);
556 GetTextureUpdateMaterialWithPath(material, "_BumpMap", newPath);
557 GetTextureUpdateMaterialWithPath(material, "_BumpMap", newPath);
558 GetTextureUpdateMaterialWithPath(material, "_BumpMap", newPath);
559 GetTextureUpdateMaterialWithPath(material, "_ParallaxMap", newPath);
560 GetTextureUpdateMaterialWithPath(material, "_OcclusionMap", newPath);
561 GetTextureUpdateMaterialWithPath(material, "_EmissionMap", newPath);
562 GetTextureUpdateMaterialWithPath(material, "_DetailMask", newPath);
563 GetTextureUpdateMaterialWithPath(material, "_DetailAlbedoMap", newPath);
564 GetTextureUpdateMaterialWithPath(material, "_DetailNormalMap", newPath);
565
566 }
567 else
568 Debug.LogError("WARNING: " + material.name + " is not a physically based shader, may not export to package correctly");
569
570 return material;
571 }
572
580 private static void GetTextureUpdateMaterialWithPath(Material material, string textureShaderName, string newPath)
581 {
582 Texture textureInQ = material.GetTexture(textureShaderName);
583 if(textureInQ != null)
584 {
585 string name = material.name + textureShaderName;
586
587 Texture newTexture = (Texture)CopyAndRenameAssetReturnObject(textureInQ, name, newPath);
588 if(newTexture != null)
589 material.SetTexture(textureShaderName, newTexture);
590 }
591 }
592
593 public static Object CopyAndRenameAssetReturnObject(Object obj, string newName, string newFolderPath)
594 {
595 #if UNITY_EDITOR
596 string path = newFolderPath;
597
598 if(path[path.Length - 1] != '/')
599 path += "/";
600 string testPath = path.Remove(path.Length - 1);
601
602 if(System.IO.Directory.Exists(testPath) == false)
603 {
604 Debug.LogError("This folder does not exist " + testPath);
605 return null;
606 }
607
608 string assetPath = AssetDatabase.GetAssetPath(obj);
609 string fileName = GetFileName(assetPath);
610 string extension = fileName.Remove(0, fileName.LastIndexOf('.'));
611
612 string newFullPathName = path + newName + extension;
613
614 if(AssetDatabase.CopyAsset(assetPath, newFullPathName) == false)
615 return null;
616
617 AssetDatabase.Refresh();
618
619 return AssetDatabase.LoadAssetAtPath(newFullPathName, typeof(Texture));
620 #else
621 return null;
622 #endif
623 }
624
630 public static string FBXFormat(float val)
631 {
632 if(false) // SET TO TRUE IF YOU USE PERIODS FOR DECIMALS IN YOUR COUNTRY AND ONLY IF (to get a slight reduction in process time)
633 return val.ToString();
634
635 string stringValue = val.ToString();
636
637 int index = CheckForCommaInsteadOfDecimal(ref stringValue);
638 if(index > -1)
639 stringValue = ReplaceCommasWithDecimals(stringValue, index);
640
641 return stringValue;
642 }
643
647 private static int CheckForCommaInsteadOfDecimal(ref string vert)
648 {
649 int newIndex = -1;
650
651 for(int i = 0, l = vert.Length; i < l; i++)
652 {
653 if(vert[i] == ',')
654 return i;
655 }
656
657 return newIndex;
658 }
659
660 private static string ReplaceCommasWithDecimals(string vert, int breakIndex)
661 {
662 return vert.Remove(breakIndex, vert.Length - breakIndex) + "." + vert.Remove(0, breakIndex + 1);
663 }
664 }
665}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
static string MeshToString(GameObject gameObj, string newPath, bool copyMaterials=false, bool copyTextures=false)
Definition: FBXExporter.cs:105
static string VersionInformation
Definition: FBXExporter.cs:96
static Object CopyAndRenameAssetReturnObject(Object obj, string newName, string newFolderPath)
Definition: FBXExporter.cs:593
static bool ExportGameObjToFBX(GameObject gameObj, string newPath, bool copyMaterials=false, bool copyTextures=false)
Definition: FBXExporter.cs:41
static void CopyComplexMaterialsToPath(GameObject gameObj, string path, bool copyTextures, string texturesFolder="/Textures", string materialsFolder="/Materials")
Definition: FBXExporter.cs:399
static string FBXFormat(float val)
Provides internationalization for countries that use commas instead of decimals to denote the break p...
Definition: FBXExporter.cs:630
static bool CopyAndRenameAsset(Object obj, string newName, string newFolderPath)
Definition: FBXExporter.cs:500
static string ExportGameObjToFBXString(GameObject gameObj)
Definition: FBXExporter.cs:90
static void GetAllMaterialsToString(GameObject gameObj, string newPath, bool copyMaterials, bool copyTextures, out Material[] materials, out string matObjects, out string connections)
Finds all materials in a gameobject and writes them to a string that can be read by the FBX writer
static long GetMeshToString(GameObject gameObj, Material[] materials, ref StringBuilder objects, ref StringBuilder connections, GameObject parentObject=null, long parentModelId=0)
Gets all the meshes and outputs to a string (even grabbing the child of each gameObject)
UnityEngine.Object Object