/// <summary> /// Upload the inputData (mesh geometry) into the input node with inputNodeID. /// </summary> /// <param name="session">Session that the input node exists in</param> /// <param name="inputNodeID">ID of the input node</param> /// <param name="inputData">Container of the mesh geometry</param> /// <returns>True if successfully uploaded data</returns> public bool UploadData(HEU_SessionBase session, HAPI_NodeId inputNodeID, HEU_InputData inputData) { HEU_InputDataMeshes inputDataMeshes = inputData as HEU_InputDataMeshes; if (inputDataMeshes == null) { Debug.LogError("Expected HEU_InputDataMeshes type for inputData, but received unsupported type."); return false; } List<Vector3> vertices = new List<Vector3>(); List<Vector3> normals = new List<Vector3>(); List<Vector2> uvs = new List<Vector2>(); List<Color> colors = new List<Color>(); List<int> pointIndexList = new List<int>(); List<int> vertIndexList = new List<int>(); int numMaterials = 0; int numMeshes = inputDataMeshes._inputMeshes.Count; // Get the parent's world transform, so when there are multiple child meshes, // can merge and apply their local transform after subtracting their parent's world transform Matrix4x4 rootInvertTransformMatrix = Matrix4x4.identity; if (numMeshes > 1) { rootInvertTransformMatrix = inputDataMeshes._inputObject.transform.worldToLocalMatrix; } // For all meshes: // Accumulate vertices, normals, uvs, colors, and indices. // Keep track of indices start and count for each mesh for later when uploading material assignments and groups. // Find shared vertices, and use unique set of vertices to use as point positions. // Need to reindex indices for both unique vertices, as well as vertex attributes. for (int i = 0; i < numMeshes; ++i) { Vector3[] meshVertices = inputDataMeshes._inputMeshes[i]._mesh.vertices; Matrix4x4 localToWorld = inputDataMeshes._inputMeshes[i]._transform.localToWorldMatrix * rootInvertTransformMatrix; List<Vector3> uniqueVertices = new List<Vector3>(); // Keep track of old vertex positions (old vertex slot points to new unique vertex slot) int[] reindexVertices = new int[meshVertices.Length]; for (int j = 0; j < meshVertices.Length; ++j) { reindexVertices[j] = -1; } // For each vertex, check against subsequent vertices for shared positions. for (int a = 0; a < meshVertices.Length; ++a) { Vector3 va = meshVertices[a]; if (reindexVertices[a] == -1) { if (numMeshes > 1 && !inputDataMeshes._hasLOD) { // For multiple meshes that are not LODs, apply local transform on vertices to get the merged mesh. uniqueVertices.Add(localToWorld.MultiplyPoint(va)); } else { uniqueVertices.Add(va); } // Reindex to point to unique vertex slot reindexVertices[a] = uniqueVertices.Count - 1; } for (int b = a + 1; b < meshVertices.Length; ++b) { if (va == meshVertices[b]) { // Shared vertex -> reindex to point to unique vertex slot reindexVertices[b] = reindexVertices[a]; } } } int vertexOffset = vertices.Count; vertices.AddRange(uniqueVertices); Vector3[] meshNormals = inputDataMeshes._inputMeshes[i]._mesh.normals; Vector2[] meshUVs = inputDataMeshes._inputMeshes[i]._mesh.uv; Color[] meshColors = inputDataMeshes._inputMeshes[i]._mesh.colors; inputDataMeshes._inputMeshes[i]._indexStart = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes]; inputDataMeshes._inputMeshes[i]._indexCount = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes]; // For each submesh: // Generate face to point index -> pointIndexList // Generate face to vertex attribute index -> vertIndexList for (int j = 0; j < inputDataMeshes._inputMeshes[i]._numSubMeshes; ++j) { int indexStart = pointIndexList.Count; int vertIndexStart = vertIndexList.Count; // Indices have to be re-indexed with our own offset int[] meshIndices = inputDataMeshes._inputMeshes[i]._mesh.GetTriangles(j); int numIndices = meshIndices.Length; for (int k = 0; k < numIndices; ++k) { int originalIndex = meshIndices[k]; meshIndices[k] = reindexVertices[originalIndex]; pointIndexList.Add(vertexOffset + meshIndices[k]); vertIndexList.Add(vertIndexStart + k); if (meshNormals != null && (originalIndex < meshNormals.Length)) { normals.Add(meshNormals[originalIndex]); } if (meshUVs != null && (originalIndex < meshUVs.Length)) { uvs.Add(meshUVs[originalIndex]); } if (meshColors != null && (originalIndex < meshColors.Length)) { colors.Add(meshColors[originalIndex]); } } inputDataMeshes._inputMeshes[i]._indexStart[j] = (uint)indexStart; inputDataMeshes._inputMeshes[i]._indexCount[j] = (uint)(pointIndexList.Count) - inputDataMeshes._inputMeshes[i]._indexStart[j]; } numMaterials += inputDataMeshes._inputMeshes[i]._materials != null ? inputDataMeshes._inputMeshes[i]._materials.Length : 0; } // It is possible for some meshes to not have normals/uvs/colors while others do. // In the case where an attribute is missing on some meshes, we clear out those attributes so we don't upload // partial attribute data. int totalAllVertexCount = vertIndexList.Count; if (normals.Count != totalAllVertexCount) { normals = null; } if (uvs.Count != totalAllVertexCount) { uvs = null; } if (colors.Count != totalAllVertexCount) { colors = null; } HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = vertIndexList.Count / 3; partInfo.vertexCount = vertIndexList.Count; partInfo.pointCount = vertices.Count; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 0; if (normals != null && normals.Count > 0) { partInfo.vertexAttributeCount++; } if (uvs != null && uvs.Count > 0) { partInfo.vertexAttributeCount++; } if (colors != null && colors.Count > 0) { partInfo.vertexAttributeCount++; } if (numMaterials > 0) { partInfo.primitiveAttributeCount++; } if (numMeshes > 0) { partInfo.primitiveAttributeCount++; } if (inputDataMeshes._hasLOD) { partInfo.primitiveAttributeCount++; partInfo.detailAttributeCount++; } HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo)) { return false; } HAPI_NodeId displayNodeID = displayGeoInfo.nodeId; if (!session.SetPartInfo(displayNodeID, 0, ref partInfo)) { Debug.LogError("Failed to set input part info. "); return false; } int[] faceCounts = new int[partInfo.faceCount]; for (int i = 0; i < partInfo.faceCount; ++i) { faceCounts[i] = 3; } int[] triIndices = pointIndexList.ToArray(); if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetFaceCount, faceCounts, 0, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry face counts."); return false; } if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetVertexList, triIndices, 0, partInfo.vertexCount)) { Debug.LogError("Failed to set input geometry indices."); return false; } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true)) { Debug.LogError("Failed to set input geometry position."); return false; } int[] vertIndices = vertIndexList.ToArray(); //if(normals != null && !SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), ref partInfo, true)) if (normals != null && !HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), vertIndices, ref partInfo, true)) { Debug.LogError("Failed to set input geometry normals."); return false; } if (uvs != null && uvs.Count > 0) { Vector3[] uvs3 = new Vector3[uvs.Count]; for (int i = 0; i < uvs.Count; ++i) { uvs3[i][0] = uvs[i][0]; uvs3[i][1] = uvs[i][1]; uvs3[i][2] = 0; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_UV, 3, uvs3, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_UV, 3, uvs3, vertIndices, ref partInfo, false)) { Debug.LogError("Failed to set input geometry UVs."); return false; } } if (colors != null && colors.Count > 0) { Vector3[] rgb = new Vector3[colors.Count]; float[] alpha = new float[colors.Count]; for (int i = 0; i < colors.Count; ++i) { rgb[i][0] = colors[i].r; rgb[i][1] = colors[i].g; rgb[i][2] = colors[i].b; alpha[i] = colors[i].a; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, vertIndices, ref partInfo, false)) { Debug.LogError("Failed to set input geometry colors."); return false; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexFloatAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, vertIndices, ref partInfo)) { Debug.LogError("Failed to set input geometry color alpha."); return false; } } // Set material names for round-trip perservation of material assignment // Each HEU_UploadMeshData might have a list of submeshes and materials // These are all combined into a single mesh, with group names if (numMaterials > 0) { bool bFoundAtleastOneValidMaterial = false; string[] materialIDs = new string[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { if (inputDataMeshes._inputMeshes[g]._numSubMeshes != inputDataMeshes._inputMeshes[g]._materials.Length) { // Number of submeshes should equal number of materials since materials determine submeshes continue; } for (int i = 0; i < inputDataMeshes._inputMeshes[g]._materials.Length; ++i) { string materialName = HEU_AssetDatabase.GetAssetPathWithSubAssetSupport(inputDataMeshes._inputMeshes[g]._materials[i]); if (materialName == null) { materialName = ""; } else if (materialName.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES)) { materialName = HEU_AssetDatabase.GetUniqueAssetPathForUnityAsset(inputDataMeshes._inputMeshes[g]._materials[i]); } bFoundAtleastOneValidMaterial |= !string.IsNullOrEmpty(materialName); int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / 3; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / 3); for (int m = faceStart; m < faceEnd; ++m) { materialIDs[m] = materialName; } } } if (bFoundAtleastOneValidMaterial) { HAPI_AttributeInfo materialIDAttrInfo = new HAPI_AttributeInfo(); materialIDAttrInfo.exists = true; materialIDAttrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; materialIDAttrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; materialIDAttrInfo.count = partInfo.faceCount; materialIDAttrInfo.tupleSize = 1; materialIDAttrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (!session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo)) { Debug.LogError("Failed to add input geometry unity material name attribute."); return false; } if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo, materialIDs, session.SetAttributeStringData, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry unity material name."); return false; } } } // Set mesh name attribute HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; attrInfo.count = partInfo.faceCount; attrInfo.tupleSize = 1; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo)) { string[] primitiveNameAttr = new string[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { for (int i = 0; i < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++i) { int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / 3; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / 3); for (int m = faceStart; m < faceEnd; ++m) { primitiveNameAttr[m] = inputDataMeshes._inputMeshes[g]._meshPath; } } } if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo, primitiveNameAttr, session.SetAttributeStringData, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry unity mesh name."); return false; } } else { return false; } // Set LOD group membership if (inputDataMeshes._hasLOD) { int[] membership = new int[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { if (g > 0) { // Clear array for (int m = 0; m < partInfo.faceCount; ++m) { membership[m] = 0; } } // Set 1 for faces belonging to this group for (int s = 0; s < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++s) { int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[s] / 3; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[s] / 3); for (int m = faceStart; m < faceEnd; ++m) { membership[m] = 1; } } if (!session.AddGroup(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName)) { Debug.LogError("Failed to add input geometry LOD group name."); return false; } if (!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName, membership, 0, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry LOD group name."); return false; } } } return session.CommitGeo(displayNodeID); }
private bool UploadData( HEU_SessionBase session, HAPI_NodeId inputNodeID, HEU_InputData inputData) { HEU_InputDataTilemap inputTilemap = inputData as HEU_InputDataTilemap; if(inputTilemap == null) { Debug.LogError("Expected HEU_InputDataTilemap type for inputData, but received unssupported type."); return false; } List<Vector3> vertices = new List<Vector3>(); List<Vector3> colors = new List<Vector3>(); //List<Vector3> uvs = new List<Vector3>(); List<Vector3> normals = new List<Vector3>(); List<string> tileNames = new List<string>(); List<Vector3> tileSizes = new List<Vector3>(); List<Vector3Int> tileCoords = new List<Vector3Int>(); Tilemap tileMap = inputTilemap._tilemap; Grid gridLayout = tileMap.layoutGrid; //Get a list of unique tiles used TileBase[] usedTiles = new TileBase[ tileMap.GetUsedTilesCount() ]; tileMap.GetUsedTilesNonAlloc(usedTiles); TileBase[] tileArray = tileMap.GetTilesBlock( tileMap.cellBounds ); //tileArray = tileArray.Where( x => x != null).ToArray(); //only existing tiles int tileCount = 0; Vector3 anchorOffset = tileMap.tileAnchor; anchorOffset.Scale(gridLayout.cellSize); Vector3 pointPos; Vector3 pointNormal = new Vector3(0.0f, 0.0f, 1.0f); Vector3Int boundsMin = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue); Vector3Int boundsMax = new Vector3Int(int.MinValue, int.MinValue, int.MinValue); foreach (Vector3Int tilePos in tileMap.cellBounds.allPositionsWithin) { if (tileMap.HasTile(tilePos)) { boundsMin = Vector3Int.Min(tilePos, boundsMin); boundsMax = Vector3Int.Max(tilePos, boundsMax); } } boundsMax += Vector3Int.one; BoundsInt tileMapBounds = new BoundsInt { min = boundsMin, max = boundsMax }; //foreach (Vector3Int tilePos in tileMap.cellBounds.allPositionsWithin) Vector3Int tilePosReverseX = new Vector3Int(); foreach(Vector3Int tilePos in tileMapBounds.allPositionsWithin) { #if !EXPORT_RECT_GRID if(!tileMap.HasTile(tilePos)) continue; #endif tilePosReverseX = tilePos; //For Hudini (to use Labs Wang Tile tools, we need to reverse point order on the x axis) //so we just iterate in reverse order on the x tilePosReverseX.x = tileMapBounds.size.x - 1 - tilePos.x + 2 * tileMapBounds.min.x; tileCount++; pointPos = tileMap.CellToLocal(tilePosReverseX) + anchorOffset; vertices.Add(pointPos); normals.Add(pointNormal); if (tileMap.HasTile(tilePosReverseX)) { Tile tile = tileMap.GetTile<Tile>(tilePosReverseX); tileNames.Add(tile.name); colors.Add(new Vector3(tile.color.r, tile.color.g, tile.color.b)); tileSizes.Add(new Vector3(tile.sprite.rect.size.x / tile.sprite.pixelsPerUnit, tile.sprite.rect.size.y / tile.sprite.pixelsPerUnit, 0.0f)); } else { tileNames.Add(""); colors.Add(Vector3.zero); tileSizes.Add(Vector3.zero); } tileCoords.Add(tilePosReverseX); } HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = 0; partInfo.vertexCount = 0; partInfo.pointCount = tileCount; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 0; HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if(!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo)) { return false; } HAPI_NodeId displayNodeID = displayGeoInfo.nodeId; if(!session.SetPartInfo(displayNodeID, 0, ref partInfo)) { Debug.LogError("Failed to set input part info. "); return false; } if(!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true)) { Debug.LogError("Failed to set point positions."); return false; } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, "size", 2, tileSizes.ToArray(), ref partInfo, false)) { Debug.Log("Failed to set tile size attributes. "); return false; } if(!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, colors.ToArray(), ref partInfo, false)) { Debug.Log("Failed to set tile color attributes. "); return false; } if(!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), ref partInfo, true)) { Debug.Log("Failed to set point normal attributes."); return false; } if(!HEU_InputMeshUtilityExt.SetMeshPointAttribute(session, displayNodeID, 0, "tilepos", 2, tileCoords.ToArray(), ref partInfo)) { Debug.Log("Failed to set point tile coordinates attributes."); return false; } #if TILENAME_GROUPS //Set point groups based on tile type int[] pointGroupMembership = new int[tileCount]; foreach(TileBase tileType in usedTiles) { if(!session.AddGroup( displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_POINT, tileType.name)) return false; int index = 0; foreach( string tileName in tileNames) { if(tileName.Equals(tileType.name)) pointGroupMembership[index] = 1; else pointGroupMembership[index] = 0; index++; } if(!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_POINT, tileType.name, pointGroupMembership, 0, tileCount)) return false; } #else if(!HEU_InputMeshUtilityExt.SetMeshPointAttribute(session, displayNodeID, 0, "tilename", tileNames.ToArray(), ref partInfo)) { Debug.Log("Failed to set point tile name attributes."); return false; } #endif if(!HEU_InputMeshUtilityExt.SetMeshDetailAttribute(session, displayNodeID, 0, "bounds", 2, tileMapBounds.size, ref partInfo)) { Debug.Log("Failed to set detail tile map bounds attribute."); return false; } return session.CommitGeo(displayNodeID); }
/// <summary> /// Upload the inputData (mesh geometry) into the input node with inputNodeID. /// </summary> /// <param name="session">Session that the input node exists in</param> /// <param name="inputNodeID">ID of the input node</param> /// <param name="inputData">Container of the mesh geometry</param> /// <returns>True if successfully uploaded data</returns> public bool UploadData(HEU_SessionBase session, HAPI_NodeId inputNodeID, HEU_InputData inputData) { HEU_InputDataMeshes inputDataMeshes = inputData as HEU_InputDataMeshes; if (inputDataMeshes == null) { Debug.LogError("Expected HEU_InputDataMeshes type for inputData, but received unsupported type."); return false; } List<Vector3> vertices = new List<Vector3>(); List<Vector3> normals = new List<Vector3>(); List<Color> colors = new List<Color>(); #if UNITY_2018_2_OR_NEWER const int NumUVSets = 8; #else const int NumUVSets = 4; #endif List<Vector3>[] uvs = new List<Vector3>[NumUVSets]; for (int u = 0; u < NumUVSets; ++u) { uvs[u] = new List<Vector3>(); } // Use tempUVs to help with reindexing List<Vector3>[] tempUVs = new List<Vector3>[NumUVSets]; for (int u = 0; u < NumUVSets; ++u) { tempUVs[u] = new List<Vector3>(); } List<int> pointIndexList = new List<int>(); List<int> vertIndexList = new List<int>(); int numMaterials = 0; int numMeshes = inputDataMeshes._inputMeshes.Count; // Get the parent's world transform, so when there are multiple child meshes, // can merge and apply their local transform after subtracting their parent's world transform Matrix4x4 rootInvertTransformMatrix = Matrix4x4.identity; if (numMeshes > 1) { rootInvertTransformMatrix = inputDataMeshes._inputObject.transform.worldToLocalMatrix; } // Always using the first submesh topology. This doesn't support mixed topology (triangles and quads). MeshTopology meshTopology = inputDataMeshes._inputMeshes[0]._mesh.GetTopology(0); int numVertsPerFace = 3; if (meshTopology == MeshTopology.Quads) { numVertsPerFace = 4; } // For all meshes: // Accumulate vertices, normals, uvs, colors, and indices. // Keep track of indices start and count for each mesh for later when uploading material assignments and groups. // Find shared vertices, and use unique set of vertices to use as point positions. // Need to reindex indices for both unique vertices, as well as vertex attributes. for (int i = 0; i < numMeshes; ++i) { Vector3[] meshVertices = inputDataMeshes._inputMeshes[i]._mesh.vertices; Matrix4x4 localToWorld = rootInvertTransformMatrix * inputDataMeshes._inputMeshes[i]._transform.localToWorldMatrix; List<Vector3> uniqueVertices = new List<Vector3>(); // Keep track of old vertex positions (old vertex slot points to new unique vertex slot) int[] reindexVertices = new int[meshVertices.Length]; Dictionary<Vector3, int> reindexMap = new Dictionary<Vector3, int>(); // For each vertex, check against subsequent vertices for shared positions. for (int a = 0; a < meshVertices.Length; ++a) { Vector3 va = meshVertices[a]; if (!reindexMap.ContainsKey(va)) { if (numMeshes > 1 && !inputDataMeshes._hasLOD) { // For multiple meshes that are not LODs, apply local transform on vertices to get the merged mesh. uniqueVertices.Add(localToWorld.MultiplyPoint(va)); } else { uniqueVertices.Add(va); } // Reindex to point to unique vertex slot reindexVertices[a] = uniqueVertices.Count - 1; reindexMap[va] = uniqueVertices.Count - 1; } else { reindexVertices[a] = reindexMap[va]; } } int vertexOffset = vertices.Count; vertices.AddRange(uniqueVertices); Vector3[] meshNormals = inputDataMeshes._inputMeshes[i]._mesh.normals; Color[] meshColors = inputDataMeshes._inputMeshes[i]._mesh.colors; // This is really silly. mesh.GetUVs gives uvs regardless if they exist or not (makes duplicates of // first uv if they don't exist), but mesh.uv* gives correct UVs, but in Vector2 format. // Since we need to convert to Vector3 later, this checks mesh.uv*, then uses mesh.GetUVs to get in Vector3. // Note skipping uv1 as its internally used (i.e. the 2nd uv set is uv2) int uindex = 0; GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv, tempUVs[0], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv2, tempUVs[1], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv3, tempUVs[2], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv4, tempUVs[3], uindex++); #if UNITY_2018_2_OR_NEWER GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv5, tempUVs[4], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv6, tempUVs[5], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv7, tempUVs[6], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv8, tempUVs[7], uindex++); #endif inputDataMeshes._inputMeshes[i]._indexStart = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes]; inputDataMeshes._inputMeshes[i]._indexCount = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes]; // For each submesh: // Generate face to point index -> pointIndexList // Generate face to vertex attribute index -> vertIndexList for (int j = 0; j < inputDataMeshes._inputMeshes[i]._numSubMeshes; ++j) { int indexStart = pointIndexList.Count; int vertIndexStart = vertIndexList.Count; // Indices have to be re-indexed with our own offset // (using GetIndices to generalize triangles and quad indices) int[] meshIndices = inputDataMeshes._inputMeshes[i]._mesh.GetIndices(j); int numIndices = meshIndices.Length; for (int k = 0; k < numIndices; ++k) { int originalIndex = meshIndices[k]; meshIndices[k] = reindexVertices[originalIndex]; pointIndexList.Add(vertexOffset + meshIndices[k]); vertIndexList.Add(vertIndexStart + k); if (meshNormals != null && (originalIndex < meshNormals.Length)) { normals.Add(meshNormals[originalIndex]); } for (int u = 0; u < NumUVSets; ++u) { if (tempUVs[u].Count > 0) { uvs[u].Add(tempUVs[u][originalIndex]); } } if (meshColors != null && (originalIndex < meshColors.Length)) { colors.Add(meshColors[originalIndex]); } } inputDataMeshes._inputMeshes[i]._indexStart[j] = (uint)indexStart; inputDataMeshes._inputMeshes[i]._indexCount[j] = (uint)(pointIndexList.Count) - inputDataMeshes._inputMeshes[i]._indexStart[j]; } numMaterials += inputDataMeshes._inputMeshes[i]._materials != null ? inputDataMeshes._inputMeshes[i]._materials.Length : 0; } // It is possible for some meshes to not have normals/uvs/colors while others do. // In the case where an attribute is missing on some meshes, we clear out those attributes so we don't upload // partial attribute data. int totalAllVertexCount = vertIndexList.Count; if (normals.Count != totalAllVertexCount) { normals = null; } if (colors.Count != totalAllVertexCount) { colors = null; } HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = vertIndexList.Count / numVertsPerFace; partInfo.vertexCount = vertIndexList.Count; partInfo.pointCount = vertices.Count; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 0; //Debug.LogFormat("Faces: {0}; Vertices: {1}; Verts/Face: {2}", partInfo.faceCount, partInfo.vertexCount, numVertsPerFace); if (normals != null && normals.Count > 0) { partInfo.vertexAttributeCount++; } for (int u = 0; u < NumUVSets; ++u) { if (uvs[u].Count > 0 && uvs[u].Count == totalAllVertexCount) { partInfo.vertexAttributeCount++; } else { uvs[u].Clear(); } } if (colors != null && colors.Count > 0) { partInfo.vertexAttributeCount++; } if (numMaterials > 0) { partInfo.primitiveAttributeCount++; } if (numMeshes > 0) { partInfo.primitiveAttributeCount++; } if (inputDataMeshes._hasLOD) { partInfo.primitiveAttributeCount++; partInfo.detailAttributeCount++; } HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo)) { return false; } HAPI_NodeId displayNodeID = displayGeoInfo.nodeId; if (!session.SetPartInfo(displayNodeID, 0, ref partInfo)) { Debug.LogError("Failed to set input part info. "); return false; } int[] faceCounts = new int[partInfo.faceCount]; for (int i = 0; i < partInfo.faceCount; ++i) { faceCounts[i] = numVertsPerFace; } int[] faceIndices = pointIndexList.ToArray(); if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetFaceCount, faceCounts, 0, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry face counts."); return false; } if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetVertexList, faceIndices, 0, partInfo.vertexCount)) { Debug.LogError("Failed to set input geometry indices."); return false; } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true)) { Debug.LogError("Failed to set input geometry position."); return false; } int[] vertIndices = vertIndexList.ToArray(); //if(normals != null && !SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), ref partInfo, true)) if (normals != null && !HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), vertIndices, ref partInfo, true)) { Debug.LogError("Failed to set input geometry normals."); return false; } for (int u = 0; u < NumUVSets; ++u) { if (uvs[u].Count > 0) { // Skip uv1 as its used internally. So it goes: uv, uv2, ..., uv8 string uvName = u == 0 ? HEU_Defines.HAPI_ATTRIB_UV : string.Format("{0}{1}", HEU_Defines.HAPI_ATTRIB_UV, u + 1); if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, uvName, 3, uvs[u].ToArray(), vertIndices, ref partInfo, false)) { Debug.LogError("Failed to set input geometry UV" + u); return false; } } } if (colors != null && colors.Count > 0) { Vector3[] rgb = new Vector3[colors.Count]; float[] alpha = new float[colors.Count]; for (int i = 0; i < colors.Count; ++i) { rgb[i][0] = colors[i].r; rgb[i][1] = colors[i].g; rgb[i][2] = colors[i].b; alpha[i] = colors[i].a; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, vertIndices, ref partInfo, false)) { Debug.LogError("Failed to set input geometry colors."); return false; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexFloatAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, vertIndices, ref partInfo)) { Debug.LogError("Failed to set input geometry color alpha."); return false; } } // Set material names for round-trip perservation of material assignment // Each HEU_UploadMeshData might have a list of submeshes and materials // These are all combined into a single mesh, with group names if (numMaterials > 0) { bool bFoundAtleastOneValidMaterial = false; string[] materialIDs = new string[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { if (inputDataMeshes._inputMeshes[g]._numSubMeshes != inputDataMeshes._inputMeshes[g]._materials.Length) { // Number of submeshes should equal number of materials since materials determine submeshes continue; } for (int i = 0; i < inputDataMeshes._inputMeshes[g]._materials.Length; ++i) { string materialName = HEU_AssetDatabase.GetAssetPathWithSubAssetSupport(inputDataMeshes._inputMeshes[g]._materials[i]); if (materialName == null) { materialName = ""; } else if (materialName.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES)) { materialName = HEU_AssetDatabase.GetUniqueAssetPathForUnityAsset(inputDataMeshes._inputMeshes[g]._materials[i]); } bFoundAtleastOneValidMaterial |= !string.IsNullOrEmpty(materialName); int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / numVertsPerFace; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / numVertsPerFace); for (int m = faceStart; m < faceEnd; ++m) { materialIDs[m] = materialName; } } } if (bFoundAtleastOneValidMaterial) { HAPI_AttributeInfo materialIDAttrInfo = new HAPI_AttributeInfo(); materialIDAttrInfo.exists = true; materialIDAttrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; materialIDAttrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; materialIDAttrInfo.count = partInfo.faceCount; materialIDAttrInfo.tupleSize = 1; materialIDAttrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (!session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo)) { Debug.LogError("Failed to add input geometry unity material name attribute."); return false; } if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo, materialIDs, session.SetAttributeStringData, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry unity material name."); return false; } } } // Set mesh name attribute HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; attrInfo.count = partInfo.faceCount; attrInfo.tupleSize = 1; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo)) { string[] primitiveNameAttr = new string[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { for (int i = 0; i < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++i) { int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / numVertsPerFace; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / numVertsPerFace); for (int m = faceStart; m < faceEnd; ++m) { primitiveNameAttr[m] = inputDataMeshes._inputMeshes[g]._meshPath; } } } if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo, primitiveNameAttr, session.SetAttributeStringData, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry unity mesh name."); return false; } } else { return false; } // Set LOD group membership if (inputDataMeshes._hasLOD) { int[] membership = new int[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { if (g > 0) { // Clear array for (int m = 0; m < partInfo.faceCount; ++m) { membership[m] = 0; } } // Set 1 for faces belonging to this group for (int s = 0; s < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++s) { int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[s] / numVertsPerFace; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[s] / numVertsPerFace); for (int m = faceStart; m < faceEnd; ++m) { membership[m] = 1; } } if (!session.AddGroup(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName)) { Debug.LogError("Failed to add input geometry LOD group name."); return false; } if (!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName, membership, 0, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry LOD group name."); return false; } } } return session.CommitGeo(displayNodeID); }
private bool UploadData(HEU_SessionBase session, HAPI_NodeId inputNodeID, HEU_InputData inputData) { if (settings == null) { HEU_Logger.LogError("Tilemap Settings not found!"); return(false); } HEU_InputDataTilemap inputTilemap = inputData as HEU_InputDataTilemap; if (inputTilemap == null) { HEU_Logger.LogError("Expected HEU_InputDataTilemap type for inputData, but received unssupported type."); return(false); } List <Vector3> vertices = new List <Vector3>(); List <Vector3> colors = new List <Vector3>(); List <string> tileNames = new List <string>(); List <Vector3> tileSizes = new List <Vector3>(); List <Vector3Int> tileCoords = new List <Vector3Int>(); Tilemap tileMap = inputTilemap._tilemap; if (!tileMap.gameObject.activeInHierarchy) { HEU_Logger.LogWarning("Tilemap inputs must be active in the hierarchy in order to properly send input data"); } Grid gridLayout = tileMap.layoutGrid; Matrix4x4 orientation = tileMap.orientationMatrix; Vector3 orientationPosition = orientation.DecomposeToPosition(); Vector3 orientationRotationEuler = orientation.DecomposeToRotation().eulerAngles; orientationRotationEuler.y = -orientationRotationEuler.y; orientationRotationEuler.z = -orientationRotationEuler.z; Quaternion orientationRotation = Quaternion.Euler(orientationRotationEuler); Vector3 orientationScale = orientation.DecomposeToScale(); List <float> pointOrient = new List <float>(); List <Vector3> pointScale = new List <Vector3>(); TileBase[] tileArray = tileMap.GetTilesBlock(tileMap.cellBounds); int tileCount = 0; Vector3 anchorOffset = tileMap.tileAnchor; anchorOffset.Scale(gridLayout.cellSize); Vector3 pointPos; Vector3Int boundsMin = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue); Vector3Int boundsMax = new Vector3Int(int.MinValue, int.MinValue, int.MinValue); foreach (Vector3Int tilePos in tileMap.cellBounds.allPositionsWithin) { if (tileMap.HasTile(tilePos)) { boundsMin = Vector3Int.Min(tilePos, boundsMin); boundsMax = Vector3Int.Max(tilePos, boundsMax); } } boundsMax += Vector3Int.one; BoundsInt tileMapBounds = new BoundsInt { min = boundsMin, max = boundsMax }; foreach (Vector3Int tilePos in tileMapBounds.allPositionsWithin) { if (!settings._exportUnusedTiles && !tileMap.HasTile(tilePos)) { continue; } Vector3Int usedTilePos = tilePos; //For Houdini (to use Labs Wang Tile tools, we need to reverse point order on the x axis) //so we just iterate in reverse order on the x //usedTilePos.x = tileMapBounds.size.x - 1 - tilePos.x + 2 * tileMapBounds.min.x; tileCount++; pointPos = tileMap.CellToLocal(usedTilePos) + anchorOffset; if (settings._applyTilemapOrientation) { pointPos += orientationPosition; pointOrient.Add(orientationRotation[0]); pointOrient.Add(orientationRotation[1]); pointOrient.Add(orientationRotation[2]); pointOrient.Add(orientationRotation[3]); pointScale.Add(orientationScale); } vertices.Add(pointPos); if (tileMap.HasTile(usedTilePos)) { Tile tile = tileMap.GetTile <Tile>(usedTilePos); tileNames.Add(tile.name); if (settings._applyTileColor) { colors.Add(new Vector3(tile.color.r, tile.color.g, tile.color.b)); } tileSizes.Add(new Vector3(tile.sprite.rect.size.x / tile.sprite.pixelsPerUnit, tile.sprite.rect.size.y / tile.sprite.pixelsPerUnit, 0.0f)); } else { tileNames.Add(""); if (settings._applyTileColor) { colors.Add(Vector3.zero); } tileSizes.Add(Vector3.zero); } tileCoords.Add(usedTilePos); } HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = 0; partInfo.vertexCount = 0; partInfo.pointCount = tileCount; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 1; if (tileSizes.Count > 0) { partInfo.pointAttributeCount++; } if (settings._applyTileColor && colors.Count > 0) { partInfo.pointAttributeCount++; } if (tileCoords.Count > 0) { partInfo.pointAttributeCount++; } if (pointOrient.Count > 0) { partInfo.pointAttributeCount++; } if (pointScale.Count > 0) { partInfo.pointAttributeCount++; } if (!settings._createGroupsForTiles && tileNames.Count > 0) { partInfo.pointAttributeCount++; } HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo)) { return(false); } HAPI_NodeId displayNodeID = displayGeoInfo.nodeId; if (!session.SetPartInfo(displayNodeID, 0, ref partInfo)) { Debug.LogError("Failed to set input part info. "); return(false); } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_HAPIConstants.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true)) { Debug.LogError("Failed to set point positions."); return(false); } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, "unity_tile_size", 2, tileSizes.ToArray(), ref partInfo, false)) { Debug.Log("Failed to set tile size attributes. "); return(false); } if (settings._applyTileColor) { if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_HAPIConstants.HAPI_ATTRIB_COLOR, 3, colors.ToArray(), ref partInfo, false)) { Debug.Log("Failed to set tile color attributes. "); return(false); } } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, "unity_tile_pos", 2, tileCoords.ToArray(), ref partInfo)) { Debug.Log("Failed to set point tile coordinates attributes."); return(false); } if (settings._createGroupsForTiles) { //Get a list of unique tiles used TileBase[] usedTiles = new TileBase[tileMap.GetUsedTilesCount()]; tileMap.GetUsedTilesNonAlloc(usedTiles); //Set point groups based on tile type int[] pointGroupMembership = new int[tileCount]; foreach (TileBase tileType in usedTiles) { if (!session.AddGroup(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_POINT, tileType.name)) { return(false); } int index = 0; foreach (string tileName in tileNames) { if (tileName.Equals(tileType.name)) { pointGroupMembership[index] = 1; } else { pointGroupMembership[index] = 0; } index++; } if (!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_POINT, tileType.name, pointGroupMembership, 0, tileCount)) { return(false); } } } else { if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, "unity_tile_name", tileNames.ToArray(), ref partInfo)) { Debug.Log("Failed to set point tile name attributes."); return(false); } } if (!HEU_InputMeshUtility.SetMeshDetailAttribute(session, displayNodeID, 0, "unity_tile_bounds", 2, tileMapBounds.size, ref partInfo)) { Debug.Log("Failed to set detail tile map bounds attribute."); return(false); } if (settings._applyTilemapOrientation) { if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ORIENT, 4, pointOrient.ToArray(), ref partInfo)) { Debug.LogError("Failed to set point rotations."); return(false); } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_SCALE, 3, pointScale.ToArray(), ref partInfo, false)) { Debug.LogError("Failed to set point scales."); return(false); } } return(session.CommitGeo(displayNodeID)); }