diff --git a/Assets/Editor/EditorConfig.cs b/Assets/Editor/EditorConfig.cs index b3b62f00..c9b6e6b3 100644 --- a/Assets/Editor/EditorConfig.cs +++ b/Assets/Editor/EditorConfig.cs @@ -13,6 +13,10 @@ public class EditorConfig public static Color DownloadButtonDisabledColor = new Color(0.45f, 0.45f, 0.45f); + public static Color ExportButtonEnabledColor = new Color(0.30f, 0.35f, 0.85f); + + public static Color ExportButtonDisabledColor = new Color(0.45f, 0.45f, 0.45f); + public static Color RemoveButtonColor = new Color(0.5f, 0.5f, 0.5f); public static GUILayoutOption SmallButtonWidth = GUILayout.Width(50.0f); diff --git a/Assets/Editor/MapzenMapEditor.cs b/Assets/Editor/MapzenMapEditor.cs index 55fedeb2..63edb68d 100644 --- a/Assets/Editor/MapzenMapEditor.cs +++ b/Assets/Editor/MapzenMapEditor.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Collections.Generic; using Mapzen; +using Mapzen.Unity; using UnityEditor; [CustomEditor(typeof(MapzenMap))] @@ -11,6 +12,7 @@ public class MapzenMapEditor : Editor private bool showTileDataFoldout = false; private bool showRegionScaleRatioFoldout = false; + void OnEnable() { this.mapzenMap = (MapzenMap)target; @@ -48,6 +50,19 @@ public override void OnInspectorGUI() EditorConfig.ResetColor(); + valid = mapzenMap.MeshFilters.Count > 0; + + EditorConfig.SetColor(valid ? + EditorConfig.ExportButtonEnabledColor : + EditorConfig.ExportButtonDisabledColor); + + if (GUILayout.Button("Export to OBJ")) + { + ModelExporter.OBJ(mapzenMap.MeshFilters, mapzenMap.RegionName); + } + + EditorConfig.ResetColor(); + SavePreferences(); } diff --git a/Assets/Editor/ModelExporter.cs b/Assets/Editor/ModelExporter.cs new file mode 100644 index 00000000..5b4e923f --- /dev/null +++ b/Assets/Editor/ModelExporter.cs @@ -0,0 +1,151 @@ +using System; +using System.Text; +using System.IO; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +public class ModelExporter +{ + private struct MaterialDesc + { + public string mainTexture; + public Color color; + } + + public static void OBJ(List meshes, string filename) + { + int vertexOffset = 0; + int normalOffset = 0; + int uvOffset = 0; + + StringBuilder builder = new StringBuilder(); + + builder.Append("# Exported with Tangram Unity\n"); + builder.Append("# Visit https://github.com/tangrams/tangram-unity/ for more details\n\n"); + builder.Append("mtllib " + filename + ".mtl\n"); + + foreach (var meshFilter in meshes) + { + Mesh mesh = meshFilter.sharedMesh; + + Material[] materials = meshFilter.GetComponent().sharedMaterials; + + builder.Append("g ").Append(meshFilter.name).Append("\n"); + + foreach (Vector3 lv in mesh.vertices) + { + Vector3 wv = meshFilter.transform.TransformPoint(lv); + builder.Append(string.Format("v {0} {1} {2}\n", -wv.x, wv.y, wv.z)); + } + + builder.Append("\n"); + + foreach (Vector3 ln in mesh.normals) + { + Vector3 wn = meshFilter.transform.TransformDirection(ln); + builder.Append(string.Format("vn {0} {1} {2}\n", -wn.x, wn.y, wn.z)); + } + + builder.Append("\n"); + + foreach (Vector2 uv in mesh.uv) + { + builder.Append(string.Format("vt {0} {1}\n", uv.x, uv.y)); + } + + for (int i = 0; i < mesh.subMeshCount; ++i) + { + var material = materials[i]; + + builder.Append("\n"); + builder.Append("usemtl ").Append(material.name).Append("\n"); + builder.Append("usemap ").Append(material.name).Append("\n"); + + int[] triangles = mesh.GetTriangles(i); + for (int j = 0; j < triangles.Length; j += 3) + { + if (mesh.uv.Length > 0) + { + builder.Append(string.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n", + triangles[j + 0] + 1 + vertexOffset, + triangles[j + 1] + 1 + vertexOffset, + triangles[j + 2] + 1 + vertexOffset)); + } + else + { + builder.Append(string.Format("f {1}//{1} {0}//{0} {2}//{2}\n", + triangles[j + 0] + 1 + vertexOffset, + triangles[j + 1] + 1 + vertexOffset, + triangles[j + 2] + 1 + vertexOffset)); + } + } + } + + vertexOffset += mesh.vertices.Length; + } + + var materialAssetPerName = new Dictionary(); + foreach (var meshFilter in meshes) + { + Mesh mesh = meshFilter.sharedMesh; + Material[] materials = meshFilter.GetComponent().sharedMaterials; + + for (int i = 0; i < mesh.subMeshCount; ++i) + { + var material = materials[i]; + string mainTexture = null; + if (material.mainTexture != null) + { + mainTexture = AssetDatabase.GetAssetPath(material.mainTexture); + } + if (!materialAssetPerName.ContainsKey(material.name)) + { + MaterialDesc materialDesc = new MaterialDesc(); + materialDesc.mainTexture = mainTexture; + materialDesc.color = material.color; + materialAssetPerName.Add(material.name, materialDesc); + } + } + } + + using (StreamWriter sw = new StreamWriter(filename + ".mtl")) + { + foreach (var materialAssetPair in materialAssetPerName) + { + MaterialDesc materialDesc = materialAssetPair.Value; + + sw.Write("\n"); + sw.Write("newmtl {0}\n", materialAssetPair.Key); + sw.Write("Ka 1.0 1.0 1.0\n"); + sw.Write(string.Format("Kd {0} {1} {2}\n", + materialDesc.color.r, + materialDesc.color.g, + materialDesc.color.b)); + sw.Write("Ks 0.0 0.0 0.0\n"); + sw.Write("d 1.0\n"); + sw.Write("Ns 0.0\n"); + sw.Write("illum 2\n"); + + if (materialDesc.mainTexture != null) + { + string dest = materialDesc.mainTexture; + int stripIndex = dest.LastIndexOf("/"); + if (stripIndex >= 0) + { + dest = dest.Substring(stripIndex + 1).Trim(); + } + File.Copy(materialDesc.mainTexture, dest); + sw.Write("map_Kd {0}\n", dest); + } + } + } + + using (StreamWriter sw = new StreamWriter(filename + ".obj")) + { + sw.Write(builder.ToString()); + } + + Debug.Log("Model exported as " + filename + ".obj"); + } +} diff --git a/Assets/Editor/ModelExporter.cs.meta b/Assets/Editor/ModelExporter.cs.meta new file mode 100644 index 00000000..4610c6d6 --- /dev/null +++ b/Assets/Editor/ModelExporter.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ed5027076d35a4998964e0f6acf76c9b +timeCreated: 1506107714 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mapzen/Unity/SceneGraph.cs b/Assets/Mapzen/Unity/SceneGraph.cs index 529a91d0..0e618849 100644 --- a/Assets/Mapzen/Unity/SceneGraph.cs +++ b/Assets/Mapzen/Unity/SceneGraph.cs @@ -17,11 +17,13 @@ public class SceneGraph /// /// The scene group to visit. /// The parent transform of the generated game object for the current scene group. - public static void Generate(SceneGroup group, Transform parent, GameObjectOptions options) + public static List Generate(SceneGroup group, Transform parent, GameObjectOptions options) { + List meshFilters = new List(); + if (group.meshData.Meshes.Count == 0 && group.childs.Count == 0) { - return; + return meshFilters; } if (group.childs.Count > 0) @@ -36,7 +38,7 @@ public static void Generate(SceneGroup group, Transform parent, GameObjectOption foreach (var child in group.childs) { - Generate(child.Value, gameObject.transform, options); + meshFilters.AddRange(Generate(child.Value, gameObject.transform, options)); } } else @@ -107,8 +109,12 @@ public static void Generate(SceneGroup group, Transform parent, GameObjectOption meshColliderComponent.material = options.PhysicMaterial; meshColliderComponent.sharedMesh = mesh; } + + meshFilters.Add(meshFilterComponent); } } + + return meshFilters; } } } diff --git a/Assets/MapzenMap.cs b/Assets/MapzenMap.cs index c62b8ec7..3ee7b855 100644 --- a/Assets/MapzenMap.cs +++ b/Assets/MapzenMap.cs @@ -1,12 +1,8 @@ using System.Collections; using System; using System.Collections.Generic; -using System.Runtime.InteropServices; using UnityEngine; -using UnityEngine.Networking; -using Mapzen.VectorData; using Mapzen.Unity; -using Mapzen.VectorData.Filters; using Mapzen; public class MapzenMap : MonoBehaviour @@ -24,7 +20,7 @@ public class MapzenMap : MonoBehaviour public string RegionName = ""; - private List tiles = new List(); + private List meshFilters = new List(); private IO tileIO = new IO(); @@ -123,13 +119,13 @@ void OnTaskReady(TileTask readyTask) { tasks.Clear(); - SceneGraph.Generate(regionMap, null, gameObjectOptions); + meshFilters = SceneGraph.Generate(regionMap, null, gameObjectOptions); } } - public List Tiles + public List MeshFilters { - get { return tiles; } + get { return meshFilters; } } public List FeatureStyling