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);
}
}
}
}