Base class for input data where temporary data can be stored for uploading into input node.
Ejemplo n.º 1
0
		/// <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);
        }
Ejemplo n.º 3
0
	/// <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));
        }