using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; namespace Pinwheel.Griffin.BillboardTool { public class GBillboardEditor : GTwoPaneWindowWindow { private int previewPadding = 10; private bool alphaBlend = true; private GBillboardRenderMode mode; private GameObject target; private int row = 4; private int column = 4; private int cellSize = 256; private Vector3 cameraOffset = new Vector3(0, 0, 5); private float cameraSize = 1; private Material atlasMaterial; private Material normalMaterial; private string srcTextureProps = "_MainTex"; private string desTextureProps = "_MainTex"; private string srcColorProps = "_Color"; private string desColorProps = "_Color"; private int cellIndex = 0; private List vertices = new List(new Vector2[] { new Vector2(0.01f, 0.01f), new Vector2(0.01f, 0.99f), new Vector2(0.99f, 0.99f), new Vector2(0.99f, 0.01f) }); private int selectedVertexIndex = -1; private float vertexClickDistance = 10; private ushort[] tris = new ushort[0]; private float bottom = 0; private float width = 1; private float height = 2; private string saveFolder; private const int MAX_VERTICES = 10; private int[] rowColumnValues = new int[] { 1, 2, 4, 8, 16 }; private string[] rowColumnLabels = new string[] { "1", "2", "4", "8", "16" }; private int[] cellSizeValues = new int[] { 128, 256, 512 }; private string[] cellSizeLabels = new string[] { "128", "256", "512" }; private RenderTexture previewRt; private const string ATLAS_INSTRUCTION = "Preview billboard atlas. Image order from bottom-left to top-right."; private const string NORMAL_INSTRUCTION = "Preview billboard normal map. Image order from bottom-left to top-right"; private const string FLIPBOOK_INSTRUCTION = "Preview billboard transition and edit billboard mesh.\n" + " - Use Scrollwheel to iterate images.\n" + " - Use Left Mouse to edit vertex position.\n" + " - Use Ctrl & Left Mouse to remove vertex.\n" + " - Use Shift & Left Mouse to add vertex.\n" + "For best rendering performance, billboard mesh is limited at maximum 10 vertices."; private const string KEY_BILLBOARDEDITOR = "billboardeditor"; private const string KEY_MODE = "mode"; private const string KEY_PREFAB = "prefab"; private const string KEY_ROW = "row"; private const string KEY_COLUMN = "column"; private const string KEY_CELLSIZE = "cellsize"; private const string KEY_CAM_X = "camx"; private const string KEY_CAM_Y = "camy"; private const string KEY_CAM_Z = "camz"; private const string KEY_CAM_SIZE = "camsize"; private const string KEY_ATLAS_MAT = "atlasmat"; private const string KEY_NORMAL_MAT = "normalmat"; private const string KEY_SRC_TEX_PROPS = "srctex"; private const string KEY_DES_TEX_PROPS = "destex"; private const string KEY_SRC_COLOR_PROPS = "srccolor"; private const string KEY_DES_COLOR_PROPS = "descolor"; private const string KEY_BOTTOM = "bottom"; private const string KEY_WIDTH = "width"; private const string KEY_HEIGHT = "height"; private const string KEY_ALPHA_BLEND = "alphablend"; private const string KEY_SAVE_FOLDER = "save-folder"; private List srcColorSuggestion; private List SrcColorSuggestion { get { if (srcColorSuggestion == null) { srcColorSuggestion = new List(); } return srcColorSuggestion; } } private List srcTextureSuggestion; private List SrcTextureSuggestion { get { if (srcTextureSuggestion == null) { srcTextureSuggestion = new List(); } return srcTextureSuggestion; } } public static void ShowWindow() { GBillboardEditor window = EditorWindow.GetWindow(); window.titleContent = new GUIContent("Billboard"); window.minSize = new Vector2(400, 300); window.Show(); } private void OnEnable() { LoadPrefs(); wantsMouseMove = true; tris = GBillboardCreator.Triangulate(vertices.ToArray()); RenderPreview(); RefreshMaterialPropsSuggestions(); } private void OnDisable() { SavePrefs(); } private void SavePrefs() { EditorPrefs.SetInt(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_MODE), (int)mode); if (target != null) { string assetPath = AssetDatabase.GetAssetPath(target); if (!string.IsNullOrEmpty(assetPath)) { EditorPrefs.SetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_PREFAB), assetPath); } } EditorPrefs.SetInt(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_ROW), row); EditorPrefs.SetInt(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_COLUMN), column); EditorPrefs.SetInt(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CELLSIZE), cellSize); EditorPrefs.SetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CAM_X), cameraOffset.x); EditorPrefs.SetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CAM_Y), cameraOffset.y); EditorPrefs.SetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CAM_Z), cameraOffset.z); EditorPrefs.SetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CAM_SIZE), cameraSize); if (atlasMaterial != null) { string assetPath = AssetDatabase.GetAssetPath(atlasMaterial); if (!string.IsNullOrEmpty(assetPath)) { EditorPrefs.SetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_ATLAS_MAT), assetPath); } } if (normalMaterial != null) { string assetPath = AssetDatabase.GetAssetPath(normalMaterial); if (!string.IsNullOrEmpty(assetPath)) { EditorPrefs.SetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_NORMAL_MAT), assetPath); } } EditorPrefs.SetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_SRC_COLOR_PROPS), srcColorProps); EditorPrefs.SetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_DES_COLOR_PROPS), desColorProps); EditorPrefs.SetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_SRC_TEX_PROPS), srcTextureProps); EditorPrefs.SetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_DES_TEX_PROPS), desTextureProps); EditorPrefs.SetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_WIDTH), width); EditorPrefs.SetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_HEIGHT), height); EditorPrefs.SetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_BOTTOM), bottom); EditorPrefs.SetBool(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_ALPHA_BLEND), alphaBlend); EditorPrefs.SetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_SAVE_FOLDER), saveFolder); } private void LoadPrefs() { mode = (GBillboardRenderMode)EditorPrefs.GetInt(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_MODE), 0); string targetAssetPath = EditorPrefs.GetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_PREFAB), string.Empty); target = AssetDatabase.LoadAssetAtPath(targetAssetPath); row = EditorPrefs.GetInt(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_ROW), 4); column = EditorPrefs.GetInt(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_COLUMN), 4); cellSize = EditorPrefs.GetInt(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CELLSIZE), 256); float camX = EditorPrefs.GetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CAM_X), 0); float camY = EditorPrefs.GetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CAM_Y), -2); float camZ = EditorPrefs.GetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CAM_Z), 0); cameraOffset = new Vector3(camX, camY, camZ); cameraSize = EditorPrefs.GetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_CAM_SIZE), 2); atlasMaterial = GEditorSettings.Instance.billboardTools.atlasMaterial; normalMaterial = GEditorSettings.Instance.billboardTools.normalMaterial; srcColorProps = EditorPrefs.GetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_SRC_COLOR_PROPS), "_Color"); desColorProps = EditorPrefs.GetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_DES_COLOR_PROPS), "_Color"); srcTextureProps = EditorPrefs.GetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_SRC_TEX_PROPS), "_MainTex"); desTextureProps = EditorPrefs.GetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_DES_TEX_PROPS), "_MainTex"); width = EditorPrefs.GetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_WIDTH), 1); height = EditorPrefs.GetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_HEIGHT), 2); bottom = EditorPrefs.GetFloat(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_BOTTOM), 0.2f); alphaBlend = EditorPrefs.GetBool(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_ALPHA_BLEND), true); saveFolder = EditorPrefs.GetString(GEditorCommon.GetProjectRelatedEditorPrefsKey(KEY_BILLBOARDEDITOR, KEY_SAVE_FOLDER), "Assets/"); } protected override void OnToolbarGUI(Rect r) { base.OnToolbarGUI(r); List buttonLabels = new List() { "Atlas", "Normal", "Flipbook", "", "A" }; List buttonRects = EditorGUIUtility.GetFlowLayoutedRects(r, EditorStyles.toolbarButton, 0, 0, buttonLabels); for (int i = 0; i < 3; ++i) { if (GUI.Button(buttonRects[i], buttonLabels[i], EditorStyles.toolbarButton)) { mode = (GBillboardRenderMode)i; RenderPreview(); } if (i == (int)mode) { Color highlightColor = EditorGUIUtility.isProSkin ? new Color(1, 1, 1, 0.1f) : new Color(1, 1, 1, 0.3f); EditorGUI.DrawRect(buttonRects[i], highlightColor); } } if (GUI.Button(buttonRects[4], buttonLabels[4], EditorStyles.toolbarButton)) { alphaBlend = !alphaBlend; } if (alphaBlend) { Color highlightColor = EditorGUIUtility.isProSkin ? new Color(1, 1, 1, 0.1f) : new Color(1, 1, 1, 0.3f); EditorGUI.DrawRect(buttonRects[4], highlightColor); } Rect saveButtonRect = new Rect( r.max.x - 100 - 2, r.min.y, 100, r.height); if (GUI.Button(saveButtonRect, "Save", EditorStyles.toolbarButton)) { SaveAssets(); } } private GBillboardCreatorArgs ConstructArgs() { GBillboardCreatorArgs args = new GBillboardCreatorArgs(); args.Mode = mode; args.Target = target; args.Row = row; args.Column = column; args.CellSize = cellSize; args.CameraOffset = cameraOffset; args.CameraSize = cameraSize; args.AtlasMaterial = atlasMaterial; args.NormalMaterial = normalMaterial; args.SrcColorProps = srcColorProps; //args.DesColorProps = desColorProps; args.DesColorProps = "_Color"; args.SrcTextureProps = srcTextureProps; //args.DesTextureProps = desTextureProps; args.DesTextureProps = "_MainTex"; args.CellIndex = cellIndex % (args.Row * args.Column); args.Vertices = vertices.ToArray(); args.Width = width; args.Height = height; args.Bottom = bottom; return args; } private void CopyArgs(GBillboardCreatorArgs args) { mode = args.Mode; target = args.Target; row = args.Row; column = args.Column; cellSize = args.CellSize; cameraOffset = args.CameraOffset; cameraSize = args.CameraSize; atlasMaterial = args.AtlasMaterial; normalMaterial = args.NormalMaterial; srcColorProps = args.SrcColorProps; desColorProps = args.DesColorProps; srcTextureProps = args.SrcTextureProps; desTextureProps = args.DesTextureProps; cellIndex = args.CellIndex; vertices = new List(args.Vertices); width = args.Width; height = args.Height; bottom = args.Bottom; tris = GBillboardCreator.Triangulate(vertices.ToArray()); } protected override void OnLeftPaneGUI(Rect r) { base.OnLeftPaneGUI(r); Rect previewRect = new Rect(); int size = Mathf.FloorToInt(Mathf.Min(LeftPaneRect.width, LeftPaneRect.height) - 2 * previewPadding); previewRect.size = new Vector2(size, size); previewRect.center = LeftPaneRect.center; if (previewRt != null) { if (alphaBlend) EditorGUI.DrawTextureTransparent(previewRect, previewRt, ScaleMode.ScaleToFit); else EditorGUI.DrawPreviewTexture(previewRect, previewRt, null, ScaleMode.ScaleToFit); } else { EditorGUI.DrawRect(previewRect, Color.black); } if (Event.current != null && Event.current.isScrollWheel && previewRect.Contains(Event.current.mousePosition) && mode == GBillboardRenderMode.Flipbook) { cellIndex += (int)(Event.current.delta.y / 2f); if (cellIndex < 0) cellIndex = row * column - 1; if (cellIndex >= row * column) cellIndex = 0; RenderPreview(); } if (mode == GBillboardRenderMode.Flipbook) { DrawBottomLine(previewRect); DrawTriangles(previewRect); DrawVertices(previewRect); Rect pageRect = new Rect( previewRect.min.x, previewRect.max.y - 20, previewRect.width, 20); EditorGUI.DropShadowLabel(pageRect, string.Format("{0}/{1}", cellIndex + 1, row * column), GEditorCommon.CenteredWhiteLabel); } } private void DrawTriangles(Rect previewRect) { if (tris == null) return; if (tris.Length % 3 != 0) { Debug.Log("Invalid tris array!"); return; } int trisCount = tris.Length / 3; for (int i = 0; i < trisCount; ++i) { Vector2 v0 = vertices[tris[i * 3 + 0]]; Vector2 v1 = vertices[tris[i * 3 + 1]]; Vector2 v2 = vertices[tris[i * 3 + 2]]; v0 = GUtilities.FlipY(v0); v1 = GUtilities.FlipY(v1); v2 = GUtilities.FlipY(v2); Vector2 p0 = Rect.NormalizedToPoint(previewRect, v0); Vector2 p1 = Rect.NormalizedToPoint(previewRect, v1); Vector2 p2 = Rect.NormalizedToPoint(previewRect, v2); GEditorCommon.DrawLine(p0, p1, Color.green); GEditorCommon.DrawLine(p1, p2, Color.green); GEditorCommon.DrawLine(p2, p0, Color.green); } } private void DrawVertices(Rect previewRect) { if (vertices == null) { vertices = new List(); tris = new ushort[0]; } for (int i = 0; i < vertices.Count; ++i) { HandleEditingVertices(previewRect, i); } HandleAddVertex(previewRect); } private void DrawBottomLine(Rect previewRect) { float h = bottom / (bottom - height); h = 1 - h; Vector2 p0 = Rect.NormalizedToPoint(previewRect, new Vector2(0, h)); Vector2 p1 = Rect.NormalizedToPoint(previewRect, new Vector2(1, h)); GEditorCommon.DrawLine(p0, p1, Color.red); } private void HandleEditingVertices(Rect previewRect, int vertIndex) { if (vertIndex >= vertices.Count) return; if (Event.current == null) return; Event e = Event.current; Vector2 uv = vertices[vertIndex]; Vector2 point = Rect.NormalizedToPoint(previewRect, GUtilities.FlipY(uv)); if (e.type == EventType.MouseDown) { selectedVertexIndex = -1; float d = Vector2.Distance(e.mousePosition, point); if (d <= vertexClickDistance) { if (e.button == 0 && !e.control) { selectedVertexIndex = vertIndex; e.Use(); } else if (e.button == 0 && e.control) { vertices.RemoveAt(vertIndex); tris = GBillboardCreator.Triangulate(vertices.ToArray()); e.Use(); } } } else if (e.type == EventType.MouseDrag) { if (selectedVertexIndex == vertIndex) { point = e.mousePosition; uv = Rect.PointToNormalized(previewRect, point); uv.Set(Mathf.Clamp01(uv.x), Mathf.Clamp01(uv.y)); vertices[vertIndex] = GUtilities.FlipY(uv); tris = GBillboardCreator.Triangulate(vertices.ToArray()); e.Use(); } } Color handleColor = selectedVertexIndex == vertIndex ? Color.yellow : Color.green; float handleSize = 6; Rect handleRect = new Rect(); handleRect.size = Vector2.one * handleSize; handleRect.center = point; EditorGUI.DrawRect(handleRect, handleColor); } private void HandleAddVertex(Rect previewRect) { if (vertices.Count >= MAX_VERTICES) return; if (Event.current == null) return; Event e = Event.current; if (e.type == EventType.MouseDown && e.shift) { if (!previewRect.Contains(e.mousePosition)) return; Vector2 uv = Rect.PointToNormalized(previewRect, e.mousePosition); vertices.Add(GUtilities.FlipY(uv)); selectedVertexIndex = vertices.Count - 1; tris = GBillboardCreator.Triangulate(vertices.ToArray()); e.Use(); } } protected override void OnRightPaneScrollViewGUI() { base.OnRightPaneScrollViewGUI(); EditorGUI.BeginChangeCheck(); float labelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 120; DrawInstruction(); DrawTargetSettings(); DrawCameraSettings(); DrawAtlasSettings(); DrawMaterialSettings(); if (mode == GBillboardRenderMode.Flipbook) { DrawMeshSettings(); } DrawUtilities(); EditorGUIUtility.labelWidth = labelWidth; if (EditorGUI.EndChangeCheck()) { RenderPreview(); } } private void DrawInstruction() { string label = "Instruction"; string id = "billboardeditor-instruction"; GEditorCommon.Foldout(label, false, id, () => { string instruction = mode == GBillboardRenderMode.Atlas ? ATLAS_INSTRUCTION : mode == GBillboardRenderMode.Normal ? NORMAL_INSTRUCTION : mode == GBillboardRenderMode.Flipbook ? FLIPBOOK_INSTRUCTION : string.Empty; EditorGUILayout.LabelField(instruction, GEditorCommon.WordWrapItalicLabel); }); } private void DrawTargetSettings() { string label = "Target"; string id = "billboardeditor-target"; GEditorCommon.Foldout(label, false, id, () => { EditorGUI.BeginChangeCheck(); target = EditorGUILayout.ObjectField("Prefab", target, typeof(GameObject), true) as GameObject; if (EditorGUI.EndChangeCheck()) { GBillboardCreatorArgs args = GBillboardCreator.FitCameraToTarget(ConstructArgs()); CopyArgs(args); RefreshMaterialPropsSuggestions(); } }); } private void DrawCameraSettings() { string label = "Camera"; string id = "billboardeditor-camera"; GEditorCommon.Foldout(label, false, id, () => { EditorGUIUtility.wideMode = true; cameraOffset = EditorGUILayout.Vector3Field("Offset", cameraOffset); EditorGUIUtility.wideMode = false; cameraSize = EditorGUILayout.FloatField("Size", cameraSize); cameraSize = Mathf.Max(0, cameraSize); }); } private void DrawAtlasSettings() { string label = "Atlas"; string id = "billboardeditor-atlas"; GEditorCommon.Foldout(label, false, id, () => { row = EditorGUILayout.IntPopup("Row", row, rowColumnLabels, rowColumnValues); column = EditorGUILayout.IntPopup("Column", column, rowColumnLabels, rowColumnValues); cellSize = EditorGUILayout.IntPopup("Cell Size", cellSize, cellSizeLabels, cellSizeValues); EditorGUILayout.LabelField("Resolution: ", string.Format("{0} x {1}", column * cellSize, row * cellSize)); }); } private void DrawMaterialSettings() { string label = "Materials"; string id = "billboardeditor-materials"; GEditorCommon.Foldout(label, false, id, () => { GUI.enabled = false; atlasMaterial = EditorGUILayout.ObjectField("Atlas Material", atlasMaterial, typeof(Material), false) as Material; normalMaterial = EditorGUILayout.ObjectField("Normal Material", normalMaterial, typeof(Material), false) as Material; GUI.enabled = true; EditorGUILayout.BeginHorizontal(); srcColorProps = EditorGUILayout.TextField("Source Color", srcColorProps); if (GUILayout.Button(GEditorCommon.contextIconText, EditorStyles.label, GUILayout.Width(14))) { GenericMenu menu = new GenericMenu(); if (SrcColorSuggestion.Count > 0) { menu.AddDisabledItem(new GUIContent("Suggestion")); menu.AddSeparator(null); for (int i = 0; i < SrcColorSuggestion.Count; ++i) { string s = SrcColorSuggestion[i]; menu.AddItem( new GUIContent(s), false, () => { srcColorProps = s; RenderPreview(); }); } } else { menu.AddDisabledItem(new GUIContent("No suitable property found!")); } menu.ShowAsContext(); } EditorGUILayout.EndHorizontal(); //desColorProps = EditorGUILayout.TextField("Des Color", desColorProps); EditorGUILayout.BeginHorizontal(); srcTextureProps = EditorGUILayout.TextField("Source Texture", srcTextureProps); if (GUILayout.Button(GEditorCommon.contextIconText, EditorStyles.label, GUILayout.Width(14))) { GenericMenu menu = new GenericMenu(); if (SrcTextureSuggestion.Count > 0) { menu.AddDisabledItem(new GUIContent("Suggestion")); menu.AddSeparator(null); for (int i = 0; i < SrcTextureSuggestion.Count; ++i) { string s = SrcTextureSuggestion[i]; menu.AddItem( new GUIContent(s), false, () => { srcTextureProps = s; RenderPreview(); }); } } else { menu.AddDisabledItem(new GUIContent("No suitable property found!")); } menu.ShowAsContext(); } EditorGUILayout.EndHorizontal(); //desTextureProps = EditorGUILayout.TextField("Des Texture", desTextureProps); }); } private void RenderPreview() { GBillboardCreatorArgs args = ConstructArgs(); GBillboardCreator.PrepareRenderTexture(ref previewRt, args); GBillboardCreator.RenderPreview(previewRt, args); } private void DrawMeshSettings() { string label = "Mesh"; string id = "billboardeditor-mesh"; GEditorCommon.Foldout(label, false, id, () => { int trisCount = tris.Length / 3; EditorGUILayout.LabelField("Triangles", trisCount.ToString()); EditorGUILayout.LabelField("Vertices", vertices.Count.ToString()); EditorGUI.indentLevel += 1; for (int i = 0; i < vertices.Count; ++i) { Vector2 v = vertices[i]; EditorGUIUtility.wideMode = true; v = EditorGUILayout.Vector2Field("Vertex " + i, v); EditorGUIUtility.wideMode = false; v.Set(Mathf.Clamp01(v.x), Mathf.Clamp01(v.y)); vertices[i] = v; } EditorGUI.indentLevel -= 1; width = EditorGUILayout.FloatField("Width", width); width = Mathf.Max(0, width); height = EditorGUILayout.FloatField("Height", height); height = Mathf.Max(0, height); bottom = EditorGUILayout.FloatField("Bottom", bottom); bottom = Mathf.Min(bottom, 0); }); } private void DrawUtilities() { string label = "Utilites"; string id = "billboardeditor-utilities"; GEditorCommon.Foldout(label, false, id, () => { Rect fitButtonRect = EditorGUILayout.GetControlRect(); if (GUI.Button(fitButtonRect, "Fit Camera")) { GBillboardCreatorArgs args = GBillboardCreator.FitCameraToTarget(ConstructArgs()); CopyArgs(args); } }); } private void SaveAssets() { if (string.IsNullOrEmpty(saveFolder)) { saveFolder = "Assets/"; } string folder = EditorUtility.OpenFolderPanel("Select Folder", saveFolder, ""); if (string.IsNullOrEmpty(folder)) return; try { saveFolder = folder; GAnalytics.Record(GAnalytics.BILLBOARD_SAVE); EditorUtility.DisplayProgressBar("Saving", "Saving assets...", 1f); GBillboardCreatorArgs args = ConstructArgs(); BillboardAsset billboard = GBillboardCreator.CreateBillboardAsset(args); Texture2D atlas = GBillboardCreator.RenderAtlas(args); Texture2D normal = GBillboardCreator.RenderNormal(args); string billboardPath = Path.Combine(FileUtil.GetProjectRelativePath(saveFolder), billboard.name + ".asset"); BillboardAsset billboardAsset = AssetDatabase.LoadAssetAtPath(billboardPath); if (billboardAsset == null) { AssetDatabase.CreateAsset(billboard, billboardPath); billboardAsset = billboard; } else { billboardAsset.SetVertices(billboard.GetVertices()); billboardAsset.SetIndices(billboard.GetIndices()); billboardAsset.SetImageTexCoords(billboard.GetImageTexCoords()); billboardAsset.width = billboard.width; billboardAsset.height = billboard.height; billboardAsset.bottom = billboard.bottom; billboardAsset.name = billboard.name; } string atlasPath = Path.Combine(FileUtil.GetProjectRelativePath(saveFolder), atlas.name + ".png"); byte[] atlasData = atlas.EncodeToPNG(); File.WriteAllBytes(atlasPath, atlasData); GUtilities.DestroyObject(atlas); string normalPath = Path.Combine(FileUtil.GetProjectRelativePath(saveFolder), normal.name + ".png"); byte[] normalData = normal.EncodeToPNG(); File.WriteAllBytes(normalPath, normalData); GUtilities.DestroyObject(normal); AssetDatabase.Refresh(); TextureImporter atlasImporter = AssetImporter.GetAtPath(atlasPath) as TextureImporter; atlasImporter.wrapMode = TextureWrapMode.Clamp; atlasImporter.alphaIsTransparency = true; atlasImporter.SaveAndReimport(); atlas = AssetDatabase.LoadAssetAtPath(atlasPath); TextureImporter normalImporter = AssetImporter.GetAtPath(normalPath) as TextureImporter; normalImporter.textureType = TextureImporterType.NormalMap; normalImporter.wrapMode = TextureWrapMode.Clamp; normalImporter.SaveAndReimport(); normal = AssetDatabase.LoadAssetAtPath(normalPath); Material mat = null; if (GCommon.CurrentRenderPipeline == GRenderPipelineType.Universal) { mat = Object.Instantiate(GRuntimeSettings.Instance.foliageRendering.urpTreeBillboardMaterial); } else { mat = Object.Instantiate(GRuntimeSettings.Instance.foliageRendering.treeBillboardMaterial); } if (mat != null) { if (mat.HasProperty("_MainTex")) { mat.SetTexture("_MainTex", atlas); } if (mat.HasProperty("_BumpMap")) { mat.SetTexture("_BumpMap", normal); } mat.name = args.Target.name + "_BillboardMaterial"; string matPath = Path.Combine(FileUtil.GetProjectRelativePath(saveFolder), mat.name + ".mat"); AssetDatabase.CreateAsset(mat, matPath); billboardAsset.material = mat; } AssetDatabase.Refresh(); } catch (System.Exception e) { Debug.LogError(e.ToString()); } EditorUtility.ClearProgressBar(); } private void RefreshMaterialPropsSuggestions() { if (target == null) { SrcColorSuggestion.Clear(); SrcColorSuggestion.Add("_Color"); SrcTextureSuggestion.Clear(); SrcTextureSuggestion.Add("_MainTex"); } else { HashSet shaders = new HashSet(); MeshRenderer[] mrs = target.GetComponentsInChildren(); for (int i = 0; i < mrs.Length; ++i) { Material[] mats = mrs[i].sharedMaterials; for (int j = 0; j < mats.Length; ++j) { shaders.Add(mats[j].shader); } } HashSet colorProps = new HashSet(); colorProps.Add("_Color"); HashSet textureProps = new HashSet(); textureProps.Add("_MainTex"); IEnumerator shaderIterator = shaders.GetEnumerator(); while (shaderIterator.MoveNext()) { colorProps.UnionWith(GShaderParser.GetProperties(shaderIterator.Current, "Color")); textureProps.UnionWith(GShaderParser.GetProperties(shaderIterator.Current, "2D")); } SrcColorSuggestion.Clear(); SrcColorSuggestion.AddRange(colorProps); SrcTextureSuggestion.Clear(); SrcTextureSuggestion.AddRange(textureProps); } } } }