private void GenerateMesh(List<HEU_LoadBufferMesh> meshBuffers) { HEU_SessionBase session = GetHoudiniSession(true); Transform parent = this.gameObject.transform; int numBuffers = meshBuffers.Count; for (int m = 0; m < numBuffers; ++m) { if (meshBuffers[m]._geoCache != null) { GameObject newGameObject = new GameObject("mesh_" + meshBuffers[m]._geoCache._partName); Transform newTransform = newGameObject.transform; newTransform.parent = parent; HEU_GeneratedOutput generatedOutput = new HEU_GeneratedOutput(); generatedOutput._outputData._gameObject = newGameObject; bool bResult = false; int numLODs = meshBuffers[m]._LODGroupMeshes != null ? meshBuffers[m]._LODGroupMeshes.Count : 0; if (numLODs > 1) { bResult = HEU_GenerateGeoCache.GenerateLODMeshesFromGeoGroups(session, meshBuffers[m]._LODGroupMeshes, meshBuffers[m]._geoCache, generatedOutput, meshBuffers[m]._defaultMaterialKey, meshBuffers[m]._bGenerateUVs, meshBuffers[m]._bGenerateTangents, meshBuffers[m]._bGenerateNormals, meshBuffers[m]._bPartInstanced); } else if (numLODs == 1) { bResult = HEU_GenerateGeoCache.GenerateMeshFromSingleGroup(session, meshBuffers[m]._LODGroupMeshes[0], meshBuffers[m]._geoCache, generatedOutput, meshBuffers[m]._defaultMaterialKey, meshBuffers[m]._bGenerateUVs, meshBuffers[m]._bGenerateTangents, meshBuffers[m]._bGenerateNormals, meshBuffers[m]._bPartInstanced); } else { // Set return state to false if no mesh and not a collider type bResult = (meshBuffers[m]._geoCache._colliderType != HEU_GenerateGeoCache.ColliderType.NONE); } if (bResult) { HEU_GenerateGeoCache.UpdateCollider(meshBuffers[m]._geoCache, generatedOutput._outputData._gameObject); meshBuffers[m]._generatedOutput = generatedOutput; _generatedOutputs.Add(generatedOutput); SetOutputVisiblity(meshBuffers[m]); } else { HEU_GeneratedOutput.DestroyGeneratedOutput(generatedOutput); } } } }
public static void CreateMaterialInfoEntryFromAttributeIndex(HEU_GenerateGeoCache geoCache, int materialAttributeIndex) { string unityMaterialName = null; string substanceName = null; int substanceIndex = -1; int materialKey = GetMaterialKeyFromAttributeIndex(geoCache, materialAttributeIndex, out unityMaterialName, out substanceName, out substanceIndex); if (!geoCache._unityMaterialInfos.ContainsKey(materialKey)) { geoCache._unityMaterialInfos.Add(materialKey, new HEU_UnityMaterialInfo(unityMaterialName, substanceName, substanceIndex)); } }
public static int GetMaterialKeyFromAttributeIndex(HEU_GenerateGeoCache geoCache, int attributeIndex, out string unityMaterialName, out string substanceName, out int substanceIndex) { unityMaterialName = null; substanceName = null; substanceIndex = -1; if (attributeIndex < geoCache._unityMaterialAttrName.Length && geoCache._unityMaterialAttrStringsMap.TryGetValue(geoCache._unityMaterialAttrName[attributeIndex], out unityMaterialName)) { if (geoCache._substanceMaterialAttrNameInfo.exists && geoCache._substanceMaterialAttrName.Length > 0) { geoCache._substanceMaterialAttrStringsMap.TryGetValue(geoCache._substanceMaterialAttrName[attributeIndex], out substanceName); } if (geoCache._substanceMaterialAttrIndexInfo.exists && string.IsNullOrEmpty(substanceName) && geoCache._substanceMaterialAttrIndex[attributeIndex] >= 0) { substanceIndex = geoCache._substanceMaterialAttrIndex[attributeIndex]; } return HEU_MaterialFactory.GetUnitySubstanceMaterialKey(unityMaterialName, substanceName, substanceIndex); } return HEU_Defines.HEU_INVALID_MATERIAL; }
public bool GenerateMeshBuffers(HEU_SessionBase session, HAPI_NodeId nodeID, List<HAPI_PartInfo> meshParts, bool bSplitPoints, bool bUseLODGroups, bool bGenerateUVs, bool bGenerateTangents, bool bGenerateNormals, out List<HEU_LoadBufferMesh> meshBuffers) { meshBuffers = null; if (meshParts.Count == 0) { return true; } bool bSuccess = true; string assetCacheFolderPath = ""; meshBuffers = new List<HEU_LoadBufferMesh>(); foreach(HAPI_PartInfo partInfo in meshParts) { HAPI_NodeId geoID = nodeID; int partID = partInfo.id; string partName = HEU_SessionManager.GetString(partInfo.nameSH, session); bool bPartInstanced = partInfo.isInstanced; if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_MESH) { List<HEU_MaterialData> materialCache = new List<HEU_MaterialData>(); HEU_GenerateGeoCache geoCache = HEU_GenerateGeoCache.GetPopulatedGeoCache(session, -1, geoID, partID, bUseLODGroups, materialCache, assetCacheFolderPath); if (geoCache == null) { // Failed to get necessary info for generating geometry. SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Failed to generate geometry cache for part: {0}", partName)); continue; } geoCache._materialCache = materialCache; // Build the GeoGroup using points or vertices bool bResult = false; List<HEU_GeoGroup> LODGroupMeshes = null; int defaultMaterialKey = 0; if (bSplitPoints) { bResult = HEU_GenerateGeoCache.GenerateGeoGroupUsingGeoCachePoints(session, geoCache, bGenerateUVs, bGenerateTangents, bGenerateNormals, bUseLODGroups, bPartInstanced, out LODGroupMeshes, out defaultMaterialKey); } else { bResult = HEU_GenerateGeoCache.GenerateGeoGroupUsingGeoCacheVertices(session, geoCache, bGenerateUVs, bGenerateTangents, bGenerateNormals, bUseLODGroups, bPartInstanced, out LODGroupMeshes, out defaultMaterialKey); } if (bResult) { HEU_LoadBufferMesh meshBuffer = new HEU_LoadBufferMesh(); meshBuffer.InitializeBuffer(partID, partName, partInfo.isInstanced, false); meshBuffer._geoCache = geoCache; meshBuffer._LODGroupMeshes = LODGroupMeshes; meshBuffer._defaultMaterialKey = defaultMaterialKey; meshBuffer._bGenerateUVs = bGenerateUVs; meshBuffer._bGenerateTangents = bGenerateTangents; meshBuffer._bGenerateNormals = bGenerateNormals; meshBuffer._bPartInstanced = partInfo.isInstanced; meshBuffers.Add(meshBuffer); } else { SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Failed to generated geometry for part: {0}", partName)); } } } return bSuccess; }
private void GenerateMesh(HAPI_NodeId cookNodeId, List <HEU_LoadBufferMesh> meshBuffers) { HEU_SessionBase session = GetHoudiniSession(true); Transform parent = this.gameObject.transform; int numBuffers = meshBuffers.Count; for (int m = 0; m < numBuffers; ++m) { if (meshBuffers[m]._geoCache != null) { GameObject newGameObject = new GameObject("mesh_" + meshBuffers[m]._geoCache._partName); HAPI_PartId partId = meshBuffers[m]._geoCache.PartID; ApplyAttributeModifiersOnGameObjectOutput(session, cookNodeId, partId, ref newGameObject); Transform newTransform = newGameObject.transform; newTransform.parent = parent; HEU_GeneratedOutput generatedOutput = new HEU_GeneratedOutput(); generatedOutput._outputData._gameObject = newGameObject; bool bResult = false; int numLODs = meshBuffers[m]._LODGroupMeshes != null ? meshBuffers[m]._LODGroupMeshes.Count : 0; if (numLODs > 1) { bResult = HEU_GenerateGeoCache.GenerateLODMeshesFromGeoGroups(session, meshBuffers[m]._LODGroupMeshes, meshBuffers[m]._geoCache, generatedOutput, meshBuffers[m]._defaultMaterialKey, meshBuffers[m]._bGenerateUVs, meshBuffers[m]._bGenerateTangents, meshBuffers[m]._bGenerateNormals, meshBuffers[m]._bPartInstanced); } else if (numLODs == 1) { bResult = HEU_GenerateGeoCache.GenerateMeshFromSingleGroup(session, meshBuffers[m]._LODGroupMeshes[0], meshBuffers[m]._geoCache, generatedOutput, meshBuffers[m]._defaultMaterialKey, meshBuffers[m]._bGenerateUVs, meshBuffers[m]._bGenerateTangents, meshBuffers[m]._bGenerateNormals, meshBuffers[m]._bPartInstanced); HEU_GeneralUtility.UpdateGeneratedAttributeStore(session, _cookNodeID, meshBuffers[m]._id, generatedOutput._outputData._gameObject); } else { // Set return state to false if no mesh and no colliders (i.e. nothing is generated) bResult = (meshBuffers[m]._geoCache._colliderInfos.Count > 0); } if (bResult) { HEU_GenerateGeoCache.UpdateColliders(meshBuffers[m]._geoCache, generatedOutput._outputData); meshBuffers[m]._generatedOutput = generatedOutput; _generatedOutputs.Add(generatedOutput); SetOutputVisiblity(meshBuffers[m]); } else { HEU_GeneratedOutput.DestroyGeneratedOutput(generatedOutput); } } } }
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; }
public static HEU_GenerateGeoCache GetPopulatedGeoCache(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID) { #if HEU_PROFILER_ON float generateGeoCacheStartTime = Time.realtimeSinceStartup; #endif HEU_GenerateGeoCache geoCache = new HEU_GenerateGeoCache(); Debug.Assert(geoID != HEU_Defines.HEU_INVALID_NODE_ID, "Invalid Geo ID! Unable to update materials!"); Debug.Assert(partID != HEU_Defines.HEU_INVALID_NODE_ID, "Invalid Part ID! Unable to update materials!"); geoCache._geoInfo = new HAPI_GeoInfo(); if (!session.GetGeoInfo(geoID, ref geoCache._geoInfo)) { return null; } geoCache._partInfo = new HAPI_PartInfo(); if (!session.GetPartInfo(geoID, partID, ref geoCache._partInfo)) { return null; } geoCache._partName = HEU_SessionManager.GetString(geoCache._partInfo.nameSH, session); // Unity max indices limitation! #if UNITY_2017_3_OR_NEWER uint UNITY_MAX_VERTS = uint.MaxValue; #else uint UNITY_MAX_VERTS = ushort.MaxValue; #endif uint maxVertexCount = Convert.ToUInt32(geoCache._partInfo.vertexCount); if (maxVertexCount > UNITY_MAX_VERTS) { Debug.LogErrorFormat("Part {0} has vertex count of {1} which is above Unity maximum of {2}.\nUse Unity 2017.3+ or reduce this in Houdini.", geoCache._partName, maxVertexCount, UNITY_MAX_VERTS); return null; } geoCache._vertexList = new int[geoCache._partInfo.vertexCount]; if (!HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetVertexList, geoCache._vertexList, 0, geoCache._partInfo.vertexCount)) { return null; } geoCache._houdiniMaterialIDs = new HAPI_NodeId[geoCache._partInfo.faceCount]; if (!session.GetMaterialNodeIDsOnFaces(geoID, partID, ref geoCache._singleFaceHoudiniMaterial, geoCache._houdiniMaterialIDs, geoCache._partInfo.faceCount)) { return null; } geoCache.PopulateUnityMaterialData(session); if (!geoCache.PopulateGeometryData(session)) { return null; } #if HEU_PROFILER_ON Debug.LogFormat("GENERATE GEO CACHE TIME:: {0}", (Time.realtimeSinceStartup - generateGeoCacheStartTime)); #endif return geoCache; }