Exemplo n.º 1
0
		/// <summary>
		/// Create the main heightfield network for input.
		/// </summary>
		/// <param name="session"></param>
		/// <param name="idt"></param>
		/// <returns>True if successfully created the network</returns>
		public bool CreateHeightFieldInputNode(HEU_SessionBase session, HEU_InputDataTerrain idt)
		{
			idt._heightfieldNodeID = HEU_Defines.HEU_INVALID_NODE_ID;
			idt._heightNodeID = HEU_Defines.HEU_INVALID_NODE_ID;
			idt._maskNodeID = HEU_Defines.HEU_INVALID_NODE_ID;
			idt._mergeNodeID = HEU_Defines.HEU_INVALID_NODE_ID;

			// Create the HeightField node network
			bool bResult = session.CreateHeightfieldInputNode(idt._parentNodeID, idt._heightFieldName, idt._numPointsX, idt._numPointsY, idt._voxelSize,
				out idt._heightfieldNodeID, out idt._heightNodeID, out idt._maskNodeID, out idt._mergeNodeID);
			if (!bResult 
				|| idt._heightfieldNodeID == HEU_Defines.HEU_INVALID_NODE_ID 
				|| idt._heightNodeID == HEU_Defines.HEU_INVALID_NODE_ID
				|| idt._maskNodeID == HEU_Defines.HEU_INVALID_NODE_ID 
				|| idt._mergeNodeID == HEU_Defines.HEU_INVALID_NODE_ID)
			{
				Debug.LogError("Failed to create new heightfield node in Houdini session!");
				return false;
			}

			if (!session.CookNode(idt._heightNodeID, false))
			{
				Debug.LogError("New input node failed to cook!");
				return false;
			}

			return true;
		}
        /// <summary>
        /// Create the main heightfield network for input.
        /// </summary>
        /// <param name="session"></param>
        /// <param name="idt"></param>
        /// <returns>True if successfully created the network</returns>
        public bool CreateHeightFieldInputNode(HEU_SessionBase session, HEU_InputDataTerrain idt)
        {
            idt._heightfieldNodeID = HEU_Defines.HEU_INVALID_NODE_ID;
            idt._heightNodeID      = HEU_Defines.HEU_INVALID_NODE_ID;
            idt._maskNodeID        = HEU_Defines.HEU_INVALID_NODE_ID;
            idt._mergeNodeID       = HEU_Defines.HEU_INVALID_NODE_ID;
            idt._parentNodeID      = HEU_Defines.HEU_INVALID_NODE_ID; // Heightfields should have its own objects.

            // Create the HeightField node network
            bool bResult = session.CreateHeightFieldInput(idt._parentNodeID, idt._heightFieldName, idt._numPointsX, idt._numPointsY, idt._voxelSize,
                                                          HAPI_HeightFieldSampling.HAPI_HEIGHTFIELD_SAMPLING_CORNER,
                                                          out idt._heightfieldNodeID, out idt._heightNodeID, out idt._maskNodeID, out idt._mergeNodeID);

            if (!bResult ||
                idt._heightfieldNodeID == HEU_Defines.HEU_INVALID_NODE_ID ||
                idt._heightNodeID == HEU_Defines.HEU_INVALID_NODE_ID ||
                idt._maskNodeID == HEU_Defines.HEU_INVALID_NODE_ID ||
                idt._mergeNodeID == HEU_Defines.HEU_INVALID_NODE_ID)
            {
                HEU_Logger.LogError("Failed to create new heightfield node in Houdini session!");
                return(false);
            }

            if (!session.CookNode(idt._heightNodeID, false))
            {
                HEU_Logger.LogError("New input node failed to cook!");
                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Generates heightfield/terrain data from the given object relevant for uploading to Houdini.
        /// </summary>
        /// <param name="inputObject"></param>
        /// <returns>Valid input object or null if given object is not supported</returns>
        public HEU_InputDataTerrain GenerateTerrainDataFromGameObject(GameObject inputObject)
        {
            HEU_InputDataTerrain inputData = null;

            Terrain terrain = inputObject.GetComponent <Terrain>();

            if (terrain != null)
            {
                TerrainData terrainData = terrain.terrainData;

                Vector3 terrainSize = terrainData.size;
                if (terrainSize.x != terrainSize.z)
                {
                    Debug.LogError("Only square sized terrains are supported for input! Change to square size and try again.");
                    return(null);
                }

                inputData = new HEU_InputDataTerrain();
                inputData._inputObject = inputObject;
                inputData._terrain     = terrain;
                inputData._terrainData = terrainData;

                // Height values in Unity are normalized between 0 and 1, so this height scale
                // will multiply them before uploading to Houdini.
                inputData._heightScale = terrainSize.y;

                // Terrain heightMapResolution is the pixel resolution, which we set to the number of voxels
                // by dividing the terrain size with it. In Houdini, this is the Grid Spacing.
                inputData._voxelSize = terrainSize.x / inputData._terrainData.heightmapResolution;

                // This is the number of heightfield voxels on each dimension.
                inputData._numPointsX = Mathf.RoundToInt(inputData._terrainData.heightmapResolution * inputData._voxelSize);
                inputData._numPointsY = Mathf.RoundToInt(inputData._terrainData.heightmapResolution * inputData._voxelSize);

                Matrix4x4 transformMatrix = inputObject.transform.localToWorldMatrix;
                //HAPI_TransformEuler transformEuler = HEU_HAPIUtility.GetHAPITransformFromMatrix(ref transformMatrix);

                // Volume transform used for all heightfield layers
                inputData._transform = new HAPI_Transform(false);

                // Unity terrain pivots are at bottom left, but Houdini uses centered heightfields so
                // apply local position offset by half sizes and account for coordinate change
                inputData._transform.position[0] = terrainSize.z * 0.5f;
                inputData._transform.position[1] = -terrainSize.x * 0.5f;
                inputData._transform.position[2] = 0;

                // Volume scale controls final size, but requires to be divided by 2
                inputData._transform.scale[0] = terrainSize.x * 0.5f;
                inputData._transform.scale[1] = terrainSize.z * 0.5f;
                inputData._transform.scale[2] = 0.5f;

                inputData._transform.rotationQuaternion[0] = 0f;
                inputData._transform.rotationQuaternion[1] = 0f;
                inputData._transform.rotationQuaternion[2] = 0f;
                inputData._transform.rotationQuaternion[3] = 1f;
            }

            return(inputData);
        }
Exemplo n.º 4
0
		/// <summary>
		/// Creates a heightfield network inside the same object as connectNodeID.
		/// Uploads the terrain data from inputObject into the new heightfield network, incuding
		/// all terrain layers/alphamaps.
		/// </summary>
		/// <param name="session">Session that connectNodeID exists in</param>
		/// <param name="connectNodeID">The node to connect the network to. Most likely a SOP/merge node</param>
		/// <param name="inputObject">The gameobject containing the Terrain components</param>
		/// <param name="inputNodeID">The created heightfield network node ID</param>
		/// <returns>True if created network and uploaded heightfield data.</returns>
		public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, HAPI_NodeId connectNodeID, GameObject inputObject, out HAPI_NodeId inputNodeID)
		{
			inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID;

			// Create input node, cook it, then upload the geometry data

			if (!HEU_HAPIUtility.IsNodeValidInHoudini(session, connectNodeID))
			{
				Debug.LogError("Connection node is invalid.");
				return false;
			}

			HEU_InputDataTerrain idt = GenerateTerrainDataFromGameObject(inputObject);
			if (idt == null)
			{
				return false;
			}

			HAPI_NodeId parentNodeID = HEU_HAPIUtility.GetParentNodeID(session, connectNodeID);
			idt._parentNodeID = parentNodeID;

			if (!CreateHeightFieldInputNode(session, idt))
			{
				return false;
			}

			if (!UploadHeightValuesWithTransform(session, idt))
			{
				return false;
			}

			inputNodeID = idt._heightfieldNodeID;

			if (!UploadAlphaMaps(session, idt))
			{
				return false;
			}

			if (!session.CookNode(inputNodeID, false))
			{
				Debug.LogError("New input node failed to cook!");
				return false;
			}

			return true;
		}
Exemplo n.º 5
0
        private bool SetMaskLayer(HEU_SessionBase session, HEU_InputDataTerrain idt, ref HAPI_VolumeInfo baseVolumeInfo)
        {
            int sizeX     = idt._terrainData.alphamapWidth;
            int sizeY     = idt._terrainData.alphamapHeight;
            int totalSize = sizeX * sizeY;

            float[] maskValues = new float[totalSize];
            if (!SetHeightFieldData(session, idt._maskNodeID, 0, maskValues, HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_MASK, ref baseVolumeInfo))
            {
                return(false);
            }

            if (!session.CommitGeo(idt._maskNodeID))
            {
                Debug.LogError("Failed to commit volume layer 'mask'");
                return(false);
            }

            return(true);
        }
Exemplo n.º 6
0
		/// <summary>
		/// Upload the alphamaps (terrain layers) into heightfield network.
		/// Note that this skips the base layer.
		/// </summary>
		/// <param name="session"></param>
		/// <param name="idt"></param>
		/// <returns></returns>
		public bool UploadAlphaMaps(HEU_SessionBase session, HEU_InputDataTerrain idt)
		{
			bool bResult = true;

			int alphaLayers = idt._terrainData.alphamapLayers;

			// Skip the base layer
			if (alphaLayers < 1)
			{
				return true;
			}

			int sizeX = idt._terrainData.alphamapWidth;
			int sizeY = idt._terrainData.alphamapHeight;
			int totalSize = sizeX * sizeY;

			float[,,] alphaMaps = idt._terrainData.GetAlphamaps(0, 0, sizeX, sizeY);

			float[][] alphaMapsConverted = new float[alphaLayers - 1][];

			// Convert the alphamap layers to double arrays.
			// Note that we're skipping the base alpha map.
			for (int m = 0; m < alphaLayers - 1; ++m)
			{
				alphaMapsConverted[m] = new float[totalSize];
				for (int j = 0; j < sizeY; j++)
				{
					for (int i = 0; i < sizeX; i++)
					{
						// Flip for coordinate system change
						float h = alphaMaps[i, (sizeY - j - 1), m + 1];

						alphaMapsConverted[m][i + j * sizeX] = h;
					}
				}
			}


			// Create volume layers for all non-base alpha maps and upload values.
			for (int m = 0; m < alphaLayers - 1; ++m)
			{
				string layerName = "unity_alphamap_" + m + 1;

				HAPI_NodeId alphaLayerID = HEU_Defines.HEU_INVALID_NODE_ID;
				if (!session.CreateHeightfieldInputVolumeNode(idt._heightfieldNodeID, out alphaLayerID, layerName,
					Mathf.RoundToInt(sizeX * idt._voxelSize), Mathf.RoundToInt(sizeY * idt._voxelSize), idt._voxelSize))
				{
					bResult = false;
					Debug.LogError("Failed to create input volume node for layer " + layerName);
					break;
				}

				if (!SetHeightFieldData(session, alphaLayerID, 0, alphaMapsConverted[m], layerName))
				{
					bResult = false;
					break;
				}

				if (!session.CommitGeo(alphaLayerID))
				{
					bResult = false;
					Debug.LogError("Failed to commit volume layer " + layerName);
					break;
				}

				// Connect to the merge node but starting from index 1 since index 0 is height layer
				if (!session.ConnectNodeInput(idt._mergeNodeID, m + 2, alphaLayerID, 0))
				{
					bResult = false;
					Debug.LogError("Unable to connect new volume node for layer " + layerName);
					break;
				}
			}

			return bResult;
		}
Exemplo n.º 7
0
		/// <summary>
		/// Upload the base height layer into heightfield network.
		/// </summary>
		/// <param name="session"></param>
		/// <param name="idt"></param>
		/// <returns></returns>
		public bool UploadHeightValuesWithTransform(HEU_SessionBase session, HEU_InputDataTerrain idt)
		{
			// Get Geo, Part, and Volume infos
			HAPI_GeoInfo geoInfo = new HAPI_GeoInfo();
			if (!session.GetGeoInfo(idt._heightNodeID, ref geoInfo))
			{
				Debug.LogError("Unable to get geo info from heightfield node!");
				return false;
			}

			HAPI_PartInfo partInfo = new HAPI_PartInfo();
			if (!session.GetPartInfo(geoInfo.nodeId, 0, ref partInfo))
			{
				Debug.LogError("Unable to get part info from heightfield node!");
				return false;
			}

			HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo();
			if (!session.GetVolumeInfo(idt._heightNodeID, 0, ref volumeInfo))
			{
				Debug.LogError("Unable to get volume info from heightfield node!");
				return false;
			}

			if (volumeInfo.xLength != Mathf.RoundToInt(idt._numPointsX / idt._voxelSize)
				|| volumeInfo.yLength != Mathf.RoundToInt(idt._numPointsY / idt._voxelSize)
				|| idt._terrainData.heightmapResolution != volumeInfo.xLength
				|| idt._terrainData.heightmapResolution != volumeInfo.yLength)
			{
				Debug.LogError("Created heightfield in Houdini differs in voxel size from input terrain!");
				return false;
			}

			// Update volume infos, and set it. This is required.
			volumeInfo.tileSize = 1;
			volumeInfo.type = HAPI_VolumeType.HAPI_VOLUMETYPE_HOUDINI;
			volumeInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_FLOAT;
			volumeInfo.transform = idt._transform;

			volumeInfo.minX = 0;
			volumeInfo.minY = 0;
			volumeInfo.minZ = 0;

			volumeInfo.tupleSize = 1;
			volumeInfo.tileSize = 1;

			volumeInfo.hasTaper = false;
			volumeInfo.xTaper = 0f;
			volumeInfo.yTaper = 0f;

			if (!session.SetVolumeInfo(idt._heightNodeID, partInfo.id, ref volumeInfo))
			{
				Debug.LogError("Unable to set volume info on input heightfield node!");
				return false;
			}

			// Now set the height data
			float[,] heights = idt._terrainData.GetHeights(0, 0, volumeInfo.xLength, volumeInfo.yLength);
			int sizeX = heights.GetLength(0);
			int sizeY = heights.GetLength(1);
			int totalSize = sizeX * sizeY;

			// Convert to single array
			float[] heightsArr = new float[totalSize];
			for (int j = 0; j < sizeY; j++)
			{
				for (int i = 0; i < sizeX; i++)
				{
					// Flip for coordinate system change
					float h = heights[i, (sizeY - j - 1)];

					heightsArr[i + j * sizeX] = h * idt._heightScale;
				}
			}

			// Set the base height layer
			if (!session.SetHeightFieldData(idt._heightNodeID, 0, "height", heightsArr, 0, totalSize))
			{
				Debug.LogError("Unable to set height values on input heightfield node!");
				return false;
			}

			if (!session.CommitGeo(idt._heightNodeID))
			{
				Debug.LogError("Unable to commit geo on input heightfield node!");
				return false;
			}

			return true;
		}
Exemplo n.º 8
0
        /// <summary>
        /// Creates a heightfield network inside the same object as connectNodeID.
        /// Uploads the terrain data from inputObject into the new heightfield network, incuding
        /// all terrain layers/alphamaps.
        /// </summary>
        /// <param name="session">Session that connectNodeID exists in</param>
        /// <param name="connectNodeID">The node to connect the network to. Most likely a SOP/merge node</param>
        /// <param name="inputObject">The gameobject containing the Terrain components</param>
        /// <param name="inputNodeID">The created heightfield network node ID</param>
        /// <returns>True if created network and uploaded heightfield data.</returns>
        public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, HAPI_NodeId connectNodeID, GameObject inputObject, out HAPI_NodeId inputNodeID)
        {
            inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID;

            // Create input node, cook it, then upload the geometry data

            if (!HEU_HAPIUtility.IsNodeValidInHoudini(session, connectNodeID))
            {
                Debug.LogError("Connection node is invalid.");
                return(false);
            }

            HEU_InputDataTerrain idt = GenerateTerrainDataFromGameObject(inputObject);

            if (idt == null)
            {
                return(false);
            }

            HAPI_NodeId parentNodeID = HEU_HAPIUtility.GetParentNodeID(session, connectNodeID);

            idt._parentNodeID = parentNodeID;

            if (!CreateHeightFieldInputNode(session, idt))
            {
                return(false);
            }

            HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo();

            if (!UploadHeightValuesWithTransform(session, idt, ref volumeInfo))
            {
                return(false);
            }

            inputNodeID = idt._heightfieldNodeID;

            bool bMaskSet = false;

            if (!UploadAlphaMaps(session, idt, ref volumeInfo, out bMaskSet))
            {
                return(false);
            }

            if (!bMaskSet)
            {
                // While the default HF created by the input node also creates a default mask layer,
                // we still need to set the mask layer's transform. So this uses the base VolumeInfo
                // to do just that.
                if (!SetMaskLayer(session, idt, ref volumeInfo))
                {
                    return(false);
                }
            }

            if (!session.CookNode(inputNodeID, false))
            {
                Debug.LogError("New input node failed to cook!");
                return(false);
            }

            return(true);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Generates heightfield/terrain data from the given object relevant for uploading to Houdini.
        /// </summary>
        /// <param name="inputObject"></param>
        /// <returns>Valid input object or null if given object is not supported</returns>
        public HEU_InputDataTerrain GenerateTerrainDataFromGameObject(GameObject inputObject)
        {
            HEU_InputDataTerrain inputData = null;

            Terrain terrain = inputObject.GetComponent <Terrain>();

            if (terrain != null)
            {
                TerrainData terrainData = terrain.terrainData;

                Vector3 terrainSize = terrainData.size;
                if (terrainSize.x != terrainSize.z)
                {
                    Debug.LogError("Only square sized terrains are supported for input! Change to square size and try again.");
                    return(null);
                }

                inputData = new HEU_InputDataTerrain();
                inputData._inputObject = inputObject;
                inputData._terrain     = terrain;
                inputData._terrainData = terrainData;

                // Height values in Unity are normalized between 0 and 1, so this height scale
                // will multiply them before uploading to Houdini.
                inputData._heightScale = terrainSize.y;

                // Terrain heightMapResolution is the pixel resolution, which we set to the number of voxels
                // by dividing the terrain size with it. In Houdini, this is the Grid Spacing.
                inputData._voxelSize = terrainSize.x / inputData._terrainData.heightmapResolution;

                // Adding voxel size here to account for the corner sampling.
                // In HEU_TerrainUtility::GenerateTerrainFromVolume we subtract 1 to
                // account for corner sampling as well. This ensures maintaining the size
                // when roundtripping the terrain.
                float hfSizeX = terrainSize.x + inputData._voxelSize;
                float hfSizeY = terrainSize.z + inputData._voxelSize;

                // Subtract 1 from size otherwise idt._terrainData.GetHeights fails with size out of bounds.
                // This is the number of heightfield voxels on each dimension.
                inputData._numPointsX = Mathf.RoundToInt(inputData._terrainData.heightmapResolution * inputData._voxelSize - inputData._voxelSize);
                inputData._numPointsY = Mathf.RoundToInt(inputData._terrainData.heightmapResolution * inputData._voxelSize - inputData._voxelSize);

                Matrix4x4 transformMatrix = inputObject.transform.localToWorldMatrix;

                // Volume transform used for all heightfield layers
                inputData._transform = new HAPI_Transform(false);

                // Unity terrain pivots are at bottom left, but Houdini uses centered heightfields so
                // apply local position offset by half sizes and account for coordinate change.
                // Subtract 1 to offset the overlap
                inputData._transform.position[0] = (hfSizeY - inputData._voxelSize) * 0.5f;
                inputData._transform.position[1] = -(hfSizeX - inputData._voxelSize) * 0.5f;
                inputData._transform.position[2] = 0;

                // Volume scale controls final size, but requires to be divided by 2
                inputData._transform.scale[0] = hfSizeX * 0.5f;
                inputData._transform.scale[1] = hfSizeY * 0.5f;
                inputData._transform.scale[2] = 0.5f;

                inputData._transform.rotationQuaternion[0] = 0f;
                inputData._transform.rotationQuaternion[1] = 0f;
                inputData._transform.rotationQuaternion[2] = 0f;
                inputData._transform.rotationQuaternion[3] = 1f;
            }

            return(inputData);
        }
Exemplo n.º 10
0
        /// <summary>
        /// Upload the alphamaps (TerrainLayers) into heightfield network.
        /// </summary>
        /// <param name="session"></param>
        /// <param name="idt"></param>
        /// <param name="baseVolumeInfo">The valid base height HAPI_VolumeInfo</param>
        /// <param name="bMaskSet">This is set to true if a mask layer was uploaded</param>
        /// <returns>True if successfully uploaded all layers</returns>
        public bool UploadAlphaMaps(HEU_SessionBase session, HEU_InputDataTerrain idt, ref HAPI_VolumeInfo baseVolumeInfo, out bool bMaskSet)
        {
            bool bResult = true;

            bMaskSet = false;

            int alphaLayers = idt._terrainData.alphamapLayers;

            if (alphaLayers < 1)
            {
                return(bResult);
            }

            int sizeX     = idt._terrainData.alphamapWidth;
            int sizeY     = idt._terrainData.alphamapHeight;
            int totalSize = sizeX * sizeY;

            float[,,] alphaMaps = idt._terrainData.GetAlphamaps(0, 0, sizeX, sizeY);

            float[][] alphaMapsConverted = new float[alphaLayers][];

            // Convert the alphamap layers to double arrays.
            for (int m = 0; m < alphaLayers; ++m)
            {
                alphaMapsConverted[m] = new float[totalSize];
                for (int j = 0; j < sizeY; j++)
                {
                    for (int i = 0; i < sizeX; i++)
                    {
                        // Flip for coordinate system change
                        float h = alphaMaps[i, (sizeY - j - 1), m];

                        alphaMapsConverted[m][i + j * sizeX] = h;
                    }
                }
            }

            // Create volume layers for all alpha maps and upload values.
            bool bMaskLayer      = false;
            int  inputLayerIndex = 1;

            for (int m = 0; m < alphaLayers; ++m)
            {
#if UNITY_2018_3_OR_NEWER
                string layerName = idt._terrainData.terrainLayers[m].name;
#else
                string layerName = "unity_alphamap_" + m + 1;
#endif

                // The Unity layer name could contain '.terrainlayer' and spaces. Remove them because Houdini doesn't allow
                // spaces, and the extension isn't necessary.
                layerName = layerName.Replace(" ", "_");
                int extIndex = layerName.LastIndexOf(HEU_Defines.HEU_EXT_TERRAINLAYER);
                if (extIndex > 0)
                {
                    layerName = layerName.Remove(extIndex);
                }
                //Debug.Log("Processing terrain layer: " + layerName);

                HAPI_NodeId alphaLayerID = HEU_Defines.HEU_INVALID_NODE_ID;

                if (layerName.Equals(HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_HEIGHT))
                {
                    // Skip height (base) layer (since it has been uploaded already)
                    continue;
                }
                else if (layerName.Equals(HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_MASK))
                {
                    //Debug.Log("Mask layer found! Skipping creating the HF.");
                    bMaskSet     = true;
                    bMaskLayer   = true;
                    alphaLayerID = idt._maskNodeID;
                }
                else
                {
                    bMaskLayer = false;

                    if (!session.CreateHeightfieldInputVolumeNode(idt._heightfieldNodeID, out alphaLayerID, layerName,
                                                                  Mathf.RoundToInt(sizeX * idt._voxelSize), Mathf.RoundToInt(sizeY * idt._voxelSize), idt._voxelSize))
                    {
                        bResult = false;
                        Debug.LogError("Failed to create input volume node for layer " + layerName);
                        break;
                    }
                }

                //Debug.Log("Uploading terrain layer: " + layerName);

                if (!SetHeightFieldData(session, alphaLayerID, 0, alphaMapsConverted[m], layerName, ref baseVolumeInfo))
                {
                    bResult = false;
                    break;
                }

#if UNITY_2018_3_OR_NEWER
                SetTerrainLayerAttributesToHeightField(session, alphaLayerID, 0, idt._terrainData.terrainLayers[m]);
#endif

                if (!session.CommitGeo(alphaLayerID))
                {
                    bResult = false;
                    Debug.LogError("Failed to commit volume layer " + layerName);
                    break;
                }

                if (!bMaskLayer)
                {
                    // Connect to the merge node but starting from index 1 since index 0 is height layer
                    if (!session.ConnectNodeInput(idt._mergeNodeID, inputLayerIndex + 1, alphaLayerID, 0))
                    {
                        bResult = false;
                        Debug.LogError("Unable to connect new volume node for layer " + layerName);
                        break;
                    }

                    inputLayerIndex++;
                }
            }

            return(bResult);
        }
        /// <summary>
        /// Upload the base height layer into heightfield network.
        /// </summary>
        /// <param name="session"></param>
        /// <param name="idt"></param>
        /// <returns></returns>
        public bool UploadHeightValuesWithTransform(HEU_SessionBase session, HEU_InputDataTerrain idt, ref HAPI_VolumeInfo volumeInfo)
        {
            // Get Geo, Part, and Volume infos
            HAPI_GeoInfo geoInfo = new HAPI_GeoInfo();

            if (!session.GetGeoInfo(idt._heightNodeID, ref geoInfo))
            {
                HEU_Logger.LogError("Unable to get geo info from heightfield node!");
                return(false);
            }

            HAPI_PartInfo partInfo = new HAPI_PartInfo();

            if (!session.GetPartInfo(geoInfo.nodeId, 0, ref partInfo))
            {
                HEU_Logger.LogError("Unable to get part info from heightfield node!");
                return(false);
            }

            volumeInfo = new HAPI_VolumeInfo();
            if (!session.GetVolumeInfo(idt._heightNodeID, 0, ref volumeInfo))
            {
                HEU_Logger.LogError("Unable to get volume info from heightfield node!");
                return(false);
            }

            if ((volumeInfo.xLength - 1) != Mathf.RoundToInt(idt._numPointsX / idt._voxelSize) ||
                (volumeInfo.yLength - 1) != Mathf.RoundToInt(idt._numPointsY / idt._voxelSize) ||
                idt._terrainData.heightmapResolution != volumeInfo.xLength ||
                idt._terrainData.heightmapResolution != volumeInfo.yLength)
            {
                HEU_Logger.LogWarning("Created heightfield in Houdini differs in voxel size from input terrain! Terrain may require resampling.");
            }

            // Update volume infos, and set it. This is required.
            volumeInfo.tileSize  = 1;
            volumeInfo.type      = HAPI_VolumeType.HAPI_VOLUMETYPE_HOUDINI;
            volumeInfo.storage   = HAPI_StorageType.HAPI_STORAGETYPE_FLOAT;
            volumeInfo.transform = idt._transform;

            volumeInfo.minX = 0;
            volumeInfo.minY = 0;
            volumeInfo.minZ = 0;

            volumeInfo.tupleSize = 1;
            volumeInfo.tileSize  = 1;

            volumeInfo.hasTaper = false;
            volumeInfo.xTaper   = 0f;
            volumeInfo.yTaper   = 0f;

            if (!session.SetVolumeInfo(idt._heightNodeID, partInfo.id, ref volumeInfo))
            {
                HEU_Logger.LogError("Unable to set volume info on input heightfield node!");
                return(false);
            }

            // Now set the height data
            float[,] heights = idt._terrainData.GetHeights(0, 0, idt._terrainData.heightmapResolution, idt._terrainData.heightmapResolution);
            int sizeX     = heights.GetLength(0);
            int sizeY     = heights.GetLength(1);
            int totalSize = sizeX * sizeY;

            // Convert to single array
            float[] heightsArr = new float[totalSize];
            for (int j = 0; j < sizeY; j++)
            {
                for (int i = 0; i < sizeX; i++)
                {
                    // Flip for coordinate system change
                    float h = heights[i, (sizeY - j - 1)];

                    heightsArr[i + j * sizeX] = h * idt._heightScale;
                }
            }

            if (volumeInfo.xLength != volumeInfo.yLength)
            {
                HEU_Logger.LogError("Error: Houdini heightmap must be square!");
                return(false);
            }

            if (idt._terrainData.heightmapResolution != volumeInfo.xLength)
            {
                // Resize heightsArr to idt._terrainData.heightmapResolution
                HEU_Logger.LogWarningFormat("Attempting to resize landscape from ({0}x{1}) to ({2}x{3})", idt._terrainData.heightmapResolution, idt._terrainData.heightmapResolution, volumeInfo.xLength, volumeInfo.xLength);
                heightsArr = HEU_TerrainUtility.ResampleData(heightsArr, idt._terrainData.heightmapResolution, idt._terrainData.heightmapResolution, volumeInfo.xLength, volumeInfo.xLength);
                sizeX      = volumeInfo.xLength;
                sizeY      = volumeInfo.yLength;
                totalSize  = sizeX * sizeY;
            }

            // Set the base height layer
            if (!session.SetHeightFieldData(idt._heightNodeID, 0, HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_HEIGHT, heightsArr, 0, totalSize))
            {
                HEU_Logger.LogError("Unable to set height values on input heightfield node!");
                return(false);
            }

            SetTerrainDataAttributesToHeightField(session, geoInfo.nodeId, 0, idt._terrainData);

            SetTreePrototypes(session, geoInfo.nodeId, 0, idt._terrainData);

            if (!session.CommitGeo(idt._heightNodeID))
            {
                HEU_Logger.LogError("Unable to commit geo on input heightfield node!");
                return(false);
            }

            return(true);
        }