Esempio n. 1
0
		public bool GenerateTerrainBuffers(HEU_SessionBase session, HAPI_NodeId nodeID, List<HAPI_PartInfo> volumeParts,
			out List<HEU_LoadBufferVolume> volumeBuffers)
		{
			volumeBuffers = null;
			if (volumeParts.Count == 0)
			{
				return true;
			}

			volumeBuffers = new List<HEU_LoadBufferVolume>();

			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);
				bool bHeightPart = volumeName.Equals("height");

				//Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable);

				HEU_LoadBufferVolumeLayer layer = new HEU_LoadBufferVolumeLayer();
				layer._layerName = volumeName;
				layer._partID = volumeParts[i].id;
				layer._heightMapSize = volumeInfo.xLength;

				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);

				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 and find the min and max height range.
				if (!HEU_GeometryUtility.GetHeightfieldValues(session, volumeInfo.xLength, volumeInfo.yLength, nodeID, volumeParts[i].id, ref layer._rawHeights, ref layer._minHeight, ref layer._maxHeight))
				{
					return false;
				}

				// TODO: Tried to replace above with this, but it flattens the heights
				//layer._rawHeights = HEU_GeometryUtility.GetHeightfieldFromPart(_session, nodeID, volumeParts[i].id, "part", volumeInfo.xLength);

				// 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, "tile", 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 (bHeightPart)
					{
						// Height layer always first layer
						volumeBuffer._layers.Insert(0, layer);

						volumeBuffer._heightMapSize = layer._heightMapSize;
						volumeBuffer._terrainSizeX = layer._terrainSizeX;
						volumeBuffer._terrainSizeY = layer._terrainSizeY;
						volumeBuffer._heightRange = (layer._maxHeight - layer._minHeight);
					}
					else
					{
						volumeBuffer._layers.Add(layer);
					}
				}

				Sleep();
			}

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

				int heightMapSize = volumeBuffer._heightMapSize;

				int numLayers = layers.Count;
				if (numLayers > 0)
				{
					// Convert heightmap values from Houdini to Unity
					volumeBuffer._heightMap = HEU_GeometryUtility.ConvertHeightMapHoudiniToUnity(heightMapSize, layers[0]._rawHeights, layers[0]._minHeight, layers[0]._maxHeight);

					Sleep();

					// Convert splatmap values from Houdini to Unity.
					List<float[]> heightFields = new List<float[]>();
					for(int m = 1; m < numLayers; ++m)
					{
						heightFields.Add(layers[m]._rawHeights);
					}
					volumeBuffer._splatMaps = HEU_GeometryUtility.ConvertHeightSplatMapHoudiniToUnity(heightMapSize, heightFields);

					volumeBuffer._position = new Vector3((volumeBuffer._terrainSizeX + volumeBuffer._layers[0]._minBounds.x), volumeBuffer._layers[0]._minHeight + volumeBuffer._layers[0]._position.y, volumeBuffer._layers[0]._minBounds.z);
				}
			}

			return true;
		}
	public static void ExecuteToolOperatorMultiple(string toolName, string toolPath, GameObject[] inputObjects)
	{
	    GameObject outputObjectToSelect = null;

	    GameObject go = HEU_HAPIUtility.InstantiateHDA(toolPath, Vector3.zero, HEU_SessionManager.GetOrCreateDefaultSession(), false);
	    if (go == null)
	    {
		HEU_Logger.LogWarningFormat("Failed to instantiate tool: {0}", toolName);
		return;
	    }

	    HEU_HoudiniAssetRoot assetRoot = go.GetComponent<HEU_HoudiniAssetRoot>();
	    if (assetRoot != null)
	    {
		HEU_HoudiniAsset asset = assetRoot._houdiniAsset;
		HEU_SessionBase session = asset.GetAssetSession(true);

		int numInputs = inputObjects.Length;

		List<HEU_InputNode> inputNodes = asset.GetInputNodes();
		if (inputNodes == null || inputNodes.Count == 0)
		{
		    HEU_Logger.LogErrorFormat("Unable to assign input geometry due to no asset inputs on selected tool.");
		}
		else
		{
		    // User could have selected any number of inputs objects, and asset could have any number of inputs.
		    // So use minimum of either to set input object into asset input.
		    int minInputCount = Mathf.Min(inputNodes.Count, numInputs);
		    for (int i = 0; i < minInputCount; ++i)
		    {
			bool bShouldUseHDA = IsValidInputHDA(inputObjects[i]);

			if (!bShouldUseHDA &&!IsValidInputMesh(inputObjects[i]))
			{
			    continue;
			}
			else if (bShouldUseHDA && !IsValidInputHDA(inputObjects[i]))
			{
			    continue;
			}
			
			GameObject inputObject = inputObjects[i];

			HEU_InputNode inputNode = inputNodes[i];
			inputNode.ResetInputNode(session);


			if (!bShouldUseHDA)
			{
			    inputNode.ChangeInputType(session, HEU_InputNode.InputObjectType.UNITY_MESH);
    
			    HEU_InputObjectInfo inputInfo = inputNode.AddInputEntryAtEndMesh(inputObject);
			    if (inputInfo != null)
			    {
			        inputInfo._useTransformOffset = false;
			        inputNode.KeepWorldTransform = true;
			        inputNode.PackGeometryBeforeMerging = false;
    
			        inputNode.RequiresUpload = true;
			    }
			    else
			    {
			        HEU_Logger.LogErrorFormat("Invalid input format: {0}", inputObject.gameObject.name);
			    }
			}
			else
			{
			    inputNode.ChangeInputType(session, HEU_InputNode.InputObjectType.HDA);

			    HEU_InputHDAInfo inputHDAInfo = inputNode.AddInputEntryAtEndHDA(inputObject);
			    if (inputHDAInfo != null)
			    {
			        inputNode.KeepWorldTransform = true;
			        inputNode.PackGeometryBeforeMerging = false;
    
			        inputNode.RequiresUpload = true;
			    }
			    else
			    {
			        HEU_Logger.LogErrorFormat("Invalid input format: {0}", inputObject.gameObject.name);
			    }
			}
		    }

		    asset.RequestCook(true, true, true, true);

		    outputObjectToSelect = assetRoot.gameObject;
		}
	    }

	    if (outputObjectToSelect != null)
	    {
		HEU_EditorUtility.SelectObject(outputObjectToSelect);
	    }
	}
Esempio n. 3
0
		/// <summary>
		/// Creates terrain from given volumeInfo for the given gameObject.
		/// If gameObject has a valid Terrain component, then it is reused.
		/// Similarly, if the Terrain component has a valid TerrainData, or if the given terrainData is valid, then it is used.
		/// Otherwise a new TerrainData is created and set to the Terrain.
		/// Populates the volumePositionOffset with the heightfield offset position.
		/// Returns true if successfully created the terrain, otherwise false.
		/// </summary>
		/// <param name="session">Houdini Engine session to query heightfield data from</param>
		/// <param name="volumeInfo">Volume info pertaining to the heightfield to generate the Terrain from</param>
		/// <param name="geoID">The geometry ID</param>
		/// <param name="partID">The part ID (height layer)</param>
		/// <param name="gameObject">The target GameObject containing the Terrain component</param>
		/// <param name="terrainData">A valid TerrainData to use, or if empty, a new one is created and populated</param>
		/// <param name="volumePositionOffset">Heightfield offset</param>
		/// <returns>True if successfully popupated the terrain</returns>
		public static bool GenerateTerrainFromVolume(HEU_SessionBase session, ref HAPI_VolumeInfo volumeInfo, HAPI_NodeId geoID, HAPI_PartId partID,
			GameObject gameObject, ref TerrainData terrainData, out Vector3 volumePositionOffset, ref Terrain terrain)
		{
			volumePositionOffset = Vector3.zero;

			if (volumeInfo.zLength == 1 && volumeInfo.tupleSize == 1)
			{
				// Heightfields will be converted to terrain in Unity.
				// Unity requires terrainData.heightmapResolution to be square power of two plus 1 (eg. 513, 257, 129, 65).
				// Houdini gives volumeInfo.xLength and volumeInfo.yLength which are the number of height values per dimension.
				// Note that volumeInfo.xLength and volumeInfo.yLength is equal to Houdini heightfield size / grid spacing.
				// The heightfield grid spacing is given as volumeTransformMatrix.scale but divided by 2 (grid spacing / 2 = volumeTransformMatrix.scale).
				// It is recommended to use grid spacing of 2.

				// Use the volumeInfo.transform to get the actual heightfield position and size.
				Matrix4x4 volumeTransformMatrix = HEU_HAPIUtility.GetMatrixFromHAPITransform(ref volumeInfo.transform, false);
				Vector3 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;
				float terrainSizeX = Mathf.Round((volumeInfo.xLength - 1) * gridSpacingX);
				float terrainSizeY = Mathf.Round((volumeInfo.yLength - 1) * gridSpacingY);

				// Test size
				//float terrainSizeX = Mathf.Round((volumeInfo.xLength) * gridSpacingX);
				//float terrainSizeY = Mathf.Round((volumeInfo.yLength) * gridSpacingY);

				//Debug.LogFormat("GS = {0},{1},{2}. SX = {1}. SY = {2}", gridSpacingX, gridSpacingY, terrainSizeX, terrainSizeY);

				//Debug.LogFormat("HeightField Pos:{0}, Scale:{1}", position, scale.ToString("{0.00}"));
				//Debug.LogFormat("HeightField tileSize:{0}, xLength:{1}, yLength:{2}", volumeInfo.tileSize.ToString("{0.00}"), volumeInfo.xLength.ToString("{0.00}"), volumeInfo.yLength.ToString("{0.00}"));
				//Debug.LogFormat("HeightField Terrain Size x:{0}, y:{1}", terrainSizeX.ToString("{0.00}"), terrainSizeY.ToString("{0.00}"));
				//Debug.LogFormat("HeightField minX={0}, minY={1}, minZ={2}", volumeInfo.minX.ToString("{0.00}"), volumeInfo.minY.ToString("{0.00}"), volumeInfo.minZ.ToString("{0.00}"));

				const int UNITY_MINIMUM_HEIGHTMAP_RESOLUTION = 33;
				if (terrainSizeX < UNITY_MINIMUM_HEIGHTMAP_RESOLUTION || terrainSizeY < UNITY_MINIMUM_HEIGHTMAP_RESOLUTION)
				{
					Debug.LogWarningFormat("Unity Terrain has a minimum heightmap resolution of {0}. This HDA heightmap size is {1}x{2}."
						+ "\nPlease resize the terrain to a value higher than this.",
						UNITY_MINIMUM_HEIGHTMAP_RESOLUTION, terrainSizeX, terrainSizeY);
					return false;
				}

				bool bNewTerrain = false;
				bool bNewTerrainData = false;
				terrain = gameObject.GetComponent<Terrain>();
				if (terrain == null)
				{
					terrain = gameObject.AddComponent<Terrain>();
					bNewTerrain = true;
				}

				TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(gameObject);

				// Look up terrain material, if specified, on the height layer
				string specifiedTerrainMaterialName = HEU_GeneralUtility.GetMaterialAttributeValueFromPart(session,
					geoID, partID);

				// This ensures to reuse existing terraindata, and only creates new if none exist or none provided
				if (terrain.terrainData == null)
				{
					if (terrainData == null)
					{
						terrainData = new TerrainData();
						bNewTerrainData = true;
					}

					terrain.terrainData = terrainData;
					SetTerrainMaterial(terrain, specifiedTerrainMaterialName);
				}

				terrainData = terrain.terrainData;
				collider.terrainData = terrainData;

				if (bNewTerrain)
				{
#if UNITY_2018_3_OR_NEWER
					terrain.allowAutoConnect = true;
					// This has to be set after setting material
					terrain.drawInstanced = true;
#endif
				}

				// Heightmap resolution must be square power-of-two plus 1. 
				// Unity will automatically resize terrainData.heightmapResolution so need to handle the changed size (if Unity changed it).
				int heightMapResolution = volumeInfo.xLength;
				terrainData.heightmapResolution = heightMapResolution;
				int terrainResizedDelta = terrainData.heightmapResolution - heightMapResolution;
				if (terrainResizedDelta < 0)
				{
					Debug.LogWarningFormat("Note that Unity automatically resized terrain resolution to {0} from {1}. Use terrain size of power of two plus 1, and grid spacing of 2.", heightMapResolution, terrainData.heightmapResolution);
					heightMapResolution = terrainData.heightmapResolution;
				}
				else if (terrainResizedDelta > 0)
				{
					Debug.LogErrorFormat("Unsupported terrain size. Use terrain size of power of two plus 1, and grid spacing of 2. Given size is {0} but Unity resized it to {1}.", heightMapResolution, terrainData.heightmapResolution);
					return false;
				}

				int mapWidth = volumeInfo.xLength;
				int mapHeight = volumeInfo.yLength;

				// Get the converted height values from Houdini and find the min and max height range.
				float minHeight = 0;
				float maxHeight = 0;
				float heightRange = 0;
				bool bUseHeightRangeOverride = true;

				float[] normalizedHeights = GetNormalizedHeightmapFromPartWithMinMax(session, geoID, partID, 
					volumeInfo.xLength, volumeInfo.yLength, ref minHeight, ref maxHeight, ref heightRange, 
					bUseHeightRangeOverride);
				float[,] unityHeights = ConvertHeightMapHoudiniToUnity(heightMapResolution, heightMapResolution, normalizedHeights);

				// The terrainData.baseMapResolution is not set here, but rather left to whatever default Unity uses
				// The terrainData.alphamapResolution is set later when setting the alphamaps.

				if (bNewTerrainData)
				{
					// 32 is the default for resolutionPerPatch
					const int detailResolution = 1024;
					const int resolutionPerPatch = 32;
					terrainData.SetDetailResolution(detailResolution, resolutionPerPatch);
				}

				// Note SetHeights must be called before setting size in next line, as otherwise
				// the internal terrain size will not change after setting the size.
				terrainData.SetHeights(0, 0, unityHeights);

				// Note that Unity uses a default height range of 600 when a flat terrain is created.
				// Without a non-zero value for the height range, user isn't able to draw heights.
				// Therefore, set 600 as the value if height range is currently 0 (due to flat heightfield).
				if (heightRange == 0)
				{
					heightRange = terrainData.size.y > 1 ? terrainData.size.y : 600;
				}

				terrainData.size = new Vector3(terrainSizeX, heightRange, terrainSizeY);

				terrain.Flush();

				// Unity Terrain has origin at bottom left, whereas Houdini uses centre of terrain. 

				// Use volume bounds to set position offset when using split tiles
				float xmin, xmax, zmin, zmax, ymin, ymax, xcenter, ycenter, zcenter;
				session.GetVolumeBounds(geoID, partID, out xmin, out ymin, out zmin, out xmax, out ymax, out zmax, out xcenter,
					out ycenter, out zcenter);
				//Debug.LogFormat("xmin: {0}, xmax: {1}, ymin: {2}, ymax: {3}, zmin: {4}, zmax: {5}, xc: {6}, yc: {7}, zc: {8}",
				//	xmin, xmax, ymin, ymax, zmin, zmax, xcenter, ycenter, zcenter);

				// Offset position is based on size of heightfield
				float offsetX = (float)heightMapResolution / (float)mapWidth;
				float offsetZ = (float)heightMapResolution / (float)mapHeight;
				//Debug.LogFormat("offsetX: {0}, offsetZ: {1}", offsetX, offsetZ);

				// Use y position from attribute if user has set it
				float ypos = position.y + minHeight;
				float userYPos;
				if (HEU_GeneralUtility.GetAttributeFloatSingle(session, geoID, partID,
					HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_YPOS, out userYPos))
				{
					ypos = userYPos;
				}

				// TODO: revisit how the position is calculated
				volumePositionOffset = new Vector3((terrainSizeX + xmin) * offsetX, ypos, zmin * offsetZ);

				// Test position
				//volumePositionOffset = new Vector3(xcenter + mapWidth, ycenter, zcenter - mapHeight);

				return true;
			}
			else
			{
				Debug.LogWarning("Non-heightfield volume type not supported!");
			}

			return false;
		}
		public bool UploadAttributeViaMeshInput(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID)
		{
			// Need to upload same geometry as in the original Editable node, and with custom attribute values.

			// First get the current geometry info and data.

			HAPI_GeoInfo geoInfo = new HAPI_GeoInfo();
			if(!session.GetGeoInfo(geoID, ref geoInfo))
			{
				return false;
			}

			HAPI_PartInfo oldPartInfo = new HAPI_PartInfo();
			if(!session.GetPartInfo(geoID, partID, ref oldPartInfo))
			{
				return false;
			}

			int pointCount = oldPartInfo.pointCount;
			int vertexCount = oldPartInfo.vertexCount;
			int faceCount = oldPartInfo.faceCount;

			// Get facecounts
			int[] faceCountData = new int[faceCount];
			if(!HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetFaceCounts, faceCountData, 0, faceCount))
			{
				return false;
			}

			// Get indices
			int[] vertexList = new int[vertexCount];
			if (!HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetVertexList, vertexList, 0, vertexCount))
			{
				return false;
			}

			List<HEU_AttributeData> pointAttributeDatas = new List<HEU_AttributeData>();
			List<HEU_AttributeData> vertexAttributeDatas = new List<HEU_AttributeData>();
			List<HEU_AttributeData> primitiveAttributeDatas = new List<HEU_AttributeData>();
			List<HEU_AttributeData> detailAttributeDatas = new List<HEU_AttributeData>();

			// Get all attributes, including editted
			GetAttributesList(session, geoID, partID, pointAttributeDatas, HAPI_AttributeOwner.HAPI_ATTROWNER_POINT, oldPartInfo.pointAttributeCount);
			GetAttributesList(session, geoID, partID, vertexAttributeDatas, HAPI_AttributeOwner.HAPI_ATTROWNER_VERTEX, oldPartInfo.vertexAttributeCount);
			GetAttributesList(session, geoID, partID, primitiveAttributeDatas, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM, oldPartInfo.primitiveAttributeCount);
			GetAttributesList(session, geoID, partID, detailAttributeDatas, HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL, oldPartInfo.detailAttributeCount);

			// Now create new geometry info and populate it

			HAPI_PartInfo newPartInfo = new HAPI_PartInfo();
			newPartInfo.faceCount = faceCount;
			newPartInfo.vertexCount = vertexCount;
			newPartInfo.pointCount = pointCount;
			newPartInfo.pointAttributeCount = oldPartInfo.pointAttributeCount;
			newPartInfo.vertexAttributeCount = oldPartInfo.vertexAttributeCount;
			newPartInfo.primitiveAttributeCount = oldPartInfo.primitiveAttributeCount;
			newPartInfo.detailAttributeCount = oldPartInfo.detailAttributeCount;

			int newPartID = partID;

			if (!session.SetPartInfo(geoID, newPartID, ref newPartInfo))
			{
				return false;
			}

			if (!HEU_GeneralUtility.SetArray2Arg(geoID, newPartID, session.SetFaceCount, faceCountData, 0, newPartInfo.faceCount))
			{
				return false;
			}

			if (!HEU_GeneralUtility.SetArray2Arg(geoID, newPartID, session.SetVertexList, vertexList, 0, newPartInfo.vertexCount))
			{
				return false;
			}

			// Upload all attributes, include editted
			UpdateAttributeList(session, geoID, partID, pointAttributeDatas);
			UpdateAttributeList(session, geoID, partID, vertexAttributeDatas);
			UpdateAttributeList(session, geoID, partID, primitiveAttributeDatas);
			UpdateAttributeList(session, geoID, partID, detailAttributeDatas);

			return session.CommitGeo(geoID);
		}
Esempio n. 5
0
		public void GenerateTransform(HEU_SessionBase session, HEU_Parameters parameters)
		{
			HAPI_TransformEuler transformEuler = new HAPI_TransformEuler(true);

			transformEuler.rstOrder = _rstOrder;
			transformEuler.rotationOrder = _xyzOrder;

			transformEuler.position[0] = 0;
			transformEuler.position[1] = 0;
			transformEuler.position[2] = 0;

			transformEuler.rotationEuler[0] = 0;
			transformEuler.rotationEuler[1] = 0;
			transformEuler.rotationEuler[2] = 0;

			transformEuler.scale[0] = 1;
			transformEuler.scale[1] = 1;
			transformEuler.scale[2] = 1;

			if (IsSpecialRSTOrder(transformEuler.rstOrder) && _handleParamTranslateBinding != null)
			{
				HEU_ParameterData parmData = parameters.GetParameterWithParmID(_handleParamTranslateBinding._parmID);
				if (parmData != null && !parmData._parmInfo.invisible)
				{
					transformEuler.position[0] = parmData._floatValues[0];
					transformEuler.position[1] = parmData._floatValues[1];
					transformEuler.position[2] = parmData._floatValues[2];
				}
			}

			if(_handleParamRotateBinding != null)
			{
				HEU_ParameterData parmData = parameters.GetParameterWithParmID(_handleParamRotateBinding._parmID);
				if (parmData != null && !parmData._parmInfo.invisible)
				{
					transformEuler.rotationEuler[0] = parmData._floatValues[0];
					transformEuler.rotationEuler[1] = parmData._floatValues[1];
					transformEuler.rotationEuler[2] = parmData._floatValues[2];
				}
			}

			if(_handleParamScaleBinding != null)
			{
				HEU_ParameterData parmData = parameters.GetParameterWithParmID(_handleParamScaleBinding._parmID);
				if (parmData != null && !parmData._parmInfo.invisible)
				{
					transformEuler.scale[0] = parmData._floatValues[0];
					transformEuler.scale[1] = parmData._floatValues[1];
					transformEuler.scale[2] = parmData._floatValues[2];
				}
			}

			if (!session.ConvertTransform(ref transformEuler, HAPI_RSTOrder.HAPI_SRT, HAPI_XYZOrder.HAPI_ZXY, out _convertedTransformEuler))
			{
				return;
			}

			// Convert to left-handed Unity
			_convertedTransformEuler.position[0] = -_convertedTransformEuler.position[0];
			_convertedTransformEuler.rotationEuler[1] = -_convertedTransformEuler.rotationEuler[1];
			_convertedTransformEuler.rotationEuler[2] = -_convertedTransformEuler.rotationEuler[2];

			if (IsSpecialRSTOrder(transformEuler.rstOrder))
			{
				_handlePosition = new Vector3(_convertedTransformEuler.position[0], _convertedTransformEuler.position[1], _convertedTransformEuler.position[2]);
			}
			else if (_handleParamTranslateBinding != null)
			{
				_handlePosition = new Vector3(-transformEuler.position[0], transformEuler.position[1], transformEuler.position[2]);
			}
			else
			{
				_handlePosition = Vector3.zero;
			}

			_handleRotation = Quaternion.Euler(_convertedTransformEuler.rotationEuler[0], _convertedTransformEuler.rotationEuler[1], _convertedTransformEuler.rotationEuler[2]);

			if (_handleParamScaleBinding != null)
			{
				_handleScale = new Vector3(transformEuler.scale[0], transformEuler.scale[1], transformEuler.scale[2]);
			}
			else
			{
				_handleScale = Vector3.one;
			}
		}
Esempio n. 6
0
		private HEU_LoadBufferInstancer GeneratePointAttributeInstancerBuffer(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, 
			string partName, HAPI_PartInfo partInfo)
		{
			int numInstances = partInfo.pointCount;
			if (numInstances <= 0)
			{
				return null;
			}

			// Find type of instancer
			string instanceAttrName = HEU_PluginSettings.InstanceAttr;
			string unityInstanceAttrName = HEU_PluginSettings.UnityInstanceAttr;
			string instancePrefixAttrName = HEU_Defines.DEFAULT_INSTANCE_PREFIX_ATTR;

			HAPI_AttributeInfo instanceAttrInfo = new HAPI_AttributeInfo();
			HAPI_AttributeInfo unityInstanceAttrInfo = new HAPI_AttributeInfo();
			HAPI_AttributeInfo instancePrefixAttrInfo = new HAPI_AttributeInfo();
			

			HEU_GeneralUtility.GetAttributeInfo(session, geoID, partID, instanceAttrName, ref instanceAttrInfo);
			HEU_GeneralUtility.GetAttributeInfo(session, geoID, partID, unityInstanceAttrName, ref unityInstanceAttrInfo);

			if (unityInstanceAttrInfo.exists)
			{
				// Object instancing via existing Unity object (path from point attribute)

				HAPI_Transform[] instanceTransforms = new HAPI_Transform[numInstances];
				if (!HEU_GeneralUtility.GetArray3Arg(geoID, partID, HAPI_RSTOrder.HAPI_SRT, session.GetInstanceTransformsOnPart, instanceTransforms, 0, numInstances))
				{
					return null;
				}

				string[] instancePrefixes = null;
				HEU_GeneralUtility.GetAttributeInfo(session, geoID, partID, instancePrefixAttrName, ref instancePrefixAttrInfo);
				if (instancePrefixAttrInfo.exists)
				{
					instancePrefixes = HEU_GeneralUtility.GetAttributeStringData(session, geoID, partID, instancePrefixAttrName, ref instancePrefixAttrInfo);
				}

				string[] assetPaths = null;

				// Attribute owner type determines whether to use single (detail) or multiple (point) asset(s) as source
				if (unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT || unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL)
				{
					assetPaths = HEU_GeneralUtility.GetAttributeStringData(session, geoID, partID, unityInstanceAttrName, ref unityInstanceAttrInfo);
				}
				else
				{
					// Other attribute owned types are unsupported
					SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unsupported attribute owner {0} for attribute {1}", 
						unityInstanceAttrInfo.owner, unityInstanceAttrName));
					return null;
				}

				if (assetPaths == null)
				{
					SetLog(HEU_LoadData.LoadStatus.ERROR, "Unable to get instanced asset path from attribute!");
					return null;
				}

				HEU_LoadBufferInstancer instancerBuffer = new HEU_LoadBufferInstancer();
				instancerBuffer.InitializeBuffer(partID, partName, partInfo.isInstanced, true);

				instancerBuffer._instanceTransforms = instanceTransforms;
				instancerBuffer._instancePrefixes = instancePrefixes;
				instancerBuffer._assetPaths = assetPaths;

				HAPI_AttributeInfo collisionGeoAttrInfo = new HAPI_AttributeInfo();
				HEU_GeneralUtility.GetAttributeInfo(session, geoID, partID, HEU_Defines.DEFAULT_COLLISION_GEO, ref collisionGeoAttrInfo);
				if (collisionGeoAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT
					|| collisionGeoAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL)
				{
					instancerBuffer._collisionAssetPaths = HEU_GeneralUtility.GetAttributeStringData(session, geoID, partID, HEU_Defines.DEFAULT_COLLISION_GEO, ref collisionGeoAttrInfo);
				}

				return instancerBuffer;
			}
			else if (instanceAttrInfo.exists)
			{
				// Object instancing via internal object path is not supported
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Object instancing is not supported (part {0})!", partName));
			}
			else
			{
				// Standard object instancing via single Houdini object is not supported
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Object instancing is not supported (part {0})!", partName));
			}

			return null;
		}
		private void PopulateAttributeData(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, HEU_AttributeData attributeData, ref HAPI_AttributeInfo attributeInfo)
		{
			attributeData._attributeInfo = attributeInfo;

			int tupleSize = attributeInfo.tupleSize;
			int attributeCount = attributeInfo.count;
			int arraySize = attributeCount * tupleSize;

			// First reset arrays if the type had changed since last sync
			if ((attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_INT && attributeData._attributeType != HEU_AttributeData.AttributeType.INT) ||
				(attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_FLOAT && attributeData._attributeType != HEU_AttributeData.AttributeType.FLOAT) ||
				(attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_STRING && attributeData._attributeType != HEU_AttributeData.AttributeType.STRING))
			{
				// Reset arrays if type is different
				attributeData._floatValues = null;
				attributeData._stringValues = null;
				attributeData._intValues = null;

				attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;

				if(attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_INT)
				{
					attributeData._attributeType = HEU_AttributeData.AttributeType.INT;
				}
				else if (attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_FLOAT)
				{
					attributeData._attributeType = HEU_AttributeData.AttributeType.FLOAT;
				}
				else if (attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_STRING)
				{
					attributeData._attributeType = HEU_AttributeData.AttributeType.STRING;
				}
			}

			// Make sure the internal array is correctly sized for syncing.
			if (attributeData._attributeType == HEU_AttributeData.AttributeType.INT)
			{
				if (attributeData._intValues == null)
				{
					attributeData._intValues = new int[arraySize];
					attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;
				}
				else if (attributeData._intValues.Length != arraySize)
				{
					System.Array.Resize<int>(ref attributeData._intValues, arraySize);
					attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;
				}
				attributeData._floatValues = null;
				attributeData._stringValues = null;

				if (attributeData._attributeState == HEU_AttributeData.AttributeState.INVALID)
				{
					int[] data = new int[0];
					HEU_GeneralUtility.GetAttribute(session, geoID, partID, attributeData._name, ref attributeInfo, ref data, session.GetAttributeIntData);
					for (int i = 0; i < attributeCount; ++i)
					{
						for (int tuple = 0; tuple < tupleSize; ++tuple)
						{
							attributeData._intValues[i * tupleSize + tuple] = data[i * tupleSize + tuple];
						}
					}
				}
			}
			else if (attributeData._attributeType == HEU_AttributeData.AttributeType.FLOAT)
			{
				if (attributeData._floatValues == null)
				{
					attributeData._floatValues = new float[arraySize];
					attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;
				}
				else if (attributeData._floatValues.Length != arraySize)
				{
					System.Array.Resize<float>(ref attributeData._floatValues, arraySize);
					attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;
				}
				attributeData._intValues = null;
				attributeData._stringValues = null;

				if (attributeData._attributeState == HEU_AttributeData.AttributeState.INVALID)
				{
					float[] data = new float[0];
					HEU_GeneralUtility.GetAttribute(session, geoID, partID, attributeData._name, ref attributeInfo, ref data, session.GetAttributeFloatData);
					for (int i = 0; i < attributeCount; ++i)
					{
						for (int tuple = 0; tuple < tupleSize; ++tuple)
						{
							attributeData._floatValues[i * tupleSize + tuple] = data[i * tupleSize + tuple];
						}
					}
				}
			}
			else if (attributeData._attributeType == HEU_AttributeData.AttributeType.STRING)
			{
				if (attributeData._stringValues == null)
				{
					attributeData._stringValues = new string[arraySize];
					attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;
				}
				else if (attributeData._stringValues.Length != arraySize)
				{
					System.Array.Resize<string>(ref attributeData._stringValues, arraySize);
					attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;
				}
				attributeData._intValues = null;
				attributeData._floatValues = null;

				if (attributeData._attributeState == HEU_AttributeData.AttributeState.INVALID)
				{
					HAPI_StringHandle[] data = new HAPI_StringHandle[0];
					HEU_GeneralUtility.GetAttribute(session, geoID, partID, attributeData._name, ref attributeInfo, ref data, session.GetAttributeStringData);
					for (int i = 0; i < attributeCount; ++i)
					{
						for (int tuple = 0; tuple < tupleSize; ++tuple)
						{
							HAPI_StringHandle stringHandle = data[i * tupleSize + tuple];
							attributeData._stringValues[i * tupleSize + tuple] = HEU_SessionManager.GetString(stringHandle, session);
						}
					}
				}
			}

			SetAttributeDataSyncd(attributeData);
		}
 protected virtual void SetupLoadTask(HEU_SessionBase session)
 {
 }
		/// <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;
			}
		}
Esempio n. 10
0
        /// <summary>
        /// Return Houdini Engine installation and session information.
        /// Tries to use existing or creates new session to find information.
        /// </summary>
        /// <returns>String containing installation and session information.</returns>
        public static string GetHoudiniEngineInstallationInfo()
        {
#if HOUDINIENGINEUNITY_ENABLED
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("Required Houdini Version: {0}.{1}.{2}\nRequired Houdini Engine Version: {3}.{4}.{5}\n\n",
                            HEU_HoudiniVersion.HOUDINI_MAJOR,
                            HEU_HoudiniVersion.HOUDINI_MINOR,
                            HEU_HoudiniVersion.HOUDINI_BUILD,
                            HEU_HoudiniVersion.HOUDINI_ENGINE_MAJOR,
                            HEU_HoudiniVersion.HOUDINI_ENGINE_MINOR,
                            HEU_HoudiniVersion.HOUDINI_ENGINE_API);

            // Check if existing session is valid, or create a new session. Then query installation information.
            HEU_SessionBase session = HEU_SessionManager.GetDefaultSession();
            if (session != null && session.IsSessionValid())
            {
                int hMajor = session.GetEnvInt(HAPI_EnvIntType.HAPI_ENVINT_VERSION_HOUDINI_MAJOR);
                int hMinor = session.GetEnvInt(HAPI_EnvIntType.HAPI_ENVINT_VERSION_HOUDINI_MINOR);
                int hBuild = session.GetEnvInt(HAPI_EnvIntType.HAPI_ENVINT_VERSION_HOUDINI_BUILD);

                int heuPatch = session.GetEnvInt(HAPI_EnvIntType.HAPI_ENVINT_VERSION_HOUDINI_PATCH);

                int heuMajor = session.GetEnvInt(HAPI_EnvIntType.HAPI_ENVINT_VERSION_HOUDINI_ENGINE_MAJOR);
                int heuMinor = session.GetEnvInt(HAPI_EnvIntType.HAPI_ENVINT_VERSION_HOUDINI_ENGINE_MINOR);
                int heuAPI   = session.GetEnvInt(HAPI_EnvIntType.HAPI_ENVINT_VERSION_HOUDINI_ENGINE_API);

                sb.AppendFormat("Installed Houdini Version: {0}.{1}.{2}{3}\n", hMajor, hMinor, hBuild, (heuPatch > 0) ? "." + heuPatch.ToString() : "");
                sb.AppendFormat("Installed Houdini Engine Version: {0}.{1}.{2}\n\n", heuMajor, heuMinor, heuAPI);

                sb.AppendFormat("Houdini Binaries Path: {0}\n", HEU_Platform.GetHoudiniEnginePath() + HEU_HoudiniVersion.HAPI_BIN_PATH);
                sb.AppendFormat("Unity Plugin Version: {0}\n\n", HEU_HoudiniVersion.UNITY_PLUGIN_VERSION);

                HEU_SessionData sessionData = session.GetSessionData();
                if (sessionData != null)
                {
                    sb.AppendFormat("Session ID: {0}\n", sessionData.SessionID);
                    sb.AppendFormat("Session Type: {0}\n", sessionData.SessionType);
                    sb.AppendFormat("Process ID: {0}\n", sessionData.ProcessID);

                    if (sessionData.SessionType == HAPI_SessionType.HAPI_SESSION_THRIFT)
                    {
                        sb.AppendFormat("Pipe name: {0}\n", sessionData.PipeName);
                    }

                    sb.AppendLine();
                }

                sb.Append("License Type Acquired: ");
                HAPI_License license = HEU_SessionManager.GetCurrentLicense(false);
                switch (license)
                {
                case HAPI_License.HAPI_LICENSE_NONE: sb.Append("None\n"); break;

                case HAPI_License.HAPI_LICENSE_HOUDINI_ENGINE: sb.Append("Houdini Engine\n"); break;

                case HAPI_License.HAPI_LICENSE_HOUDINI: sb.Append("Houdini (Escape)\n"); break;

                case HAPI_License.HAPI_LICENSE_HOUDINI_FX: sb.Append("Houdini FX\n"); break;

                case HAPI_License.HAPI_LICENSE_HOUDINI_ENGINE_INDIE: sb.Append("Houdini Engine Indie"); break;

                case HAPI_License.HAPI_LICENSE_HOUDINI_INDIE: sb.Append("Houdini Indie\n"); break;

                default: sb.Append("Unknown\n"); break;
                }
            }
            else             // Unable to establish a session
            {
                sb.AppendLine("Unable to detect Houdini Engine installation.");
                sb.AppendLine("License Type Acquired: Unknown\n");
                if (session != null)
                {
                    sb.AppendLine("Failure possibly due to: " + session.GetLastSessionError());
                }
            }

            sb.AppendLine();
            sb.Append("PATH: \n" + GetEnvironmentPath());

            Debug.Log(sb.ToString());

            return(sb.ToString());
#else
            return("");
#endif
        }
        HAPI_NodeId GetParentNodeID(HEU_SessionBase session)
        {
            HAPI_NodeInfo nodeInfo = new HAPI_NodeInfo();

            return((session.GetNodeInfo(_cookNodeID, ref nodeInfo, false)) ? nodeInfo.parentId : -1);
        }
Esempio n. 12
0
 /// <summary>
 /// Creates a new input asset in scene, as well as in a Houdini session.
 /// </summary>
 /// <returns>A valid input asset gameobject or null if failed.</returns>
 public static GameObject CreateNewInputAsset(Transform parentTransform = null, HEU_SessionBase session = null, bool bBuildAsync = true)
 {
     return(CreateNewAsset(HEU_HoudiniAsset.HEU_AssetType.TYPE_INPUT, "HoudiniInput", parentTransform, session, bBuildAsync));
 }
Esempio n. 13
0
        public static GameObject CreateNewAsset(HEU_HoudiniAsset.HEU_AssetType assetType, string rootName = "HoudiniAsset", Transform parentTransform = null, HEU_SessionBase session = null, bool bBuildAsync = true)
        {
            if (session == null)
            {
                session = HEU_SessionManager.GetOrCreateDefaultSession();
            }
            if (!session.IsSessionValid())
            {
                Debug.LogWarning("Invalid Houdini Engine session!");
                return(null);
            }

            // This will be the root GameObject for the HDA. Adding HEU_HoudiniAssetRoot
            // allows to use a custom Inspector.
            GameObject           rootGO    = new GameObject();
            HEU_HoudiniAssetRoot assetRoot = rootGO.AddComponent <HEU_HoudiniAssetRoot>();

            // Set the game object's name to the asset's name
            rootGO.name = string.Format("{0}{1}", rootName, rootGO.GetInstanceID());

            // Under the root, we'll add the HEU_HoudiniAsset onto another GameObject
            // This will be marked as EditorOnly to strip out for builds
            GameObject hdaGEO = new GameObject(HEU_PluginSettings.HDAData_Name);

            hdaGEO.transform.parent = rootGO.transform;

            // This holds all Houdini Engine data
            HEU_HoudiniAsset asset = hdaGEO.AddComponent <HEU_HoudiniAsset>();

            // Marking as EditorOnly to be excluded from builds
            if (HEU_GeneralUtility.DoesUnityTagExist(HEU_PluginSettings.EditorOnly_Tag))
            {
                hdaGEO.tag = HEU_PluginSettings.EditorOnly_Tag;
            }

            // Bind the root to the asset
            assetRoot._houdiniAsset = asset;

            // Populate asset with what we know
            asset.SetupAsset(assetType, null, rootGO, session);

            // Build it in Houdini Engine
            asset.RequestReload(bBuildAsync);

            if (parentTransform != null)
            {
                rootGO.transform.parent        = parentTransform;
                rootGO.transform.localPosition = Vector3.zero;
            }
            else
            {
                rootGO.transform.position = Vector3.zero;
            }

            return(rootGO);
        }
Esempio n. 14
0
        /// <summary>
        /// Load and instantiate an HDA asset in Unity and Houdini, for the asset located at given path.
        /// </summary>
        /// <param name="filePath">Full path to the HDA in Unity project</param>
        /// <param name="initialPosition">Initial location to create the instance in Unity.</param>
        /// <returns>Returns the newly created gameobject for the asset in the scene, or null if creation failed.</returns>
        public static GameObject InstantiateHDA(string filePath, Vector3 initialPosition, HEU_SessionBase session, bool bBuildAsync)
        {
            if (filePath == null || !HEU_Platform.DoesFileExist(filePath))
            {
                return(null);
            }

            // This will be the root GameObject for the HDA. Adding HEU_HoudiniAssetRoot
            // allows to use a custom Inspector.
            GameObject           rootGO    = new GameObject(HEU_Defines.HEU_DEFAULT_ASSET_NAME);
            HEU_HoudiniAssetRoot assetRoot = rootGO.AddComponent <HEU_HoudiniAssetRoot>();

            // Under the root, we'll add the HEU_HoudiniAsset onto another GameObject
            // This will be marked as EditorOnly to strip out for builds
            GameObject hdaGEO = new GameObject(HEU_PluginSettings.HDAData_Name);

            hdaGEO.transform.parent = rootGO.transform;

            // This holds all Houdini Engine data
            HEU_HoudiniAsset asset = hdaGEO.AddComponent <HEU_HoudiniAsset>();

            // Marking as EditorOnly to be excluded from builds
            if (HEU_GeneralUtility.DoesUnityTagExist(HEU_PluginSettings.EditorOnly_Tag))
            {
                hdaGEO.tag = HEU_PluginSettings.EditorOnly_Tag;
            }

            // Bind the root to the asset
            assetRoot._houdiniAsset = asset;

            // Populate asset with what we know
            asset.SetupAsset(HEU_HoudiniAsset.HEU_AssetType.TYPE_HDA, filePath, rootGO, session);

            // Build it in Houdini Engine
            asset.RequestReload(bBuildAsync);

            // Apply Unity transform and possibly upload to Houdini Engine
            rootGO.transform.position = initialPosition;

            Debug.LogFormat("{0}: Created new HDA asset from {1} of type {2}.", HEU_Defines.HEU_NAME, filePath, asset.AssetType);

            return(rootGO);
        }
Esempio n. 15
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("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable);

				// 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);

						// 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))
				{
					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;
		}
Esempio n. 16
0
		/// <summary>
		/// Find all TOP networks from linked HDA, as well as the TOP nodes within, and populate internal state.
		/// </summary>
		/// <returns>True if successfully populated data</returns>
		public bool PopulateTOPNetworks()
		{
			HEU_SessionBase session = GetHAPISession();

			HAPI_NodeInfo assetInfo = new HAPI_NodeInfo();
			if (!session.GetNodeInfo(_assetID, ref assetInfo, true))
			{
				return false;
			}

			// Get all networks within the asset, recursively.
			// The reason to get all networks is because there can be TOP network SOP which isn't a TOP network type, but rather a SOP type
			int nodeCount = 0;
			if (!session.ComposeChildNodeList(_assetID, (int)(HAPI_NodeType.HAPI_NODETYPE_ANY), (int)HAPI_NodeFlags.HAPI_NODEFLAGS_NETWORK, true, ref nodeCount))
			{
				return false;
			}

			HAPI_NodeId[] nodeIDs = new HAPI_NodeId[nodeCount];
			if (!session.GetComposedChildNodeList(_assetID, nodeIDs, nodeCount))
			{
				return false;
			}

			// Holds TOP networks in use
			List<HEU_TOPNetworkData> newNetworks = new List<HEU_TOPNetworkData>();

			// For each network, only add those with TOP child nodes (therefore guaranteeing only TOP networks are added).
			for (int t = 0; t < nodeCount; ++t)
			{
				HAPI_NodeInfo topNodeInfo = new HAPI_NodeInfo();
				if (!session.GetNodeInfo(nodeIDs[t], ref topNodeInfo))
				{
					return false;
				}

				string nodeName = HEU_SessionManager.GetString(topNodeInfo.nameSH, session);
				//Debug.LogFormat("Top node: {0} - {1}", nodeName, topNodeInfo.type);

				// Skip any non TOP or SOP networks
				if (topNodeInfo.type != HAPI_NodeType.HAPI_NODETYPE_TOP && topNodeInfo.type != HAPI_NodeType.HAPI_NODETYPE_SOP)
				{
					continue;
				}

				// Get list of all TOP nodes within this network.
				HAPI_NodeId[] topNodeIDs = null;
				if (!HEU_SessionManager.GetComposedChildNodeList(session, nodeIDs[t], (int)(HAPI_NodeType.HAPI_NODETYPE_TOP), (int)HAPI_NodeFlags.HAPI_NODEFLAGS_TOP_NONSCHEDULER, true, out topNodeIDs))
				{
					continue;
				}

				// Skip networks without TOP nodes
				if (topNodeIDs == null || topNodeIDs.Length == 0)
				{
					continue;
				}

				// Get any filter tags from spare parms on TOP nodes
				TOPNodeTags tags = new TOPNodeTags();
				if (_useHEngineData)
				{
					ParseHEngineData(session, nodeIDs[t], ref topNodeInfo, ref tags);

					if (!tags._show)
					{
						continue;
					}
				}
				else
				{
					tags._show = true;
				}

				HEU_TOPNetworkData topNetworkData = GetTOPNetworkByName(nodeName, _topNetworks);
				if (topNetworkData == null)
				{
					topNetworkData = new HEU_TOPNetworkData();
				}
				else
				{
					// Found previous TOP network, so remove it from old list. This makes
					// sure to not remove it when cleaning up old nodes.
					_topNetworks.Remove(topNetworkData);
				}

				newNetworks.Add(topNetworkData);

				topNetworkData._nodeID = nodeIDs[t];
				topNetworkData._nodeName = nodeName;
				topNetworkData._parentName = _assetName;
				topNetworkData._tags = tags;

				PopulateTOPNodes(session, topNetworkData, topNodeIDs, _useHEngineData);
			}

			// Clear old TOP networks and nodes
			ClearAllTOPData();
			_topNetworks = newNetworks;

			// Update latest TOP network names
			_topNetworkNames = new string[_topNetworks.Count];
			for(int i = 0; i < _topNetworks.Count; ++i)
			{
				_topNetworkNames[i] = _topNetworks[i]._nodeName;
			}

			return true;
		}
Esempio n. 17
0
		public bool GenerateMeshBuffers(HEU_SessionBase session, HAPI_NodeId nodeID, List<HAPI_PartInfo> meshParts, 
			bool bSplitPoints, bool bUseLODGroups, bool bGenerateUVs, bool bGenerateTangents, bool bGenerateNormals,
			out List<HEU_LoadBufferMesh> meshBuffers)
        {
			meshBuffers = null;
			if (meshParts.Count == 0)
            {
                return true;
            }

			bool bSuccess = true;
			string assetCacheFolderPath = "";

			meshBuffers = new List<HEU_LoadBufferMesh>();

			foreach(HAPI_PartInfo partInfo in meshParts)
			{
				HAPI_NodeId geoID = nodeID;
				int partID = partInfo.id;
				string partName = HEU_SessionManager.GetString(partInfo.nameSH, session);
				bool bPartInstanced = partInfo.isInstanced;

				if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_MESH)
				{
					List<HEU_MaterialData> materialCache = new List<HEU_MaterialData>();

					HEU_GenerateGeoCache geoCache = HEU_GenerateGeoCache.GetPopulatedGeoCache(session, -1, geoID, partID, bUseLODGroups,
						materialCache, assetCacheFolderPath);
					if (geoCache == null)
					{
						// Failed to get necessary info for generating geometry.
						SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Failed to generate geometry cache for part: {0}", partName));
						continue;
					}

					geoCache._materialCache = materialCache;

					// Build the GeoGroup using points or vertices
					bool bResult = false;
					List<HEU_GeoGroup> LODGroupMeshes = null;
					int defaultMaterialKey = 0;
					if (bSplitPoints)
					{
						bResult = HEU_GenerateGeoCache.GenerateGeoGroupUsingGeoCachePoints(session, geoCache, bGenerateUVs, bGenerateTangents, bGenerateNormals, bUseLODGroups, bPartInstanced,
							out LODGroupMeshes, out defaultMaterialKey);
					}
					else
					{
						bResult = HEU_GenerateGeoCache.GenerateGeoGroupUsingGeoCacheVertices(session, geoCache, bGenerateUVs, bGenerateTangents, bGenerateNormals, bUseLODGroups, bPartInstanced,
							out LODGroupMeshes, out defaultMaterialKey);
					}

					if (bResult)
					{
						HEU_LoadBufferMesh meshBuffer = new HEU_LoadBufferMesh();
						meshBuffer.InitializeBuffer(partID, partName, partInfo.isInstanced, false);

						meshBuffer._geoCache = geoCache;
						meshBuffer._LODGroupMeshes = LODGroupMeshes;
						meshBuffer._defaultMaterialKey = defaultMaterialKey;

						meshBuffer._bGenerateUVs = bGenerateUVs;
						meshBuffer._bGenerateTangents = bGenerateTangents;
						meshBuffer._bGenerateNormals = bGenerateNormals;
						meshBuffer._bPartInstanced = partInfo.isInstanced;

						meshBuffers.Add(meshBuffer);
					}
					else
					{
						SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Failed to generated geometry for part: {0}", partName));
					}
				}
			}

			return bSuccess;
        }
Esempio n. 18
0
		/// <summary>
		/// Given TOP nodes from a TOP network, populate internal state from each TOP node.
		/// </summary>
		/// <param name="session">Houdini Engine session</param>
		/// <param name="topNetwork">TOP network to query TOP nodes from</param>
		/// <param name="topNodeIDs">List of TOP nodes in the TOP network</param>
		/// <param name="useHEngineData">Whether or not to use HEngine data for filtering</param>
		/// <returns>True if successfully populated data</returns>
		public static bool PopulateTOPNodes(HEU_SessionBase session, HEU_TOPNetworkData topNetwork, HAPI_NodeId[] topNodeIDs, bool useHEngineData)
		{
			// Holds list of found TOP nodes
			List<HEU_TOPNodeData> newNodes = new List<HEU_TOPNodeData>();

			foreach(HAPI_NodeId topNodeID in topNodeIDs)
			{
				// Not necessary. Blocks main thread.
				//session.CookNode(childNodeID, HEU_PluginSettings.CookTemplatedGeos);

				HAPI_NodeInfo childNodeInfo = new HAPI_NodeInfo();
				if (!session.GetNodeInfo(topNodeID, ref childNodeInfo))
				{
					return false;
				}

				string nodeName = HEU_SessionManager.GetString(childNodeInfo.nameSH, session);
				//Debug.LogFormat("TOP Node: name={0}, type={1}", nodeName, childNodeInfo.type);

				TOPNodeTags tags = new TOPNodeTags();
				if (useHEngineData)
				{
					ParseHEngineData(session, topNodeID, ref childNodeInfo, ref tags);

					if (!tags._show)
					{
						continue;
					}
				}
				else
				{
					tags._show = true;
				}

				HEU_TOPNodeData topNodeData = GetTOPNodeByName(nodeName, topNetwork._topNodes);
				if (topNodeData == null)
				{
					topNodeData = new HEU_TOPNodeData();
				}
				else
				{
					topNetwork._topNodes.Remove(topNodeData);
				}

				newNodes.Add(topNodeData);

				//topNodeData.Reset();
				topNodeData._nodeID = topNodeID;
				topNodeData._nodeName = nodeName;
				topNodeData._parentName = topNetwork._parentName + "_" + topNetwork._nodeName;
				topNodeData._tags = tags;
			}

			// Clear old unused TOP nodes
			for (int i = 0; i < topNetwork._topNodes.Count; ++i)
			{
				ClearTOPNodeWorkItemResults(topNetwork._topNodes[i]);
			}
			topNetwork._topNodes = newNodes;

			// Get list of updated TOP node names
			topNetwork._topNodeNames = new string[topNetwork._topNodes.Count];
			for (int i = 0; i < topNetwork._topNodes.Count; ++i)
			{
				topNetwork._topNodeNames[i] = topNetwork._topNodes[i]._nodeName;
			}

			return true;
		}
		public void SyncAllAttributesFrom(HEU_SessionBase session, HEU_HoudiniAsset asset, HAPI_NodeId geoID, ref HAPI_PartInfo partInfo, GameObject outputGameObject)
		{
			_geoID = geoID;
			_partID = partInfo.id;

			HAPI_GeoInfo geoInfo = new HAPI_GeoInfo();
			if(session.GetGeoInfo(_geoID, ref geoInfo))
			{
				_geoName = HEU_SessionManager.GetString(geoInfo.nameSH, session);
			}

			_outputTransform = outputGameObject.transform;

			// Need the vertex list of indices to map the positions to vertex colors
			_vertexIndices = new int[partInfo.vertexCount];
			if (!HEU_GeneralUtility.GetArray2Arg(geoID, partInfo.id, session.GetVertexList, _vertexIndices, 0, partInfo.vertexCount))
			{
				return;
			}

			// Note that this currently only supports point attributes
			int attributePointCount = partInfo.attributeCounts[(int)HAPI_AttributeOwner.HAPI_ATTROWNER_POINT];
			string[] pointAttributeNames = new string[attributePointCount];
			if(!session.GetAttributeNames(geoID, partInfo.id, HAPI_AttributeOwner.HAPI_ATTROWNER_POINT, ref pointAttributeNames, attributePointCount))
			{
				Debug.LogErrorFormat("Failed to sync attributes. Unable to retrieve attribute names.");
				return;
			}

			// Create new list of attributes. We'll move existing attributes that are still in use as we find them.
			List<HEU_AttributeData> newAttributeDatas = new List<HEU_AttributeData>();

			foreach (string pointAttributeName in pointAttributeNames)
			{
				if(string.IsNullOrEmpty(pointAttributeName))
				{
					continue;
				}

				// Get position attribute values separately. Used for painting and editing points in 3D scene.
				HAPI_AttributeInfo pointAttributeInfo = new HAPI_AttributeInfo();
				if(session.GetAttributeInfo(geoID, partInfo.id, pointAttributeName, HAPI_AttributeOwner.HAPI_ATTROWNER_POINT, ref pointAttributeInfo))
				{
					if (pointAttributeName.Equals(HEU_Defines.HAPI_ATTRIB_POSITION))
					{
						if (pointAttributeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT)
						{
							Debug.LogErrorFormat("Expected float type for position attribute, but got {0}", pointAttributeInfo.storage);
							return;
						}

						_positionAttributeValues = new Vector3[pointAttributeInfo.count];
						float[] data = new float[0];
						HEU_GeneralUtility.GetAttribute(session, geoID, partInfo.id, pointAttributeName, ref pointAttributeInfo, ref data, session.GetAttributeFloatData);
						for (int i = 0; i < pointAttributeInfo.count; ++i)
						{
							_positionAttributeValues[i] = new Vector3(-data[i * pointAttributeInfo.tupleSize + 0], data[i * pointAttributeInfo.tupleSize + 1], data[i * pointAttributeInfo.tupleSize + 2]);
						}

						// We don't let position attributes be editted (for now anyway)
						continue;
					}


					HEU_AttributeData attrData = GetAttributeData(pointAttributeName);
					if (attrData == null)
					{
						// Attribute data not found. Create it.

						attrData = CreateAttribute(pointAttributeName, ref pointAttributeInfo);
						//Debug.LogFormat("Created attribute data: {0}", pointAttributeName);
					}

					// Add to new list.
					newAttributeDatas.Add(attrData);

					// Sync the attribute info to data.
					PopulateAttributeData(session, geoID, partInfo.id, attrData, ref pointAttributeInfo);

					if(pointAttributeName.Equals(HEU_Defines.HAPI_ATTRIB_COLOR) || pointAttributeInfo.typeInfo == HAPI_AttributeTypeInfo.HAPI_ATTRIBUTE_TYPE_COLOR)
					{
						_hasColorAttribute = true;
					}
				}
				else
				{
					// Failed to get point attribute info!
				}
			}

			// Overwriting the old list with the new should automatically remove unused attribute datas.
			_attributeDatas = newAttributeDatas;
		}
Esempio n. 20
0
		/// <summary>
		/// Load the geometry generated as results of the given work item, of the given TOP node.
		/// The load will be done asynchronously.
		/// Results must be tagged with 'file', and must have a file path, otherwise will not be loaded.
		/// </summary>
		/// <param name="session">Houdini Engine session that the TOP node is in</param>
		/// <param name="topNode">TOP node that the work item belongs to</param>
		/// <param name="workItemInfo">Work item whose results to load</param>
		/// <param name="resultInfos">Results data</param>
		/// <param name="workItemID">The work item's ID. Required for clearning its results.</param>
		public void LoadResults(HEU_SessionBase session, HEU_TOPNodeData topNode, HAPI_PDG_WorkitemInfo workItemInfo, HAPI_PDG_WorkitemResultInfo[] resultInfos, HAPI_PDG_WorkitemId workItemID)
		{
			// Create HEU_GeoSync objects, set results, and sync it

			string workItemName = HEU_SessionManager.GetString(workItemInfo.nameSH, session);
			//Debug.LogFormat("Work item: {0}:: name={1}, results={2}", workItemInfo.index, workItemName, workItemInfo.numResults);

			// Clear previously generated result
			ClearWorkItemResultByIndex(topNode, workItemInfo.index);

			if (resultInfos == null || resultInfos.Length == 0)
			{
				return;
			}

			HEU_TOPWorkResult result = GetWorkResultByIndex(topNode, workItemInfo.index);
			if (result == null)
			{
				result = new HEU_TOPWorkResult();
				result._workItemIndex = workItemInfo.index;
				result._workItemID = workItemID;

				topNode._workResults.Add(result);
			}

			// Load each result geometry
			int numResults = resultInfos.Length;
			for (int i = 0; i < numResults; ++i)
			{
				if (resultInfos[i].resultTagSH <= 0 || resultInfos[i].resultSH <= 0)
				{
					continue;
				}

				string tag = HEU_SessionManager.GetString(resultInfos[i].resultTagSH, session);
				string path = HEU_SessionManager.GetString(resultInfos[i].resultSH, session);

				//Debug.LogFormat("Result for work item {0}: result={1}, tag={2}, path={3}", result._workItemIndex, i, tag, path);

				if (string.IsNullOrEmpty(tag) || !tag.StartsWith("file"))
				{
					continue;
				}

				string name = string.Format("{0}_{1}_{2}",
					topNode._parentName,
					workItemName,
					workItemInfo.index);

				// Get or create parent GO
				if (topNode._workResultParentGO == null)
				{
					topNode._workResultParentGO = new GameObject(topNode._nodeName);
					HEU_GeneralUtility.SetParentWithCleanTransform(GetLoadRootTransform(), topNode._workResultParentGO.transform);
					topNode._workResultParentGO.SetActive(topNode._showResults);
				}

				GameObject newGO = new GameObject(name);
				HEU_GeneralUtility.SetParentWithCleanTransform(topNode._workResultParentGO.transform, newGO.transform);

				result._generatedGOs.Add(newGO);

				// HEU_GeoSync does the loading
				HEU_GeoSync geoSync = newGO.AddComponent<HEU_GeoSync>();
				if (geoSync != null)
				{
					geoSync._filePath = path;
					geoSync.StartSync();
				}
			}
		}
		/// <summary>
		/// This reverts local modifcations, refresh upstream inputs, and cooks the editable node.
		/// This ensures that the node will use the latest upstream input data before applying its own changes.
		/// </summary>
		/// <param name="session"></param>
		public void RefreshUpstreamInputs(HEU_SessionBase session)
		{
			session.RevertGeo(_geoID);

			HEU_HAPIUtility.CookNodeInHoudini(session, _geoID, false, "");
		}
Esempio n. 22
0
		/// <summary>
		/// Retrieves object info from Houdini session and updates internal state.
		/// New geo nodes are created, unused geo nodes are destroyed.
		/// Geo nodes are then refreshed to be in sync with Houdini session.
		/// </summary>
		/// <returns>True if internal state has changed (including geometry).</returns>
		public void UpdateObject(HEU_SessionBase session, bool bForceUpdate)
		{
			// Update the geo info
			if (!session.GetObjectInfo(ObjectID, ref _objectInfo))
			{
				return;
			}
			SyncWithObjectInfo(session);

			// Update the object transform
			_objectTransform = ParentAsset.GetObjectTransform(session, ObjectID);

			bool bApplyHAPITransform = false;

			// Container for existing geo nodes that are still in use
			List<HEU_GeoNode> geoNodesToKeep = new List<HEU_GeoNode>();
			
			// Container for new geo infos that need to be created
			List<HAPI_GeoInfo> newGeoInfosToCreate = new List<HAPI_GeoInfo>();

			if (_objectInfo.haveGeosChanged || bForceUpdate)
			{
				// Indicates that the geometry nodes have changed
				//Debug.Log("Geos have changed!");

				// Form a list of geo infos that are now present after cooking
				List<HAPI_GeoInfo> postCookGeoInfos = new List<HAPI_GeoInfo>();

				// Get the display geo info
				HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo();
				if (!session.GetDisplayGeoInfo(_objectInfo.nodeId, ref displayGeoInfo))
				{
					return;
				}
				postCookGeoInfos.Add(displayGeoInfo);

				// Get editable nodes, cook em, then create geo nodes for them
				HAPI_NodeId[] editableNodes = null;
				HEU_SessionManager.GetComposedChildNodeList(session, _objectInfo.nodeId, (int)HAPI_NodeType.HAPI_NODETYPE_SOP, (int)HAPI_NodeFlags.HAPI_NODEFLAGS_EDITABLE, true, out editableNodes);
				if (editableNodes != null)
				{
					foreach (HAPI_NodeId editNodeID in editableNodes)
					{
						if (editNodeID != displayGeoInfo.nodeId)
						{
							session.CookNode(editNodeID, HEU_PluginSettings.CookTemplatedGeos);

							HAPI_GeoInfo editGeoInfo = new HAPI_GeoInfo();
							if (session.GetGeoInfo(editNodeID, ref editGeoInfo))
							{
								postCookGeoInfos.Add(editGeoInfo);
							}
						}
					}
				}
				
				// Now for each geo node that are present after cooking, we check if its
				// new or whether we already have it prior to cooking.
				int numPostCookGeoInfos = postCookGeoInfos.Count;
				for (int i = 0; i < numPostCookGeoInfos; i++)
				{
					bool bFound = false;
					for (int j = 0; j < _geoNodes.Count; j++)
					{
						string geoName = HEU_SessionManager.GetString(postCookGeoInfos[i].nameSH, session);
						if(geoName.Equals(_geoNodes[j].GeoName))
						{
							_geoNodes[j].SetGeoInfo(postCookGeoInfos[i]);

							geoNodesToKeep.Add(_geoNodes[j]);
							_geoNodes.RemoveAt(j);

							bFound = true;
							break;
						}
					}

					if (!bFound)
					{
						newGeoInfosToCreate.Add(postCookGeoInfos[i]);
					}
				}

				// Whatever is left in _geoNodes is no longer needed so clean up
				int numCurrentGeos = _geoNodes.Count;
				for(int i = 0; i < numCurrentGeos; ++i)
				{
					_geoNodes[i].DestroyAllData();
				}

				bApplyHAPITransform = true;
			}
			else
			{
				Debug.Assert(_objectInfo.geoCount == _geoNodes.Count, "Expected same number of geometry nodes.");
			}
			
			// Go through the old geo nodes that are still in use and update if necessary.
			foreach (HEU_GeoNode geoNode in geoNodesToKeep)
			{
				// Get geo info and check if geo changed
				bool bGeoChanged = bForceUpdate || geoNode.HasGeoNodeChanged(session);
				if(bGeoChanged)
				{
					geoNode.UpdateGeo(session);
				}
				else
				{
					if (_objectInfo.haveGeosChanged)
					{
						// Clear object instances since the object info has changed.
						// Without this, the object instances were never getting updated
						// if only the inputs changed but not outputs (of instancers).
						geoNode.ClearObjectInstances();
					}

					// Visiblity might have changed, so update that
					geoNode.CalculateVisiblity(IsVisible());
				}
			}

			// Create the new geo infos and add to our keep list
			foreach (HAPI_GeoInfo newGeoInfo in newGeoInfosToCreate)
			{
				geoNodesToKeep.Add(CreateGeoNode(session, newGeoInfo));
			}

			// Overwrite the old list with new
			_geoNodes = geoNodesToKeep;

			// Update transform to all geo nodes whether they were created newly, or
			// this object's transform has changed
			if (bApplyHAPITransform || bForceUpdate || _objectInfo.hasTransformChanged)
			{
				// This has been moved to GenerateGeometry but kept here just in case.
				//ApplyObjectTransformToGeoNodes();
			}
		}
Esempio n. 23
0
		//  LOGIC -----------------------------------------------------------------------------------------------------

		public bool SetupHandle(HEU_SessionBase session, HAPI_NodeId assetID, int handleIndex, string handleName, 
			HEU_HandleType handleType, ref HAPI_HandleInfo handleInfo, HEU_Parameters parameters)
		{
			_handleIndex = handleIndex;
			_handleName = handleName;
			_handleType = handleType;

			HAPI_HandleBindingInfo[] handleBindingInfos = new HAPI_HandleBindingInfo[handleInfo.bindingsCount];
			if (!session.GetHandleBindingInfo(assetID, _handleIndex, handleBindingInfos, 0, handleInfo.bindingsCount))
			{
				return false;
			}

			HAPI_ParmId translateParmID = -1;
			HAPI_ParmId rotateParmID = -1;
			HAPI_ParmId scaleParmID = -1;
			HAPI_ParmId rstOrderParmID = -1;
			HAPI_ParmId xyzOrderParmID = -1;

			_rstOrder = HAPI_RSTOrder.HAPI_SRT;
			_xyzOrder = HAPI_XYZOrder.HAPI_XYZ;

			_handleParamTranslateBinding = null;
			_handleParamRotateBinding = null;
			_handleParamScaleBinding = null;

			for (int i = 0; i < handleBindingInfos.Length; ++i)
			{
				string parmName = HEU_SessionManager.GetString(handleBindingInfos[i].handleParmNameSH, session);

				//string assetParmName = HEU_SessionManager.GetString(handleBindingInfos[i].assetParmNameSH, session);
				//Debug.LogFormat("Handle {0} has parm {1} with asset parm {2} with asset parm id {3}", handleName, parmName, assetParmName, handleBindingInfos[i].assetParmId);

				if (parmName.Equals("tx") || parmName.Equals("ty") || parmName.Equals("tz"))
				{
					translateParmID = handleBindingInfos[i].assetParmId;

					if(_handleParamTranslateBinding == null)
					{
						HEU_ParameterData parmData = parameters.GetParameterWithParmID(translateParmID);
						if (parmData != null && !parmData._parmInfo.invisible)
						{
							_handleParamTranslateBinding = new HEU_HandleParamBinding();
							_handleParamTranslateBinding._paramType = HEU_HandleParamBinding.HEU_HandleParamType.TRANSLATE;
							_handleParamTranslateBinding._parmID = parmData.ParmID;
							_handleParamTranslateBinding._paramName = parmData._name;
							_handleParamTranslateBinding._bDisabled = parmData._parmInfo.disabled;
						}
					}

					if(_handleParamTranslateBinding != null)
					{
						if(parmName.Equals("tx"))
						{
							_handleParamTranslateBinding._boundChannels[0] = true;
						}
						else if (parmName.Equals("ty"))
						{
							_handleParamTranslateBinding._boundChannels[1] = true;
						}
						else if (parmName.Equals("tz"))
						{
							_handleParamTranslateBinding._boundChannels[2] = true;
						}
					}
				}

				if (parmName.Equals("rx") || parmName.Equals("ry") || parmName.Equals("rz"))
				{
					rotateParmID = handleBindingInfos[i].assetParmId;

					if(_handleParamRotateBinding == null)
					{
						HEU_ParameterData parmData = parameters.GetParameterWithParmID(rotateParmID);
						if (parmData != null && !parmData._parmInfo.invisible)
						{
							_handleParamRotateBinding = new HEU_HandleParamBinding();
							_handleParamRotateBinding._paramType = HEU_HandleParamBinding.HEU_HandleParamType.ROTATE;
							_handleParamRotateBinding._parmID = parmData.ParmID;
							_handleParamRotateBinding._paramName = parmData._name;
							_handleParamRotateBinding._bDisabled = parmData._parmInfo.disabled;
						}
					}

					if (_handleParamRotateBinding != null)
					{
						if (parmName.Equals("rx"))
						{
							_handleParamRotateBinding._boundChannels[0] = true;
						}
						else if (parmName.Equals("ry"))
						{
							_handleParamRotateBinding._boundChannels[1] = true;
						}
						else if (parmName.Equals("rz"))
						{
							_handleParamRotateBinding._boundChannels[2] = true;
						}
					}
				}

				if (parmName.Equals("sx") || parmName.Equals("sy") || parmName.Equals("sz"))
				{
					scaleParmID = handleBindingInfos[i].assetParmId;

					if (_handleParamScaleBinding == null)
					{
						HEU_ParameterData parmData = parameters.GetParameterWithParmID(scaleParmID);
						if (parmData != null && !parmData._parmInfo.invisible)
						{
							_handleParamScaleBinding = new HEU_HandleParamBinding();
							_handleParamScaleBinding._paramType = HEU_HandleParamBinding.HEU_HandleParamType.SCALE;
							_handleParamScaleBinding._parmID = parmData.ParmID;
							_handleParamScaleBinding._paramName = parmData._name;
							_handleParamScaleBinding._bDisabled = parmData._parmInfo.disabled;
						}
					}

					if (_handleParamScaleBinding != null)
					{
						if (parmName.Equals("sx"))
						{
							_handleParamScaleBinding._boundChannels[0] = true;
						}
						else if (parmName.Equals("sy"))
						{
							_handleParamScaleBinding._boundChannels[1] = true;
						}
						else if (parmName.Equals("sz"))
						{
							_handleParamScaleBinding._boundChannels[2] = true;
						}
					}
				}

				if(parmName.Equals("trs_order"))
				{
					rstOrderParmID = handleBindingInfos[i].assetParmId;
				}

				if (parmName.Equals("xyz_order"))
				{
					xyzOrderParmID = handleBindingInfos[i].assetParmId;
				}
			}

			if (rstOrderParmID >= 0)
			{
				HEU_ParameterData parmData = parameters.GetParameter(rstOrderParmID);
				if (parmData != null)
				{
					_rstOrder = (HAPI_RSTOrder)parmData._intValues[0];
				}
			}

			if (xyzOrderParmID >= 0)
			{
				HEU_ParameterData parmData = parameters.GetParameter(xyzOrderParmID);
				if (parmData != null)
				{
					_xyzOrder = (HAPI_XYZOrder)parmData._intValues[0];
				}
			}

			GenerateTransform(session, parameters);

			return true;
		}
Esempio n. 24
0
		public void GenerateGeometry(HEU_SessionBase session)
		{
			// Volumes could come in as a geonode + part for each heightfield layer.
			// Otherwise the other geo types can be done individually.

			bool bResult = false;

			List<HEU_PartData> meshParts = new List<HEU_PartData>();
			List<HEU_PartData> volumeParts = new List<HEU_PartData>();

			List<HEU_PartData> partsToDestroy = new List<HEU_PartData>();

			HEU_HoudiniAsset parentAsset = ParentAsset;

			foreach (HEU_GeoNode geoNode in _geoNodes)
			{
				geoNode.GetPartsByOutputType(meshParts, volumeParts);

				if(volumeParts.Count > 0)
				{
					// Volumes
					// Each layer in the volume is retrieved as a volume part, in the display geo node. 
					// But we need to handle all layers as 1 terrain output in Unity, with 1 height layer and 
					// other layers as alphamaps.
					geoNode.ProcessVolumeParts(session, volumeParts);

					// Clear the volume parts after processing since we are done with this set
					volumeParts.Clear();
				}
			}

			// Meshes
			foreach (HEU_PartData part in meshParts)
			{
				// This returns false when there is no valid geometry or is not instancing. Should remove it as otherwise
				// stale data sticks around on recook
				bResult = part.GenerateMesh(session, parentAsset.GenerateUVs, parentAsset.GenerateTangents, parentAsset.GenerateNormals, parentAsset.UseLODGroups);
				if (!bResult)
				{
					partsToDestroy.Add(part);
				}
			}

			int numPartsToDestroy = partsToDestroy.Count;
			for(int i = 0; i < numPartsToDestroy; ++i)
			{
				HEU_GeoNode parentNode = partsToDestroy[i].ParentGeoNode;
				if (parentNode != null)
				{
					parentNode.RemoveAndDestroyPart(partsToDestroy[i]);
				}
				else
				{
					HEU_PartData.DestroyPart(partsToDestroy[i]);
				}
			}
			partsToDestroy.Clear();

			ApplyObjectTransformToGeoNodes();

			// Set visibility and attribute-based tag, layer, and scripts
			bool bIsVisible = IsVisible();
			foreach (HEU_GeoNode geoNode in _geoNodes)
			{
				geoNode.CalculateVisiblity(bIsVisible);

				geoNode.SetAttributeModifiersOnPartOutputs(session);
			}

			// Create editable attributes.
			// This should happen after visibility has been calculated above
			// since we need to show/hide the intermediate geometry during painting.
			foreach (HEU_PartData part in meshParts)
			{
				if (part.ParentGeoNode.IsIntermediateOrEditable())
				{
					part.SetupAttributeGeometry(session);
				}
			}
		}
	public static void ExecuteToolOperatorSingle(string toolName, string toolPath, GameObject[] inputObjects)
	{
	    // Single operator means single asset input. If multiple inputs are provided, create tool for each input.

	    bool bShouldUseHDA = ShouldUseHDA(inputObjects);

	    List<GameObject> outputObjectsToSelect = new List<GameObject>();

	    int numInputs = inputObjects.Length;
	    for (int i = 0; i < numInputs; ++i)
	    {
		if (inputObjects[i] == null)
		{
		    continue;
		}

		if (!bShouldUseHDA && !IsValidInputMesh(inputObjects[i]))
		{
		    HEU_Logger.LogWarningFormat("Specified object {0} does not contain a valid mesh!", inputObjects[i].name);
		    continue;
		}

		if (bShouldUseHDA && !IsValidInputHDA(inputObjects[i]))
		{
		    HEU_Logger.LogWarningFormat("Specified object {0} does not contain a valid HDA input!", inputObjects[i].name);
		    continue;
		}

		GameObject inputObject = inputObjects[i];

		GameObject go = HEU_HAPIUtility.InstantiateHDA(toolPath, Vector3.zero, HEU_SessionManager.GetOrCreateDefaultSession(), false);
		if (go != null)
		{
		    HEU_HoudiniAssetRoot assetRoot = go.GetComponent<HEU_HoudiniAssetRoot>();
		    if (assetRoot != null)
		    {
			HEU_HoudiniAsset asset = assetRoot._houdiniAsset;
			HEU_SessionBase session = asset.GetAssetSession(true);

			List<HEU_InputNode> inputNodes = asset.GetInputNodes();
			if (inputNodes == null || inputNodes.Count == 0)
			{
			    HEU_Logger.LogErrorFormat("Unable to assign input geometry due to no asset inputs on selected tool.");
			}
			else
			{
			    HEU_InputNode inputNode = inputNodes[0];

			    inputNode.ResetInputNode(session);

			    if (!bShouldUseHDA)
			    {
				inputNode.ChangeInputType(session, HEU_InputNode.InputObjectType.UNITY_MESH);

				HEU_InputObjectInfo inputInfo = inputNode.AddInputEntryAtEndMesh(inputObject);
				if (inputInfo != null)
				{
				    inputInfo._useTransformOffset = false;
				    inputNode.KeepWorldTransform = true;
				    inputNode.PackGeometryBeforeMerging = false;

				    inputNode.RequiresUpload = true;

				    asset.RequestCook(true, true, true, true);

				    outputObjectsToSelect.Add(assetRoot.gameObject);
				}
				else
				{
				    HEU_Logger.LogErrorFormat("Invalid input format: {0}", inputObject.gameObject.name);
				}
			    }
			    else
			    {
				inputNode.ChangeInputType(session, HEU_InputNode.InputObjectType.HDA);

				HEU_InputHDAInfo inputHDAInfo = inputNode.AddInputEntryAtEndHDA(inputObject);
				if (inputHDAInfo != null)
				{
				    inputNode.KeepWorldTransform = true;
				    inputNode.PackGeometryBeforeMerging = false;

				    inputNode.RequiresUpload = true;
				    asset.RequestCook(true, true, true, true);
				    outputObjectsToSelect.Add(assetRoot.gameObject);
				}
				else
				{
				    HEU_Logger.LogErrorFormat("Invalid input format: {0}", inputObject.gameObject.name);
				}
			    }
			}
		    }
		}
		else
		{
		    HEU_Logger.LogWarningFormat("Failed to instantiate tool: {0}", toolName);
		}
	    }

	    if (outputObjectsToSelect.Count > 0)
	    {
		HEU_EditorUtility.SelectObjects(outputObjectsToSelect.ToArray());
	    }
	}
Esempio n. 26
0
		/// <summary>
		/// Generates object instances.
		/// Skips parts that already have their instances generated.
		/// </summary>
		/// <param name="session">Active session to use</param>
		public void GenerateObjectInstances(HEU_SessionBase session)
		{
			if (!IsInstancer())
			{
				Debug.LogErrorFormat("Generate object instances called on a non-instancer object {0} for asset {1}!", ObjectName, ParentAsset.AssetName);
				return;
			}

			//Debug.LogFormat("Generate Object Instances:: id={5}, name={0}, isInstancer={1}, isInstanced={2}, instancePath={3}, instanceId={4}", HEU_SessionManager.GetString(_objectInfo.nameSH, session), 
			//	_objectInfo.isInstancer, _objectInfo.isInstanced, HEU_SessionManager.GetString(_objectInfo.objectInstancePathSH, session), _objectInfo.objectToInstanceId, _objectInfo.nodeId);

			// Is this a Houdini attribute instancer?
			string instanceAttrName = HEU_PluginSettings.InstanceAttr;
			string unityInstanceAttrName = HEU_PluginSettings.UnityInstanceAttr;
			string instancePrefixAttrName = HEU_Defines.DEFAULT_INSTANCE_PREFIX_ATTR;

			HAPI_AttributeInfo instanceAttrInfo = new HAPI_AttributeInfo();
			HAPI_AttributeInfo unityInstanceAttrInfo = new HAPI_AttributeInfo();
			HAPI_AttributeInfo instancePrefixAttrInfo = new HAPI_AttributeInfo();

			int numGeos = _geoNodes.Count;
			for(int i = 0; i < numGeos; ++i)
			{
				if(_geoNodes[i].Displayable)
				{
					List<HEU_PartData> parts = _geoNodes[i].GetParts();
					int numParts = parts.Count;
					for(int j = 0; j < numParts; ++j)
					{
						if(parts[j].ObjectInstancesBeenGenerated)
						{
							// This prevents instances being created unnecessarily (e.g. part hasn't changed since last cook)
							continue;
						}

						HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, instanceAttrName, ref instanceAttrInfo);
						HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, unityInstanceAttrName, ref unityInstanceAttrInfo);

						string[] instancePrefixes = null;
						HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, instancePrefixAttrName, ref instancePrefixAttrInfo);
						if(instancePrefixAttrInfo.exists)
						{
							instancePrefixes = HEU_GeneralUtility.GetAttributeStringData(session, _geoNodes[i].GeoID, parts[j].PartID, instancePrefixAttrName, ref instancePrefixAttrInfo);
						}

						// Must clear out instances, as otherwise we get duplicates
						parts[j].ClearInstances();

						// Clear out invalid object instance infos that no longer have any valid parts
						parts[j].ClearInvalidObjectInstanceInfos();

						if (instanceAttrInfo.exists)
						{
							// Object instancing via Houdini instance attribute

							parts[j].GenerateInstancesFromObjectIds(session, instancePrefixes);
						}
						else if (unityInstanceAttrInfo.exists)
						{
							// Object instancing via existing Unity object (path from point attribute)

							// Attribute owner type determines whether to use single instanced object (detail) or multiple (point)
							if (unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT)
							{
								parts[j].GenerateInstancesFromUnityAssetPathAttribute(session, unityInstanceAttrName);
							}
							else if(unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL)
							{
								bool bInstanced = false;
								int[] scriptAttr = new int[unityInstanceAttrInfo.count];
								if(session.GetAttributeStringData(_geoNodes[i].GeoID, parts[j].PartID, unityInstanceAttrName, ref unityInstanceAttrInfo, scriptAttr, 0, unityInstanceAttrInfo.count))
								{
									string assetPath = HEU_SessionManager.GetString(scriptAttr[0]);
									if (!string.IsNullOrEmpty(assetPath))
									{
										parts[j].GenerateInstancesFromUnityAssetPath(session, assetPath, instancePrefixes);
										bInstanced = true;
									}
								}

								if (!bInstanced)
								{
									Debug.LogWarningFormat("Unable to get instanced object path from detail instance attribute!");
								}
							}
							else
							{
								// Other attribute owned types are unsupported
								Debug.LogWarningFormat("Unsupported attribute owner {0} for attribute {1}", unityInstanceAttrInfo.owner, unityInstanceAttrName);
							}
						}
						else
						{
							// Standard object instancing via single Houdini object

							if (_objectInfo.objectToInstanceId == HEU_Defines.HEU_INVALID_NODE_ID)
							{
								Debug.LogAssertionFormat("Invalid object ID {0} used for object instancing. " 
									+ "Make sure to turn on Full point instancing and set the correct Instance Object.", _objectInfo.objectToInstanceId);
								continue;
							}

							parts[j].GenerateInstancesFromObjectID(session, _objectInfo.objectToInstanceId, instancePrefixes);
						}
					}
				}
			}
		}
Esempio n. 27
0
		/// <summary>
		/// Retrieves the heightmap from Houdini for the given volume part, converts to Unity coordinates,
		/// normalizes to 0 and 1, along with min and max height values, as well as the range.
		/// </summary>
		/// <param name="session">Current Houdini session</param>
		/// <param name="geoID">Geometry object ID</param>
		/// <param name="partID">The volume part ID</param>
		/// <param name="heightMapSize">Size of each dimension of the heightmap (assumes equal sides).</param>
		/// <param name="minHeight">Found minimum height value in the heightmap.</param>
		/// <param name="maxHeight">Found maximum height value in the heightmap.</param>
		/// <param name="heightRange">Found height range in the heightmap.</param>
		/// <returns>The converted heightmap from Houdini.</returns>
		public static float[] GetNormalizedHeightmapFromPartWithMinMax(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, 
			int heightMapWidth, int heightMapHeight, ref float minHeight, ref float maxHeight, ref float heightRange, bool bUseHeightRangeOverride)
		{
			minHeight = float.MaxValue;
			maxHeight = float.MinValue;
			heightRange = 1;

			HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo();
			bool bResult = session.GetVolumeInfo(geoID, partID, ref volumeInfo);
			if (!bResult)
			{
				return null;
			}

			int volumeXLength = volumeInfo.xLength;
			int volumeYLength = volumeInfo.yLength;

			// Number of heightfield values
			int totalHeightValues = volumeXLength * volumeYLength;

			float[] heightValues = new float[totalHeightValues];
			if (!GetHeightmapFromPart(session, volumeXLength, volumeYLength, geoID, partID, ref heightValues, ref minHeight, ref maxHeight))
			{
				return null;
			}

			heightRange = (maxHeight - minHeight);

			// Use the override height range if user has set via attribute
			bool bHeightRangeOverriden = false;
			if (bUseHeightRangeOverride)
			{
				float userHeightRange = GetHeightRangeFromHeightfield(session, geoID, partID);
				if (userHeightRange > 0)
				{
					heightRange = userHeightRange;
					bHeightRangeOverriden = true;
				}
			}

			if (heightRange == 0f)
			{
				// Always use a non-zero height range, otherwise user can't paint height on Terrain.
				heightRange = 1f;
			}

			//Debug.LogFormat("{0} : {1}", HEU_SessionManager.GetString(volumeInfo.nameSH, session), heightRange);

			const int UNITY_MAX_HEIGHT_RANGE = 65536;
			if (Mathf.RoundToInt(heightRange) > UNITY_MAX_HEIGHT_RANGE)
			{
				Debug.LogWarningFormat("Unity Terrain has maximum height range of {0}. This HDA height range is {1}, so it will be maxed out at {0}.\nPlease resize to within valid range!",
					UNITY_MAX_HEIGHT_RANGE, Mathf.RoundToInt(heightRange));
				heightRange = UNITY_MAX_HEIGHT_RANGE;
			}

			// Remap height values to fit terrain size
			int paddingWidth = heightMapWidth - volumeXLength;
			int paddingLeft = Mathf.CeilToInt(paddingWidth * 0.5f);
			int paddingRight = heightMapWidth - paddingLeft;
			//Debug.LogFormat("Padding: Width={0}, Left={1}, Right={2}", paddingWidth, paddingLeft, paddingRight);

			int paddingHeight = heightMapHeight - volumeYLength;
			int paddingTop = Mathf.CeilToInt(paddingHeight * 0.5f);
			int paddingBottom = heightMapHeight - paddingTop;
			//Debug.LogFormat("Padding: Height={0}, Top={1}, Bottom={2}", paddingHeight, paddingTop, paddingBottom);

			// Normalize the height values into the range between 0 and 1, inclusive.
			float inverseHeightRange = 1f / heightRange;
			float normalizeMinHeight = minHeight;
			if (minHeight >= 0f && minHeight <= 1f && maxHeight >= 0f && maxHeight <= 1f)
			{
				// Its important to leave the values alone if they are already normalized.
				// So these values don't actually do anything in the normalization calculation below.
				inverseHeightRange = 1f;
				normalizeMinHeight = 0f;
			}

			// Set height values at centre of the terrain, with padding on the sides if we resized
			float[] resizedHeightValues = new float[heightMapWidth * heightMapHeight];
			for (int y = 0; y < heightMapHeight; ++y)
			{
				for (int x = 0; x < heightMapWidth; ++x)
				{
					if (y >= paddingTop && y < (paddingBottom) && x >= paddingLeft && x < (paddingRight))
					{
						int ay = x - paddingLeft;
						int ax = y - paddingTop;

						float f = heightValues[ay + ax * volumeXLength];

						if (!bHeightRangeOverriden)
						{
							f -= normalizeMinHeight;
						}

						f *= inverseHeightRange;

						// Flip for right-hand to left-handed coordinate system
						int ix = x;
						int iy = heightMapHeight - (y + 1);

						// Unity expects height array indexing to be [y, x].
						resizedHeightValues[iy + ix * heightMapWidth] = f;
					}
				}
			}

			return resizedHeightValues;
		}
Esempio n. 28
0
		private void SyncWithObjectInfo(HEU_SessionBase session)
		{
			_objName = HEU_SessionManager.GetString(_objectInfo.nameSH, session);
		}
Esempio n. 29
0
		/// <summary>
		/// Grab the scatter data for the given part.
		/// This finds the properties of TreeInstances via attributes.
		/// </summary>
		/// <param name="session">Houdini session</param>
		/// <param name="geoID">Geometry ID</param>
		/// <param name="partID">Part (volume layer) ID</param>
		/// <param name="pointCount">Number of expected scatter points</param>
		public static void PopulateScatterTrees(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, int pointCount,
			ref HEU_VolumeScatterTrees scatterTrees)
		{
			// The HEU_VolumeScatterTrees might already have been created when the volumecache was queried.
			// The "height" layer might have had prototype data which is set in _scatterTrees.
			if (scatterTrees == null)
			{
				scatterTrees = new HEU_VolumeScatterTrees();
			}

			// Get prototype indices. These indices refer to _scatterTrees._treePrototypes.
			HAPI_AttributeInfo indicesAttrInfo = new HAPI_AttributeInfo();
			int[] indices = new int[0];
			if (HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_PROTOTYPEINDEX, ref indicesAttrInfo, ref indices, session.GetAttributeIntData))
			{
				if (indices != null && indices.Length == pointCount)
				{
					scatterTrees._prototypeIndices = indices;
				}
				else
				{
					Debug.LogWarningFormat("Scatter instance index count for attribute {0} is not valid. Expected {1} but got {2}",
						HEU_Defines.HEIGHTFIELD_TREEINSTANCE_PROTOTYPEINDEX, pointCount, (indices != null ? indices.Length : 0));
				}
			}

			// Using the UVs as position of the instances, since they are properly mapped to the terrain tile.
			// Also getting other attributes for the TreeInstances, if they are set.
			HAPI_AttributeInfo uvAttrInfo = new HAPI_AttributeInfo();
			float[] uvs = new float[0];
			if (!HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HAPI_ATTRIB_UV, ref uvAttrInfo, ref uvs, session.GetAttributeFloatData))
			{
				Debug.LogWarning("UVs for scatter instances not found or valid.");
			}

			if (uvs != null && uvs.Length == (pointCount * uvAttrInfo.tupleSize))
			{
				// Get height scales
				HAPI_AttributeInfo heightAttrInfo = new HAPI_AttributeInfo();
				float[] heightscales = new float[0];
				HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_HEIGHTSCALE, ref heightAttrInfo, ref heightscales, session.GetAttributeFloatData);

				// Get width scales
				HAPI_AttributeInfo widthAttrInfo = new HAPI_AttributeInfo();
				float[] widthscales = new float[0];
				HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_WIDTHSCALE, ref widthAttrInfo, ref widthscales, session.GetAttributeFloatData);

				// Get orientation
				HAPI_AttributeInfo orientAttrInfo = new HAPI_AttributeInfo();
				float[] orients = new float[0];
				HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HAPI_ATTRIB_ORIENT, ref orientAttrInfo, ref orients, session.GetAttributeFloatData);

				// Get color
				HAPI_AttributeInfo colorAttrInfo = new HAPI_AttributeInfo();
				float[] colors = new float[0];
				HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HAPI_ATTRIB_COLOR, ref colorAttrInfo, ref colors, session.GetAttributeFloatData);

				// Get lightmap color
				HAPI_AttributeInfo lightmapColorAttrInfo = new HAPI_AttributeInfo();
				float[] lightmapColors = new float[0];
				HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_LIGHTMAPCOLOR, ref lightmapColorAttrInfo, ref lightmapColors, session.GetAttributeFloatData);

				scatterTrees._positions = new Vector3[pointCount];

				if (heightAttrInfo.exists && (heightscales.Length == pointCount))
				{
					scatterTrees._heightScales = heightscales;
				}

				if (widthAttrInfo.exists && (widthscales.Length == pointCount))
				{
					scatterTrees._widthScales = widthscales;
				}

				if (orientAttrInfo.exists && (orients.Length == orientAttrInfo.tupleSize * pointCount))
				{
					scatterTrees._rotations = new float[pointCount];
				}

				if (colorAttrInfo.exists && (colors.Length == colorAttrInfo.tupleSize * pointCount))
				{
					scatterTrees._colors = new Color32[pointCount];
				}

				if (lightmapColorAttrInfo.exists && (lightmapColors.Length == lightmapColorAttrInfo.tupleSize * pointCount))
				{
					scatterTrees._lightmapColors = new Color32[pointCount];
				}

				for (int i = 0; i < pointCount; ++i)
				{
					scatterTrees._positions[i] = new Vector3(1.0f - uvs[i * uvAttrInfo.tupleSize + 1],
																	 0,
																	 uvs[i * uvAttrInfo.tupleSize + 0]);

					if (scatterTrees._colors != null)
					{
						scatterTrees._colors[i] =
							new Color32((byte)(colors[i * colorAttrInfo.tupleSize + 0] * 255),
										(byte)(colors[i * colorAttrInfo.tupleSize + 1] * 255),
										(byte)(colors[i * colorAttrInfo.tupleSize + 2] * 255),
										(byte)(colors[i * colorAttrInfo.tupleSize + 3] * 255));
					}

					if (scatterTrees._lightmapColors != null)
					{
						scatterTrees._lightmapColors[i] =
							new Color32((byte)(lightmapColors[i * lightmapColorAttrInfo.tupleSize + 0] * 255),
										(byte)(lightmapColors[i * lightmapColorAttrInfo.tupleSize + 1] * 255),
										(byte)(lightmapColors[i * lightmapColorAttrInfo.tupleSize + 2] * 255),
										(byte)(lightmapColors[i * lightmapColorAttrInfo.tupleSize + 3] * 255));
					}

					if (scatterTrees._rotations != null)
					{
						Quaternion quaternion = new Quaternion(
							orients[i * orientAttrInfo.tupleSize + 0], 
							orients[i * orientAttrInfo.tupleSize + 1], 
							orients[i * orientAttrInfo.tupleSize + 2], 
							orients[i * orientAttrInfo.tupleSize + 3]);
						Vector3 euler = quaternion.eulerAngles;
						euler.y = -euler.y;
						euler.z = -euler.z;
						scatterTrees._rotations[i] = euler.y * Mathf.Deg2Rad;
					}
				}
			}
		}
Esempio n. 30
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);
	}