Example #1
0
	public static void CopyPrototype(HEU_DetailPrototype srcProto, HEU_DetailPrototype destProto)
	{
	    destProto._bendFactor = srcProto._bendFactor;
	    destProto._dryColor = srcProto._dryColor;
	    destProto._healthyColor = srcProto._healthyColor;
	    destProto._maxHeight = srcProto._maxHeight;
	    destProto._maxWidth = srcProto._maxWidth;
	    destProto._minHeight = srcProto._minHeight;
	    destProto._minWidth = srcProto._minWidth;
	    destProto._noiseSpread = srcProto._noiseSpread;
	    destProto._renderMode = srcProto._renderMode;
	}
		/// <summary>
		/// Fill up the given detailPrototype with values from the specified heightfield part.
		/// </summary>
		/// <param name="session">Houdini Engine session to query</param>
		/// <param name="geoID">The geometry ID in Houdini</param>
		/// <param name="partID">The part ID in Houdini</param>
		/// <param name="detailPrototype">The detail prototype object to populate</param>
		public static void PopulateDetailPrototype(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID,
			ref HEU_DetailPrototype detailPrototype)
		{
			// Get the detail prototype properties from attributes on this layer

			if (detailPrototype == null)
			{
				detailPrototype = new HEU_DetailPrototype();
			}

			HAPI_AttributeInfo prefabAttrInfo = new HAPI_AttributeInfo();
			string[] prefabPaths = HEU_GeneralUtility.GetAttributeStringData(session, geoID, partID, 
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_PREFAB, ref prefabAttrInfo);

			if (prefabAttrInfo.exists && prefabPaths.Length >= 1)
			{
				detailPrototype._prototypePrefab = prefabPaths[0];
			}

			HAPI_AttributeInfo textureAttrInfo = new HAPI_AttributeInfo();
			string[] texturePaths = HEU_GeneralUtility.GetAttributeStringData(session, geoID, partID, 
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_TEXTURE, ref textureAttrInfo);

			if (textureAttrInfo.exists && texturePaths.Length >= 1)
			{
				detailPrototype._prototypeTexture = texturePaths[0];
			}

			float fvalue = 0;
			if (HEU_GeneralUtility.GetAttributeFloatSingle(session, geoID, partID,
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_BENDFACTOR, out fvalue))
			{
				detailPrototype._bendFactor = fvalue;
			}

			Color color = Color.white;
			if (HEU_GeneralUtility.GetAttributeColorSingle(session, geoID, partID,
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_DRYCOLOR, ref color))
			{
				detailPrototype._dryColor = color;
			}

			if (HEU_GeneralUtility.GetAttributeColorSingle(session, geoID, partID,
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_HEALTHYCOLOR, ref color))
			{
				detailPrototype._healthyColor = color;
			}

			if (HEU_GeneralUtility.GetAttributeFloatSingle(session, geoID, partID,
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_MAXHEIGHT, out fvalue))
			{
				detailPrototype._maxHeight = fvalue;
			}

			if (HEU_GeneralUtility.GetAttributeFloatSingle(session, geoID, partID,
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_MAXWIDTH, out fvalue))
			{
				detailPrototype._maxWidth = fvalue;
			}

			if (HEU_GeneralUtility.GetAttributeFloatSingle(session, geoID, partID,
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_MINHEIGHT, out fvalue))
			{
				detailPrototype._minHeight = fvalue;
			}

			if (HEU_GeneralUtility.GetAttributeFloatSingle(session, geoID, partID,
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_MINWIDTH, out fvalue))
			{
				detailPrototype._minWidth = fvalue;
			}

			if (HEU_GeneralUtility.GetAttributeFloatSingle(session, geoID, partID,
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_NOISESPREAD, out fvalue))
			{
				detailPrototype._noiseSpread = fvalue;
			}

			int iValue = 0;
			if (HEU_GeneralUtility.GetAttributeIntSingle(session, geoID, partID,
				HEU_Defines.HEIGHTFIELD_DETAIL_PROTOTYPE_RENDERMODE, out iValue))
			{
				detailPrototype._renderMode = iValue;
			}
		}
		/// <summary>
		/// Apply the given detail layers and properties to the given terrain.
		/// The detail distance and resolution will be set, along with detail prototypes, and layers.
		/// </summary>
		/// <param name="terrain">The Terrain to set the detail properies on</param>
		/// <param name="terrainData">The TerrainData to apply the layers to</param>
		/// <param name="detailProperties">Container for detail distance and resolution</param>
		/// <param name="heuDetailPrototypes">Data for creating DetailPrototypes</param>
		/// <param name="convertedDetailMaps">The detail maps to set for the detail layers</param>
		public static void ApplyDetailLayers(Terrain terrain, TerrainData terrainData, HEU_DetailProperties detailProperties,
			List<HEU_DetailPrototype> heuDetailPrototypes, List<int[,]> convertedDetailMaps)
		{
#if UNITY_2018_3_OR_NEWER

			if (detailProperties != null)
			{
				if (detailProperties._detailDistance >= 0)
				{
					terrain.detailObjectDistance = detailProperties._detailDistance;
				}

				if (detailProperties._detailDensity >= 0)
				{
					terrain.detailObjectDensity = detailProperties._detailDensity;
				}

				int resPerPath = detailProperties._detailResolutionPerPatch > 0 ?
						detailProperties._detailResolutionPerPatch : terrainData.detailResolutionPerPatch;

				int detailRes = detailProperties._detailResolution > 0 ?
					detailProperties._detailResolution : terrainData.detailResolutionPerPatch;

				if (resPerPath > 0 && detailRes > 0)
				{
					// This should match with half the terrain size
					terrainData.SetDetailResolution(detailRes, resPerPath);
				}
			}

			if (heuDetailPrototypes.Count != convertedDetailMaps.Count)
			{
				Debug.LogError("Number of volume detail layers differs from converted detail maps. Unable to apply detail layers.");
				return;
			}

			// For now, just override existing detail prototypes and layers
			// If user asks for appending/overwriting them, then can use a new index attribute to map them

			List<DetailPrototype> detailPrototypes = new List<DetailPrototype>();

			int numDetailLayers = heuDetailPrototypes.Count;
			for(int i = 0; i < numDetailLayers; ++i)
			{
				DetailPrototype detailPrototype = new DetailPrototype();

				HEU_DetailPrototype heuDetail = heuDetailPrototypes[i];

				if (!string.IsNullOrEmpty(heuDetail._prototypePrefab))
				{
					detailPrototype.prototype = HEU_AssetDatabase.LoadAssetAtPath(heuDetail._prototypePrefab, typeof(GameObject)) as GameObject;
					detailPrototype.usePrototypeMesh = true;
				}
				else if (!string.IsNullOrEmpty(heuDetail._prototypeTexture))
				{
					detailPrototype.prototypeTexture = HEU_MaterialFactory.LoadTexture(heuDetail._prototypeTexture);
					detailPrototype.usePrototypeMesh = false;
				}

				detailPrototype.bendFactor = heuDetail._bendFactor;
				detailPrototype.dryColor = heuDetail._dryColor;
				detailPrototype.healthyColor = heuDetail._healthyColor;
				detailPrototype.maxHeight = heuDetail._maxHeight;
				detailPrototype.maxWidth = heuDetail._maxWidth;
				detailPrototype.minHeight = heuDetail._minHeight;
				detailPrototype.minWidth = heuDetail._minWidth;
				detailPrototype.noiseSpread = heuDetail._noiseSpread;

				detailPrototype.renderMode = (DetailRenderMode)heuDetail._renderMode;

				detailPrototypes.Add(detailPrototype);
			}

			// Set the DetailPrototypes

			if (detailPrototypes.Count > 0)
			{
				terrainData.detailPrototypes = detailPrototypes.ToArray();
			}

			// Set the DetailLayers
			for(int i = 0; i < numDetailLayers; ++i)
			{
				terrainData.SetDetailLayer(0, 0, i, convertedDetailMaps[i]);
			}
#endif
		}
Example #4
0
		public bool GenerateTerrainBuffers(HEU_SessionBase session, HAPI_NodeId nodeID, List<HAPI_PartInfo> volumeParts,
			List<HAPI_PartInfo> scatterInstancerParts, out List<HEU_LoadBufferVolume> volumeBuffers)
		{
			volumeBuffers = null;
			if (volumeParts.Count == 0)
			{
				return true;
			}

			volumeBuffers = new List<HEU_LoadBufferVolume>();
			int detailResolution = 0;

			int numParts = volumeParts.Count;
			for (int i = 0; i < numParts; ++i)
			{
				HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo();
				bool bResult = session.GetVolumeInfo(nodeID, volumeParts[i].id, ref volumeInfo);
				if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT)
				{
					SetLog(HEU_LoadData.LoadStatus.ERROR, "This heightfield is not supported. Please check documentation.");
					return false;
				}

				if (volumeInfo.xLength != volumeInfo.yLength)
				{
					SetLog(HEU_LoadData.LoadStatus.ERROR, "Non-square sized terrain not supported.");
					return false;
				}

				string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session);

				HFLayerType layerType = HEU_TerrainUtility.GetHeightfieldLayerType(session, nodeID, volumeParts[i].id, volumeName);

				//Debug.LogFormat("Index: {0}, Part id: {1}, Part Name: {2}, Volume Name: {3}", i, volumeParts[i].id, HEU_SessionManager.GetString(volumeParts[i].nameSH), volumeName);

				// Ignoring mask layer because it is Houdini-specific (same behaviour as regular HDA terrain generation)
				if (layerType == HFLayerType.MASK)
				{
					continue;
				}

				HEU_LoadBufferVolumeLayer layer = new HEU_LoadBufferVolumeLayer();
				layer._layerName = volumeName;
				layer._partID = volumeParts[i].id;
				layer._heightMapWidth = volumeInfo.xLength;
				layer._heightMapHeight = volumeInfo.yLength;
				layer._layerType = layerType;

				Matrix4x4 volumeTransformMatrix = HEU_HAPIUtility.GetMatrixFromHAPITransform(ref volumeInfo.transform, false);
				layer._position = HEU_HAPIUtility.GetPosition(ref volumeTransformMatrix);
				Vector3 scale = HEU_HAPIUtility.GetScale(ref volumeTransformMatrix);

				// Calculate real terrain size in both Houdini and Unity.
				// The height values will be mapped over this terrain size.
				float gridSpacingX = scale.x * 2f;
				float gridSpacingY = scale.y * 2f;
				layer._terrainSizeX = Mathf.Round((volumeInfo.xLength - 1) * gridSpacingX);
				layer._terrainSizeY = Mathf.Round((volumeInfo.yLength - 1) * gridSpacingY);

				// Get volume bounds for calculating position offset
				session.GetVolumeBounds(nodeID, volumeParts[i].id, 
					out layer._minBounds.x, out layer._minBounds.y, out layer._minBounds.z, 
					out layer._maxBounds.x, out layer._maxBounds.y, out layer._maxBounds.z, 
					out layer._center.x, out layer._center.y, out layer._center.z);

				// Look up TerrainLayer file via attribute if user has set it
				layer._layerPath = HEU_GeneralUtility.GetAttributeStringValueSingle(session, nodeID, volumeParts[i].id,
					HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TERRAINLAYER_FILE_ATTR, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM);

				if (layerType != HFLayerType.DETAIL)
				{
					layer._hasLayerAttributes = HEU_TerrainUtility.VolumeLayerHasAttributes(session, nodeID, volumeParts[i].id);

					if (layer._hasLayerAttributes)
					{
						LoadStringFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_DIFFUSE_ATTR, ref layer._diffuseTexturePath);
						LoadStringFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_MASK_ATTR, ref layer._maskTexturePath);
						LoadStringFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_NORMAL_ATTR, ref layer._normalTexturePath);

						LoadFloatFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_NORMAL_SCALE_ATTR, ref layer._normalScale);
						LoadFloatFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_METALLIC_ATTR, ref layer._metallic);
						LoadFloatFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SMOOTHNESS_ATTR, ref layer._smoothness);

						LoadLayerColorFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SPECULAR_ATTR, ref layer._specularColor);
						LoadLayerVector2FromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_OFFSET_ATTR, ref layer._tileOffset);
						LoadLayerVector2FromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_SIZE_ATTR, ref layer._tileSize);
					}

					// Get the height values from Houdini along with the min and max height range.
					layer._normalizedHeights = HEU_TerrainUtility.GetNormalizedHeightmapFromPartWithMinMax(
						_session, nodeID, volumeParts[i].id, volumeInfo.xLength, volumeInfo.yLength,
						ref layer._minHeight, ref layer._maxHeight, ref layer._heightRange,
						(layerType == HFLayerType.HEIGHT));
				}

				// Get the tile index, if it exists, for this part
				HAPI_AttributeInfo tileAttrInfo = new HAPI_AttributeInfo();
				int[] tileAttrData = new int[0];
				HEU_GeneralUtility.GetAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData);

				int tileIndex = 0;
				if (tileAttrInfo.exists && tileAttrData.Length == 1)
				{
					tileIndex = tileAttrData[0];
				}

				// Add layer based on tile index
				if (tileIndex >= 0)
				{
					HEU_LoadBufferVolume volumeBuffer = null;
					for(int j = 0; j < volumeBuffers.Count; ++j)
					{
						if (volumeBuffers[j]._tileIndex == tileIndex)
						{
							volumeBuffer = volumeBuffers[j];
							break;
						}
					}

					if (volumeBuffer == null)
					{
						volumeBuffer = new HEU_LoadBufferVolume();
						volumeBuffer.InitializeBuffer(volumeParts[i].id, volumeName, false, false);

						volumeBuffer._tileIndex = tileIndex;
						volumeBuffers.Add(volumeBuffer);
					}

					if (layerType == HFLayerType.HEIGHT)
					{
						// Height layer always first layer
						volumeBuffer._splatLayers.Insert(0, layer);

						volumeBuffer._heightMapWidth = layer._heightMapWidth;
						volumeBuffer._heightMapHeight = layer._heightMapHeight;
						volumeBuffer._terrainSizeX = layer._terrainSizeX;
						volumeBuffer._terrainSizeY = layer._terrainSizeY;
						volumeBuffer._heightRange = layer._heightRange;

						// The terrain heightfield position in y requires offset of min height
						layer._position.y += layer._minHeight;

						// Use y position from attribute if user has set it
						float userYPos;
						if (HEU_GeneralUtility.GetAttributeFloatSingle(session, nodeID, volumeParts[i].id,
							HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_YPOS, out userYPos))
						{
							layer._position.y = userYPos;
						}

						// Look up TerrainData file path via attribute if user has set it
						volumeBuffer._terrainDataPath = HEU_GeneralUtility.GetAttributeStringValueSingle(session, nodeID, volumeBuffer._id,
							HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TERRAINDATA_FILE_ATTR, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM);

						// Look up TerrainData export file path via attribute if user has set it
						volumeBuffer._terrainDataExportPath = HEU_GeneralUtility.GetAttributeStringValueSingle(session, nodeID, volumeBuffer._id,
							HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TERRAINDATA_EXPORT_FILE_ATTR, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM);

						// Load the TreePrototype buffers
						List<HEU_TreePrototypeInfo> treePrototypeInfos = HEU_TerrainUtility.GetTreePrototypeInfosFromPart(session, nodeID, volumeBuffer._id);
						if (treePrototypeInfos != null)
						{
							if (volumeBuffer._scatterTrees == null)
							{
								volumeBuffer._scatterTrees = new HEU_VolumeScatterTrees();
							}
							volumeBuffer._scatterTrees._treePrototypInfos = treePrototypeInfos;
						}

						HEU_TerrainUtility.PopulateDetailProperties(session, nodeID, 
							volumeBuffer._id, ref volumeBuffer._detailProperties);

						// Get specified material if any
						volumeBuffer._specifiedTerrainMaterialName = HEU_GeneralUtility.GetMaterialAttributeValueFromPart(session,
							nodeID, volumeBuffer._id);
					}
					else if(layer._layerType == HFLayerType.DETAIL)
					{
						// Get detail prototype
						HEU_DetailPrototype detailPrototype = null;
						HEU_TerrainUtility.PopulateDetailPrototype(session, nodeID, volumeParts[i].id, ref detailPrototype);

						int[,] detailMap = HEU_TerrainUtility.GetDetailMapFromPart(session, nodeID,
							volumeParts[i].id, out detailResolution);

						volumeBuffer._detailPrototypes.Add(detailPrototype);
						volumeBuffer._detailMaps.Add(detailMap);

						// Set the detail resolution which is formed from the detail layer
						if (volumeBuffer._detailProperties == null)
						{
							volumeBuffer._detailProperties = new HEU_DetailProperties();
						}
						volumeBuffer._detailProperties._detailResolution = detailResolution;
					}
					else
					{
						volumeBuffer._splatLayers.Add(layer);
					}
				}

				Sleep();
			}

			// Each volume buffer is a self contained terrain tile
			foreach(HEU_LoadBufferVolume volumeBuffer in volumeBuffers)
			{
				List<HEU_LoadBufferVolumeLayer> layers = volumeBuffer._splatLayers;
				//Debug.LogFormat("Heightfield: tile={0}, layers={1}", tile._tileIndex, layers.Count);

				int heightMapWidth = volumeBuffer._heightMapWidth;
				int heightMapHeight = volumeBuffer._heightMapHeight;

				int numLayers = layers.Count;
				if (numLayers > 0)
				{
					// Convert heightmap values from Houdini to Unity
					volumeBuffer._heightMap = HEU_TerrainUtility.ConvertHeightMapHoudiniToUnity(heightMapWidth, heightMapHeight, layers[0]._normalizedHeights);

					Sleep();

					// Convert splatmap values from Houdini to Unity.
					// Start at 2nd index since height is strictly for height values (not splatmap).
					List<float[]> heightFields = new List<float[]>();
					for(int m = 1; m < numLayers; ++m)
					{
						// Ignore Detail layers as they are handled differently
						if(layers[m]._layerType != HFLayerType.DETAIL)
						{
							heightFields.Add(layers[m]._normalizedHeights);
						}
					}

					// The number of maps are the number of splatmaps (ie. non height/mask layers)
					int numMaps = heightFields.Count;
					if (numMaps > 0)
					{
						// Using the first splatmap size for all splatmaps
						volumeBuffer._splatMaps = HEU_TerrainUtility.ConvertHeightFieldToAlphaMap(layers[1]._heightMapWidth, layers[1]._heightMapHeight, heightFields);
					}
					else
					{
						volumeBuffer._splatMaps = null;
					}

					// TODO: revisit how the position is calculated
					volumeBuffer._position = new Vector3(
						volumeBuffer._terrainSizeX + volumeBuffer._splatLayers[0]._minBounds.x, 
						volumeBuffer._splatLayers[0]._position.y, 
						volumeBuffer._splatLayers[0]._minBounds.z);
				}
			}

			// Process the scatter instancer parts to get the scatter data
			for (int i = 0; i < scatterInstancerParts.Count; ++i)
			{
				// Find the terrain tile (use primitive attr). Assume 0 tile if not set (i.e. not split into tiles)
				int terrainTile = 0;
				HAPI_AttributeInfo tileAttrInfo = new HAPI_AttributeInfo();
				int[] tileAttrData = new int[0];
				if (!HEU_GeneralUtility.GetAttribute(session, nodeID, scatterInstancerParts[i].id, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData))
				{
					// Try part 0 (the height layer) to get the tile index.
					// For scatter points merged with HF, in some cases the part ID doesn't have the tile attribute.
					HEU_GeneralUtility.GetAttribute(session, nodeID, 0, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData);
				}

				if (tileAttrData != null && tileAttrData.Length > 0)
				{
					terrainTile = tileAttrData[0];
				}

				// Find the volume layer associated with this part using the terrain tile index
				HEU_LoadBufferVolume volumeBuffer = GetLoadBufferVolumeFromTileIndex(terrainTile, volumeBuffers);
				if (volumeBuffer == null)
				{
					continue;
				}

				HEU_TerrainUtility.PopulateScatterTrees(session, nodeID, scatterInstancerParts[i].id, scatterInstancerParts[i].pointCount, ref volumeBuffer._scatterTrees);
			}

			return true;
		}