Jump to content
drbii

Save Additional Data Stream

Recommended Posts

Ok started polybrushing a mesh and after doing some pretty major and time consuming work realize that additional vertex streams was checked on...so they are not actually applied to the mesh itself.  Is there a way to apply those to the mesh and continue with data streams off.  We tried saving as an OBJ but does not save the data stream manipulations done by polybrush.  NEED THIS!

 

Share this post


Link to post
Share on other sites
13 hours ago, karl said:

That's a good feature request - and I think pretty easy to add.  I'll try to get a quick utility written as a workaround for now.

That would be awesome.  We work between unity and external 3d tools frequently and it would allow us to take our polybrushed objects back into them.  Normally we don't have the additional streams on but sometimes (inevitably under short deadline) its on for some reason and we are stuck.

 

A tool that saves the additional streams to the mesh would give us the best of both worlds....we could keep the additional stream and its effiecencies until we had to go back into a 3d program.  

 

Thanks!  You guys rock!

Share this post


Link to post
Share on other sites

Okay, here's a utility for quickly merging additional vertex streams into the current mesh or a new composite mesh.  If you've painted a single base mesh many times with the vertex streams option enabled make sure you use the "Create New Composite Mesh" options - if you apply to current you could lose all your other modifications.

IMPORTANT - Make sure to back up your project before using this utility.  There is no Undo and this will overwrite mesh assets.
 

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace 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.
	 */
	public class z_BakeAdditionalVertexStreams : EditorWindow
	{
		[MenuItem("Tools/Polybrush/Bake Vertex Streams")]
		static void Init()
		{
			EditorWindow.GetWindow<z_BakeAdditionalVertexStreams>(true, "Bake Vertex Streams", true);
		}

		void OnEnable()
		{
			Undo.undoRedoPerformed += UndoRedoPerformed;
			autoRepaintOnSceneChange = true;
			OnSelectionChange();
		}

		void OnFocus()
		{
			OnSelectionChange();
		}

		void OnDisable()
		{
			Undo.undoRedoPerformed -= UndoRedoPerformed;
		}

		private List<z_AdditionalVertexStreams> m_VertexStreams = new List<z_AdditionalVertexStreams>();
		private Vector2 scroll = Vector2.zero;

		private GUIContent m_BatchNewMesh = new GUIContent("Create New\nComposite Mesh", "Create a new mesh for each selected mesh, automatically prefixing the built meshes with z_ 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()
		{
			GUILayout.BeginHorizontal();

				GUILayout.BeginVertical();
					GUILayout.Label("Selected", EditorStyles.boldLabel);

					scroll = EditorGUILayout.BeginScrollView(scroll, false, true);

					foreach(z_AdditionalVertexStreams vertexStream in m_VertexStreams)
					{
						if(vertexStream != null)
							GUILayout.Label(string.Format("{0} ({1})", vertexStream.gameObject.name, vertexStream.mesh == null ? "null" : vertexStream.mesh.name));
					}

					EditorGUILayout.EndScrollView();
				GUILayout.EndVertical();

				GUILayout.BeginVertical();
					GUILayout.Label("Bake Options", EditorStyles.boldLabel);

					GUI.enabled = m_VertexStreams.Count == 1;
					
					if(GUILayout.Button("Apply to\nCurrent 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 stream in m_VertexStreams)
								CreateComposite(stream, true);
						}

						m_VertexStreams.Clear();
					}

					if(GUILayout.Button("Create New\nComposite Mesh"))
					{
						foreach(var stream in m_VertexStreams)
							CreateComposite(stream, false);

						m_VertexStreams.Clear();
					}

					GUI.enabled = m_VertexStreams.Count > 0;

					GUILayout.Label("Batch Options", EditorStyles.boldLabel);

					if(GUILayout.Button(m_BatchNewMesh))
					{
						string path = EditorUtility.OpenFolderPanel("Save Vertex Stream Meshes", "Assets", "");

						for(int i = 0; i < m_VertexStreams.Count; i++)
						{
							path = path.Replace(Application.dataPath, "Assets");

							if(m_VertexStreams[i] == null || m_VertexStreams[i].mesh == null)
								continue;

							CreateComposite(m_VertexStreams[i], false, string.Format("{0}/{1}.asset", path, m_VertexStreams[i].mesh.name));
						}

						m_VertexStreams.Clear();
					}

				GUILayout.EndVertical();

			GUILayout.EndHorizontal();
		}

		void OnSelectionChange()
		{
			m_VertexStreams = Selection.transforms.SelectMany(x => x.GetComponentsInChildren<z_AdditionalVertexStreams>()).ToList();
			Repaint();
		}

		void UndoRedoPerformed()
		{
			foreach(Mesh m in Selection.transforms.SelectMany(x => x.GetComponentsInChildren<MeshFilter>()).Select(y => y.sharedMesh))
			{
				if(m != null)
					m.UploadMeshData(false);
			}
		}

		void CreateComposite(z_AdditionalVertexStreams vertexStream, bool applyToCurrent, string path = null)
		{
			GameObject go = vertexStream.gameObject;

			Mesh source = go.GetMesh();
			Mesh stream = vertexStream.mesh;

			if(source == null || stream == null)
			{
				Debug.LogWarning("Mesh filter or vertex stream mesh is null, cannot continue.");
				return;
			}

			if(applyToCurrent)
			{
				CreateCompositeMesh(source, stream, source);

				MeshRenderer renderer = go.GetComponent<MeshRenderer>();

				if(renderer != null)
					renderer.additionalVertexStreams = null;

				GameObject.DestroyImmediate(vertexStream);
			}
			else
			{
				Mesh composite = new Mesh();
				CreateCompositeMesh(source, stream, composite);

				if( string.IsNullOrEmpty(path) )
				{
					z_EditorUtility.SaveMeshAsset(composite, go.GetComponent<MeshFilter>(), go.GetComponent<SkinnedMeshRenderer>());
				}
				else
				{
					AssetDatabase.CreateAsset(composite, path);

					MeshFilter mf = go.GetComponent<MeshFilter>();
					
					SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();

					if(mf != null)
						mf.sharedMesh = composite;
					else if(smr != null)
						smr.sharedMesh = composite;
				}


				Undo.DestroyObjectImmediate(vertexStream);
			}
		}

		void CreateCompositeMesh(Mesh source, Mesh vertexStream, Mesh composite)
		{
			int vertexCount = source.vertexCount;
			bool isNewMesh = composite.vertexCount != vertexCount;

			composite.name = source.name;

			composite.vertices = vertexStream.vertices != null && vertexStream.vertices.Length == vertexCount ?
				vertexStream.vertices :
				source.vertices;

			composite.normals = vertexStream.normals != null  && vertexStream.normals.Length == vertexCount ?
				vertexStream.normals :
				source.normals;

			composite.tangents = vertexStream.tangents != null && vertexStream.tangents.Length == vertexCount ?
				vertexStream.tangents :
				source.tangents;

			composite.boneWeights = vertexStream.boneWeights != null && vertexStream.boneWeights.Length == vertexCount ?
				vertexStream.boneWeights :
				source.boneWeights;

			composite.colors32 = vertexStream.colors32 != null && vertexStream.colors32.Length == vertexCount ?
				vertexStream.colors32 :
				source.colors32;

			composite.bindposes = vertexStream.bindposes != null && vertexStream.bindposes.Length == vertexCount ?
				vertexStream.bindposes :
				source.bindposes;
	
			List<Vector4> uvs = new List<Vector4>();

			vertexStream.GetUVs(0, uvs);
			if(uvs == null || uvs.Count != vertexCount)
				source.GetUVs(0, uvs);
			composite.SetUVs(0, uvs);

			vertexStream.GetUVs(1, uvs);
			if(uvs == null || uvs.Count != vertexCount)
				source.GetUVs(1, uvs);
			composite.SetUVs(1, uvs);

			vertexStream.GetUVs(2, uvs);
			if(uvs == null || uvs.Count != vertexCount)
				source.GetUVs(2, uvs);
			composite.SetUVs(2, uvs);

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

Share this post


Link to post
Share on other sites

Getting around to trying out the above code but I keep getting the following message:  Assets/ProCore/Polybrush/z_BakeAdditionalVertexStreams.cs(157,21): error CS0103: The name `z_EditorUtility' does not exist in the current context

 

Share this post


Link to post
Share on other sites

Hmmm.  Well I thought it was working as expected by I guess it is not.  It does save the stream to the mesh, but I cannot use polybrush on the object after I bake the stream on to the mesh.  Any mesh that I have baked cannot be rebrushed by polybrush.  See the attached image.   Any ideas?  Looks like something is being manipulated (the blue mesh which is the brush highlight) deforms but the mesh does not...also painting textures stops as well.

wingrab.jpg

Share this post


Link to post
Share on other sites

So you used the "Apply to Current Mesh" option?  If that is the case the attributes will have been baked into the selection.  I can't think of a reason that it wouldn't be editable after this process, unless there was a mixup wherein Polybrush thinks there is an Additional Vertex Streams mesh still present.   Make sure that there isn't a "z_AdditionalVertexStreams" component on that mesh still.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×