using UnityEngine; using System.Collections.Generic; using System.Linq; using UnityEngine.Polybrush; namespace UnityEditor.Polybrush { /// /// Utility for applying vertex stream data directly to a mesh. Can either override the existing /// mesh arrays or create a new mesh from the composite. /// internal class BakeAdditionalVertexStreams : EditorWindow { void OnEnable() { Undo.undoRedoPerformed += UndoRedoPerformed; autoRepaintOnSceneChange = true; OnSelectionChange(); } void OnFocus() { OnSelectionChange(); } void OnDisable() { Undo.undoRedoPerformed -= UndoRedoPerformed; } private List m_polyMeshes = new List(); private Vector2 scroll = Vector2.zero; private GUIContent m_BatchNewMesh = new GUIContent("Create New Composite Mesh", "Create a new mesh for each selected mesh, automatically prefixing the built meshes with and index. This is useful in situations where you have used Additional Vertex Streams to paint a single mesh source many times and would like to ensure that all meshes remain unique."); void OnGUI() { using (new GUILayout.HorizontalScope()) { using (new GUILayout.VerticalScope()) { EditorGUILayout.LabelField("Selected", EditorStyles.boldLabel); scroll = EditorGUILayout.BeginScrollView(scroll, false, true); foreach (PolybrushMesh polyMesh in m_polyMeshes) { if (polyMesh != null) EditorGUILayout.LabelField(string.Format("{0} ({1})", polyMesh.gameObject.name, polyMesh.storedMesh == null ? "null" : polyMesh.storedMesh.name)); } EditorGUILayout.EndScrollView(); } using (new GUILayout.VerticalScope()) { EditorGUILayout.LabelField("Bake Options", EditorStyles.boldLabel); GUI.enabled = m_polyMeshes.Count == 1; if (GUILayout.Button("Apply to Current Mesh")) { if (EditorUtility.DisplayDialog("Apply Vertex Streams to Mesh", "This action is not undo-able, are you sure you want to continue?", "Yes", "Cancel")) { foreach (var polyMesh in m_polyMeshes) CreateComposite(polyMesh, true); } m_polyMeshes.Clear(); } if (GUILayout.Button("Create New Composite Mesh")) { foreach (var polyMesh in m_polyMeshes) CreateComposite(polyMesh, false); m_polyMeshes.Clear(); } GUI.enabled = m_polyMeshes.Count > 0; EditorGUILayout.LabelField("Batch Options", EditorStyles.boldLabel); if (GUILayout.Button(m_BatchNewMesh)) { string path = EditorUtility.OpenFolderPanel("Save Vertex Stream Meshes", "Assets", ""); for (int i = 0; i < m_polyMeshes.Count; i++) { path = path.Replace(Application.dataPath, "Assets"); if (m_polyMeshes[i] == null || m_polyMeshes[i].storedMesh == null) continue; CreateComposite(m_polyMeshes[i], false, string.Format("{0}/{1}.asset", path, m_polyMeshes[i].storedMesh.name)); } m_polyMeshes.Clear(); } } } } void OnSelectionChange() { m_polyMeshes = Selection.transforms.SelectMany(x => x.GetComponentsInChildren()).ToList(); Repaint(); } void UndoRedoPerformed() { foreach(Mesh m in Selection.transforms.SelectMany(x => x.GetComponentsInChildren()).Select(y => y.sharedMesh)) { if(m != null) m.UploadMeshData(false); } } /// /// Create a Composite Mesh from AdditionnalVertexStreams /// /// the object used to create the composite mesh /// Create a new Mesh or apply it directly to the current one? /// where to save the new mesh void CreateComposite(PolybrushMesh polyMesh, bool applyToCurrent, string path = null) { GameObject go = polyMesh.gameObject; Mesh source = go.GetMesh(); Mesh mesh = polyMesh.storedMesh; if(source == null || mesh == null) { Debug.LogWarning("Mesh filter or vertex stream mesh is null, cannot continue."); return; } if(applyToCurrent) { CreateCompositeMesh(source, mesh, polyMesh.sourceMesh); MeshRenderer renderer = go.GetComponent(); if(renderer != null) renderer.additionalVertexStreams = null; Undo.DestroyObjectImmediate(polyMesh); } else { Mesh composite = new Mesh(); CreateCompositeMesh(source, mesh, composite); if( string.IsNullOrEmpty(path) ) { PolyEditorUtility.SaveMeshAsset(composite, go.GetComponent(), go.GetComponent()); } else { AssetDatabase.CreateAsset(composite, path); MeshFilter mf = go.GetComponent(); SkinnedMeshRenderer smr = go.GetComponent(); if(mf != null) mf.sharedMesh = composite; else if(smr != null) smr.sharedMesh = composite; } Undo.DestroyObjectImmediate(polyMesh); } } void CreateCompositeMesh(Mesh source, Mesh mesh, Mesh composite) { int vertexCount = source.vertexCount; bool isNewMesh = composite.vertexCount != vertexCount; composite.name = source.name; composite.vertices = mesh.vertices != null && mesh.vertices.Length == vertexCount ? mesh.vertices : source.vertices; composite.normals = mesh.normals != null && mesh.normals.Length == vertexCount ? mesh.normals : source.normals; composite.tangents = mesh.tangents != null && mesh.tangents.Length == vertexCount ? mesh.tangents : source.tangents; composite.boneWeights = mesh.boneWeights != null && mesh.boneWeights.Length == vertexCount ? mesh.boneWeights : source.boneWeights; composite.colors32 = mesh.colors32 != null && mesh.colors32.Length == vertexCount ? mesh.colors32 : source.colors32; composite.bindposes = mesh.bindposes != null && mesh.bindposes.Length == vertexCount ? mesh.bindposes : source.bindposes; List uvs = new List(); mesh.GetUVs(0, uvs); if(uvs == null || uvs.Count != vertexCount) source.GetUVs(0, uvs); composite.SetUVs(0, uvs); mesh.GetUVs(1, uvs); if(uvs == null || uvs.Count != vertexCount) source.GetUVs(1, uvs); composite.SetUVs(1, uvs); mesh.GetUVs(2, uvs); if(uvs == null || uvs.Count != vertexCount) source.GetUVs(2, uvs); composite.SetUVs(2, uvs); mesh.GetUVs(3, uvs); if(uvs == null || uvs.Count != vertexCount) source.GetUVs(3, uvs); composite.SetUVs(3, uvs); if(isNewMesh) { composite.subMeshCount = source.subMeshCount; for(int i = 0; i < source.subMeshCount; i++) composite.SetIndices(source.GetIndices(i), source.GetTopology(i), i); } } } }