using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using Mapbox.Unity.MeshGeneration.Data; using System.IO; namespace Mapbox.Editor { class AtlasTemplateGenerator : EditorWindow { [MenuItem("Mapbox/Atlas Template Generator")] public static void ShowWindow() { EditorWindow.GetWindow(typeof(AtlasTemplateGenerator)); } public class PixelRect { public int x; public int y; public int xx; public int yy; } public string m_saveFileName = "AtlasTemplate"; public Texture2D m_texture; public AtlasInfo m_atlasInfo; public Color[] m_colors; public int m_textureResolution = 2048; public bool m_generateFacadesTemplate = true; public bool m_generateRoofsTemplate = false; private int _drawCount; private const int _DEFAULT_TEX_SIZE = 2048; private const int _MIN_TEX_SIZE = 512; private const int _MAX_TEX_SIZE = 2048; private const float _cellRatioMargin = 0.01f; private void Awake() { CreateTexture(); } private void CreateTexture() { m_texture = new Texture2D(m_textureResolution, m_textureResolution, TextureFormat.ARGB32, false); m_texture.filterMode = FilterMode.Point; } void OnGUI() { GUILayout.Space(20); GUIStyle titleStyle = new GUIStyle(EditorStyles.label); titleStyle.fontSize = 32; titleStyle.normal.textColor = Color.white; titleStyle.fontStyle = FontStyle.Bold; titleStyle.stretchWidth = true; titleStyle.stretchHeight = true; titleStyle.alignment = TextAnchor.MiddleCenter; GUILayout.BeginVertical(); EditorGUILayout.LabelField("Mapbox Atlas Template Generator", titleStyle, GUILayout.Height(100)); GUILayout.BeginHorizontal(); GUILayout.BeginVertical(GUILayout.MinWidth(200), GUILayout.MaxWidth(300)); EditorGUI.BeginChangeCheck(); EditorGUI.indentLevel++; m_atlasInfo = EditorGUILayout.ObjectField("Atlas info:", m_atlasInfo, typeof(AtlasInfo), true) as Mapbox.Unity.MeshGeneration.Data.AtlasInfo; EditorGUILayout.Space(); m_generateFacadesTemplate = EditorGUILayout.Toggle("Create Facades", m_generateFacadesTemplate); m_generateRoofsTemplate = EditorGUILayout.Toggle("Create Roofs", m_generateRoofsTemplate); if (EditorGUI.EndChangeCheck()) { if (m_atlasInfo != null) { int facadeCount = m_generateFacadesTemplate ? m_atlasInfo.Textures.Count : 0; int roofCount = m_generateRoofsTemplate ? m_atlasInfo.Roofs.Count : 0; int textureCount = facadeCount + roofCount; m_colors = new Color[textureCount]; float hueIncrement = (float)1.0f / textureCount; float hue = 0.0f; for (int i = 0; i < textureCount; i++) { m_colors[i] = Color.HSVToRGB(hue, 1.0f, 1.0f); hue += hueIncrement; } } else { m_colors = new Color[0]; CreateTexture(); } } EditorGUI.BeginChangeCheck(); m_textureResolution = Mathf.Clamp(EditorGUILayout.IntField("Texture resolution:", m_textureResolution), _MIN_TEX_SIZE, _MAX_TEX_SIZE); if (EditorGUI.EndChangeCheck()) { CreateTexture(); } EditorGUILayout.Space(); if (m_colors != null) { for (int i = 0; i < m_colors.Length; i++) { string colorFieldName = string.Format("Color {0}", i); m_colors[i] = EditorGUILayout.ColorField(colorFieldName, m_colors[i]); } } if (GUILayout.Button("Generate Template")) { if (m_atlasInfo == null) { EditorUtility.DisplayDialog("Atlas Template Generator", "Error: No AtlasInfo object selected.", "Ok"); return; } if (!m_generateFacadesTemplate && !m_generateRoofsTemplate) { EditorUtility.DisplayDialog("Atlas Template Generator", "Error: Template generation requires Create Facades and/or Create Roofs to be enabled.", "Ok"); return; } GenerateTemplate(); } EditorGUILayout.Space(); if (GUILayout.Button("Save to file")) { SaveTextureAsPNG(); } EditorGUI.indentLevel--; GUILayout.EndVertical(); GUIStyle boxStyle = new GUIStyle(); boxStyle.alignment = TextAnchor.UpperLeft; GUILayout.Box(m_texture, boxStyle, GUILayout.Width(300), GUILayout.Height(300), GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true)); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.Space(20); } private int GetPixelCoorFromAtlasRatio(float ratio) { return (int)(m_textureResolution * ratio); } private PixelRect ConvertUVRectToPixelRect(Rect atlasRect) { PixelRect pixelRect = new PixelRect(); pixelRect.x = GetPixelCoorFromAtlasRatio(atlasRect.x); pixelRect.y = GetPixelCoorFromAtlasRatio(atlasRect.y); pixelRect.xx = GetPixelCoorFromAtlasRatio(atlasRect.x + atlasRect.width); pixelRect.yy = GetPixelCoorFromAtlasRatio(atlasRect.y + atlasRect.height); return pixelRect; } private void DrawRect(PixelRect pr, Color color) { for (int i = pr.x; i < pr.xx; i++) { for (int j = pr.y; j < pr.yy; j++) { m_texture.SetPixel(i, j, color); } } } private void DrawWatermark(int x, int y) { m_texture.SetPixel(x, y, Color.black); m_texture.SetPixel(x + 3, y, Color.black); m_texture.SetPixel(x, y + 3, Color.black); m_texture.SetPixel(x + 3, y + 3, Color.black); m_texture.SetPixel(x + 1, y + 1, Color.black); m_texture.SetPixel(x + 2, y + 1, Color.black); m_texture.SetPixel(x + 1, y + 2, Color.black); m_texture.SetPixel(x + 2, y + 2, Color.black); } private void DrawCornerWatermarks(PixelRect pr) { DrawWatermark(pr.x, pr.y); DrawWatermark(pr.xx - 4, pr.y); DrawWatermark(pr.x, pr.yy - 4); DrawWatermark(pr.xx - 4, pr.yy - 4); } private void DrawDebugCross(PixelRect pr) { int centerX = (pr.x + pr.xx) / 2; int centerY = (pr.y + pr.yy) / 2; m_texture.SetPixel(centerX, centerY, Color.black); for (int x = pr.x; x < pr.xx; x++) { m_texture.SetPixel(x, centerY, Color.black); m_texture.SetPixel(x, centerY - 1, Color.black); m_texture.SetPixel(x, centerY + 1, Color.black); } for (int y = pr.y; y < pr.yy; y++) { m_texture.SetPixel(centerX, y, Color.black); m_texture.SetPixel(centerX - 1, y, Color.black); m_texture.SetPixel(centerX + 1, y, Color.black); } } private void DrawAtlasEntityData(List aeList) { for (int i = 0; i < aeList.Count; i++) { AtlasEntity ae = aeList[i]; Rect baseRect = ae.TextureRect; float topRatio = ae.TopSectionRatio * baseRect.height; float bottomRatio = ae.BottomSectionRatio * baseRect.height; float middleRatio = baseRect.height - (topRatio + bottomRatio); Rect groundFloorRect = new Rect(baseRect.x, baseRect.y, baseRect.width, bottomRatio); Rect topFloorRect = new Rect(baseRect.x, baseRect.y + baseRect.height - topRatio, baseRect.width, topRatio); PixelRect basePixelRect = ConvertUVRectToPixelRect(baseRect); PixelRect groundFloorPixelRect = ConvertUVRectToPixelRect(groundFloorRect); PixelRect topFloorPixelRect = ConvertUVRectToPixelRect(topFloorRect); Color color = m_colors[_drawCount]; Color colorLight = (color + Color.white) / 2; Color colorDark = (color + Color.black) / 2; DrawRect(basePixelRect, color); DrawRect(groundFloorPixelRect, colorLight); DrawRect(topFloorPixelRect, colorDark); DrawDebugCross(groundFloorPixelRect); DrawDebugCross(topFloorPixelRect); int numColumns = (int)ae.ColumnCount; int numMidFloors = ae.MidFloorCount; float colWidth = baseRect.width / numColumns; float floorHeight = middleRatio / numMidFloors; float midFloorBase = baseRect.y + bottomRatio; float mrgn = _cellRatioMargin; float halfMrgn = mrgn / 2; for (int j = 0; j < numMidFloors; j++) { float floorStart = midFloorBase + (floorHeight * j); for (int k = 0; k < numColumns; k++) { float columnStart = baseRect.x + (colWidth * k); Rect cellRect = new Rect(columnStart + halfMrgn, floorStart + halfMrgn, colWidth - mrgn, floorHeight - mrgn); PixelRect cellPixelRect = ConvertUVRectToPixelRect(cellRect); DrawRect(cellPixelRect, Color.white); DrawDebugCross(cellPixelRect); } } DrawCornerWatermarks(groundFloorPixelRect); DrawCornerWatermarks(topFloorPixelRect); _drawCount++; } } public void GenerateTemplate() { _drawCount = 0; if (m_generateFacadesTemplate) { DrawAtlasEntityData(m_atlasInfo.Textures); } if (m_generateRoofsTemplate) { DrawAtlasEntityData(m_atlasInfo.Roofs); } m_texture.Apply(); } public void SaveTextureAsPNG() { var path = EditorUtility.SaveFilePanel("Save texture as PNG", "Assets", "AtlasTemplate.png", "png"); if (path.Length == 0) { return; } byte[] pngData = m_texture.EncodeToPNG(); if (pngData != null) { File.WriteAllBytes(path, pngData); Debug.Log(pngData.Length / 1024 + "Kb was saved as: " + path); AssetDatabase.Refresh(); } } } }