Jump to content
Sign in to follow this  
syedaf

Rounded Cube with Dynamic Textures

Recommended Posts

I just purchased ProCore after pulling my hairs out with Blender imports into Unity. Here is what I am trying to do. I have already spent a week on this. It seems like this should be straightforward with ProBuilder.

 

I am trying to build a rounded cube to which I could dynamically apply textures on each face. Different textures on each face. (I can do this with a regular cube with 24 vertices). It is the rounded cube which poses several problems. Here is what I have tried so far:

 

1. Develop a rounded cube in Blender.

2. Import the cube into Unity and dynamically set the faces, vertices, uvs, normal in code. As you can imagine there are at least 192 vertices, 384 triangles etc for the smallest bevel. I gave up after a while. You have to code the triangle vertices etc in the right order otherwise things mess up easily.

 

I emailed support and they asked me to post on the forums. Someone from Dev (Karl?) would look into it.

 

Thanks

Syed

Share this post


Link to post
Share on other sites

Correct. That is the exact shape I got with the minimum bevel. It isn't very rounded, but I chose it because of the least complexity (192 verts). If you can bevel it a bit more without raising the code complexity, that would be great. I need to change the images on all the 6 faces dynamically through code. Since I have seen quite a few posts on the Unity forums for rounded cubes and also for dynamic textures with no definitive answer, a walkthrough of the sample solution would be very much appreciated.

 

Thanks,

Syed

Share this post


Link to post
Share on other sites

I did a quick video on how I achieved this effect - find it here: http://youtu.be/lRhUlcbjvzU

 

Here are the scripts I talk about in the video:

using UnityEngine;
using System.Collections;

public class SetFaceMaterials : MonoBehaviour 
{
	[System.Serializable]
	public class FaceMaterialAssignments
	{
		public int index;		// index of the face in pb.faces
		public Material material;
	}

	public FaceMaterialAssignments[] FaceMaterialList; 

	// Use this for initialization
	void Start () {
		
		pb_Object pb = GetComponent<pb_Object>();

		foreach(FaceMaterialAssignments face in FaceMaterialList)
		{
			pb.SetFaceMaterial(pb.faces[face.index], face.material);
		}
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using ProBuilder2.Common;
using ProBuilder2.MeshOperations;

/**
 * Merge 2 or more faces into a single face.  Merged face
 * retains the properties of the first selected face in the
 * event of conflicts.
 *
 * Note!! This will be included by default in ProBuilder 2.4+
 */
public class pb_MergeFaces : Editor
{
	[MenuItem("Tools/ProBuilder/Geometry/Merge Faces")]
	public static void MenuMergeFaces()
	{
		pbUndo.RecordObjects(Selection.transforms, "Merge Faces");

		foreach(pb_Object pb in pbUtil.GetComponents<pb_Object>(Selection.transforms))
		{
			if(pb.SelectedFaceCount > 1)
			{
				MergeFaces(pb, pb.SelectedFaces);
			}
		}

		pb_Editor_Utility.ShowNotification("Merged Faces");

		if(pb_Editor.instance)
			pb_Editor.instance.UpdateSelection(true);
	}

	static void MergeFaces(pb_Object pb, pb_Face[] faces)
	{
		List<int> collectedIndices = new List<int>(faces[0].indices);
		
		for(int i = 1; i < faces.Length; i++)
		{
			collectedIndices.AddRange(faces[i].indices);
		}

		pb_Face mergedFace = new pb_Face(collectedIndices.ToArray(),
		                                 faces[0].material,
		                                 faces[0].uv,
		                                 faces[0].smoothingGroup,
		                                 faces[0].textureGroup,
		                                 faces[0].elementGroup,
		                                 faces[0].manualUV,
		                                 faces[0].colors[0]);

		pb_Face[] rebuiltFaces = new pb_Face[pb.faces.Length - faces.Length + 1];

		int n = 0;
		foreach(pb_Face f in pb.faces)
		{
			if(System.Array.IndexOf(faces, f) < 0)
			{
				rebuiltFaces[n++] = f;
			}
		}
		
		rebuiltFaces[n] = mergedFace;

		pb.SetFaces(rebuiltFaces);

		// merge vertices that are on top of one another now that they share a face
		Dictionary<int, int> shared = new Dictionary<int, int>();

		for(int i = 0; i < mergedFace.indices.Length; i++)
		{
			int sharedIndex = pb.sharedIndices.IndexOf(mergedFace.indices[i]);

			if(shared.ContainsKey(sharedIndex))
			{
				mergedFace.indices[i] = shared[sharedIndex];
			}
			else
			{
				shared.Add(sharedIndex, mergedFace.indices[i]);
			}
		}

		pb.RemoveUnusedVertices();

		pb.Refresh();
	}
}


using UnityEditor;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using ProBuilder2.Common;
using ProBuilder2.Math;
using ProBuilder2.MeshOperations;

#if PB_DEBUG
using Parabox.Debug;
#endif

/**
 * Debugging menu items for ProBuilder.
 */
public class pb_DebugWindow : EditorWindow 
{
	static pb_Editor editor { get { return pb_Editor.instance; } }

	[MenuItem("Tools/ProBuilder/Debug/ProBuilder Debug Window")]
	public static void MenuSceneViewDebug()
	{
		EditorWindow.GetWindow<pb_DebugWindow>();
	}

	void OnEnable()
	{
		HookSceneViewDelegate();
	}

	private void HookSceneViewDelegate()
	{
		if(SceneView.onSceneGUIDelegate != this.OnSceneGUI)
		{
			SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
			SceneView.onSceneGUIDelegate += this.OnSceneGUI;
		}

	}

	void OnDisable()
	{
		SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
	}

	public bool edgeInfo = false;
	public bool faceInfo = false;
	public bool elementGroupInfo = false;
	public bool vertexInfo = true;
	public bool autoUVInfo = false;
	Vector2 scroll = Vector2.zero;

	class ParamView
	{
		public bool showObject;
		public bool showVertices;
		public bool showUv;
		public bool showUv2;
		public bool showAutoUV;

		public ParamView()
		{
			this.showObject = true;
			this.showVertices = false;
			this.showUv = false;
			this.showUv2 = false;
			this.showAutoUV = false;
		}
	}
	Hashtable showParams = new Hashtable();
	
	pb_Object[] selection = new pb_Object[0];

	void OnGUI()
	{
		selection = editor != null ? editor.selection : new pb_Object[0];

		edgeInfo = EditorGUILayout.Toggle("Edge Info", edgeInfo);
		faceInfo = EditorGUILayout.Toggle("Face Info", faceInfo);
		elementGroupInfo = EditorGUILayout.Toggle("Element Group Info", elementGroupInfo);
		vertexInfo = EditorGUILayout.Toggle("Vertex Info", vertexInfo);

		GUILayout.Label("Active Selection", EditorStyles.boldLabel);
		if(selection.Length > 0)
		{
			if(selection[0].SelectedTriangles.Length < 256)
			{
				GUILayout.Label("Faces: [" + selection[0].SelectedFaceIndices.Length + "/" + selection[0].faces.Length + "]  " + selection[0].SelectedFaceIndices.ToFormattedString(", "));
				GUILayout.Label("Edges: [" + selection[0].SelectedEdges.Length + "]  " + selection[0].SelectedEdges.ToFormattedString(", "));
				GUILayout.Label("Triangles: [" + selection[0].SelectedTriangles.Length + "]  " + selection[0].SelectedTriangles.ToFormattedString(", "));
			}
		}

		GUILayout.Space(8);

		scroll = GUILayout.BeginScrollView(scroll);

			foreach(pb_Object pb in selection)
			{
				Mesh m = pb.msh;
				Renderer ren = pb.GetComponent<MeshRenderer>();

				ParamView pv;
				int id = pb.gameObject.GetInstanceID();

				if(showParams.ContainsKey(id))
				{
					pv = (ParamView)showParams[id];
				}
				else
				{
					showParams.Add(id, new ParamView());
					pv = (ParamView)showParams[id];
				}

				pv.showObject = EditorGUILayout.Foldout(pv.showObject, pb.name + "(" + pb.id +")");
				if(pv.showObject)
				{
					/* VERTICES */			
					GUILayout.BeginHorizontal();
						GUILayout.Space(24);
						pv.showVertices = EditorGUILayout.Foldout(pv.showVertices, "Vertices: " + pb.vertexCount);
					GUILayout.EndHorizontal();
		
					GUILayout.BeginHorizontal();
					GUILayout.Space(48);
						if(pv.showVertices)
						{
							if(m == null)
							{
								GUILayout.Label("" + pb.vertices.ToFormattedString("\n"));						
							}
							else
							{
								GUILayout.BeginVertical();
								for(int i = 0; i < m.subMeshCount; i++)
								{
									GUILayout.Label("Mat: " + ren.sharedMaterials[i].name + "\n" + pb.GetVertices( m.GetTriangles(i) ).ToFormattedString("\n") + "\n");
								}
								GUILayout.EndVertical();
							}
						}
					GUILayout.EndHorizontal();
					
					/* UV  */			
					GUILayout.BeginHorizontal();
						GUILayout.Space(24);
						pv.showUv = EditorGUILayout.Foldout(pv.showUv, "UVs: " + pb.uv.Length);
					GUILayout.EndHorizontal();
		
					GUILayout.BeginHorizontal();
					GUILayout.Space(48);
						if(pv.showUv)
							GUILayout.Label("" + pb.uv.ToFormattedString("\n"));
					GUILayout.EndHorizontal();

					/* UV 2 */			
					GUILayout.BeginHorizontal();
						GUILayout.Space(24);
						pv.showUv2 = EditorGUILayout.Foldout(pv.showUv2, "UV2: " + (m ? m.uv2.Length.ToString() : "NULL"));
					GUILayout.EndHorizontal();
		
					GUILayout.BeginHorizontal();
					GUILayout.Space(48);
						if(pv.showUv2 && m != null)
							GUILayout.Label("" + m.uv2.ToFormattedString("\n"));
					GUILayout.EndHorizontal();

					/* Auto UV params */
					GUILayout.BeginHorizontal();
						GUILayout.Space(24);
						pv.showAutoUV = EditorGUILayout.Foldout(pv.showAutoUV, "Auto-UV Params");
					GUILayout.EndHorizontal();

					GUILayout.BeginHorizontal();
					GUILayout.Space(48);
						if(pv.showAutoUV)
							GUILayout.Label("" + pb.SelectedFaces.Select(x => x.uv).ToArray().ToFormattedString("\n"));
					GUILayout.EndHorizontal();

				}
			}
		GUILayout.EndScrollView();
	}

	void OnSceneGUI(SceneView scn)
	{
		foreach(pb_Object pb in pbUtil.GetComponents<pb_Object>(Selection.transforms))
			DrawStats(pb);

		Repaint();
	}

	void DrawStats(pb_Object pb)
	{
		StringBuilder sb = new StringBuilder();

		Handles.BeginGUI();

		if(edgeInfo)
		foreach(pb_Edge f in pb.SelectedEdges)
		{
			Vector2 cen = HandleUtility.WorldToGUIPoint( pb.transform.TransformPoint((pb.vertices[f.x] + pb.vertices[f.y])/ 2f) );
			GUI.Box(new Rect(cen.x, cen.y, 60, 40), f.ToString() + "\n");
			// GUI.Label(new Rect(cen.x, cen.y, 200, 200), f.ToString());
		}

		/**
		 * SHARED INDICES
		 */
		// foreach(pb_IntArray arr in pb.sharedIndices)
		// {
		// 	Vector2 cen = HandleUtility.WorldToGUIPoint( pb.transform.TransformPoint(pb.vertices[arr[0]]) );
						
		// 	GUI.Label(new Rect(cen.x, cen.y, 200, 200), ((int[])arr).ToFormattedString("\n"));
		// }

		if(faceInfo)
		foreach(pb_Face f in pb.SelectedFaces)
		{
			Vector2 cen = HandleUtility.WorldToGUIPoint( pb.transform.TransformPoint( pb_Math.Average( pb.GetVertices(f.distinctIndices) ) ) );
			
				GUI.Box( new Rect(cen.x, cen.y, 300, 100), "Face: " + f.ToString() + "Element Group: " + f.elementGroup);			

		}

		if(elementGroupInfo)
		foreach(pb_Face f in pb.faces)
		{
			Vector2 cen = HandleUtility.WorldToGUIPoint( pb.transform.TransformPoint( pb_Math.Average( pb.GetVertices(f.distinctIndices) ) ) );
			GUI.Label( new Rect(cen.x, cen.y, 300, 100), f.elementGroup.ToString(), EditorStyles.boldLabel);			
		}

			// sb.AppendLine(f.ToString() + ", ");


		// foreach(pb_Face face in pb.SelectedFaces)
		// 	sb.AppendLine(face.colors.ToFormattedString("\n") + "\n");

		// sb.AppendLine("\n");

		// foreach(pb_IntArray si in pb.sharedIndices)
		// {
		// 	sb.AppendLine(si.array.ToFormattedString(", "));
		// }

		// sb.AppendLine("\n");

		if(vertexInfo)
		{
			foreach(pb_IntArray arr in pb.sharedIndices)
			{
				Vector3 v = pb.vertices[arr[0]];

				Vector2 cen = HandleUtility.WorldToGUIPoint( pb.transform.TransformPoint( v ) );
				GUI.Label(new Rect(cen.x, cen.y, 500, 64), arr.array.ToFormattedString(", "), EditorStyles.boldLabel);
			}
		}

		Handles.EndGUI();

		Handles.BeginGUI();
		{
			GUI.Label(new Rect(10, 10, 400, 800), sb.ToString());
		}
		Handles.EndGUI();
	}
}

Share this post


Link to post
Share on other sites

It is working great. Thanks for the debugger window. It is a useful utility. I had a question which might be related. Once I run the scene, I cannot move or rotate the cubes with the gizmos in the Scene view. I get a label stating 'Static'. I can however move it by changing the values in the transform fields. Am I missing something?

 

Thanks,

Syed

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
Sign in to follow this  

×