Exemple #1
0
		public static bool GenerateMeshUsingGeoCache(HEU_SessionBase session, HEU_HoudiniAsset asset, GameObject gameObject,
			HEU_GenerateGeoCache geoCache, bool bGenerateUVs, bool bGenerateTangents, bool bPartInstanced)
		{
#if HEU_PROFILER_ON
			float generateMeshTime = Time.realtimeSinceStartup;
#endif

			string collisionGroupName = HEU_PluginSettings.CollisionGroupName;
			string renderCollisionGroupName = HEU_PluginSettings.RenderedCollisionGroupName;

			// Stores submesh data based on material key (ie. a submesh for each unique material)

			// Unity requires that if using multiple materials in the same GameObject, then we
			// need to create corresponding number of submeshes as materials.
			// So we'll create a submesh for each material in use. 
			// Each submesh will have a list of vertices and their attributes which
			// we'll collect in a helper class (HEU_MeshData).
			// Once we collected all the submesh data, we create a CombineInstance for each
			// submesh, then combine it while perserving the submeshes.
			Dictionary<int, HEU_MeshData> subMeshesMap = new Dictionary<int, HEU_MeshData>();

			string defaultMaterialName = HEU_HoudiniAsset.GenerateDefaultMaterialName(geoCache.GeoID, geoCache.PartID);
			int defaultMaterialKey = HEU_MaterialFactory.MaterialNameToKey(defaultMaterialName);

			int singleFaceUnityMaterialKey = HEU_Defines.HEU_INVALID_MATERIAL;
			int singleFaceHoudiniMaterialKey = HEU_Defines.HEU_INVALID_MATERIAL;

			// Now go through each group data and acquire the vertex data.
			// We'll create the collider mesh rightaway and assign to the gameobject.
			int numCollisionMeshes = 0;
			foreach (KeyValuePair<string, int[]> groupSplitFacesPair in geoCache._groupSplitVertexIndices)
			{
				string groupName = groupSplitFacesPair.Key;
				int[] groupVertexList = groupSplitFacesPair.Value;

				bool bIsCollidable = groupName.Contains(collisionGroupName);
				bool bIsRenderCollidable = groupName.Contains(renderCollisionGroupName);
				if (bIsCollidable || bIsRenderCollidable)
				{
					if (numCollisionMeshes > 0)
					{
						Debug.LogWarningFormat("More than 1 collision mesh detected for part {0}.\nOnly a single collision mesh is supported per part.", geoCache._partName);
					}

					if (geoCache._partInfo.type == HAPI_PartType.HAPI_PARTTYPE_BOX)
					{
						// Box collider

						HAPI_BoxInfo boxInfo = new HAPI_BoxInfo();
						if (session.GetBoxInfo(geoCache.GeoID, geoCache.PartID, ref boxInfo))
						{
							BoxCollider boxCollider = HEU_GeneralUtility.GetOrCreateComponent<BoxCollider>(gameObject);

							boxCollider.center = new Vector3(-boxInfo.center[0], boxInfo.center[1], boxInfo.center[2]);
							boxCollider.size = new Vector3(boxInfo.size[0] * 2f, boxInfo.size[1] * 2f, boxInfo.size[2] * 2f);
							// TODO: Should we apply the box info rotation here to the box collider?
							//		 If so, it should be in its own gameobject?
						}
					}
					else if (geoCache._partInfo.type == HAPI_PartType.HAPI_PARTTYPE_SPHERE)
					{
						// Sphere collider

						HAPI_SphereInfo sphereInfo = new HAPI_SphereInfo();
						if (session.GetSphereInfo(geoCache.GeoID, geoCache.PartID, ref sphereInfo))
						{
							SphereCollider sphereCollider = HEU_GeneralUtility.GetOrCreateComponent<SphereCollider>(gameObject);

							sphereCollider.center = new Vector3(-sphereInfo.center[0], sphereInfo.center[1], sphereInfo.center[2]);
							sphereCollider.radius = sphereInfo.radius;
						}
					}
					else
					{
						// Mesh collider

						List<Vector3> collisionVertices = new List<Vector3>();
						for (int v = 0; v < groupVertexList.Length; ++v)
						{
							int index = groupVertexList[v];
							if (index >= 0 && index < geoCache._posAttr.Length)
							{
								collisionVertices.Add(new Vector3(-geoCache._posAttr[index * 3], geoCache._posAttr[index * 3 + 1], geoCache._posAttr[index * 3 + 2]));
							}
						}

						int[] collisionIndices = new int[collisionVertices.Count];
						for (int i = 0; i < collisionIndices.Length; ++i)
						{
							collisionIndices[i] = i;
						}

						Mesh collisionMesh = new Mesh();
#if UNITY_2017_3_OR_NEWER
						collisionMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#endif
						collisionMesh.name = groupName;
						collisionMesh.vertices = collisionVertices.ToArray();
						collisionMesh.triangles = collisionIndices;
						collisionMesh.RecalculateBounds();

						MeshCollider meshCollider = HEU_GeneralUtility.GetOrCreateComponent<MeshCollider>(gameObject);
						meshCollider.sharedMesh = collisionMesh;
					}

					numCollisionMeshes++;
				}


				if (bIsCollidable && !bIsRenderCollidable)
				{
					continue;
				}

				// After this point, we'll be only processing renderable geometry

				// Transfer indices for each attribute from the single large list into group lists

				float[] groupColorAttr = new float[0];
				HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._colorAttrInfo, geoCache._colorAttr, ref groupColorAttr);

				float[] groupAlphaAttr = new float[0];
				HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._alphaAttrInfo, geoCache._alphaAttr, ref groupAlphaAttr);

				float[] groupNormalAttr = new float[0];
				HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._normalAttrInfo, geoCache._normalAttr, ref groupNormalAttr);

				float[] groupTangentsAttr = new float[0];
				HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._tangentAttrInfo, geoCache._tangentAttr, ref groupTangentsAttr);

				float[] groupUVAttr = new float[0];
				HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._uvAttrInfo, geoCache._uvAttr, ref groupUVAttr);

				float[] groupUV2Attr = new float[0];
				HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._uv2AttrInfo, geoCache._uv2Attr, ref groupUV2Attr);

				float[] groupUV3Attr = new float[0];
				HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._uv3AttrInfo, geoCache._uv3Attr, ref groupUV3Attr);

				// Unity mesh creation requires # of vertices must equal # of attributes (color, normal, uvs).
				// HAPI gives us point indices. Since our attributes are via vertex, we need to therefore
				// create new indices of vertices that correspond to our attributes.

				// To reindex, we go through each index, add each attribute corresponding to that index to respective lists.
				// Then we set the index of where we added those attributes as the new index.

				int numIndices = groupVertexList.Length;
				for (int vertexIndex = 0; vertexIndex < numIndices; vertexIndex += 3)
				{
					// groupVertexList contains -1 for unused indices, and > 0 for used
					if (groupVertexList[vertexIndex] == -1)
					{
						continue;
					}

					int faceIndex = vertexIndex / 3;
					int faceMaterialID = geoCache._houdiniMaterialIDs[faceIndex];

					// Get the submesh ID for this face. Depends on whether it is a Houdini or Unity material.
					// Using default material as failsafe
					int submeshID = HEU_Defines.HEU_INVALID_MATERIAL;

					if (geoCache._unityMaterialAttrInfo.exists)
					{
						// This face might have a Unity or Substance material attribute. 
						// Formulate the submesh ID by combining the material attributes.

						if (geoCache._singleFaceUnityMaterial)
						{
							if (singleFaceUnityMaterialKey == HEU_Defines.HEU_INVALID_MATERIAL && geoCache._unityMaterialInfos.Count > 0)
							{
								// Use first material
								var unityMaterialMapEnumerator = geoCache._unityMaterialInfos.GetEnumerator();
								if (unityMaterialMapEnumerator.MoveNext())
								{
									singleFaceUnityMaterialKey = unityMaterialMapEnumerator.Current.Key;
								}
							}
							submeshID = singleFaceUnityMaterialKey;
						}
						else
						{
							int attrIndex = faceIndex;
							if (geoCache._unityMaterialAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM || geoCache._unityMaterialAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT)
							{
								if (geoCache._unityMaterialAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT)
								{
									attrIndex = groupVertexList[vertexIndex];
								}

								string unityMaterialName = "";
								string substanceName = "";
								int substanceIndex = -1;
								submeshID = HEU_GenerateGeoCache.GetMaterialKeyFromAttributeIndex(geoCache, attrIndex, out unityMaterialName, out substanceName, out substanceIndex);
							}
							else
							{
								// (geoCache._unityMaterialAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL) should have been handled as geoCache._singleFaceMaterial above

								Debug.LogErrorFormat("Unity material attribute not supported for attribute type {0}!", geoCache._unityMaterialAttrInfo.owner);
							}
						}
					}

					if (submeshID == HEU_Defines.HEU_INVALID_MATERIAL)
					{
						// Check if has Houdini material assignment

						if (geoCache._houdiniMaterialIDs.Length > 0)
						{
							if (geoCache._singleFaceHoudiniMaterial)
							{
								if (singleFaceHoudiniMaterialKey == HEU_Defines.HEU_INVALID_MATERIAL)
								{
									singleFaceHoudiniMaterialKey = geoCache._houdiniMaterialIDs[0];
								}
								submeshID = singleFaceHoudiniMaterialKey;
							}
							else if (faceMaterialID > 0)
							{
								submeshID = faceMaterialID;
							}
						}

						if (submeshID == HEU_Defines.HEU_INVALID_MATERIAL)
						{
							// Use default material
							submeshID = defaultMaterialKey;
						}
					}

					if (!subMeshesMap.ContainsKey(submeshID))
					{
						// New submesh
						subMeshesMap.Add(submeshID, new HEU_MeshData());
					}

					HEU_MeshData subMeshData = subMeshesMap[submeshID];

					for (int triIndex = 0; triIndex < 3; ++triIndex)
					{
						int vertexTriIndex = vertexIndex + triIndex;
						int positionIndex = groupVertexList[vertexTriIndex];

						// Position
						Vector3 position = new Vector3(-geoCache._posAttr[positionIndex * 3 + 0], geoCache._posAttr[positionIndex * 3 + 1], geoCache._posAttr[positionIndex * 3 + 2]);
						subMeshData._vertices.Add(position);

						// Color
						if (geoCache._colorAttrInfo.exists)
						{
							Color tempColor = new Color();
							tempColor.r = Mathf.Clamp01(groupColorAttr[vertexTriIndex * geoCache._colorAttrInfo.tupleSize + 0]);
							tempColor.g = Mathf.Clamp01(groupColorAttr[vertexTriIndex * geoCache._colorAttrInfo.tupleSize + 1]);
							tempColor.b = Mathf.Clamp01(groupColorAttr[vertexTriIndex * geoCache._colorAttrInfo.tupleSize + 2]);

							if (geoCache._alphaAttrInfo.exists)
							{
								tempColor.a = Mathf.Clamp01(groupAlphaAttr[vertexTriIndex]);
							}
							else if (geoCache._colorAttrInfo.tupleSize == 4)
							{
								tempColor.a = Mathf.Clamp01(groupColorAttr[vertexTriIndex * geoCache._colorAttrInfo.tupleSize + 3]);
							}
							else
							{
								tempColor.a = 1f;
							}
							subMeshData._colors.Add(tempColor);
						}
						else
						{
							subMeshData._colors.Add(Color.white);
						}

						// Normal
						if (vertexTriIndex < groupNormalAttr.Length)
						{
							// Flip the x
							Vector3 normal = new Vector3(-groupNormalAttr[vertexTriIndex * 3 + 0], groupNormalAttr[vertexTriIndex * 3 + 1], groupNormalAttr[vertexTriIndex * 3 + 2]);
							subMeshData._normals.Add(normal);
						}
						else
						{
							// We'll be calculating normals later
							subMeshData._normals.Add(Vector3.zero);
						}

						// UV1
						if (vertexTriIndex < groupUVAttr.Length)
						{
							Vector2 uv = new Vector2(groupUVAttr[vertexTriIndex * 2 + 0], groupUVAttr[vertexTriIndex * 2 + 1]);
							subMeshData._UVs.Add(uv);
						}

						// UV2
						if (vertexTriIndex < groupUV2Attr.Length)
						{
							Vector2 uv = new Vector2(groupUV2Attr[vertexTriIndex * 2 + 0], groupUV2Attr[vertexTriIndex * 2 + 1]);
							subMeshData._UV2s.Add(uv);
						}

						// UV3
						if (vertexTriIndex < groupUV3Attr.Length)
						{
							Vector2 uv = new Vector2(groupUV3Attr[vertexTriIndex * 2 + 0], groupUV3Attr[vertexTriIndex * 2 + 1]);
							subMeshData._UV3s.Add(uv);
						}

						// Tangents
						if (bGenerateTangents && vertexTriIndex < groupTangentsAttr.Length)
						{
							Vector4 tangent = Vector4.zero;
							if (geoCache._tangentAttrInfo.tupleSize == 4)
							{
								tangent = new Vector4(-groupTangentsAttr[vertexTriIndex * 4 + 0], groupTangentsAttr[vertexTriIndex * 4 + 1], groupTangentsAttr[vertexTriIndex * 4 + 2], groupTangentsAttr[vertexTriIndex * 4 + 3]);
							}
							else if (geoCache._tangentAttrInfo.tupleSize == 3)
							{
								tangent = new Vector4(-groupTangentsAttr[vertexTriIndex * 3 + 0], groupTangentsAttr[vertexTriIndex * 3 + 1], groupTangentsAttr[vertexTriIndex * 3 + 2], 1);
							}

							subMeshData._tangents.Add(tangent);
						}

						subMeshData._indices.Add(subMeshData._vertices.Count - 1);
						//Debug.LogFormat("Submesh index mat {0} count {1}", faceMaterialID, subMeshData._indices.Count);
					}

					if (!geoCache._normalAttrInfo.exists)
					{
						// To generate normals after all the submeshes have been defined, we
						// calculate and store each triangle normal, along with the list
						// of connected vertices for each vertex

						int triIndex = subMeshData._indices.Count - 3;
						int i1 = subMeshData._indices[triIndex + 0];
						int i2 = subMeshData._indices[triIndex + 1];
						int i3 = subMeshData._indices[triIndex + 2];

						// Triangle normal
						Vector3 p1 = subMeshData._vertices[i2] - subMeshData._vertices[i1];
						Vector3 p2 = subMeshData._vertices[i3] - subMeshData._vertices[i1];
						Vector3 normal = Vector3.Cross(p1, p2).normalized;
						subMeshData._triangleNormals.Add(normal);
						int normalIndex = subMeshData._triangleNormals.Count - 1;

						// Connected vertices
						geoCache._sharedNormalIndices[groupVertexList[vertexIndex + 0]].Add(new VertexEntry(submeshID, i1, normalIndex));
						geoCache._sharedNormalIndices[groupVertexList[vertexIndex + 1]].Add(new VertexEntry(submeshID, i2, normalIndex));
						geoCache._sharedNormalIndices[groupVertexList[vertexIndex + 2]].Add(new VertexEntry(submeshID, i3, normalIndex));
					}
				}
			}

			int numSubmeshes = subMeshesMap.Keys.Count;

			bool bGenerated = false;
			if (numSubmeshes > 0)
			{
				if (!geoCache._normalAttrInfo.exists)
				{
					// Normal calculation
					// Go throuch each vertex for the entire geometry and calculate the normal vector based on connected
					// vertices. This includes vertex connections between submeshes so we should get smooth transitions across submeshes.

					int numSharedNormals = geoCache._sharedNormalIndices.Length;
					for (int a = 0; a < numSharedNormals; ++a)
					{
						for (int b = 0; b < geoCache._sharedNormalIndices[a].Count; ++b)
						{
							Vector3 sumNormal = new Vector3();
							VertexEntry leftEntry = geoCache._sharedNormalIndices[a][b];
							HEU_MeshData leftSubMesh = subMeshesMap[leftEntry._meshKey];

							List<VertexEntry> rightList = geoCache._sharedNormalIndices[a];
							for (int c = 0; c < rightList.Count; ++c)
							{
								VertexEntry rightEntry = rightList[c];
								HEU_MeshData rightSubMesh = subMeshesMap[rightEntry._meshKey];

								if (leftEntry._vertexIndex == rightEntry._vertexIndex)
								{
									sumNormal += rightSubMesh._triangleNormals[rightEntry._normalIndex];
								}
								else
								{
									float dot = Vector3.Dot(leftSubMesh._triangleNormals[leftEntry._normalIndex],
										rightSubMesh._triangleNormals[rightEntry._normalIndex]);
									if (dot >= geoCache._cosineThreshold)
									{
										sumNormal += rightSubMesh._triangleNormals[rightEntry._normalIndex];
									}
								}
							}

							leftSubMesh._normals[leftEntry._vertexIndex] = sumNormal.normalized;
						}
					}
				}


				// Go through each valid submesh data and upload into a CombineInstance for combining.
				// Each CombineInstance represents a submesh in the final mesh.
				// And each submesh in that final mesh corresponds to a material.

				// Filter out only the submeshes with valid geometry
				List<Material> validMaterials = new List<Material>();
				List<int> validSubmeshes = new List<int>();

				Dictionary<int, HEU_MaterialData> assetMaterialMap = asset.GetMaterialDataMap();

				foreach (KeyValuePair<int, HEU_MeshData> meshPair in subMeshesMap)
				{
					HEU_MeshData meshData = meshPair.Value;
					if (meshData._indices.Count > 0)
					{
						int materialKey = meshPair.Key;

						// Find the material or create it
						HEU_MaterialData materialData = null;

						HEU_UnityMaterialInfo unityMaterialInfo = null;
						if (geoCache._unityMaterialInfos.TryGetValue(materialKey, out unityMaterialInfo))
						{
							if (!assetMaterialMap.TryGetValue(materialKey, out materialData))
							{
								// Create the material
								materialData = asset.CreateUnitySubstanceMaterialData(materialKey, unityMaterialInfo._unityMaterialPath, unityMaterialInfo._substancePath, unityMaterialInfo._substanceIndex);
								assetMaterialMap.Add(materialData._materialKey, materialData);
							}
						}
						else if (!assetMaterialMap.TryGetValue(materialKey, out materialData))
						{
							if (materialKey == defaultMaterialKey)
							{
								materialData = asset.GetOrCreateDefaultMaterialInCache(session, geoCache.GeoID, geoCache.PartID, false);
							}
							else
							{
								materialData = asset.CreateHoudiniMaterialData(session, materialKey, geoCache.GeoID, geoCache.PartID);
							}
						}

						if (materialData != null)
						{
							validSubmeshes.Add(meshPair.Key);
							validMaterials.Add(materialData._material);

							if (materialData != null && bPartInstanced)
							{
								// Handle GPU instancing on material for instanced meshes

								if (materialData._materialSource != HEU_MaterialData.Source.UNITY && materialData._materialSource != HEU_MaterialData.Source.SUBSTANCE)
								{
									// Always enable GPU instancing for material generated from Houdini
									HEU_MaterialFactory.EnableGPUInstancing(materialData._material);
								}
							}
						}
					}
				}

				int validNumSubmeshes = validSubmeshes.Count;
				CombineInstance[] meshCombiner = new CombineInstance[validNumSubmeshes];
				for (int submeshIndex = 0; submeshIndex < validNumSubmeshes; ++submeshIndex)
				{
					HEU_MeshData submesh = subMeshesMap[validSubmeshes[submeshIndex]];

					CombineInstance combine = new CombineInstance();
					combine.mesh = new Mesh();
#if UNITY_2017_3_OR_NEWER
					combine.mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#endif

					combine.mesh.SetVertices(submesh._vertices);

					combine.mesh.SetIndices(submesh._indices.ToArray(), MeshTopology.Triangles, 0);

					if (submesh._colors.Count > 0)
					{
						combine.mesh.SetColors(submesh._colors);
					}

					if (submesh._normals.Count > 0)
					{
						combine.mesh.SetNormals(submesh._normals);
					}

					if (submesh._tangents.Count > 0)
					{
						combine.mesh.SetTangents(submesh._tangents);
					}

					if (bGenerateUVs)
					{
						// TODO: revisit to test this out
						Vector2[] generatedUVs = HEU_GeometryUtility.GeneratePerTriangle(combine.mesh);
						if (generatedUVs != null)
						{
							combine.mesh.uv = generatedUVs;
						}
					}
					else if (submesh._UVs.Count > 0)
					{
						combine.mesh.SetUVs(0, submesh._UVs);
					}

					if (submesh._UV2s.Count > 0)
					{
						combine.mesh.SetUVs(1, submesh._UV2s);
					}

					if (submesh._UV3s.Count > 0)
					{
						combine.mesh.SetUVs(2, submesh._UV3s);
					}

					combine.transform = Matrix4x4.identity;
					combine.mesh.RecalculateBounds();

					//Debug.LogFormat("Number of submeshes {0}", combine.mesh.subMeshCount);

					meshCombiner[submeshIndex] = combine;
				}

				// Geometry data
				MeshFilter meshFilter = HEU_GeneralUtility.GetOrCreateComponent<MeshFilter>(gameObject);
				meshFilter.sharedMesh = new Mesh();
#if UNITY_2017_3_OR_NEWER
				meshFilter.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#endif
				meshFilter.sharedMesh.name = geoCache._partName + "_mesh";
				meshFilter.sharedMesh.CombineMeshes(meshCombiner, false, false);

				meshFilter.sharedMesh.RecalculateBounds();

				if (!geoCache._tangentAttrInfo.exists && bGenerateTangents)
				{
					HEU_GeometryUtility.CalculateMeshTangents(meshFilter.sharedMesh);
				}

				meshFilter.sharedMesh.UploadMeshData(true);

				// Render data
				MeshRenderer meshRenderer = HEU_GeneralUtility.GetOrCreateComponent<MeshRenderer>(gameObject);
				meshRenderer.sharedMaterials = validMaterials.ToArray();

				bGenerated = true;
			}

#if HEU_PROFILER_ON
			Debug.LogFormat("GENERATE MESH TIME:: {0}", (Time.realtimeSinceStartup - generateMeshTime));
#endif

			return bGenerated;
		}