/// <summary>
		/// Returns the various geometry types (parts) from the given node.
		/// Only part instancers and point instancers (via attributes) are returned.
		/// </summary>
		private bool QueryParts(HAPI_NodeId nodeID, ref List<HAPI_PartInfo> meshParts, ref List<HAPI_PartInfo> volumeParts,
			ref List<HAPI_PartInfo> instancerParts, ref List<HAPI_PartInfo> curveParts)
		{
			// Get display geo info
			HAPI_GeoInfo geoInfo = new HAPI_GeoInfo();
			if (!_session.GetGeoInfo(nodeID, ref geoInfo))
			{
				return false;
			}

			//Debug.LogFormat("GeoNode name:{0}, type: {1}, isTemplated: {2}, isDisplayGeo: {3}, isEditable: {4}, parts: {5}",
			//	HEU_SessionManager.GetString(geoInfo.nameSH, _session),
			//	geoInfo.type, geoInfo.isTemplated,
			//	geoInfo.isDisplayGeo, geoInfo.isEditable, geoInfo.partCount);

			if (geoInfo.type == HAPI_GeoType.HAPI_GEOTYPE_DEFAULT)
			{
				int numParts = geoInfo.partCount;
				for(int i = 0; i < numParts; ++i)
				{
					HAPI_PartInfo partInfo = new HAPI_PartInfo();
					if (!_session.GetPartInfo(geoInfo.nodeId, i, ref partInfo))
					{
						return false;
					}

					bool isAttribInstancer = false;
					// Preliminary check for attribute instancing (mesh type with no verts but has points with instances)
					if (HEU_HAPIUtility.IsSupportedPolygonType(partInfo.type) && partInfo.vertexCount == 0 && partInfo.pointCount > 0)
					{
						HAPI_AttributeInfo instanceAttrInfo = new HAPI_AttributeInfo();
						HEU_GeneralUtility.GetAttributeInfo(_session, nodeID, partInfo.id, HEU_PluginSettings.UnityInstanceAttr, ref instanceAttrInfo);
						if (instanceAttrInfo.exists && instanceAttrInfo.count > 0)
						{
							isAttribInstancer = true;
						}
					}

					if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_VOLUME)
					{
						volumeParts.Add(partInfo);
					}
					else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_INSTANCER || isAttribInstancer)
					{
						instancerParts.Add(partInfo);
					}
					else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_CURVE)
					{
						curveParts.Add(partInfo);
					}
					else if(HEU_HAPIUtility.IsSupportedPolygonType(partInfo.type))
					{
						meshParts.Add(partInfo);
					}
					else
					{
						string partName = HEU_SessionManager.GetString(partInfo.nameSH, _session);
						SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Part {0} with type {1} is not supported for GeoSync.", partName, partInfo.type));
					}
				}
			}
			else if(geoInfo.type == HAPI_GeoType.HAPI_GEOTYPE_CURVE)
			{
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Currently {0} geo type is not implemented for threaded geo loading!", geoInfo.type));
			}

			return true;
		}
		public static bool GetAttributeInfo(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attribName, ref HAPI_AttributeInfo attribInfo)
		{
			bool bResult = false;
			for (HAPI_AttributeOwner type = 0; type < HAPI_AttributeOwner.HAPI_ATTROWNER_MAX; ++type)
			{
				bResult = session.GetAttributeInfo(geoID, partID, attribName, type, ref attribInfo);
				if (!bResult)
				{
					attribInfo.exists = false;
					return false;
				}
				else if(attribInfo.exists)
				{
					break;
				}
			}
			return true;
		}
Пример #3
0
		public static bool GetAttribute<T>(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string name, ref HAPI_AttributeInfo info, ref T[] data, GetAttributeArrayInputFunc<T> getFunc)
		{
			int originalTupleSize = info.tupleSize;
			bool bResult = false;
			for (HAPI_AttributeOwner type = 0; type < HAPI_AttributeOwner.HAPI_ATTROWNER_MAX; ++type)
			{
				bResult = session.GetAttributeInfo(geoID, partID, name, type, ref info);
				if (bResult && info.exists)
				{
					break;
				}
			}

			if (!bResult || !info.exists)
			{
				return false;
			}

			if (originalTupleSize > 0)
			{
				info.tupleSize = originalTupleSize;
			}

			data = new T[info.count * info.tupleSize];
			return GetAttributeArray(geoID, partID, name, ref info, data, getFunc, info.count);
		}
Пример #4
0
 public static bool DoesGeoPartHaveAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attrName, HAPI_AttributeOwner owner, ref HAPI_AttributeInfo attributeInfo)
 {
     if (session.GetAttributeInfo(geoID, partID, attrName, owner, ref attributeInfo))
     {
         return(attributeInfo.exists);
         //Debug.LogFormat("Attr {0} exists={1}, with count={2}, type={3}, storage={4}, tuple={5}", "Cd", colorAttrInfo.exists, colorAttrInfo.count, colorAttrInfo.typeInfo, colorAttrInfo.storage, colorAttrInfo.tupleSize);
     }
     return(false);
 }
		public static bool SetAttributeArray<T>(HAPI_NodeId geoID, HAPI_PartId partID, string attrName, ref HAPI_AttributeInfo attrInfo, T[] items, SetAttributeArrayFunc<T> setFunc, int count)
		{
			bool bResult = false;

			int maxArraySize = 0;
			if(typeof(T) == typeof(string))
			{
				int maxStringLength = 1;
				foreach(T s in items)
				{
					string str = (string)(object)s;
					if(str.Length > maxStringLength)
					{
						maxStringLength = str.Length;
					}
				}
				maxArraySize = HEU_Defines.HAPI_MAX_PAGE_SIZE / (maxStringLength * Marshal.SizeOf(typeof(char)) * attrInfo.tupleSize);
			}
			else
			{
				maxArraySize = HEU_Defines.HAPI_MAX_PAGE_SIZE / (Marshal.SizeOf(typeof(T)) * attrInfo.tupleSize);
			}

			int localCount = count;
			int currentIndex = 0;

			while(localCount > 0)
			{
				int length = 0;
				if(localCount > maxArraySize)
				{
					length = maxArraySize;
					localCount -= maxArraySize;
				}
				else
				{
					length = localCount;
					localCount = 0;
				}

				T[] localArray = new T[length * attrInfo.tupleSize];

				// Copy subset to temp array
				for(int i = currentIndex; i < currentIndex + length; ++i)
				{
					for(int j = 0; j < attrInfo.tupleSize; ++j)
					{
						localArray[(i - currentIndex) * attrInfo.tupleSize + j] = items[i * attrInfo.tupleSize + j];
					}
				}

				bResult = setFunc(geoID, partID, attrName, ref attrInfo, localArray, currentIndex, length);
				if (!bResult)
				{
					break;
				}

				currentIndex += length;
			}

			return bResult;
		}
Пример #6
0
	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);
	    }

	    if (outputGameObject != null)
	    {
		_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;
	}
Пример #7
0
		public bool GenerateTerrainBuffers(HEU_SessionBase session, HAPI_NodeId nodeID, List<HAPI_PartInfo> volumeParts,
			List<HAPI_PartInfo> scatterInstancerParts, out List<HEU_LoadBufferVolume> volumeBuffers)
		{
			volumeBuffers = null;
			if (volumeParts.Count == 0)
			{
				return true;
			}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

				Sleep();
			}

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

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

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

					Sleep();

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

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

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

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

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

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

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

			return true;
		}
Пример #8
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);
	}
Пример #9
0
		/// <summary>
		/// Get the main geometry info struct.
		/// </summary>
		/// <param name="nodeID">The SOP node ID</param>
		/// <param name="partID">The part ID</param>
		/// <param name="name">Attribute name</param>
		/// <param name="owner">Attribute owner</param>
		/// <param name="attributeInfo">Info to populate</param>
		/// <returns>True if successfully queried the attribute info</returns>
		public virtual bool GetAttributeInfo(HAPI_NodeId nodeID, HAPI_PartId partID, string name, HAPI_AttributeOwner owner, ref HAPI_AttributeInfo attributeInfo)
		{
			return false;
		}
		/// <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;
			}
		}
Пример #11
0
        /// <summary>
        /// Process the part at the given index, creating its data (geometry),
        /// and adding it to the list of parts.
        /// </summary>
        /// <param name="session"></param>
        /// <param name="partID"></param>
        /// <returns>A valid HEU_PartData if it has been successfully processed.</returns>
        private void ProcessPart(HEU_SessionBase session, int partID, ref HAPI_PartInfo partInfo, ref HEU_PartData partData)
        {
            HEU_HoudiniAsset parentAsset = ParentAsset;
            bool             bResult     = true;

            //Debug.LogFormat("Part: name={0}, id={1}, type={2}, instanced={3}, instance count={4}, instance part count={5}", HEU_SessionManager.GetString(partInfo.nameSH, session), partID, partInfo.type, partInfo.isInstanced, partInfo.instanceCount, partInfo.instancedPartCount);

#if HEU_PROFILER_ON
            float processPartStartTime = Time.realtimeSinceStartup;
#endif

            bool isPartEditable    = IsIntermediateOrEditable();
            bool isAttribInstancer = false;

            if (IsGeoInputType())
            {
                // Setup for input node to accept inputs
                if (_inputNode == null)
                {
                    string partName = HEU_SessionManager.GetString(partInfo.nameSH, session);
                    _inputNode = HEU_InputNode.CreateSetupInput(GeoID, 0, partName, partName, HEU_InputNode.InputNodeType.NODE, ParentAsset);
                    if (_inputNode != null)
                    {
                        ParentAsset.AddInputNode(_inputNode);
                    }
                }

                if (HEU_HAPIUtility.IsSupportedPolygonType(partInfo.type) && partInfo.vertexCount == 0)
                {
                    // No geometry for input asset

                    if (partData != null)
                    {
                        // Clean up existing part
                        HEU_PartData.DestroyPart(partData);
                        partData = null;
                    }

                    // No need to process further since we don't have geometry
                    return;
                }
            }
            else
            {
                // Preliminary check for attribute instancing (mesh type with no verts but has points with instances)
                if (HEU_HAPIUtility.IsSupportedPolygonType(partInfo.type) && partInfo.vertexCount == 0 && partInfo.pointCount > 0)
                {
                    HAPI_AttributeInfo instanceAttrInfo = new HAPI_AttributeInfo();
                    HEU_GeneralUtility.GetAttributeInfo(session, GeoID, partID, HEU_PluginSettings.UnityInstanceAttr, ref instanceAttrInfo);
                    if (instanceAttrInfo.exists && instanceAttrInfo.count > 0)
                    {
                        isAttribInstancer = true;
                    }
                }
            }

            if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_INVALID)
            {
                // Clean up invalid parts
                if (partData != null)
                {
                    HEU_PartData.DestroyPart(partData);
                    partData = null;
                }
            }
            else if (partInfo.type < HAPI_PartType.HAPI_PARTTYPE_MAX)
            {
                // Process the part based on type. Keep or ignore.

                // We treat parts of type curve as curves, along with geo nodes that are editable and type curves
                if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_CURVE)
                {
                    if (partData == null)
                    {
                        partData = ScriptableObject.CreateInstance <HEU_PartData>();
                    }

                    partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo,
                                        HEU_PartData.PartOutputType.CURVE, isPartEditable, _containerObjectNode.IsInstancer(), false);
                    SetupGameObjectAndTransform(partData, parentAsset);
                    partData.ProcessCurvePart(session);
                }
                else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_VOLUME)
                {
                    // We only process "height" volume parts. Other volume parts are ignored for now.

#if TERRAIN_SUPPORTED
                    HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo();
                    bResult = session.GetVolumeInfo(GeoID, partID, ref volumeInfo);
                    if (!bResult)
                    {
                        Debug.LogErrorFormat("Unable to get volume info for geo node {0} and part {1} ", GeoID, partID);
                    }
                    else
                    {
                        if (Displayable && !IsIntermediateOrEditable())
                        {
                            if (partData == null)
                            {
                                partData = ScriptableObject.CreateInstance <HEU_PartData>();
                            }
                            else
                            {
                                // Clear volume data (case where switching from polygonal mesh to volume output)
                                partData.ClearGeneratedMeshOutput();
                            }

                            partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo,
                                                HEU_PartData.PartOutputType.VOLUME, isPartEditable, _containerObjectNode.IsInstancer(), false);
                            SetupGameObjectAndTransform(partData, ParentAsset);
                        }
                    }
#else
                    Debug.LogWarningFormat("Terrain (heightfield volume) is not yet supported.");
#endif
                }
                else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_INSTANCER || isAttribInstancer)
                {
                    if (partData == null)
                    {
                        partData = ScriptableObject.CreateInstance <HEU_PartData>();
                    }
                    else
                    {
                        partData.ClearGeneratedMeshOutput();
                        partData.ClearGeneratedVolumeOutput();
                    }

                    partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo,
                                        HEU_PartData.PartOutputType.INSTANCER, isPartEditable, _containerObjectNode.IsInstancer(), isAttribInstancer);
                    SetupGameObjectAndTransform(partData, parentAsset);
                }
                else if (HEU_HAPIUtility.IsSupportedPolygonType(partInfo.type))
                {
                    if (partData == null)
                    {
                        partData = ScriptableObject.CreateInstance <HEU_PartData>();
                    }
                    else
                    {
                        // Clear volume data (case where switching from something other output to mesh)
                        partData.ClearGeneratedVolumeOutput();
                    }

                    partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo,
                                        HEU_PartData.PartOutputType.MESH, isPartEditable, _containerObjectNode.IsInstancer(), false);

                    // This check allows to ignore editable non-display nodes by default, but commented out to allow
                    // them for now. Users can also ignore them by turning on IgnoreNonDisplayNodes
                    //if (Displayable || (Editable && ParentAsset.EditableNodesToolsEnabled))
                    {
                        SetupGameObjectAndTransform(partData, parentAsset);
                    }
                }
                else
                {
                    Debug.LogWarningFormat("Unsupported part type {0}", partInfo.type);
                }

                if (partData != null)
                {
                    // Success!
                    _parts.Add(partData);

                    // Set unique name for the part
                    string partName = HEU_PluginSettings.UseFullPathNamesForOutput ? GeneratePartFullName(partData.PartName) : partData.PartName;
                    partData.SetGameObjectName(partName);

                    // For intermediate or default-type editable nodes, setup the HEU_AttributeStore
                    if (isPartEditable)
                    {
                        partData.SyncAttributesStore(session, _geoInfo.nodeId, ref partInfo);
                    }
                    else
                    {
                        // Remove attributes store if it has it
                        partData.DestroyAttributesStore();
                    }
                }
            }

#if HEU_PROFILER_ON
            Debug.LogFormat("PART PROCESS TIME:: NAME={0}, TIME={1}", HEU_SessionManager.GetString(partInfo.nameSH, session), (Time.realtimeSinceStartup - processPartStartTime));
#endif
        }
        public void ProcessVolumeParts(HEU_SessionBase session, List <HEU_PartData> volumeParts, bool bRebuild)
        {
            int numVolumeParts = volumeParts.Count;

            if (numVolumeParts == 0)
            {
                DestroyVolumeCache();
            }
            else if (_volumeCaches == null)
            {
                _volumeCaches = new List <HEU_VolumeCache>();
            }

            // First update volume caches. Each volume cache represents a set of terrain layers grouped by tile index.
            // Therefore each volume cache represents a potential Unity Terrain (containing layers)
            _volumeCaches = HEU_VolumeCache.UpdateVolumeCachesFromParts(session, this, volumeParts, _volumeCaches);

            // Heightfield scatter nodes come in as mesh-type parts with attribute instancing.
            // So process them here to get all the tree/detail instance scatter information.
            int numParts = _parts.Count;

            for (int i = 0; i < numParts; ++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, GeoID, _parts[i].PartID, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData))
                {
                    if (tileAttrData != null && tileAttrData.Length > 0)
                    {
                        terrainTile = tileAttrData[0];
                    }
                }

                // Find the volumecache associated with this part using the terrain tile index
                HEU_VolumeCache volumeCache = GetVolumeCacheByTileIndex(terrainTile);
                if (volumeCache == null)
                {
                    continue;
                }

                HEU_VolumeLayer volumeLayer = volumeCache.GetLayer(_parts[i].GetVolumeLayerName());
                if (volumeLayer != null && volumeLayer._layerType == HFLayerType.DETAIL)
                {
                    // Clear out outputs since it might have been created when the part was created.
                    _parts[i].DestroyAllData();

                    volumeCache.PopulateDetailPrototype(session, GeoID, _parts[i].PartID, volumeLayer);
                }
                else if (_parts[i].IsAttribInstancer())
                {
                    HAPI_AttributeInfo treeInstAttrInfo = new HAPI_AttributeInfo();
                    if (HEU_GeneralUtility.GetAttributeInfo(session, GeoID, _parts[i].PartID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_PROTOTYPEINDEX, ref treeInstAttrInfo))
                    {
                        if (treeInstAttrInfo.exists && treeInstAttrInfo.count > 0)
                        {
                            // Clear out outputs since it might have been created when the part was created.
                            _parts[i].DestroyAllData();

                            // Mark the instancers as having been created so that the object instancer step skips this.
                            _parts[i].ObjectInstancesBeenGenerated = true;

                            // Now populate scatter trees based on attributes on this part
                            volumeCache.PopulateScatterTrees(session, GeoID, _parts[i].PartID, treeInstAttrInfo.count);
                        }
                    }
                }
            }

            // Now generate the terrain for each volume cache
            foreach (HEU_VolumeCache cache in _volumeCaches)
            {
                cache.GenerateTerrainWithAlphamaps(session, ParentAsset, bRebuild);

                cache.IsDirty = false;
            }
        }
Пример #13
0
	/// <summary>
	/// Generates object instances.
	/// Skips parts that already have their instances generated.
	/// </summary>
	/// <param name="session">Active session to use</param>
	internal void GenerateObjectInstances(HEU_SessionBase session)
	{
	    if (ParentAsset == null)
	    {
		return;
	    }
	    
	    if (!IsInstancer())
	    {
		HEU_Logger.LogErrorFormat("Generate object instances called on a non-instancer object {0} for asset {1}!", ObjectName, ParentAsset.AssetName);
		return;
	    }

	    //HEU_Logger.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();
	    HAPI_AttributeInfo materialAttrInfo = 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]._objectInstancesGenerated || parts[j].IsPartVolume())
			{
			    // This prevents instances being created unnecessarily (e.g. part hasn't changed since last cook).
			    // Or for volumes that might have instance attributes.
			    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);
			}

			string[] instanceMaterialPaths = null;
			HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, HEU_PluginSettings.UnityMaterialAttribName, ref materialAttrInfo);
			if (materialAttrInfo.exists)
			{
			    instanceMaterialPaths = HEU_GeneralUtility.GetAttributeStringData(session, _geoNodes[i].GeoID, parts[j].PartID, HEU_PluginSettings.UnityMaterialAttribName, ref materialAttrInfo);
			}

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

			    parts[j].GenerateInstancesFromObjectIds(session, instancePrefixes, instanceMaterialPaths);
			}
			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 ||
			        unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL)
			    {
				parts[j].GenerateInstancesFromUnityAssetPathAttribute(session, unityInstanceAttrName);
			    }
			    else
			    {
				// Other attribute owned types are unsupported.
				// Originally had a warning here, but unnecessary as in some cases (e.g. heightfield attrbiutes) the
				// attribute owner could be changed in HAPI.
			    }
			}
			else
			{
			    // Standard object instancing via single Houdini object

			    if (_objectInfo.objectToInstanceId == HEU_Defines.HEU_INVALID_NODE_ID)
			    {
				// HEU_Logger.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);
				// Could be a part instancer
				continue;
			    }

			    parts[j].GenerateInstancesFromObjectID(session, _objectInfo.objectToInstanceId, instancePrefixes, instanceMaterialPaths);
			}
		    }
		}
	    }
	}
		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;
		}
Пример #15
0
		/// <summary>
		/// Returns true if given part in geometry has the given attribute with name.
		/// </summary>
		/// <param name="session">Houdini Engine session</param>
		/// <param name="geoID">Geometry object ID</param>
		/// <param name="partID">Part ID</param>
		/// <param name="attribName">Name of the attribute</param>
		/// <returns>True if attribute exists</returns>
		public static bool HasValidInstanceAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attribName)
		{
			HAPI_AttributeInfo instanceAttrInfo = new HAPI_AttributeInfo();
			GetAttributeInfo(session, geoID, partID, attribName, ref instanceAttrInfo);
			return (instanceAttrInfo.exists && instanceAttrInfo.count > 0);
		}
Пример #16
0
		/// <summary>
		/// Get the attribute string data.
		/// </summary>
		/// <param name="nodeID">The node ID</param>
		/// <param name="partID">The part ID</param>
		/// <param name="name">Attribute name</param>
		/// <param name="attributeInfo">Atttribute info</param>
		/// <param name="dataArray">Array to populate. Must be at least the size of length * HAPI_AttributeInfo::tupleSize</param>
		/// <param name="start">First index of range. Must be at least 0 and at most HAPI_AttributeInfo::count - 1</param>
		/// <param name="length">Must be at least 0 and at most HAPI_AttributeInfo::count - start</param>
		/// <returns>True if successfully queried the atttribute string data</returns>
		public virtual bool GetAttributeStringData(HAPI_NodeId nodeID, HAPI_PartId partID, string name, ref HAPI_AttributeInfo attributeInfo, [Out] HAPI_StringHandle[] dataArray, int start, int length)
		{
			return false;
		}
Пример #17
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 || parts[j].IsPartVolume())
			{
			    // This prevents instances being created unnecessarily (e.g. part hasn't changed since last cook).
			    // Or for volumes that might have instance attributes.
			    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];
				HEU_GeneralUtility.GetAttribute(session, _geoNodes[i].GeoID, parts[j].PartID, unityInstanceAttrName, ref unityInstanceAttrInfo, ref scriptAttr, session.GetAttributeStringData);
				if (unityInstanceAttrInfo.exists)
				{
				    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.
				// Originally had a warning here, but unnecessary as in some cases (e.g. heightfield attrbiutes) the
				// attribute owner could be changed in HAPI.
			    }
			}
			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);
			}
		    }
		}
	    }
	}
Пример #18
0
		/// <summary>
		/// Get the attribute float data.
		/// </summary>
		/// <param name="nodeID">The node ID</param>
		/// <param name="partID">The part ID</param>
		/// <param name="name">Attribut ename</param>
		/// <param name="attributeInfo">Should be same struct returned by HAPI_GetAttributeInfo</param>
		/// <param name="data">A float array at least the size of length * HAPI_AttributeInfo::tupleSize</param>
		/// <param name="start">First index of range. Must be at least 0 and at most HAPI_AttributeInfo::count - 1</param>
		/// <param name="length">Must be at least 0 and at most HAPI_AttributeInfo::count - start.</param>
		/// <returns>True if successfully queried the atttribute float data</returns>
		public virtual bool GetAttributeFloatData(HAPI_NodeId nodeID, HAPI_PartId partID, string name, ref HAPI_AttributeInfo attributeInfo, [Out] float[] data, int start, int length)
		{
			return false;
		}
Пример #19
0
	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)
	    {
		// Temp storage to use for reassigning old values to new array
		int[] oldValues = null;

		if (attributeData._intValues == null)
		{
		    attributeData._intValues = new int[arraySize];
		    attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;
		}
		else if (attributeData._intValues.Length != arraySize)
		{
		    ArrayExtensions.CopyToWithResize(attributeData._intValues, ref oldValues);

		    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 index = 0;
		    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)
			{
			    index = i * tupleSize + tuple;
			    if (oldValues != null && index < oldValues.Length)
			    {
				attributeData._intValues[index] = oldValues[index];
			    }
			    else
			    {
				attributeData._intValues[index] = data[index];
			    }
			}
		    }
		}
	    }
	    else if (attributeData._attributeType == HEU_AttributeData.AttributeType.FLOAT)
	    {
		// Temp storage to use for reassigning old values to new array
		float[] oldValues = null;

		if (attributeData._floatValues == null)
		{
		    attributeData._floatValues = new float[arraySize];
		    attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;
		}
		else if (attributeData._floatValues.Length != arraySize)
		{
		    ArrayExtensions.CopyToWithResize(attributeData._floatValues, ref oldValues);

		    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)
		{
		    int index = 0;
		    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)
			{
			    index = i * tupleSize + tuple;
			    if (oldValues != null && index < oldValues.Length)
			    {
				attributeData._floatValues[index] = oldValues[index];
			    }
			    else
			    {
				attributeData._floatValues[index] = data[index];
			    }
			}
		    }
		}
	    }
	    else if (attributeData._attributeType == HEU_AttributeData.AttributeType.STRING)
	    {
		// Temp storage to use for reassigning old values to new array
		string[] oldValues = null;

		if (attributeData._stringValues == null)
		{
		    attributeData._stringValues = new string[arraySize];
		    attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID;
		}
		else if (attributeData._stringValues.Length != arraySize)
		{
		    ArrayExtensions.CopyToWithResize(attributeData._stringValues, ref oldValues);

		    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)
		{
		    int index = 0;
		    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)
			{
			    index = i * tupleSize + tuple;
			    if (oldValues != null && index < oldValues.Length)
			    {
				attributeData._stringValues[index] = oldValues[index];
			    }
			    else
			    {
				attributeData._stringValues[index] = HEU_SessionManager.GetString(data[index], session);
			    }
			}
		    }
		}
	    }

	    SetAttributeDataSyncd(attributeData);
	}
Пример #20
0
		public virtual bool SetAttributeStringData(HAPI_NodeId nodeID, HAPI_PartId partID, string name, ref HAPI_AttributeInfo attrInfo,
			string[] data, int start, int length)
		{
			return false;
		}
Пример #21
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_PluginSettings.CollisionGroupName, 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_PluginSettings.CollisionGroupName, 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;
		}
Пример #22
0
		public virtual bool AddAttribute(HAPI_NodeId nodeID, HAPI_PartId partID, string name, ref HAPI_AttributeInfo attrInfo)
		{
			return false;
		}
		public static string[] GetAttributeStringData(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string name, ref HAPI_AttributeInfo attrInfo)
		{
			int[] stringHandles = new int[0];
			if (GetAttribute(session, geoID, partID, name, ref attrInfo, ref stringHandles, session.GetAttributeStringData))
			{
				return HEU_SessionManager.GetStringValuesFromStringIndices(stringHandles);
			}
			return null;
		}
Пример #24
0
		//	LOGIC -----------------------------------------------------------------------------------------------------

		public static List<HEU_VolumeCache> UpdateVolumeCachesFromParts(HEU_SessionBase session, HEU_GeoNode ownerNode, List<HEU_PartData> volumeParts, List<HEU_VolumeCache> volumeCaches)
		{
			HEU_HoudiniAsset parentAsset = ownerNode.ParentAsset;

			foreach (HEU_VolumeCache cache in volumeCaches)
			{
				// Remove current volume caches from parent asset.
				// These get added back in below.
				parentAsset.RemoveVolumeCache(cache);

				// Mark the cache for updating
				cache.StartUpdateLayers();
			}

			// This will keep track of volume caches still in use
			List<HEU_VolumeCache> updatedCaches = new List<HEU_VolumeCache>();

			int numParts = volumeParts.Count;
			for (int i = 0; i < numParts; ++i)
			{
				// 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, ownerNode.GeoID, volumeParts[i].PartID, "tile", ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData);
				if (tileAttrData != null && tileAttrData.Length > 0)
				{
					//Debug.LogFormat("Tile: {0}", tileAttrData[0]);

					int tile = tileAttrData[0];
					HEU_VolumeCache volumeCache = null;

					// Find cache in updated list
					for (int j = 0; j < updatedCaches.Count; ++j)
					{
						if (updatedCaches[j] != null && updatedCaches[j].TileIndex == tile)
						{
							volumeCache = updatedCaches[j];
							break;
						}
					}

					if (volumeCache != null)
					{
						volumeCache.UpdateLayerFromPart(session, volumeParts[i]);

						// Skip adding new cache since already found in updated list
						continue;
					}

					// Find existing cache in old list
					if (volumeCaches != null && volumeCaches.Count > 0)
					{
						for(int j = 0; j < volumeCaches.Count; ++j)
						{
							if (volumeCaches[j] != null && volumeCaches[j].TileIndex == tile)
							{
								volumeCache = volumeCaches[j];
								break;
							}
						}
					}

					// Create new cache for this tile if not found
					if (volumeCache == null)
					{
						volumeCache = ScriptableObject.CreateInstance<HEU_VolumeCache>();
						volumeCache.Initialize(ownerNode, tile);
						volumeCache.StartUpdateLayers();
					}

					volumeCache.UpdateLayerFromPart(session, volumeParts[i]);

					if (!updatedCaches.Contains(volumeCache))
					{
						updatedCaches.Add(volumeCache);
					}
				}
				else
				{
					// No tile index. Most likely a single terrain tile.

					HEU_VolumeCache volumeCache = null;

					if (updatedCaches.Count == 0)
					{
						// Create a single volume cache, or use existing if it was just 1.
						// If more than 1 volume cache exists, this will recreate a single one

						if (volumeCaches == null || volumeCaches.Count != 1)
						{
							volumeCache = ScriptableObject.CreateInstance<HEU_VolumeCache>();
							volumeCache.Initialize(ownerNode, 0);
							volumeCache.StartUpdateLayers();
						}
						else if (volumeCaches.Count == 1)
						{
							// Keep the single volumecache
							volumeCache = volumeCaches[0];
						}

						if (!updatedCaches.Contains(volumeCache))
						{
							updatedCaches.Add(volumeCache);
						}
					}
					else
					{
						// Reuse the updated cache
						volumeCache = updatedCaches[0];
					}

					volumeCache.UpdateLayerFromPart(session, volumeParts[i]);
				}
			}

			foreach (HEU_VolumeCache cache in updatedCaches)
			{
				// Add to parent for UI and preset
				parentAsset.AddVolumeCache(cache);

				// Finish update by keeping just the layers in use for each volume cache.
				cache.FinishUpdateLayers();
			}

			return updatedCaches;
		}
		public static bool SetAttribute<T>(HAPI_NodeId geoID, HAPI_PartId partID, string attrName, ref HAPI_AttributeInfo attrInfo, T[] items, SetAttributeArrayFunc<T> setFunc)
		{
			return SetAttributeArray(geoID, partID, attrName, ref attrInfo, items, setFunc, attrInfo.count);
		}
Пример #26
0
		private void GetPartLayerAttributes(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_NodeId partID, HEU_VolumeLayer layer)
		{
			// 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, geoID, partID, "tile", ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData);
			if (tileAttrData != null && tileAttrData.Length > 0)
			{
				layer._tile = tileAttrData[0];
				//Debug.LogFormat("Tile: {0}", tileAttrData[0]);
			}
			else
			{
				layer._tile = 0;
			}

			// Get the layer textures, and other layer values from attributes

			Texture2D defaultTexture = LoadDefaultSplatTexture();

			if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Diffuse) && (layer._diffuseTexture == null || layer._diffuseTexture == defaultTexture))
			{
				layer._diffuseTexture = LoadLayerTextureFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_DIFFUSE_ATTR);

				if (layer._diffuseTexture == null)
				{
					layer._diffuseTexture = defaultTexture;
				}
			}

			if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Mask) && layer._maskTexture == null)
			{
				layer._maskTexture = LoadLayerTextureFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_MASK_ATTR);
			}

			if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Normal) && layer._normalTexture == null)
			{
				layer._normalTexture = LoadLayerTextureFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_NORMAL_ATTR);
			}

			if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.NormalScale))
			{
				LoadLayerFloatFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_NORMAL_SCALE_ATTR, ref layer._normalScale);
			}

			if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Metallic))
			{
				LoadLayerFloatFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_METALLIC_ATTR, ref layer._metallic);
			}

			if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Smoothness))
			{
				LoadLayerFloatFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SMOOTHNESS_ATTR, ref layer._smoothness);
			}

			if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Specular))
			{
				LoadLayerColorFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SPECULAR_ATTR, ref layer._specularColor);
			}

			if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.TileOffset))
			{
				LoadLayerVector2FromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_OFFSET_ATTR, ref layer._tileOffset);
			}

			if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.TileSize))
			{
				LoadLayerVector2FromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_SIZE_ATTR, ref layer._tileSize);
			}
		}
Пример #27
0
		public static bool GetAttributeArray<T>(HAPI_NodeId geoID, HAPI_PartId partID, string name, ref HAPI_AttributeInfo info, T[] items, GetAttributeArrayInputFunc<T> getFunc, int count)
		{
			int maxArraySize = HEU_Defines.HAPI_MAX_PAGE_SIZE / (Marshal.SizeOf(typeof(T)) * info.tupleSize);

			int localCount = count;
			int currentIndex = 0;

			bool bResult = false;

			while (localCount > 0)
			{
				int length = 0;
				if (localCount > maxArraySize)
				{
					length = maxArraySize;
					localCount -= maxArraySize;
				}
				else
				{
					length = localCount;
					localCount = 0;
				}

				T[] localArray = new T[length * info.tupleSize];
				bResult = getFunc(geoID, partID, name, ref info, localArray, currentIndex, length);
				if (!bResult)
				{
					break;
				}

				// Copy data from temporary array
				for (int i = currentIndex; i < currentIndex + length; ++i)
				{
					for (int j = 0; j < info.tupleSize; ++j)
					{
						items[i * info.tupleSize + j] = localArray[(i - currentIndex) * info.tupleSize + j];
					}
				}

				currentIndex += length;
			}

			return bResult;
		}
		/// <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 PopulateScatterInfo(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();
			}
			Debug.Log("Point count is : " + pointCount);

			// 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;
					}
				}
			}
		}
Пример #29
0
		public static void GetAttributeStringDataHelper(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string name, ref HAPI_AttributeInfo info, ref HAPI_StringHandle[] data)
		{
			int originalTupleSize = info.tupleSize;
			bool bResult = false;
			for (HAPI_AttributeOwner type = 0; type < HAPI_AttributeOwner.HAPI_ATTROWNER_MAX; ++type)
			{
				bResult = session.GetAttributeInfo(geoID, partID, name, type, ref info);
				if (bResult && info.exists)
				{
					break;
				}
			}

			if (!bResult || !info.exists)
			{
				return;
			}

			if (originalTupleSize > 0)
			{
				info.tupleSize = originalTupleSize;
			}

			data = new HAPI_StringHandle[info.count * info.tupleSize];
			bResult = session.GetAttributeStringData(geoID, partID, name, ref info, data, 0, info.count);
			if(!bResult)
			{
				Debug.LogErrorFormat("Failed to get string IDs for attribute {0}", name);
			}
		}
Пример #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<Vector2> uvs = new List<Vector2>();
			List<Color> colors = new List<Color>();

			List<int> pointIndexList = new List<int>();
			List<int> vertIndexList = new List<int>();

			int numMaterials = 0;

			int numMeshes = inputDataMeshes._inputMeshes.Count;

			// Get the parent's world transform, so when there are multiple child meshes,
			// can merge and apply their local transform after subtracting their parent's world transform
			Matrix4x4 rootInvertTransformMatrix = Matrix4x4.identity;
			if (numMeshes > 1)
			{
				rootInvertTransformMatrix = inputDataMeshes._inputObject.transform.worldToLocalMatrix;
			}

			// For all meshes:
			// Accumulate vertices, normals, uvs, colors, and indices.
			// Keep track of indices start and count for each mesh for later when uploading material assignments and groups.
			// Find shared vertices, and use unique set of vertices to use as point positions.
			// Need to reindex indices for both unique vertices, as well as vertex attributes.
			for (int i = 0; i < numMeshes; ++i)
			{
				Vector3[] meshVertices = inputDataMeshes._inputMeshes[i]._mesh.vertices;
				Matrix4x4 localToWorld = inputDataMeshes._inputMeshes[i]._transform.localToWorldMatrix * rootInvertTransformMatrix;

				List<Vector3> uniqueVertices = new List<Vector3>();

				// Keep track of old vertex positions (old vertex slot points to new unique vertex slot)
				int[] reindexVertices = new int[meshVertices.Length];

				for (int j = 0; j < meshVertices.Length; ++j)
				{
					reindexVertices[j] = -1;
				}

				// For each vertex, check against subsequent vertices for shared positions.
				for (int a = 0; a < meshVertices.Length; ++a)
				{
					Vector3 va = meshVertices[a];

					if (reindexVertices[a] == -1)
					{
						if (numMeshes > 1 && !inputDataMeshes._hasLOD)
						{
							// For multiple meshes that are not LODs, apply local transform on vertices to get the merged mesh.
							uniqueVertices.Add(localToWorld.MultiplyPoint(va));
						}
						else
						{
							uniqueVertices.Add(va);
						}

						// Reindex to point to unique vertex slot
						reindexVertices[a] = uniqueVertices.Count - 1;
					}

					for (int b = a + 1; b < meshVertices.Length; ++b)
					{
						if (va == meshVertices[b])
						{
							// Shared vertex -> reindex to point to unique vertex slot
							reindexVertices[b] = reindexVertices[a];
						}
					}
				}

				int vertexOffset = vertices.Count;
				vertices.AddRange(uniqueVertices);

				Vector3[] meshNormals = inputDataMeshes._inputMeshes[i]._mesh.normals;
				Vector2[] meshUVs = inputDataMeshes._inputMeshes[i]._mesh.uv;
				Color[] meshColors = inputDataMeshes._inputMeshes[i]._mesh.colors;

				inputDataMeshes._inputMeshes[i]._indexStart = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes];
				inputDataMeshes._inputMeshes[i]._indexCount = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes];

				// For each submesh:
				// Generate face to point index -> pointIndexList
				// Generate face to vertex attribute index -> vertIndexList
				for (int j = 0; j < inputDataMeshes._inputMeshes[i]._numSubMeshes; ++j)
				{
					int indexStart = pointIndexList.Count;
					int vertIndexStart = vertIndexList.Count;

					// Indices have to be re-indexed with our own offset
					int[] meshIndices = inputDataMeshes._inputMeshes[i]._mesh.GetTriangles(j);
					int numIndices = meshIndices.Length;
					for (int k = 0; k < numIndices; ++k)
					{
						int originalIndex = meshIndices[k];
						meshIndices[k] = reindexVertices[originalIndex];

						pointIndexList.Add(vertexOffset + meshIndices[k]);
						vertIndexList.Add(vertIndexStart + k);

						if (meshNormals != null && (originalIndex < meshNormals.Length))
						{
							normals.Add(meshNormals[originalIndex]);
						}

						if (meshUVs != null && (originalIndex < meshUVs.Length))
						{
							uvs.Add(meshUVs[originalIndex]);
						}

						if (meshColors != null && (originalIndex < meshColors.Length))
						{
							colors.Add(meshColors[originalIndex]);
						}
					}

					inputDataMeshes._inputMeshes[i]._indexStart[j] = (uint)indexStart;
					inputDataMeshes._inputMeshes[i]._indexCount[j] = (uint)(pointIndexList.Count) - inputDataMeshes._inputMeshes[i]._indexStart[j];
				}

				numMaterials += inputDataMeshes._inputMeshes[i]._materials != null ? inputDataMeshes._inputMeshes[i]._materials.Length : 0;
			}

			// It is possible for some meshes to not have normals/uvs/colors while others do.
			// In the case where an attribute is missing on some meshes, we clear out those attributes so we don't upload
			// partial attribute data.
			int totalAllVertexCount = vertIndexList.Count;
			if (normals.Count != totalAllVertexCount)
			{
				normals = null;
			}

			if (uvs.Count != totalAllVertexCount)
			{
				uvs = null;
			}

			if (colors.Count != totalAllVertexCount)
			{
				colors = null;
			}

			HAPI_PartInfo partInfo = new HAPI_PartInfo();
			partInfo.faceCount = vertIndexList.Count / 3;
			partInfo.vertexCount = vertIndexList.Count;
			partInfo.pointCount = vertices.Count;
			partInfo.pointAttributeCount = 1;
			partInfo.vertexAttributeCount = 0;
			partInfo.primitiveAttributeCount = 0;
			partInfo.detailAttributeCount = 0;

			if (normals != null && normals.Count > 0)
			{
				partInfo.vertexAttributeCount++;
			}

			if (uvs != null && uvs.Count > 0)
			{
				partInfo.vertexAttributeCount++;
			}

			if (colors != null && colors.Count > 0)
			{
				partInfo.vertexAttributeCount++;
			}

			if (numMaterials > 0)
			{
				partInfo.primitiveAttributeCount++;
			}

			if (numMeshes > 0)
			{
				partInfo.primitiveAttributeCount++;
			}

			if (inputDataMeshes._hasLOD)
			{
				partInfo.primitiveAttributeCount++;
				partInfo.detailAttributeCount++;
			}

			HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo();
			if (!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo))
			{
				return false;
			}

			HAPI_NodeId displayNodeID = displayGeoInfo.nodeId;

			if (!session.SetPartInfo(displayNodeID, 0, ref partInfo))
			{
				Debug.LogError("Failed to set input part info. ");
				return false;
			}

			int[] faceCounts = new int[partInfo.faceCount];
			for (int i = 0; i < partInfo.faceCount; ++i)
			{
				faceCounts[i] = 3;
			}

			int[] triIndices = pointIndexList.ToArray();

			if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetFaceCount, faceCounts, 0, partInfo.faceCount))
			{
				Debug.LogError("Failed to set input geometry face counts.");
				return false;
			}

			if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetVertexList, triIndices, 0, partInfo.vertexCount))
			{
				Debug.LogError("Failed to set input geometry indices.");
				return false;
			}

			if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true))
			{
				Debug.LogError("Failed to set input geometry position.");
				return false;
			}

			int[] vertIndices = vertIndexList.ToArray();

			//if(normals != null && !SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), ref partInfo, true))
			if (normals != null && !HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), vertIndices, ref partInfo, true))
			{
				Debug.LogError("Failed to set input geometry normals.");
				return false;
			}

			if (uvs != null && uvs.Count > 0)
			{
				Vector3[] uvs3 = new Vector3[uvs.Count];
				for (int i = 0; i < uvs.Count; ++i)
				{
					uvs3[i][0] = uvs[i][0];
					uvs3[i][1] = uvs[i][1];
					uvs3[i][2] = 0;
				}
				//if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_UV, 3, uvs3, ref partInfo, false))
				if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_UV, 3, uvs3, vertIndices, ref partInfo, false))
				{
					Debug.LogError("Failed to set input geometry UVs.");
					return false;
				}
			}

			if (colors != null && colors.Count > 0)
			{
				Vector3[] rgb = new Vector3[colors.Count];
				float[] alpha = new float[colors.Count];
				for (int i = 0; i < colors.Count; ++i)
				{
					rgb[i][0] = colors[i].r;
					rgb[i][1] = colors[i].g;
					rgb[i][2] = colors[i].b;

					alpha[i] = colors[i].a;
				}

				//if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, ref partInfo, false))
				if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, vertIndices, ref partInfo, false))
				{
					Debug.LogError("Failed to set input geometry colors.");
					return false;
				}

				//if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, ref partInfo, false))
				if (!HEU_InputMeshUtility.SetMeshVertexFloatAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, vertIndices, ref partInfo))
				{
					Debug.LogError("Failed to set input geometry color alpha.");
					return false;
				}
			}

			// Set material names for round-trip perservation of material assignment
			// Each HEU_UploadMeshData might have a list of submeshes and materials
			// These are all combined into a single mesh, with group names
			if (numMaterials > 0)
			{
				bool bFoundAtleastOneValidMaterial = false;

				string[] materialIDs = new string[partInfo.faceCount];
				for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g)
				{
					if (inputDataMeshes._inputMeshes[g]._numSubMeshes != inputDataMeshes._inputMeshes[g]._materials.Length)
					{
						// Number of submeshes should equal number of materials since materials determine submeshes
						continue;
					}

					for (int i = 0; i < inputDataMeshes._inputMeshes[g]._materials.Length; ++i)
					{
						string materialName = HEU_AssetDatabase.GetAssetPathWithSubAssetSupport(inputDataMeshes._inputMeshes[g]._materials[i]);
						if (materialName == null)
						{
							materialName = "";
						}
						else if (materialName.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES))
						{
							materialName = HEU_AssetDatabase.GetUniqueAssetPathForUnityAsset(inputDataMeshes._inputMeshes[g]._materials[i]);
						}

						bFoundAtleastOneValidMaterial |= !string.IsNullOrEmpty(materialName);

						int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / 3;
						int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / 3);
						for (int m = faceStart; m < faceEnd; ++m)
						{
							materialIDs[m] = materialName;
						}
					}
				}

				if (bFoundAtleastOneValidMaterial)
				{
					HAPI_AttributeInfo materialIDAttrInfo = new HAPI_AttributeInfo();
					materialIDAttrInfo.exists = true;
					materialIDAttrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM;
					materialIDAttrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING;
					materialIDAttrInfo.count = partInfo.faceCount;
					materialIDAttrInfo.tupleSize = 1;
					materialIDAttrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID;

					if (!session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo))
					{
						Debug.LogError("Failed to add input geometry unity material name attribute.");
						return false;
					}

					if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo, materialIDs, session.SetAttributeStringData, partInfo.faceCount))
					{
						Debug.LogError("Failed to set input geometry unity material name.");
						return false;
					}
				}
			}

			// Set mesh name attribute
			HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo();
			attrInfo.exists = true;
			attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM;
			attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING;
			attrInfo.count = partInfo.faceCount;
			attrInfo.tupleSize = 1;
			attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID;

			if (session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo))
			{
				string[] primitiveNameAttr = new string[partInfo.faceCount];

				for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g)
				{
					for (int i = 0; i < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++i)
					{
						int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / 3;
						int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / 3);
						for (int m = faceStart; m < faceEnd; ++m)
						{
							primitiveNameAttr[m] = inputDataMeshes._inputMeshes[g]._meshPath;
						}
					}
				}

				if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo, primitiveNameAttr, session.SetAttributeStringData, partInfo.faceCount))
				{
					Debug.LogError("Failed to set input geometry unity mesh name.");
					return false;
				}
			}
			else
			{
				return false;
			}

			// Set LOD group membership
			if (inputDataMeshes._hasLOD)
			{
				int[] membership = new int[partInfo.faceCount];

				for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g)
				{
					if (g > 0)
					{
						// Clear array
						for (int m = 0; m < partInfo.faceCount; ++m)
						{
							membership[m] = 0;
						}
					}

					// Set 1 for faces belonging to this group
					for (int s = 0; s < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++s)
					{
						int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[s] / 3;
						int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[s] / 3);
						for (int m = faceStart; m < faceEnd; ++m)
						{
							membership[m] = 1;
						}
					}

					if (!session.AddGroup(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName))
					{
						Debug.LogError("Failed to add input geometry LOD group name.");
						return false;
					}

					if (!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName, membership, 0, partInfo.faceCount))
					{
						Debug.LogError("Failed to set input geometry LOD group name.");
						return false;
					}
				}
			}

			return session.CommitGeo(displayNodeID);
		}