├── ThreeExporter ├── .gitignore ├── code │ ├── Core.cs.meta │ ├── Util.cs.meta │ ├── Exporter.cs.meta │ ├── Settings.cs.meta │ ├── Util.cs │ ├── Settings.cs │ ├── Exporter.cs │ └── Core.cs ├── ThreeExporter.userprefs ├── ThreeExporter.sln ├── AssemblyInfo.cs └── ThreeExporter.csproj ├── ThreeExporter.userprefs ├── ThreeExporter.sln ├── LICENSE └── README.md /ThreeExporter/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | bin/* 3 | obj -------------------------------------------------------------------------------- /ThreeExporter/code/Core.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a83909a014ea74696ac56df916d16fa8 3 | timeCreated: 1474100988 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /ThreeExporter/code/Util.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0e983a81ede9c4102b78e8363080afde 3 | timeCreated: 1474100988 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /ThreeExporter/code/Exporter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2a719ae9026464eba9c9ac1b47df6a60 3 | timeCreated: 1474100988 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /ThreeExporter/code/Settings.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 534629c61aa8b49a6b2a5dc4477817de 3 | timeCreated: 1474100988 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /ThreeExporter.userprefs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ThreeExporter/ThreeExporter.userprefs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ThreeExporter/code/Util.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace Three { 6 | public static class Util { 7 | 8 | public static string ColorToHex(Color32 color) { 9 | string hex = color.r.ToString("X2") + color.g.ToString("X2") + color.b.ToString("X2"); 10 | return hex; 11 | } 12 | 13 | public static string ToJSONArray(string[] strings) { 14 | return "[" + string.Join (",", strings) + "]"; 15 | } 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /ThreeExporter/code/Settings.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace Three { 5 | public struct Settings { 6 | 7 | public int decimalPlaces; 8 | public string saveFolder; 9 | public float lightMapRGBMConstant; 10 | public bool exportCameras; 11 | public bool exportColliders; 12 | public bool exportTextures; 13 | public bool exportLightmaps; 14 | public bool exportLights; 15 | public bool exportAmbientLight; 16 | public bool exportScripts; 17 | 18 | public int threeRevision; 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /ThreeExporter/ThreeExporter.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThreeExporter", "ThreeExporter.csproj", "{7B2BF8C3-45F4-464A-A822-5478F0D02AFB}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {7B2BF8C3-45F4-464A-A822-5478F0D02AFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {7B2BF8C3-45F4-464A-A822-5478F0D02AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {7B2BF8C3-45F4-464A-A822-5478F0D02AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {7B2BF8C3-45F4-464A-A822-5478F0D02AFB}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | EndGlobal 18 | -------------------------------------------------------------------------------- /ThreeExporter.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThreeExporter", "ThreeExporter\ThreeExporter.csproj", "{7B2BF8C3-45F4-464A-A822-5478F0D02AFB}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {7B2BF8C3-45F4-464A-A822-5478F0D02AFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {7B2BF8C3-45F4-464A-A822-5478F0D02AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {7B2BF8C3-45F4-464A-A822-5478F0D02AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {7B2BF8C3-45F4-464A-A822-5478F0D02AFB}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(MonoDevelopProperties) = preSolution 18 | StartupItem = ThreeExporter\ThreeExporter.csproj 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Nick Janssen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity to three.js exporter 2 | 3 | **Due to changing API's this plugin is currently broken on Unity 5.5\. The last working version is Unity 5.4. Pull requests welcome!** 4 | 5 | Export your Unity scenes to the popular JSON format used in Three.js. 6 | 7 | [Demo](http://threejsexporter.nickjanssen.com/) - [Changelog](https://docs.google.com/document/d/1H9vKY0JmplnizOQT7eYrC5wk91BsCfSEu_v6pGOdnYk/pub?embedded=true) - [Source code](https://github.com/nickjanssen/ThreeExporter) - [FAQ](https://docs.google.com/document/u/1/d/e/2PACX-1vSUesxdSq9f2ysP3TdDqRrISXwYq9bFK4dGTDPhsmVhk9bjiZHReEnSBzprcz5DvsU02hE2uScqR3wq/pub) 8 | 9 | **Supported:** 10 | * Vertices 11 | * UV's 12 | * Normals 13 | * Faces 14 | * Textures 15 | * Materials 16 | * Colliders 17 | * Cameras 18 | * Lights 19 | * Lightmaps 20 | * Script Properties 21 | 22 | Requires three.js r71 or higher. 23 | 24 | The Three.js Exporter can be found under the Window menu bar. 25 | 26 | To load the exported JSON file, use the [THREE.ObjectLoader](http://threejs.org/docs/#Reference/Loaders/ObjectLoader) class. See the demo's source code for an example. -------------------------------------------------------------------------------- /ThreeExporter/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("ThreeExporter")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("nickjanssen")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /ThreeExporter/ThreeExporter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {7B2BF8C3-45F4-464A-A822-5478F0D02AFB} 9 | Library 10 | ThreeExporter 11 | ThreeExporter 12 | v3.5 13 | 14 | 15 | True 16 | full 17 | False 18 | bin 19 | DEBUG; 20 | prompt 21 | 4 22 | False 23 | 24 | 25 | none 26 | True 27 | bin 28 | prompt 29 | 4 30 | False 31 | 32 | 33 | 34 | 35 | lib\UnityEditor.dll 36 | 37 | 38 | lib\UnityEngine.dll 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ThreeExporter/code/Exporter.cs: -------------------------------------------------------------------------------- 1 | // Three.js Exporter for Unity 2 | // http://threejsexporter.nickjanssen.com/ 3 | // Written by Nick Janssen - info@nickjanssen.com 4 | 5 | using UnityEditor; 6 | using UnityEditor.SceneManagement; 7 | using UnityEngine; 8 | using System; 9 | using System.IO; 10 | using System.Collections; 11 | using System.Collections.Generic; 12 | 13 | namespace Three { 14 | public class Exporter : EditorWindow 15 | { 16 | public string[] threeRevisions = new string[] {"r71", "r72 and above"}; 17 | 18 | public Settings settings; 19 | private static Core threeExporter = new Core(); 20 | 21 | private static bool CreateTargetFolder(string folder) { 22 | try { 23 | System.IO.Directory.CreateDirectory(folder); 24 | } 25 | catch (Exception e) { 26 | Debug.LogException(e); 27 | return false; 28 | } 29 | 30 | return true; 31 | } 32 | 33 | private static bool CleanDirectory(string folder) { 34 | System.IO.DirectoryInfo dirInfo = new DirectoryInfo(folder); 35 | 36 | try { 37 | foreach (FileInfo file in dirInfo.GetFiles()) { 38 | file.Delete(); 39 | } 40 | foreach (DirectoryInfo dir in dirInfo.GetDirectories()) { 41 | dir.Delete(true); 42 | } 43 | } 44 | catch (Exception e) { 45 | Debug.LogException(e); 46 | return false; 47 | } 48 | 49 | return true; 50 | } 51 | 52 | 53 | bool advancedSettings; 54 | int decimalPlaces = 4; 55 | float lightMapRGBMConstant = 5.0f; 56 | string targetPath = Path.GetFullPath(".") + Path.DirectorySeparatorChar + "ThreeExport"; 57 | string subFolderName; 58 | string jsonFilename = "scene.json"; 59 | bool openFolderAfterExporting = true; 60 | bool exportCameras = true; 61 | bool exportColliders = true; 62 | bool exportTextures = true; 63 | bool exportLightmaps = true; 64 | bool exportLights = true; 65 | bool exportAmbientLight = true; 66 | bool exportScripts = true; 67 | bool includeInactiveObjects = false; 68 | int threeRevision = 1; 69 | 70 | Vector2 scrollPos; 71 | 72 | [MenuItem("Window/Three.js Exporter")] 73 | public static void ShowWindow() { 74 | EditorWindow.GetWindow(typeof(Exporter)); 75 | } 76 | 77 | void OnEnable() 78 | { 79 | SetSceneName (false); 80 | } 81 | 82 | void Export() { 83 | settings.saveFolder = targetPath + System.IO.Path.DirectorySeparatorChar + System.IO.Path.GetFileNameWithoutExtension(subFolderName); 84 | settings.decimalPlaces = decimalPlaces; 85 | settings.lightMapRGBMConstant = lightMapRGBMConstant; 86 | settings.exportCameras = exportCameras; 87 | settings.exportColliders = exportColliders; 88 | settings.exportTextures = exportTextures; 89 | settings.exportLightmaps = exportLightmaps; 90 | settings.exportLights = exportLights; 91 | settings.exportAmbientLight = exportAmbientLight; 92 | settings.threeRevision = threeRevision + 71; 93 | 94 | settings.exportScripts = exportScripts; 95 | 96 | Transform[] selection = Selection.GetTransforms (SelectionMode.Editable | SelectionMode.ExcludePrefab); 97 | 98 | if (selection.Length == 0) { 99 | EditorUtility.DisplayDialog ("Nothing selected.", "Please select one or more objects.", ""); 100 | return; 101 | } 102 | 103 | if (!CreateTargetFolder (settings.saveFolder)) { 104 | return; 105 | } 106 | 107 | // if (!CleanDirectory (saveFolder)) 108 | // return; 109 | 110 | List allSelectedObjects = new List(); 111 | foreach (Transform selectedObject in selection) { 112 | foreach (var child in selectedObject.GetComponentsInChildren(includeInactiveObjects)) { 113 | allSelectedObjects.Add(child); 114 | } 115 | } 116 | Transform[] allSelectedObjectsArray = allSelectedObjects.ToArray (); 117 | 118 | string meshToJSON = threeExporter.ConvertToJSON(allSelectedObjectsArray, settings); 119 | string fullSavePath = System.IO.Path.Combine(settings.saveFolder, jsonFilename); 120 | 121 | using (StreamWriter sw = new StreamWriter(fullSavePath)) { 122 | sw.Write (meshToJSON); 123 | } 124 | 125 | if (exportTextures) { 126 | threeExporter.CopyTextures (allSelectedObjectsArray, settings); 127 | } 128 | 129 | EditorUtility.DisplayDialog("Completed.", "Exported " + allSelectedObjectsArray.Length + " objects to " + fullSavePath, ""); 130 | 131 | if (openFolderAfterExporting) { 132 | EditorUtility.RevealInFinder (settings.saveFolder); 133 | } 134 | 135 | } 136 | 137 | void SetSceneName(bool showWarning) 138 | { 139 | string name = EditorSceneManager.GetActiveScene ().name; 140 | 141 | if (showWarning && name == "") { 142 | EditorUtility.DisplayDialog ("Cannot find scene.", "Please save the scene you are currently working on or load a different one.", ""); 143 | } else { 144 | subFolderName = Path.GetFileNameWithoutExtension (name); 145 | } 146 | } 147 | 148 | 149 | void OnGUI() 150 | { 151 | bool cannotContinue = false; 152 | 153 | EditorGUILayout.BeginVertical(); 154 | scrollPos = 155 | EditorGUILayout.BeginScrollView(scrollPos); 156 | { 157 | GUILayout.Label ("Basic Settings", EditorStyles.boldLabel); 158 | 159 | EditorGUILayout.LabelField ("Target Folder"); 160 | EditorGUILayout.HelpBox (targetPath, MessageType.None); 161 | 162 | GUILayout.BeginHorizontal (); 163 | GUILayout.FlexibleSpace (); 164 | 165 | if (GUILayout.Button ("Change Target Path")) { 166 | targetPath = EditorUtility.OpenFolderPanel ("Choose Target Path", targetPath, ""); 167 | } 168 | 169 | GUILayout.EndHorizontal (); 170 | 171 | EditorGUILayout.HelpBox ("Inside the Target Path, I will create an additional folder that contains a JSON file and all textures.", MessageType.Info); 172 | 173 | 174 | subFolderName = EditorGUILayout.TextField ("Folder Name", subFolderName); 175 | 176 | GUILayout.BeginHorizontal (); 177 | GUILayout.FlexibleSpace (); 178 | if (GUILayout.Button ("Set to Scene Name")) { 179 | SetSceneName (true); 180 | } 181 | GUILayout.EndHorizontal (); 182 | 183 | EditorGUILayout.Space (); 184 | 185 | jsonFilename = EditorGUILayout.TextField ("JSON Filename ", jsonFilename); 186 | 187 | EditorGUILayout.Space (); 188 | 189 | GUILayout.BeginHorizontal (); 190 | EditorGUILayout.LabelField ("Target Three.js Revision"); 191 | GUILayout.FlexibleSpace (); 192 | threeRevision = EditorGUILayout.Popup(threeRevision, threeRevisions); 193 | GUILayout.EndHorizontal (); 194 | 195 | 196 | 197 | EditorGUILayout.Space (); 198 | 199 | exportCameras = EditorGUILayout.Toggle ("Export Cameras", exportCameras); 200 | exportColliders = EditorGUILayout.Toggle ("Export Colliders", exportColliders); 201 | exportTextures = EditorGUILayout.Toggle ("Export Textures", exportTextures); 202 | exportLightmaps = EditorGUILayout.Toggle ("Export Lightmaps", exportLightmaps); 203 | 204 | 205 | if (exportLightmaps && Lightmapping.giWorkflowMode == Lightmapping.GIWorkflowMode.Iterative) { 206 | cannotContinue = true; 207 | EditorGUILayout.HelpBox ("Please turn off Auto Baking and rebake the scene once in order for me to access the lightmaps.", MessageType.Error); 208 | 209 | GUILayout.BeginHorizontal (); 210 | GUILayout.FlexibleSpace (); 211 | if (GUILayout.Button ("Turn off Auto Baking")) { 212 | Lightmapping.giWorkflowMode = Lightmapping.GIWorkflowMode.OnDemand; 213 | Lightmapping.BakeAsync (); 214 | } 215 | GUILayout.EndHorizontal (); 216 | } 217 | 218 | 219 | exportLights = EditorGUILayout.Toggle ("Export Lights", exportLights); 220 | if (exportLights) { 221 | EditorGUILayout.HelpBox ("Lights that are only used for lightmapping will not be exported. Check the Baking property of your lights and change it to Mixed if you do want them.", MessageType.Info); 222 | } 223 | 224 | exportAmbientLight = EditorGUILayout.Toggle ("Export Ambient Light", exportAmbientLight); 225 | if (exportAmbientLight) { 226 | EditorGUILayout.HelpBox ("An ambient light will be exported with the Ambient Color set in your scene's Lighting Settings.", MessageType.Info); 227 | } 228 | 229 | exportScripts = EditorGUILayout.Toggle ("Export Script Properties", exportScripts); 230 | if (exportScripts) { 231 | EditorGUILayout.HelpBox ("If your Gameobjects contain Scripts, I will export their public properties. I will take all public properties of each script and put them in the userData field of the object. The actual scripts will not be exported.", MessageType.Info); 232 | } 233 | 234 | includeInactiveObjects = EditorGUILayout.Toggle ("Include Inactive Objects", includeInactiveObjects); 235 | 236 | EditorGUILayout.Space (); 237 | 238 | advancedSettings = EditorGUILayout.BeginToggleGroup ("Advanced Settings", advancedSettings); 239 | 240 | lightMapRGBMConstant = EditorGUILayout.FloatField ("Lightmap Contrast", lightMapRGBMConstant); 241 | EditorGUILayout.HelpBox ("In order to convert Unity lightmaps to Three.js, I have to create PNG's from Unity's EXR format. This may cause the resulting PNG lightmaps to look slightly different. Adjust this constant to tweak the contrast of your exported PNG lightmaps. Lower values make your lightmaps darker, higher values make them brighter.", MessageType.Info); 242 | 243 | decimalPlaces = EditorGUILayout.IntField ("Decimal Places", decimalPlaces); 244 | EditorGUILayout.HelpBox ("I will round all position, rotation and scale vectors using these given decimal places. A lower value results in a lighter file but may cause the model to look inaccurate. ", MessageType.Info); 245 | 246 | EditorGUILayout.EndToggleGroup (); 247 | 248 | GUILayout.BeginHorizontal (); 249 | EditorGUILayout.LabelField ("Open Target Folder After Exporting"); 250 | GUILayout.FlexibleSpace (); 251 | openFolderAfterExporting = EditorGUILayout.Toggle (openFolderAfterExporting); 252 | GUILayout.EndHorizontal (); 253 | } 254 | EditorGUILayout.EndScrollView(); 255 | EditorGUILayout.EndVertical(); 256 | 257 | if (cannotContinue) { 258 | GUI.enabled = false; 259 | } 260 | if (GUILayout.Button ("Export")) { 261 | Export(); 262 | } 263 | GUI.enabled = true; 264 | } 265 | } 266 | } -------------------------------------------------------------------------------- /ThreeExporter/code/Core.cs: -------------------------------------------------------------------------------- 1 | // Three.js Exporter for Unity 2 | // http://threejsexporter.nickjanssen.com/ 3 | // Written by Nick Janssen - info@nickjanssen.com 4 | 5 | using UnityEngine; 6 | using UnityEditor; 7 | using System; 8 | using System.Text; 9 | using System.IO; 10 | using System.Collections; 11 | using System.Collections.Generic; 12 | using System.Reflection; 13 | 14 | 15 | namespace Three { 16 | public class Core { 17 | 18 | List commonTextures = new List 19 | { "_MainTex", "_BumpMap", "_OcclusionMap" }; 20 | 21 | string GetValidFileNameFromString(string file) { 22 | Array.ForEach(Path.GetInvalidFileNameChars(), 23 | c => file = file.Replace(c.ToString(), String.Empty)); 24 | 25 | return file; 26 | } 27 | 28 | public bool CopyTextures(Transform[] transforms, Settings settings) { 29 | 30 | Dictionary usedMaterials = new Dictionary (); 31 | 32 | foreach (Transform transform in transforms) { 33 | MeshFilter meshFilter = transform.GetComponent(); 34 | if (meshFilter != null) { 35 | Material[] mats = meshFilter.GetComponent().sharedMaterials; 36 | 37 | for (int i=0; i < mats.Length; i ++) { 38 | Material mat = mats[i]; 39 | 40 | if (!mat) { 41 | continue; 42 | } 43 | 44 | if (!usedMaterials.ContainsValue(mat)) { 45 | usedMaterials.Add(System.Guid.NewGuid().ToString(), mat); 46 | } 47 | } 48 | } 49 | } 50 | 51 | 52 | foreach(KeyValuePair entry in usedMaterials) { 53 | commonTextures.ForEach(delegate(String texName) { 54 | if (!entry.Value.HasProperty(texName) || !entry.Value.GetTexture(texName)) return; 55 | 56 | Texture2D tex = (Texture2D) entry.Value.GetTexture(texName); 57 | 58 | var texturePath = AssetDatabase.GetAssetPath(tex); 59 | var textureExtension = System.IO.Path.GetExtension(texturePath).ToLower(); 60 | 61 | // var filename = System.IO.Path.GetFileName(texturePath); 62 | // var destFile = System.IO.Path.Combine(settings.saveFolder, filename); 63 | // System.IO.File.Copy(texturePath, destFile, true); 64 | // } 65 | // else { 66 | // else if (textureExtension != ".psd") { 67 | var texturePathPNG = System.IO.Path.ChangeExtension(texturePath, ".png"); 68 | var texturePathJPG = System.IO.Path.ChangeExtension(texturePath, ".jpg"); 69 | 70 | var filenamePNG = GetValidFileNameFromString(entry.Value.name) + "_" + System.IO.Path.GetFileName(texturePathPNG); 71 | var filenameJPG = GetValidFileNameFromString(entry.Value.name) + "_" + System.IO.Path.GetFileName(texturePathJPG); 72 | 73 | var destFilePNG = System.IO.Path.Combine(settings.saveFolder, filenamePNG); 74 | var destFileJPG = System.IO.Path.Combine(settings.saveFolder, filenameJPG); 75 | 76 | TextureImporter ti = (TextureImporter) TextureImporter.GetAtPath(texturePath); 77 | // TextureImporterFormat oldformat = ti.textureFormat; 78 | TextureImporterType oldtype = ti.textureType; 79 | 80 | if (texName == "_BumpMap") { 81 | ti.textureType = TextureImporterType.Default; 82 | } 83 | 84 | // ti.textureFormat = TextureImporterFormat.RGBA32; 85 | ti.isReadable = true; 86 | 87 | AssetDatabase.ImportAsset(texturePath); 88 | 89 | if (textureExtension == ".jpg") { 90 | File.WriteAllBytes(destFileJPG, tex.EncodeToJPG()); 91 | } 92 | else { 93 | File.WriteAllBytes(destFilePNG, tex.EncodeToPNG()); 94 | } 95 | 96 | 97 | ti.textureType = oldtype; 98 | // ti.textureFormat = oldformat; 99 | ti.isReadable = false; 100 | AssetDatabase.ImportAsset(texturePath); 101 | 102 | }); 103 | } 104 | 105 | // Copy lightmaps 106 | LightmapData[] lightmaps = UnityEngine.LightmapSettings.lightmaps; 107 | List lightmapTextures = new List(); 108 | if (settings.exportLightmaps) { 109 | for (int i = 0; i < lightmaps.Length; i++) { 110 | if (lightmaps [i].lightmapLight) { 111 | lightmapTextures.Add (lightmaps [i].lightmapLight); 112 | } 113 | if (lightmaps [i].lightmapDir) { 114 | lightmapTextures.Add (lightmaps [i].lightmapDir); 115 | } 116 | } 117 | } 118 | 119 | 120 | lightmapTextures.ForEach (delegate(Texture2D tex) { 121 | 122 | var texturePath = AssetDatabase.GetAssetPath (tex); 123 | var texturePathPNG = System.IO.Path.ChangeExtension (texturePath, ".png"); 124 | 125 | //System.IO.File.Copy(texturePath, System.IO.Path.Combine(settings.saveFolder, System.IO.Path.GetFileName(texturePath)), true); 126 | 127 | var destFilePNG = System.IO.Path.Combine (settings.saveFolder, System.IO.Path.GetFileName (texturePathPNG)); 128 | 129 | TextureImporter ti = (TextureImporter)TextureImporter.GetAtPath (texturePath); 130 | // TextureImporterFormat oldformat = ti.textureFormat; 131 | TextureImporterType oldType = ti.textureType; 132 | TextureImporterSettings textureImporterSettings = new TextureImporterSettings (); 133 | if (oldType == TextureImporterType.Lightmap) { 134 | ti.textureType = TextureImporterType.Default; 135 | // ti.lightmap = false; 136 | 137 | ti.ReadTextureSettings (textureImporterSettings); 138 | // settings.rgbm = TextureImporterRGBMMode.Off; 139 | ti.SetTextureSettings (textureImporterSettings); 140 | } 141 | 142 | // ti.textureCompression = TextureImporterCompression.Uncompressed; 143 | ti.isReadable = true; 144 | ti.SaveAndReimport (); 145 | 146 | Color[] pix = tex.GetPixels (0, 0, tex.width, tex.height); 147 | 148 | for (int i = 0; i < pix.Length; i++) { 149 | // http://graphicrants.blogspot.jp/2009/04/rgbm-color-encoding.html 150 | pix [i].r *= pix [i].a * settings.lightMapRGBMConstant; 151 | pix [i].g *= pix [i].a * settings.lightMapRGBMConstant; 152 | pix [i].b *= pix [i].a * settings.lightMapRGBMConstant; 153 | } 154 | 155 | Texture2D destTex = new Texture2D (tex.width, tex.height, TextureFormat.RGB24, false); 156 | destTex.hideFlags = HideFlags.HideAndDontSave; 157 | destTex.SetPixels (pix); 158 | destTex.Apply (); 159 | 160 | File.WriteAllBytes (destFilePNG, destTex.EncodeToPNG ()); 161 | 162 | GameObject.DestroyImmediate (destTex); 163 | 164 | if (oldType == TextureImporterType.Lightmap) { 165 | ti.textureType = TextureImporterType.Lightmap; 166 | 167 | ti.ReadTextureSettings (textureImporterSettings); 168 | ti.SetTextureSettings (textureImporterSettings); 169 | } 170 | 171 | ti.isReadable = false; 172 | ti.SaveAndReimport (); 173 | 174 | }); 175 | 176 | return true; 177 | } 178 | 179 | public struct Submesh { 180 | public MeshFilter meshFilter; 181 | public Mesh mesh; 182 | public int subMeshIndex; 183 | } 184 | 185 | public struct TextureImageLink { 186 | public Texture texture; 187 | public string filename; 188 | } 189 | 190 | public struct MeshMaterialLink { 191 | public MeshFilter meshFilter; 192 | public Material material; 193 | public int subMeshIndex; 194 | } 195 | 196 | public struct MaterialTextureLink { 197 | public Material material; 198 | public Texture texture; 199 | public string textureUnityIdentifier; 200 | public bool needsCleanup; 201 | } 202 | 203 | private StringBuilder jsonFile = new StringBuilder(); 204 | 205 | private void writeJSON(int tabs, string content, bool writeNewLine=true) { 206 | for (int i = 0; i < tabs; i++) { 207 | jsonFile.Append("\t"); 208 | } 209 | jsonFile.Append(content); 210 | if (writeNewLine) { 211 | jsonFile.Append(System.Environment.NewLine); 212 | } 213 | } 214 | 215 | private void writeMatrix(int tabs, Transform transform, Settings settings) { 216 | writeJSON(tabs, "\"matrix\": [", false); 217 | { 218 | Matrix4x4 m = Matrix4x4.TRS(new Vector3(), Quaternion.identity, new Vector3(-1, 1, 1)); 219 | m *= transform.localToWorldMatrix; 220 | m *= Matrix4x4.TRS(new Vector3(), Quaternion.AngleAxis(180, new Vector3(1, 0, 0)), new Vector3(1, 1, 1)); 221 | m *= Matrix4x4.TRS(new Vector3(), Quaternion.AngleAxis(90, new Vector3(0, 1, 0)), new Vector3(1, 1, 1)); 222 | m *= Matrix4x4.TRS(new Vector3(), Quaternion.identity, new Vector3(1, -1, 1)); 223 | 224 | for (int j = 0; j <= 3; j++) { 225 | for (int k = 0; k <= 3; k++) { 226 | float test = m[k,j]; 227 | test = (float)Math.Round(test, settings.decimalPlaces); 228 | writeJSON(0, test.ToString(), false); 229 | if (!(j == 3 && k == 3)) { 230 | writeJSON(0, ",", false); 231 | } 232 | } 233 | } 234 | writeJSON(0, "]"); 235 | } 236 | } 237 | 238 | 239 | public string ConvertToJSON(Transform[] transforms, Settings settings) { 240 | 241 | Dictionary usedMaterials = new Dictionary (); 242 | 243 | foreach (Transform transform in transforms) { 244 | MeshFilter meshFilter = transform.GetComponent(); 245 | if (meshFilter != null) { 246 | Material[] mats = meshFilter.GetComponent().sharedMaterials; 247 | 248 | for (int i=0; i < mats.Length; i ++) { 249 | Material mat = mats[i]; 250 | 251 | if (!mat) { 252 | continue; 253 | } 254 | 255 | if (!usedMaterials.ContainsValue(mat)) { 256 | usedMaterials.Add(System.Guid.NewGuid().ToString(), mat); 257 | } 258 | } 259 | } 260 | } 261 | 262 | Dictionary usedSubmeshes = new Dictionary (); 263 | 264 | foreach (Transform transform in transforms) { 265 | MeshFilter meshFilter = transform.GetComponent(); 266 | if (meshFilter != null) { 267 | if (meshFilter.sharedMesh == null) { 268 | Debug.LogError("'" + transform.name + "' appears to be missing a mesh! Please verify its mesh filter.", transform); 269 | } 270 | else { 271 | for (int i = 0; i < meshFilter.sharedMesh.subMeshCount; i++) { 272 | Submesh submesh; 273 | submesh.mesh = meshFilter.sharedMesh; 274 | submesh.meshFilter = meshFilter; 275 | submesh.subMeshIndex = i; 276 | usedSubmeshes.Add(System.Guid.NewGuid().ToString(), submesh); 277 | } 278 | } 279 | } 280 | } 281 | 282 | Dictionary meshMaterialLinks = new Dictionary (); 283 | Dictionary usedLightmapTextures = new Dictionary (); 284 | Dictionary usedImages = new Dictionary (); 285 | List addedTextures = new List(); 286 | 287 | foreach (Transform transform in transforms) { 288 | MeshFilter meshFilter = transform.GetComponent(); 289 | if (meshFilter != null) { 290 | Material[] mats = meshFilter.GetComponent().sharedMaterials; 291 | 292 | for (int i=0; i < mats.Length; i ++) { 293 | 294 | if (!mats[i]) { 295 | Debug.LogError("'" + transform.name + "' has a bad/missing material!", transform); 296 | continue; 297 | } 298 | 299 | MeshMaterialLink meshMaterialLink; 300 | 301 | meshMaterialLink.meshFilter = meshFilter; 302 | meshMaterialLink.material = mats[i]; 303 | meshMaterialLink.subMeshIndex = i; 304 | 305 | commonTextures.ForEach(delegate(String texName) { 306 | 307 | if ( !meshMaterialLink.material.HasProperty(texName) ) return; 308 | 309 | Texture tex = meshMaterialLink.material.GetTexture(texName); 310 | if (tex != null && !addedTextures.Contains(tex)) { 311 | 312 | // MaterialTextureLink materialTextureLink; 313 | // materialTextureLink.texture = tex; 314 | // materialTextureLink.material = meshMaterialLink.material; 315 | // materialTextureLink.textureUnityIdentifier = texName; 316 | // materialTextureLink.needsCleanup = false; 317 | 318 | TextureImageLink textureImageLink; 319 | textureImageLink.texture = tex; 320 | string texturePath = AssetDatabase.GetAssetPath(tex); 321 | 322 | var textureExtension = System.IO.Path.GetExtension(texturePath).ToLower(); 323 | if (textureExtension != ".png" && textureExtension != ".jpg") { 324 | // Other textures get converted to png automatically 325 | texturePath = System.IO.Path.ChangeExtension (texturePath, ".png"); 326 | } 327 | 328 | textureImageLink.filename = GetValidFileNameFromString(meshMaterialLink.material.name) + "_" +System.IO.Path.GetFileName(texturePath); 329 | 330 | addedTextures.Add(tex); 331 | // usedTextures.Add(System.Guid.NewGuid().ToString(), materialTextureLink); 332 | usedImages.Add(System.Guid.NewGuid().ToString(), textureImageLink); 333 | } 334 | }); 335 | 336 | meshMaterialLinks.Add(System.Guid.NewGuid().ToString(), meshMaterialLink); 337 | 338 | } 339 | } 340 | } 341 | 342 | // Add lightmaps 343 | LightmapData[] lightmapData = UnityEngine.LightmapSettings.lightmaps; 344 | if (settings.exportLightmaps) { 345 | for (int i = 0; i < lightmapData.Length; i++) { 346 | if (lightmapData [i].lightmapLight) { 347 | TextureImageLink textureImageLink; 348 | textureImageLink.texture = lightmapData [i].lightmapLight; 349 | 350 | string texturePath = AssetDatabase.GetAssetPath (lightmapData [i].lightmapLight); 351 | string texturePathPNG = System.IO.Path.ChangeExtension (texturePath, ".png"); 352 | textureImageLink.filename = System.IO.Path.GetFileName (texturePathPNG); 353 | 354 | Material lightMapMaterial = new Material (Shader.Find ("Diffuse")); 355 | lightMapMaterial.hideFlags = HideFlags.HideAndDontSave; 356 | MaterialTextureLink materialTextureLink; 357 | materialTextureLink.texture = textureImageLink.texture; 358 | lightMapMaterial.name = "lightmap"; 359 | lightMapMaterial.SetTexture ("_MainTex", materialTextureLink.texture); 360 | materialTextureLink.material = lightMapMaterial; 361 | materialTextureLink.textureUnityIdentifier = "_MainTex"; 362 | materialTextureLink.needsCleanup = true; 363 | 364 | usedLightmapTextures.Add (System.Guid.NewGuid ().ToString (), materialTextureLink); 365 | 366 | usedImages.Add (System.Guid.NewGuid ().ToString (), textureImageLink); 367 | } 368 | 369 | if (lightmapData [i].lightmapDir) { 370 | TextureImageLink textureImageLink; 371 | textureImageLink.texture = lightmapData [i].lightmapDir; 372 | 373 | string texturePath = AssetDatabase.GetAssetPath (lightmapData [i].lightmapDir); 374 | string texturePathPNG = System.IO.Path.ChangeExtension (texturePath, ".png"); 375 | textureImageLink.filename = System.IO.Path.GetFileName (texturePathPNG); 376 | 377 | usedImages.Add (System.Guid.NewGuid ().ToString (), textureImageLink); 378 | } 379 | } 380 | } 381 | 382 | jsonFile.Length = 0; 383 | 384 | writeJSON (0, "{"); 385 | 386 | // Metadata 387 | writeJSON (1, "\"metadata\": {"); 388 | { 389 | 390 | writeJSON(2, "\"version\": \"" + 4.3f + "\","); 391 | writeJSON(2, "\"type\": " + "\"Object\","); 392 | writeJSON(2, "\"generator\": " + "\"UnityThreeExporter\""); 393 | 394 | } 395 | 396 | writeJSON (1, "},"); 397 | 398 | // Geometries 399 | writeJSON (1, "\"geometries\": ["); 400 | { 401 | int count = 0; 402 | foreach(KeyValuePair entry in usedSubmeshes) { 403 | 404 | writeJSON (2, "{"); 405 | 406 | int[] subMeshTriangles = entry.Value.mesh.GetTriangles(entry.Value.subMeshIndex); 407 | // List subMeshTrianglesList = new List(subMeshTriangles); 408 | 409 | writeJSON(3, "\"uuid\": \"" + entry.Key + "\", "); 410 | writeJSON(3, "\"type\": \"" + "Geometry" + "\", "); 411 | 412 | int lowestFaceIndex = int.MaxValue; 413 | for (int i = 0; i < subMeshTriangles.Length; i++) { 414 | if (subMeshTriangles[i] < lowestFaceIndex) { 415 | lowestFaceIndex = subMeshTriangles[i]; 416 | } 417 | } 418 | 419 | int highestFaceIndex = -1; 420 | for (int i = 0; i < subMeshTriangles.Length; i++) { 421 | if (subMeshTriangles[i] > highestFaceIndex) { 422 | highestFaceIndex = subMeshTriangles[i]; 423 | } 424 | } 425 | 426 | writeJSON(3, "\"data\": {"); 427 | { 428 | 429 | writeJSON(4, "\"vertices\": [", false); 430 | { 431 | for (int i = lowestFaceIndex; i <= highestFaceIndex; i++) { 432 | Vector3 vertex = entry.Value.mesh.vertices[i]; 433 | writeJSON(0, Math.Round(vertex.z, settings.decimalPlaces) + ",", false); 434 | writeJSON(0, Math.Round(vertex.y, settings.decimalPlaces) + ",", false); 435 | writeJSON(0, Math.Round(vertex.x, settings.decimalPlaces) + ((i < highestFaceIndex) ? "," : ""), false); 436 | } 437 | } 438 | writeJSON(0, "],"); 439 | 440 | writeJSON(4, "\"normals\": [", false); 441 | { 442 | if (entry.Value.mesh.normals.Length > 0) { 443 | for (int i = lowestFaceIndex; i <= highestFaceIndex; i++) { 444 | Vector3 normal = entry.Value.mesh.normals[i]; 445 | writeJSON(0, Math.Round(normal.z, settings.decimalPlaces) + ",", false); 446 | writeJSON(0, Math.Round(normal.y, settings.decimalPlaces) + ",", false); 447 | writeJSON(0, Math.Round(normal.x, settings.decimalPlaces) + ((i < highestFaceIndex) ? "," : ""), false); 448 | } 449 | } 450 | } 451 | writeJSON(0, "],"); 452 | 453 | writeJSON(4, "\"uvs\": [", false); 454 | { 455 | Vector2[] meshUv1 = entry.Value.mesh.uv; 456 | Vector2[] meshUv2 = entry.Value.mesh.uv2; 457 | 458 | if (entry.Value.meshFilter.GetComponent().lightmapIndex >= 0 && 459 | meshUv2.Length == 0) { 460 | meshUv2 = (Vector2[]) meshUv1.Clone(); 461 | } 462 | 463 | // Alter lightmaps 464 | if (entry.Value.meshFilter.GetComponent().lightmapIndex >= 0) { 465 | if (entry.Value.mesh.uv2.Length > 0) { 466 | for (int i = lowestFaceIndex; i <= highestFaceIndex; i++) { 467 | meshUv2[i].x *= entry.Value.meshFilter.GetComponent().lightmapScaleOffset.x; 468 | meshUv2[i].y *= entry.Value.meshFilter.GetComponent().lightmapScaleOffset.y; 469 | 470 | meshUv2[i].x += entry.Value.meshFilter.GetComponent().lightmapScaleOffset.z; 471 | meshUv2[i].y += entry.Value.meshFilter.GetComponent().lightmapScaleOffset.w; 472 | } 473 | } 474 | else { 475 | for (int i = lowestFaceIndex; i <= highestFaceIndex; i++) { 476 | meshUv1[i].x *= entry.Value.meshFilter.GetComponent().lightmapScaleOffset.x; 477 | meshUv1[i].y *= entry.Value.meshFilter.GetComponent().lightmapScaleOffset.y; 478 | 479 | meshUv1[i].x += entry.Value.meshFilter.GetComponent().lightmapScaleOffset.z; 480 | meshUv1[i].y += entry.Value.meshFilter.GetComponent().lightmapScaleOffset.w; 481 | } 482 | } 483 | } 484 | 485 | writeJSON(0, "[", false); 486 | { 487 | if (meshUv1.Length > 0) { 488 | for (int i = lowestFaceIndex; i <= highestFaceIndex; i++) { 489 | Vector3 uv = meshUv1[i]; 490 | writeJSON(0, uv.x + ",", false); 491 | writeJSON(0, uv.y + ((i < highestFaceIndex) ? "," : ""), false); 492 | } 493 | } 494 | } 495 | writeJSON(0, "],", false); 496 | 497 | 498 | writeJSON(0, "[", false); 499 | { 500 | int lightmapIndex = entry.Value.meshFilter.GetComponent().lightmapIndex; 501 | if (entry.Value.mesh.uv2.Length > 0) { 502 | for (int i = lowestFaceIndex; i <= highestFaceIndex; i++) { 503 | Vector3 uv = meshUv2[i]; 504 | writeJSON(0, uv.x + ",", false); 505 | writeJSON(0, uv.y + ((i < highestFaceIndex) ? "," : ""), false); 506 | } 507 | } 508 | else if (entry.Value.meshFilter.GetComponent().lightmapIndex >= 0 && 509 | entry.Value.mesh.uv2.Length == 0) { 510 | for (int i = lowestFaceIndex; i <= highestFaceIndex; i++) { 511 | Vector3 uv = meshUv1[i]; 512 | writeJSON(0, uv.x + ",", false); 513 | writeJSON(0, uv.y + ((i < highestFaceIndex) ? "," : ""), false); 514 | } 515 | } 516 | } 517 | writeJSON(0, "]", false); 518 | 519 | } 520 | writeJSON(0, "],"); 521 | 522 | writeJSON(4, "\"faces\": [", false); 523 | { 524 | 525 | for (int i = 0; i < subMeshTriangles.Length; i++) { 526 | subMeshTriangles[i] = subMeshTriangles[i] - lowestFaceIndex; 527 | } 528 | 529 | int[] triangles = subMeshTriangles; 530 | for (int j = 0; j < triangles.Length; j += 3) { 531 | 532 | writeJSON(0, 8 + ",", false); 533 | 534 | writeJSON(0, triangles[j+2] + ",", false); 535 | writeJSON(0, triangles[j+1] + ",", false); 536 | writeJSON(0, triangles[j].ToString(), false); 537 | 538 | if (entry.Value.mesh.uv.Length > 0) { 539 | writeJSON(0, ",", false); 540 | writeJSON(0, triangles[j+2] + ",", false); 541 | writeJSON(0, triangles[j+1] + ",", false); 542 | writeJSON(0, triangles[j].ToString(), false); 543 | } 544 | 545 | if (entry.Value.mesh.uv2.Length > 0 || 546 | (entry.Value.meshFilter.GetComponent().lightmapIndex >= 0 && 547 | entry.Value.mesh.uv2.Length == 0)) { 548 | writeJSON(0, ",", false); 549 | writeJSON(0, triangles[j+2] + ",", false); 550 | writeJSON(0, triangles[j+1] + ",", false); 551 | writeJSON(0, triangles[j].ToString(), false); 552 | } 553 | 554 | writeJSON(0, ((j < triangles.Length - 3) ? "," : ""), false); 555 | 556 | } 557 | 558 | } 559 | writeJSON(0, "]"); 560 | } 561 | writeJSON (3, "}"); 562 | 563 | 564 | writeJSON (2, "}", false); 565 | 566 | if (count < usedSubmeshes.Count - 1) { 567 | writeJSON (0, ","); 568 | } 569 | else { 570 | writeJSON (0, ""); 571 | } 572 | 573 | count++; 574 | } 575 | } 576 | writeJSON (1, "],"); 577 | 578 | // Materials 579 | writeJSON (1, "\"materials\": ["); 580 | { 581 | int count = 0; 582 | foreach(KeyValuePair meshMaterialLink in meshMaterialLinks) { 583 | 584 | writeJSON (2, "{"); 585 | 586 | Material mat = meshMaterialLink.Value.material; 587 | 588 | writeJSON(3, "\"uuid\": \"" + meshMaterialLink.Key + "\","); 589 | writeJSON(3, "\"name\": \"" + mat.name + "\", "); 590 | 591 | writeJSON(3, "\"type\": \"" + "MeshPhongMaterial" + "\","); 592 | 593 | if (meshMaterialLink.Value.material.HasProperty("_Color")) { 594 | writeJSON(3, "\"color\": \"" + "0x" + Util.ColorToHex(meshMaterialLink.Value.material.GetColor("_Color")) + "\","); 595 | } 596 | 597 | if (meshMaterialLink.Value.material.HasProperty("_SpecColor")) { 598 | writeJSON(3, "\"specular\": \"" + "0x" + Util.ColorToHex(meshMaterialLink.Value.material.GetColor("_SpecColor")) + "\","); 599 | } 600 | if (meshMaterialLink.Value.material.HasProperty("_EmissionColor")) { 601 | writeJSON(3, "\"emissive\": \"" + "0x" + Util.ColorToHex(meshMaterialLink.Value.material.GetColor("_EmissionColor")) + "\","); 602 | } 603 | 604 | if (meshMaterialLink.Value.material.HasProperty("_Shininess")) { 605 | writeJSON(3, "\"shininess\": " + meshMaterialLink.Value.material.GetFloat("_Shininess") + ","); 606 | } 607 | 608 | if (meshMaterialLink.Value.material.HasProperty("_Color")) { 609 | writeJSON(3, "\"opacity\": " + (meshMaterialLink.Value.material.GetColor("_Color").a) + ","); 610 | } 611 | 612 | string mapId = null; 613 | string normalMapId = null; 614 | string aoMapId = null; 615 | 616 | foreach(KeyValuePair usedMaterial in usedMaterials) { 617 | if (usedMaterial.Value == mat) { 618 | if (mat.HasProperty("_MainTex") && 619 | mat.GetTexture ("_MainTex")) { 620 | mapId = usedMaterial.Key + "_MainTex"; 621 | } 622 | if (mat.HasProperty("_BumpMap") && 623 | mat.GetTexture ("_BumpMap")) { 624 | normalMapId = usedMaterial.Key + "_BumpMap"; 625 | } 626 | if (mat.HasProperty("_OcclusionMap") && 627 | mat.GetTexture ("_OcclusionMap")) { 628 | aoMapId = usedMaterial.Key + "_OcclusionMap"; 629 | } 630 | } 631 | } 632 | 633 | 634 | if (mapId != null) { 635 | writeJSON(3, "\"map\": \"" + mapId + "\","); 636 | } 637 | if (normalMapId != null) { 638 | writeJSON(3, "\"normalMap\": \"" + normalMapId + "\","); 639 | } 640 | if (aoMapId != null) { 641 | writeJSON(3, "\"aoMap\": \"" + aoMapId + "\","); 642 | } 643 | 644 | // Add lightmaps 645 | // Problem is that in Unity, lightmaps are per object while in ThreeJS 646 | // they are per material. Because of this, we have create a separate material for each object. 647 | int lightmapIndex = meshMaterialLink.Value.meshFilter.GetComponent().lightmapIndex; 648 | 649 | // Only if the object is lightmapped 650 | if (lightmapIndex >= 0) { 651 | LightmapData usedLightMap = lightmapData[lightmapIndex]; 652 | 653 | foreach(KeyValuePair usedTexture in usedLightmapTextures) { 654 | if (usedTexture.Value.texture == usedLightMap.lightmapLight) { 655 | writeJSON(3, "\"lightMap\": \"" + usedTexture.Key + "_MainTex\","); 656 | } 657 | } 658 | } 659 | 660 | bool transparent = false; 661 | if (meshMaterialLink.Value.material.HasProperty("_Mode") && 662 | meshMaterialLink.Value.material.GetFloat("_Mode") != 0) { 663 | transparent = true; 664 | } 665 | else if (meshMaterialLink.Value.material.shader.name.Contains("Transparent")) { 666 | transparent = true; 667 | } 668 | 669 | if (meshMaterialLink.Value.material.shader.name.Contains("Additive")) { 670 | transparent = true; 671 | writeJSON(3, "\"blending\": 2,"); 672 | } 673 | 674 | writeJSON(3, "\"transparent\": " + ((transparent) ? "true" : "false") + ","); 675 | 676 | bool wireframe = false; 677 | writeJSON(3, "\"wireframe\": " + ((wireframe) ? "true" : "false")); 678 | 679 | writeJSON (2, "}", false); 680 | 681 | if (count < meshMaterialLinks.Count - 1) { 682 | writeJSON (0, ","); 683 | } 684 | else { 685 | writeJSON (0, ""); 686 | } 687 | 688 | count++; 689 | 690 | } 691 | 692 | } 693 | writeJSON (1, "],"); 694 | 695 | 696 | writeJSON (1, "\"object\": {"); 697 | // Objects 698 | { 699 | writeJSON(2, "\"uuid\": \"" + System.Guid.NewGuid().ToString() + "\","); 700 | writeJSON(2, "\"type\": \"" + "Object3D" + "\","); 701 | writeJSON(2, "\"name\": \"" + "World" + "\","); 702 | 703 | writeJSON(2, "\"matrix\": [", false); 704 | { 705 | Matrix4x4 m = Matrix4x4.identity; 706 | 707 | for (int j = 0; j <= 3; j++) { 708 | for (int k = 0; k <= 3; k++) { 709 | float test = m[k,j]; 710 | test = (float)Math.Round(test, settings.decimalPlaces); 711 | writeJSON(0, test.ToString(), false); 712 | if (!(j == 3 && k == 3)) { 713 | writeJSON(0, ",", false); 714 | } 715 | } 716 | } 717 | } 718 | writeJSON(0, "],"); 719 | 720 | writeJSON(2, "\"children\": ["); 721 | 722 | if (settings.exportAmbientLight) { 723 | // Add ambient light 724 | writeJSON(3, "{"); 725 | { 726 | writeJSON(4, "\"uuid\": \"" + System.Guid.NewGuid().ToString() + "\","); 727 | writeJSON(4, "\"type\": \"" + "AmbientLight" + "\","); 728 | writeJSON(4, "\"color\": \"" + "#" + Util.ColorToHex(RenderSettings.ambientLight) + "\","); 729 | writeJSON(4, "\"name\": \"RenderSettingsAmbientLight\""); 730 | } 731 | writeJSON(3, "},", true); 732 | } 733 | 734 | int transformsThatWillBeWritten = 0; 735 | foreach (Transform transform in transforms) { 736 | 737 | if (settings.exportCameras) { 738 | Camera camera = transform.GetComponent(); 739 | if (camera != null) { 740 | writeJSON(3, "{"); 741 | 742 | { 743 | writeJSON(4, "\"uuid\": \"" + System.Guid.NewGuid().ToString() + "\","); 744 | writeJSON(4, "\"type\": \"" + "PerspectiveCamera" + "\","); 745 | writeJSON(4, "\"name\": \"" + transform.name + "\","); 746 | writeJSON(4, "\"fov\": \"" + camera.fieldOfView.ToString() + "\","); 747 | writeJSON(4, "\"aspect\": \"" + camera.aspect.ToString() + "\","); 748 | writeJSON(4, "\"near\": \"" + camera.nearClipPlane.ToString() + "\","); 749 | writeJSON(4, "\"far\": \"" + camera.farClipPlane.ToString() + "\", "); 750 | 751 | writeMatrix(4, transform, settings); 752 | } 753 | 754 | writeJSON(3, "},", true); 755 | 756 | transformsThatWillBeWritten++; 757 | } 758 | } 759 | 760 | if (settings.exportLights) { 761 | Light light = transform.GetComponent(); 762 | if (light != null) { 763 | if (light.type == LightType.Directional || light.type == LightType.Point || light.type == LightType.Spot) { 764 | 765 | SerializedObject serialObj = new SerializedObject(light); 766 | SerializedProperty lightmapProp = serialObj.FindProperty("m_Lightmapping"); 767 | 768 | // Debug.Log ("m_Lightmapping"); 769 | // Debug.Log (lightmapProp.intValue); 770 | 771 | // Only add the lights if they are set to something else than Baked Only 772 | if (lightmapProp.intValue != 2) { 773 | writeJSON(3, "{"); 774 | 775 | { 776 | writeJSON(4, "\"uuid\": \"" + System.Guid.NewGuid().ToString() + "\","); 777 | 778 | if (light.type == LightType.Directional) { 779 | writeJSON(4, "\"type\": \"" + "DirectionalLight" + "\","); 780 | } 781 | 782 | if (light.type == LightType.Point) { 783 | writeJSON(4, "\"type\": \"" + "PointLight" + "\","); 784 | } 785 | 786 | if (light.type == LightType.Spot) { 787 | writeJSON(4, "\"type\": \"" + "SpotLight" + "\","); 788 | writeJSON(4, "\"distance\": " + light.range + ","); 789 | writeJSON(4, "\"angle\": " + light.spotAngle + ","); 790 | } 791 | 792 | 793 | // THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay ); 794 | 795 | 796 | writeJSON(4, "\"color\": \"" + "#" + Util.ColorToHex(light.color) + "\","); 797 | 798 | writeJSON(4, "\"name\": \"" + transform.name + "\","); 799 | writeJSON(4, "\"intensity\": " + light.intensity.ToString() + ","); 800 | 801 | writeMatrix(4, transform, settings); 802 | } 803 | 804 | writeJSON(3, "},", true); 805 | 806 | transformsThatWillBeWritten++; 807 | } 808 | } 809 | } 810 | } 811 | 812 | 813 | writeJSON(3, "{"); 814 | 815 | writeJSON(4, "\"uuid\": \"" + System.Guid.NewGuid().ToString() + "\","); 816 | writeJSON(4, "\"type\": \"" + "Object3D" + "\","); 817 | writeJSON(4, "\"name\": \"" + transform.name + "\","); 818 | 819 | 820 | if ((settings.exportColliders && transform.GetComponent() != null) || 821 | (transform.GetComponent() != null) || 822 | (settings.exportScripts && transform.GetComponent() != null)) { 823 | 824 | writeJSON(4, "\"children\": ["); 825 | 826 | bool hasWrittenChildren = false; 827 | 828 | if (settings.exportColliders) { 829 | Collider collider = transform.GetComponent(); 830 | if (collider != null) { 831 | hasWrittenChildren = true; 832 | writeJSON(5, "{"); 833 | 834 | { 835 | writeJSON(6, "\"uuid\": \"" + System.Guid.NewGuid().ToString() + "\","); 836 | writeJSON(6, "\"name\": \"" + transform.name + "_Collider\","); 837 | writeJSON(6, "\"type\": \"" + "Object3D" + "\","); 838 | 839 | writeJSON(6, "\"userData\": {"); 840 | 841 | 842 | BoxCollider boxCollider = transform.GetComponent(); 843 | if (boxCollider != null) { 844 | writeJSON(7, "\"type\": \"" + "BoxCollider" + "\","); 845 | } 846 | 847 | SphereCollider sphereCollider = transform.GetComponent(); 848 | if (sphereCollider != null) { 849 | writeJSON(7, "\"type\": \"" + "SphereCollider" + "\","); 850 | } 851 | 852 | MeshCollider meshCollider = transform.GetComponent(); 853 | if (meshCollider != null) { 854 | writeJSON(7, "\"type\": \"" + "MeshCollider" + "\""); 855 | // NO COMMA! 856 | } 857 | 858 | CapsuleCollider capsuleCollider = transform.GetComponent(); 859 | if (capsuleCollider != null) { 860 | writeJSON(7, "\"type\": \"" + "CapsuleCollider" + "\","); 861 | } 862 | 863 | 864 | { 865 | if (boxCollider != null) { 866 | 867 | Vector3 adjustedCenter = boxCollider.center; 868 | 869 | //adjustedCenter.x *= -1; 870 | 871 | string[] center = {adjustedCenter.z.ToString(), 872 | adjustedCenter.y.ToString(), 873 | adjustedCenter.x.ToString()}; 874 | 875 | writeJSON(7, "\"center\": " + Util.ToJSONArray(center) + ","); 876 | 877 | string[] size = {boxCollider.size.z.ToString(), 878 | boxCollider.size.y.ToString(), 879 | boxCollider.size.x.ToString()}; 880 | 881 | writeJSON(7, "\"size\": " + Util.ToJSONArray(size) + ""); 882 | } 883 | } 884 | { 885 | if (sphereCollider != null) { 886 | 887 | Vector3 adjustedCenter = sphereCollider.center; 888 | 889 | //adjustedCenter.x *= -1; 890 | 891 | string[] center = {adjustedCenter.z.ToString(), 892 | adjustedCenter.y.ToString(), 893 | adjustedCenter.x.ToString()}; 894 | 895 | 896 | writeJSON(7, "\"center\": " + Util.ToJSONArray(center) + ","); 897 | writeJSON(7, "\"radius\": " + sphereCollider.radius.ToString() + ""); 898 | } 899 | } 900 | { 901 | if (capsuleCollider != null) { 902 | 903 | Vector3 adjustedCenter = capsuleCollider.center; 904 | 905 | //adjustedCenter.x *= -1; 906 | 907 | string[] center = {adjustedCenter.z.ToString(), 908 | adjustedCenter.y.ToString(), 909 | adjustedCenter.x.ToString()}; 910 | 911 | writeJSON(7, "\"center\": " + Util.ToJSONArray(center) + ","); 912 | writeJSON(7, "\"radius\": " + capsuleCollider.radius.ToString() + ","); 913 | writeJSON(7, "\"height\": " + capsuleCollider.height.ToString() + ""); 914 | } 915 | } 916 | writeJSON(6, "}"); 917 | } 918 | 919 | writeJSON(5, "},", true); 920 | 921 | } 922 | } 923 | 924 | MeshFilter meshFilter = transform.GetComponent(); 925 | if (meshFilter != null && meshFilter.sharedMesh != null) { 926 | 927 | for (int i = 0; i < meshFilter.sharedMesh.subMeshCount; i++) { 928 | 929 | string geoKey = null; 930 | foreach(KeyValuePair usedSubmesh in usedSubmeshes) { 931 | if (usedSubmesh.Value.meshFilter == meshFilter && usedSubmesh.Value.subMeshIndex == i) { 932 | geoKey = usedSubmesh.Key; 933 | } 934 | } 935 | 936 | string matKey = null; 937 | foreach(KeyValuePair meshMaterialLink in meshMaterialLinks) { 938 | if (meshMaterialLink.Value.meshFilter == meshFilter && i == meshMaterialLink.Value.subMeshIndex) { 939 | matKey = meshMaterialLink.Key; 940 | } 941 | } 942 | 943 | if (geoKey != null && matKey != null ) { 944 | hasWrittenChildren = true; 945 | writeJSON(5, "{"); 946 | { 947 | writeJSON(6, "\"uuid\": \"" + System.Guid.NewGuid().ToString() + "\","); 948 | writeJSON(6, "\"type\": \"" + "Mesh" + "\","); 949 | writeJSON(6, "\"name\": \"" + meshFilter.name + "_SubMesh " + i + "\","); 950 | 951 | foreach(KeyValuePair usedSubmesh in usedSubmeshes) { 952 | if (usedSubmesh.Value.meshFilter == meshFilter && usedSubmesh.Value.subMeshIndex == i) { 953 | writeJSON(6, "\"geometry\": \"" + geoKey + "\","); 954 | 955 | 956 | 957 | foreach(KeyValuePair meshMaterialLink in meshMaterialLinks) { 958 | if (meshMaterialLink.Value.meshFilter == meshFilter && i == meshMaterialLink.Value.subMeshIndex) { 959 | writeJSON(6, "\"material\": \"" + matKey + "\","); 960 | break; 961 | } 962 | } 963 | 964 | break; 965 | } 966 | } 967 | 968 | 969 | writeJSON(6, "\"userdata\": {}"); 970 | 971 | 972 | 973 | } 974 | writeJSON(5, "},", true); 975 | } 976 | 977 | } 978 | } 979 | 980 | MonoBehaviour monoBehaviour = transform.GetComponent(); 981 | if (monoBehaviour != null) { 982 | hasWrittenChildren = true; 983 | 984 | writeJSON(5, "{"); 985 | { 986 | writeJSON(6, "\"uuid\": \"" + System.Guid.NewGuid().ToString() + "\","); 987 | writeJSON(6, "\"type\": \"" + "Object3D" + "\","); 988 | writeJSON(6, "\"name\": \"" + monoBehaviour.name + "_Script\","); 989 | 990 | writeJSON(6, "\"userData\": {"); 991 | 992 | { 993 | const BindingFlags flags = /*BindingFlags.NonPublic | */BindingFlags.Public | 994 | BindingFlags.Instance | BindingFlags.Static; 995 | FieldInfo[] fields = monoBehaviour.GetType().GetFields(flags); 996 | foreach (FieldInfo fieldInfo in fields) 997 | { 998 | object fieldObj = fieldInfo.GetValue(monoBehaviour); 999 | if (fieldObj != null) { 1000 | string fieldValue = fieldObj.ToString(); 1001 | // if (fieldInfo.FieldType == typeof(float) || 1002 | // fieldInfo.FieldType == typeof(int) || 1003 | // fieldInfo.FieldType == typeof(double)) { 1004 | double n; 1005 | bool needQuotes = !double.TryParse(fieldValue, out n); 1006 | 1007 | if (fieldValue == "True" || fieldValue == "False") { 1008 | fieldValue = fieldValue.ToLower(); 1009 | needQuotes = false; 1010 | } 1011 | 1012 | if (!needQuotes) { 1013 | writeJSON(7, "\"" + fieldInfo.Name + "\": " + fieldValue + ","); 1014 | } 1015 | else { 1016 | writeJSON(7, "\"" + fieldInfo.Name + "\": \"" + fieldValue + "\","); 1017 | } 1018 | } 1019 | } 1020 | 1021 | writeJSON(7, "\"type\": \"" + "Script" + "\""); 1022 | 1023 | } 1024 | writeJSON(6, "}"); 1025 | } 1026 | writeJSON(5, "},", true); 1027 | 1028 | 1029 | } 1030 | 1031 | if (hasWrittenChildren) { 1032 | jsonFile.Length = jsonFile.Length - (1 + System.Environment.NewLine.Length); 1033 | writeJSON (0, "", true); 1034 | } 1035 | 1036 | writeJSON(4, "],"); 1037 | 1038 | }//s 1039 | 1040 | writeMatrix(4, transform, settings); 1041 | 1042 | writeJSON(3, "},", true); 1043 | transformsThatWillBeWritten++; 1044 | 1045 | } 1046 | 1047 | 1048 | 1049 | // Remove the final comma 1050 | if (transformsThatWillBeWritten > 0) { 1051 | jsonFile.Length = jsonFile.Length - (1 + System.Environment.NewLine.Length); 1052 | writeJSON(0, "", true); 1053 | } 1054 | 1055 | writeJSON (2, "]"); 1056 | } 1057 | writeJSON (1, "},"); 1058 | 1059 | // Images 1060 | writeJSON (1, "\"images\": ["); 1061 | { 1062 | int count = 0; 1063 | foreach(KeyValuePair entry in usedImages) { 1064 | writeJSON (2, "{"); 1065 | 1066 | writeJSON(3, "\"url\": \"" + entry.Value.filename + "\","); 1067 | writeJSON(3, "\"uuid\": \"" + entry.Key + "\","); 1068 | writeJSON(3, "\"name\": \"" + entry.Value.filename + "\","); 1069 | writeJSON(3, "\"originalUrl\": \"" + entry.Value.filename + "\""); 1070 | 1071 | writeJSON (2, "}", false); 1072 | 1073 | if (count < usedImages.Count - 1) { 1074 | writeJSON (0, ","); 1075 | } 1076 | else { 1077 | writeJSON (0, ""); 1078 | } 1079 | 1080 | count++; 1081 | } 1082 | } 1083 | writeJSON (1, "],"); 1084 | 1085 | // Textures 1086 | writeJSON (1, "\"textures\": ["); 1087 | { 1088 | int count = 0; 1089 | 1090 | 1091 | foreach(KeyValuePair usedTexture in usedLightmapTextures) { 1092 | // Quickly mix in the remaining lightmap textures 1093 | usedMaterials.Add(usedTexture.Key, usedTexture.Value.material); 1094 | } 1095 | 1096 | 1097 | foreach(KeyValuePair usedMaterial in usedMaterials) { 1098 | 1099 | commonTextures.ForEach (delegate(String texName) { 1100 | if (usedMaterial.Value.name != "lightmap") { 1101 | if (!usedMaterial.Value.HasProperty (texName) || !usedMaterial.Value.GetTexture (texName)) { 1102 | return; 1103 | } 1104 | } 1105 | else { 1106 | if (texName != "_MainTex") { 1107 | return; 1108 | } 1109 | } 1110 | 1111 | Texture2D tex = (Texture2D)usedMaterial.Value.GetTexture (texName); 1112 | 1113 | writeJSON (2, "{"); 1114 | 1115 | writeJSON (3, "\"uuid\": \"" + usedMaterial.Key + texName + "\","); 1116 | // writeJSON (3, "\"name\": \"_" + usedMaterial.Value.name + "\","); 1117 | writeJSON (3, "\"offset\": [", false); 1118 | { 1119 | Vector2 vecOffset = usedMaterial.Value.GetTextureOffset (texName); 1120 | 1121 | writeJSON (0, vecOffset.x + ", ", false); 1122 | writeJSON (0, vecOffset.y.ToString (), false); 1123 | } 1124 | writeJSON (0, "],"); 1125 | 1126 | writeJSON (3, "\"repeat\": [", false); 1127 | { 1128 | Vector2 vecScale = usedMaterial.Value.GetTextureScale ("_MainTex"); 1129 | writeJSON (0, vecScale.x + ", ", false); 1130 | writeJSON (0, vecScale.y.ToString (), false); 1131 | } 1132 | writeJSON (0, "],"); 1133 | 1134 | if (tex.filterMode == FilterMode.Point) { 1135 | if (settings.threeRevision == 71) { 1136 | writeJSON (3, "\"magFilter\": \"" + "NearestFilter" + "\","); 1137 | writeJSON (3, "\"minFilter\": \"" + "NearestMipMapNearestFilter" + "\","); 1138 | } else { 1139 | writeJSON (3, "\"magFilter\": " + "1003" + ","); 1140 | writeJSON (3, "\"minFilter\": " + "1004" + ","); 1141 | } 1142 | } else { 1143 | if (settings.threeRevision == 71) { 1144 | writeJSON (3, "\"magFilter\": \"" + "LinearFilter" + "\","); 1145 | writeJSON (3, "\"minFilter\": \"" + "LinearMipMapLinearFilter" + "\","); 1146 | } else { 1147 | writeJSON (3, "\"magFilter\": " + "1006" + ","); 1148 | writeJSON (3, "\"minFilter\": " + "1008" + ","); 1149 | } 1150 | } 1151 | 1152 | writeJSON (3, "\"wrap\": [", false); 1153 | { 1154 | if (tex.wrapMode == TextureWrapMode.Repeat) { 1155 | if (settings.threeRevision == 71) { 1156 | writeJSON (0, "\"RepeatWrapping\",", false); 1157 | writeJSON (0, "\"RepeatWrapping\"", false); 1158 | } else { 1159 | writeJSON (0, "1000,", false); 1160 | writeJSON (0, "1000", false); 1161 | } 1162 | } else { 1163 | if (settings.threeRevision == 71) { 1164 | writeJSON (0, "\"ClampToEdgeWrapping\",", false); 1165 | writeJSON (0, "\"ClampToEdgeWrapping\"", false); 1166 | } else { 1167 | writeJSON (0, "1001,", false); 1168 | writeJSON (0, "1001", false); 1169 | } 1170 | } 1171 | } 1172 | writeJSON (0, "],"); 1173 | 1174 | foreach (KeyValuePair usedImage in usedImages) { 1175 | if (usedImage.Value.texture == tex) { 1176 | writeJSON (3, "\"image\": \"" + usedImage.Key + "\","); 1177 | writeJSON (3, "\"name\": \"" + usedImage.Value.filename + "\","); 1178 | } 1179 | } 1180 | 1181 | writeJSON (3, "\"anisotropy\": " + 16 + ""); 1182 | 1183 | writeJSON (2, "}", false); 1184 | 1185 | writeJSON (0, ","); 1186 | 1187 | count++; 1188 | 1189 | }); 1190 | 1191 | } 1192 | 1193 | if (count > 0) { 1194 | // Remove final comma 1195 | jsonFile.Length = jsonFile.Length - (1 + System.Environment.NewLine.Length); 1196 | writeJSON(0, ""); 1197 | } 1198 | } 1199 | writeJSON (1, "]"); 1200 | 1201 | writeJSON (0, "}", false); 1202 | 1203 | // Free created Materials 1204 | foreach (KeyValuePair usedTexture in usedLightmapTextures) { 1205 | if (usedTexture.Value.needsCleanup) { 1206 | GameObject.DestroyImmediate(usedTexture.Value.material); 1207 | } 1208 | } 1209 | 1210 | return jsonFile.ToString(); 1211 | } 1212 | 1213 | 1214 | } 1215 | } --------------------------------------------------------------------------------