Esempio n. 1
0
	/// <summary>
	/// Loads a copy of the srcAsset at copyPath. Creates a copy if not found.
	/// </summary>
	/// <param name="srcAsset">The source asset object</param>
	/// <param name="copyPath">The full path to the copy</param>
	/// <param name="type">The type of source asset</param>
	/// <param name="bOverwriteExisting">Whether to overwrite existing copy if found</param>
	/// <returns>Returns loaded copy if exists or created, otherwise null</returns>
	public static Object CopyAndLoadAssetAtAnyPath(Object srcAsset, string copyPath, System.Type type, bool bOverwriteExisting)
	{
#if UNITY_EDITOR
	    string srcAssetPath = GetAssetPath(srcAsset);
	    if (!string.IsNullOrEmpty(srcAssetPath))
	    {
		CreatePathWithFolders(copyPath);

		string fileName = HEU_Platform.GetFileName(srcAssetPath);
		string fullCopyPath = HEU_Platform.BuildPath(copyPath, fileName);

		if ((!bOverwriteExisting && HEU_Platform.DoesFileExist(fullCopyPath)) || CopyAsset(srcAssetPath, fullCopyPath))
		{
		    // Refresh database as otherwise we won't be able to load it in the next line.
		    SaveAndRefreshDatabase();

		    return LoadAssetAtPath(fullCopyPath, type);
		}
		else
		{
		    HEU_Logger.LogErrorFormat("Failed to copy and load asset from {0} to {1}!", srcAssetPath, fullCopyPath);
		}
	    }
	    return null;
#else
			// TODO RUNTIME: AssetDatabase is not supported at runtime. Do we need to support this for runtime?
			HEU_Logger.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED);
			return null;
#endif
	}
Esempio n. 2
0
	/// <summary>
	/// Copy the file of the given srcAsset into the given targetPath, which must be absolute.
	/// If targetPath doesn't have a file name, the srcAsset's file name will be used.
	/// </summary>
	/// <param name="srcAsset">Source asset to copy</param>
	/// <param name="targetPath">Absolute path of destination</param>
	/// <param name="type">Type of the asset</param>
	/// <returns></returns>
	public static Object CopyAndLoadAssetAtGivenPath(Object srcAsset, string targetPath, System.Type type)
	{
#if UNITY_EDITOR
	    string srcAssetPath = GetAssetPath(srcAsset);
	    if (!string.IsNullOrEmpty(srcAssetPath))
	    {
		string targetFolderPath = HEU_Platform.GetFolderPath(targetPath);
		CreatePathWithFolders(targetFolderPath);

		string targetFileName = HEU_Platform.GetFileName(targetPath);
		if (string.IsNullOrEmpty(targetFileName))
		{
		    HEU_Logger.LogErrorFormat("Copying asset failed. Destination path must end with a file name: {0}!", targetPath);
		    return null;
		}

		if (CopyAsset(srcAssetPath, targetPath))
		{
		    // Refresh database as otherwise we won't be able to load it in the next line.
		    SaveAndRefreshDatabase();

		    return LoadAssetAtPath(targetPath, type);
		}
		else
		{
		    HEU_Logger.LogErrorFormat("Failed to copy and load asset from {0} to {1}!", srcAssetPath, targetPath);
		}
	    }
	    return null;
#else
			// TODO RUNTIME: AssetDatabase is not supported at runtime. Do we need to support this for runtime?
			HEU_Logger.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED);
			return null;
#endif
	}
	/// <summary>
	/// Populate TOP data from linked HDA
	/// </summary>
	private void PopulateFromHDA()
	{
	    if (!_heu.IsAssetValid())
	    {
		_linkState = LinkState.ERROR_NOT_LINKED;
		return;
	    }

	    if (_heu != null)
	    {
		_assetID = _heu.AssetID;
		_assetName = _heu.AssetName;
	    }

	    if (PopulateTOPNetworks())
	    {
		_linkState = LinkState.LINKED;
	    }
	    else
	    {
		_linkState = LinkState.ERROR_NOT_LINKED;
		HEU_Logger.LogErrorFormat("Failed to populate TOP network info for asset {0}!", _assetName);
	    }

	    RepaintUI();
	}
	public static Material LoadSubstanceMaterialWithIndex(string materialPath, int substanceMaterialIndex)
	{
	    Material material = LoadUnityMaterial(materialPath);
#if UNITY_2017_4_OR_NEWER || UNITY_2018_1_OR_NEWER
	    HEU_Logger.LogErrorFormat("Houdini Engine for Unity does not support the new Substance plugin as of yet!");
#elif UNITY_EDITOR
			if (material != null)
			{
				string assetPath = HEU_AssetDatabase.GetAssetPath(material);
				SubstanceImporter substanceImporter = AssetImporter.GetAtPath(assetPath) as SubstanceImporter;

				if(substanceMaterialIndex >= 0 && substanceMaterialIndex < substanceImporter.GetMaterialCount())
				{
					material = substanceImporter.GetMaterials()[substanceMaterialIndex];
				}
			}
#endif
	    if (material != null)
	    {
		HEU_Logger.LogFormat("Loaded Substance material with index {0} from path {1}.", substanceMaterialIndex, materialPath);
	    }
	    else
	    {
		HEU_Logger.LogWarningFormat("Failed to load Substance material with index {0} from path {1}.", substanceMaterialIndex, materialPath);
	    }

	    return material;
	}
	public static bool LoadToolsFromDirectory(string folderPath, out List<HEU_ShelfToolData> tools)
	{
	    tools = new List<HEU_ShelfToolData>();

	    string[] filePaths = HEU_Platform.GetFilesInFolder(folderPath, "*.json", true);
	    bool bResult = false;
	    try
	    {
		if (filePaths != null)
		{
		    foreach (string fileName in filePaths)
		    {
			HEU_ShelfToolData tool = LoadToolFromJsonFile(fileName);
			if (tool != null)
			{
			    tools.Add(tool);
			}
		    }

		    bResult = true;
		}
	    }
	    catch (System.Exception ex)
	    {
		HEU_Logger.LogErrorFormat("Parsing JSON files in directory caused exception: {0}", ex);
		return false;
	    }

	    return bResult;
	}
        /// <summary>
        /// Cook the PDG graph of the specified TOP network
        /// </summary>
        /// <param name="topNetwork"></param>
        public void CookTOPNetworkOutputNode(HEU_TOPNetworkData topNetwork, System.Action <HEU_PDGCookedEventData> OnCook = null)
        {
#if HOUDINIENGINEUNITY_ENABLED
            ClearEventMessages();

            HEU_SessionBase session = GetHAPIPDGSession();
            if (session == null || !session.IsSessionValid())
            {
                return;
            }

            // Cancel all cooks. This is required as otherwise the graph gets into an infinite cook
            // state (bug?)
            if (_pdgContextIDs != null)
            {
                foreach (HAPI_PDG_GraphContextId contextID in _pdgContextIDs)
                {
                    session.CancelPDGCook(contextID);
                }
            }

            if (!session.CookPDG(topNetwork._nodeID, 0, 0))
            {
                HEU_Logger.LogErrorFormat("Cook node failed!");
            }

            _cookedDataEvent = OnCook;

            ResetCallbackVariables();
#endif
        }
 private GameObject GetGameObjectFromType(Object obj, System.Type type)
 {
     if (type == typeof(GameObject))
     {
         return(obj as GameObject);
     }
     else if (type == typeof(HEU_HoudiniAssetRoot))
     {
         HEU_HoudiniAssetRoot heuRoot = obj as HEU_HoudiniAssetRoot;
         return(heuRoot.gameObject);
     }
     else if (type == typeof(Terrain))
     {
         Terrain terrain = obj as Terrain;
         return(terrain.gameObject);
     }
     else if (type == typeof(HEU_BoundingVolume))
     {
         HEU_BoundingVolume volume = obj as HEU_BoundingVolume;
         return(volume.gameObject);
     }
     else if (type == typeof(Tilemap))
     {
         Tilemap tilemap = obj as Tilemap;
         return(tilemap.gameObject);
     }
     else
     {
         HEU_Logger.LogErrorFormat("Unsupported type {0} for Selection Window.", type);
         return(null);
     }
 }
	public static Material LoadSubstanceMaterialWithName(string materialPath, string substanceName)
	{
	    Material material = LoadUnityMaterial(materialPath);
#if UNITY_2017_4_OR_NEWER || UNITY_2018_1_OR_NEWER
	    HEU_Logger.LogErrorFormat("Houdini Engine for Unity does not support the new Substance plugin as of yet!");
#elif UNITY_EDITOR
			if (material != null)
			{
				string assetPath = HEU_AssetDatabase.GetAssetPath(material);
				
				SubstanceImporter substanceImporter = AssetImporter.GetAtPath(assetPath) as SubstanceImporter;

				ProceduralMaterial[] proceduralMaterials = substanceImporter.GetMaterials();
				for(int i = 0; i < proceduralMaterials.Length; ++i)
				{
					if(proceduralMaterials[i].name.Equals(substanceName))
					{
						material = proceduralMaterials[i];
						break;
					}
				}
			}
#endif

	    if (material != null)
	    {
		HEU_Logger.LogFormat("Loaded Substance material with name {0} from path {1}.", substanceName, materialPath);
	    }
	    else
	    {
		HEU_Logger.LogWarningFormat("Failed to load Substance material with name {0} from path {1}.", substanceName, materialPath);
	    }

	    return material;
	}
	public static void CreateNodeSync(HEU_SessionBase session, string opName, string nodeNabel)
	{
	    if (session == null)
	    {
		session = HEU_SessionManager.GetDefaultSession();
	    }
	    if (session == null || !session.IsSessionValid())
	    {
		return;
	    }

	    HAPI_NodeId newNodeID = -1;
	    HAPI_NodeId parentNodeId = -1;

	    if (!session.CreateNode(parentNodeId, opName, nodeNabel, true, out newNodeID))
	    {
		HEU_Logger.LogErrorFormat("Unable to create merge SOP node for connecting input assets.");
		return;
	    }

	    if (parentNodeId == -1)
	    {
		// When creating a node without a parent, for SOP nodes, a container
		// geometry object will have been created by HAPI.
		// In all cases we want to use the node ID of that object container
		// so the below code sets the parent's node ID.

		// But for SOP/subnet we actually do want the subnet SOP node ID
		// hence the useSOPNodeID argument here is to override it.
		bool useSOPNodeID = opName.Equals("SOP/subnet");

		HAPI_NodeInfo nodeInfo = new HAPI_NodeInfo();
		if (!session.GetNodeInfo(newNodeID, ref nodeInfo))
		{
		    return;
		}

		if (nodeInfo.type == HAPI_NodeType.HAPI_NODETYPE_SOP)
		{
		    if (!useSOPNodeID)
		    {
			newNodeID = nodeInfo.parentId;
		    }
		}
		else if (nodeInfo.type != HAPI_NodeType.HAPI_NODETYPE_OBJ)
		{
		    HEU_Logger.LogErrorFormat("Unsupported node type {0}", nodeInfo.type);
		    return;
		}
	    }

	    GameObject newGO = HEU_GeneralUtility.CreateNewGameObject(nodeNabel);

	    HEU_NodeSync nodeSync = newGO.AddComponent<HEU_NodeSync>();
	    nodeSync.InitializeFromHoudini(session, newNodeID, nodeNabel, "");
	}
	public virtual bool DoAssetLoad()
	{
	    string assetPath = _filePath;
	    if (!HEU_Platform.DoesFileExist(assetPath))
	    {
		assetPath = HEU_AssetDatabase.GetValidAssetPath(assetPath);
	    }

	    HAPI_NodeId libraryID = -1;
	    HAPI_NodeId newNodeID = -1;

	    byte[] buffer = null;
	    bool bResult = HEU_Platform.LoadFileIntoMemory(assetPath, out buffer);
	    if (bResult)
	    {
		if (!_session.LoadAssetLibraryFromMemory(buffer, true, out libraryID))
		{
		    HEU_Logger.LogErrorFormat("Unable to load asset library.");
		    return false;
		}
		//HEU_Logger.Log("Loaded asset");

		int assetCount = 0;
		bResult = _session.GetAvailableAssetCount(libraryID, out assetCount);
		if (!bResult)
		{
		    return false;
		}

		int[] assetNameLengths = new int[assetCount];
		bResult = _session.GetAvailableAssets(libraryID, ref assetNameLengths, assetCount);
		if (!bResult)
		{
		    return false;
		}

		string[] assetNames = new string[assetCount];
		for (int i = 0; i < assetCount; ++i)
		{
		    assetNames[i] = HEU_SessionManager.GetString(assetNameLengths[i], _session);
		}

		// Create top level node. Note that CreateNode will cook the node if HAPI was initialized with threaded cook setting on.
		string topNodeName = assetNames[0];
		bResult = _session.CreateNode(-1, topNodeName, "", false, out newNodeID);
		if (!bResult)
		{
		    return false;
		}
		//HEU_Logger.Log("Created asset node");

		_loadData._cookNodeID = newNodeID;
	    }

	    return true;
	}
	private void UpdateAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, HEU_AttributeData attributeData)
	{
	    int attrCount = attributeData._attributeInfo.count;

	    // Presuming we are working with point attributes
	    HAPI_AttributeInfo newAttrInfo = new HAPI_AttributeInfo();
	    newAttrInfo.exists = true;
	    newAttrInfo.owner = attributeData._attributeInfo.owner;
	    newAttrInfo.storage = attributeData._attributeInfo.storage;
	    newAttrInfo.count = attributeData._attributeInfo.count;
	    newAttrInfo.tupleSize = attributeData._attributeInfo.tupleSize;
	    newAttrInfo.originalOwner = attributeData._attributeInfo.originalOwner;

	    if (!session.AddAttribute(geoID, partID, attributeData._name, ref newAttrInfo))
	    {
		HEU_Logger.LogErrorFormat("Failed to add attribute: {0}", attributeData._name);
		return;
	    }

	    if (newAttrInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_INT)
	    {
		int[] pointData = new int[attrCount * newAttrInfo.tupleSize];
		for (int j = 0; j < attrCount; ++j)
		{
		    for (int tuple = 0; tuple < newAttrInfo.tupleSize; ++tuple)
		    {
			pointData[j * newAttrInfo.tupleSize + tuple] = attributeData._intValues[j * newAttrInfo.tupleSize + tuple];
		    }
		}
		HEU_GeneralUtility.SetAttributeArray(geoID, partID, attributeData._name, ref newAttrInfo, pointData, session.SetAttributeIntData, attrCount);
	    }
	    else if (newAttrInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_FLOAT)
	    {
		float[] pointData = new float[attrCount * newAttrInfo.tupleSize];
		for (int j = 0; j < attrCount; ++j)
		{
		    for (int tuple = 0; tuple < newAttrInfo.tupleSize; ++tuple)
		    {
			pointData[j * newAttrInfo.tupleSize + tuple] = attributeData._floatValues[j * newAttrInfo.tupleSize + tuple];
		    }
		}
		HEU_GeneralUtility.SetAttributeArray(geoID, partID, attributeData._name, ref newAttrInfo, pointData, session.SetAttributeFloatData, attrCount);
	    }
	    else if (newAttrInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_STRING)
	    {
		string[] pointData = new string[attrCount * newAttrInfo.tupleSize];
		for (int j = 0; j < attrCount; ++j)
		{
		    for (int tuple = 0; tuple < newAttrInfo.tupleSize; ++tuple)
		    {
			pointData[j * newAttrInfo.tupleSize + tuple] = attributeData._stringValues[j * newAttrInfo.tupleSize + tuple];
		    }
		}
		HEU_GeneralUtility.SetAttributeArray(geoID, partID, attributeData._name, ref newAttrInfo, pointData, session.SetAttributeStringData, attrCount);
	    }
	}
Esempio n. 12
0
	internal static bool CreateInputNodeWithMultiAssets(HEU_SessionBase session, HEU_HoudiniAsset parentAsset,
		ref HAPI_NodeId connectMergeID, ref List<HEU_InputHDAInfo> inputAssetInfos,
		 bool bKeepWorldTransform, HAPI_NodeId mergeParentID = -1)
	{
	    // Create the merge SOP node that the input nodes are going to connect to.
	    if (!session.CreateNode(mergeParentID, "SOP/merge", null, true, out connectMergeID))
	    {
		HEU_Logger.LogErrorFormat("Unable to create merge SOP node for connecting input assets.");
		return false;
	    }

	    int numInputs = inputAssetInfos.Count;
	    for (int i = 0; i < numInputs; ++i)
	    {
		inputAssetInfos[i]._connectedInputNodeID = HEU_Defines.HEU_INVALID_NODE_ID;

		if (inputAssetInfos[i]._pendingGO == null)
		{
		    continue;
		}

		// ID of the asset that will be connected
		HAPI_NodeId inputAssetID = HEU_Defines.HEU_INVALID_NODE_ID;

		HEU_HoudiniAssetRoot inputAssetRoot = inputAssetInfos[i]._pendingGO.GetComponent<HEU_HoudiniAssetRoot>();
		if (inputAssetRoot != null && inputAssetRoot._houdiniAsset != null)
		{
		    if (!inputAssetRoot._houdiniAsset.IsAssetValidInHoudini(session))
		    {
			// Force a recook if its not valid (in case it hasn't been loaded into the session)
			inputAssetRoot._houdiniAsset.RequestCook(true, false, true, true);
		    }

		    inputAssetID = inputAssetRoot._houdiniAsset.AssetID;
		}

		if (inputAssetID == HEU_Defines.HEU_INVALID_NODE_ID)
		{
		    continue;
		}

		if (!session.ConnectNodeInput(connectMergeID, i, inputAssetID))
		{
		    HEU_Logger.LogErrorFormat("Unable to connect input nodes!");
		    return false;
		}

		inputAssetInfos[i]._connectedInputNodeID = inputAssetID;
		inputAssetInfos[i]._connectedGO = inputAssetInfos[i]._pendingGO;
		inputAssetInfos[i]._connectedMergeNodeID = connectMergeID;

		parentAsset.ConnectToUpstream(inputAssetRoot._houdiniAsset);
	    }

	    return true;
	}
	public static HEU_MaterialData CreateUnitySubstanceMaterialData(int materialKey, string materialPath, string substanceName, int substanceIndex, List<HEU_MaterialData> materialCache,
		string assetCacheFolderPath)
	{
	    // Let's make sure we can find the Unity or Substance material first
	    Material material = null;

	    HEU_MaterialData.Source sourceType = HEU_MaterialData.Source.UNITY;
	    if (!string.IsNullOrEmpty(substanceName))
	    {
		sourceType = HEU_MaterialData.Source.SUBSTANCE;
		material = HEU_MaterialFactory.LoadSubstanceMaterialWithName(materialPath, substanceName);
	    }
	    else if (substanceIndex >= 0)
	    {
		sourceType = HEU_MaterialData.Source.SUBSTANCE;
		material = HEU_MaterialFactory.LoadSubstanceMaterialWithIndex(materialPath, substanceIndex);
	    }
	    else if (!string.IsNullOrEmpty(materialPath))
	    {
		material = HEU_MaterialFactory.LoadUnityMaterial(materialPath);
	    }

	    if (material != null)
	    {
		HEU_MaterialData materialData = ScriptableObject.CreateInstance<HEU_MaterialData>();
		materialData._materialSource = sourceType;
		materialData._materialKey = materialKey;
		materialData._material = material;

		materialCache.Add(materialData);
		return materialData;
	    }
	    else
	    {
		// We can't find the material in Unity, so notify user and use a default one which allows to at least get the geometry in.
		if (string.IsNullOrEmpty(materialPath))
		{
		    HEU_Logger.LogWarningFormat("Empty material name found. Using default material.");
		}
		else if (materialPath.Contains("Resources/unity_builtin_extra"))
		{
		    // Built in material. Don't display error.
		}
		else
		{
		    HEU_Logger.LogErrorFormat("Unable to find {0} material {1}. Using a default material instead. Please check material exists in project and reload asset!", sourceType, materialPath);
		}

		// The materialKey helps uniquely identify this material for further look ups. But we also need to get a valid file name
		// to create the default material, so strip out just the file name.
		string strippedFileName = HEU_Platform.GetFileName(materialPath);
		return CreateMaterialInCache(materialKey, strippedFileName, HEU_MaterialData.Source.UNITY, false, materialCache, assetCacheFolderPath);
	    }
	}
Esempio n. 14
0
 /// <summary>
 /// Returns all files (with their paths) in a given folder, with or without pattern, either recursively or just the first.
 /// </summary>
 /// <param name="folderPath">Path to folder</param>
 /// <param name="searchPattern">File name pattern to search for</param>
 /// <param name="bRecursive">Search all directories or just the top</param>
 /// <returns>Array of file paths found or null if error</returns>
 public static string[] GetFilesInFolder(string folderPath, string searchPattern, bool bRecursive)
 {
     try
     {
         return(Directory.GetFiles(folderPath, searchPattern, bRecursive ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories));
     }
     catch (Exception ex)
     {
         HEU_Logger.LogErrorFormat("Getting files in directory {0} threw exception: {1}", folderPath, ex);
         return(null);
     }
 }
Esempio n. 15
0
 public static bool WriteAllText(string path, string text)
 {
     try
     {
         File.WriteAllText(path, text);
         return(true);
     }
     catch (System.Exception ex)
     {
         HEU_Logger.LogErrorFormat("Unable to save session to file: {0}. Exception: {1}", text, ex.ToString());
     }
     return(false);
 }
        /// <summary>
        /// Helper to set heightfield data for a specific volume node.
        /// Used for a specific terrain layer.
        /// </summary>
        /// <param name="session">Session that the volume node resides in.</param>
        /// <param name="volumeNodeID">ID of the target volume node</param>
        /// <param name="partID">Part ID</param>
        /// <param name="heightValues">Array of height or alpha values</param>
        /// <param name="heightFieldName">Name of the layer</param>
        /// <returns>True if successfully uploaded heightfield values</returns>
        public bool SetHeightFieldData(HEU_SessionBase session, HAPI_NodeId volumeNodeID, HAPI_PartId partID, float[] heightValues, string heightFieldName, ref HAPI_VolumeInfo baseVolumeInfo)
        {
            // Cook the node to get infos below
            if (!session.CookNode(volumeNodeID, false))
            {
                return(false);
            }

            // Get Geo, Part, and Volume infos
            HAPI_GeoInfo geoInfo = new HAPI_GeoInfo();

            if (!session.GetGeoInfo(volumeNodeID, ref geoInfo))
            {
                return(false);
            }

            HAPI_PartInfo partInfo = new HAPI_PartInfo();

            if (!session.GetPartInfo(geoInfo.nodeId, partID, ref partInfo))
            {
                return(false);
            }

            HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo();

            if (!session.GetVolumeInfo(volumeNodeID, partInfo.id, ref volumeInfo))
            {
                return(false);
            }

            volumeInfo.tileSize = 1;
            // Use same transform as base layer
            volumeInfo.transform = baseVolumeInfo.transform;

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

            // Now set the height data
            if (!session.SetHeightFieldData(geoInfo.nodeId, partInfo.id, heightFieldName, heightValues, 0, heightValues.Length))
            {
                HEU_Logger.LogErrorFormat("Unable to set `{0}` height values on input heightfield node!\n" +
                                          "Check your terrain sizes including Control Texture Resolution is less than the Heightmap Resolution.",
                                          heightFieldName);
                return(false);
            }

            return(true);
        }
Esempio n. 17
0
 public static string ReadAllText(string path)
 {
     try
     {
         if (File.Exists(path))
         {
             return(File.ReadAllText(path));
         }
     }
     catch (System.Exception ex)
     {
         HEU_Logger.LogErrorFormat("Unable to load from file: {0}. Exception: {1}", path, ex.ToString());
     }
     return("");
 }
        /// <summary>
        /// Calculate the index format based on number of vertices.
        /// </summary>
        public void CalculateIndexFormat(int numVertices)
        {
            uint maxVertexCount = ushort.MaxValue;
            uint vertexCount    = Convert.ToUInt32(numVertices);

            if (vertexCount > maxVertexCount)
            {
#if UNITY_2017_3_OR_NEWER
                // For vertex count larger than 16-bit, use 32-bit buffer
                _indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#else
                HEU_Logger.LogErrorFormat("Vertex count {0} which is above Unity maximum of {1}.\nUse Unity 2017.3+ or reduce this in Houdini.",
                                          vertexCount, maxVertexCount);
#endif
            }
        }
	private static Texture2D ExtractHoudiniImageToTextureRaw(HEU_SessionBase session, HAPI_MaterialInfo materialInfo, string imagePlanes)
	{
	    Texture2D textureResult = null;

	    HAPI_ImageInfo imageInfo = new HAPI_ImageInfo();
	    if (!session.GetImageInfo(materialInfo.nodeId, ref imageInfo))
	    {
		return textureResult;
	    }

	    imageInfo.dataFormat = HAPI_ImageDataFormat.HAPI_IMAGE_DATA_INT8;
	    imageInfo.interleaved = true;
	    imageInfo.packing = HAPI_ImagePacking.HAPI_IMAGE_PACKING_RGBA;
	    imageInfo.gamma = HEU_PluginSettings.ImageGamma;

	    session.SetImageInfo(materialInfo.nodeId, ref imageInfo);

	    // Extract image to buffer
	    byte[] imageData = null;
	    if (!session.ExtractImageToMemory(materialInfo.nodeId, HEU_HAPIConstants.HAPI_RAW_FORMAT_NAME, imagePlanes, out imageData))
	    {
		return textureResult;
	    }

	    int colorDataSize = imageInfo.xRes * imageInfo.yRes;
	    if (colorDataSize * 4 != imageData.Length)
	    {
		HEU_Logger.LogErrorFormat("Extracted image size does not match expected image info size."
			+ " Try using non-raw format for texture extraction.");
		return textureResult;
	    }

	    Color32[] colorData = new Color32[colorDataSize];
	    for (int i = 0; i < colorDataSize; ++i)
	    {
		colorData[i].r = imageData[i * 4 + 0];
		colorData[i].g = imageData[i * 4 + 1];
		colorData[i].b = imageData[i * 4 + 2];
		colorData[i].a = imageData[i * 4 + 3];
	    }

	    textureResult = new Texture2D(imageInfo.xRes, imageInfo.yRes, TextureFormat.ARGB32, false);
	    textureResult.SetPixels32(colorData);
	    textureResult.Apply();

	    return textureResult;
	}
	/// <summary>
	/// Removes the session data file.
	/// </summary>
	public static void DeleteAllSavedSessionData()
	{
#if UNITY_EDITOR
	    string path = SessionFilePath();
	    try
	    {
		if (File.Exists(path))
		{
		    File.Delete(SessionFilePath());
		}
	    }
	    catch (System.Exception ex)
	    {
		HEU_Logger.LogErrorFormat("Unable to deletion session file: {0}. Exception: {1}", path, ex.ToString());
	    }
	    
#endif
	}
	public static HEU_ShelfToolData LoadToolFromJsonFile(string jsonFilePath)
	{
	    string json = null;
	    try
	    {
		StreamReader fileReader = new StreamReader(jsonFilePath);
		json = fileReader.ReadToEnd();
		fileReader.Close();
	    }
	    catch (System.Exception ex)
	    {
		HEU_Logger.LogErrorFormat("Exception while reading {0}: {1}", jsonFilePath, ex);
		return null;
	    }

	    HEU_ShelfToolData tool = LoadToolFromJsonString(json, jsonFilePath);
	    return tool;
	}
Esempio n. 22
0
	/// <summary>
	/// Create a unique asset cache folder for the given asset path.
	/// The given asset path should be the HDA's path in the project.
	/// </summary>
	/// <param name="suggestedAssetPath">A suggested path to try. Will use default if empty or null./param>
	/// <returns>Unique asset cache folder for given asset path</returns>
	public static string CreateAssetCacheFolder(string suggestedAssetPath, int hash = 0)
	{
#if UNITY_EDITOR
	    // We create a unique folder inside our plugin's asset database cache folder.

	    string assetDBPath = GetAssetCachePath();
	    string assetWorkingPath = HEU_Platform.BuildPath(assetDBPath, HEU_Defines.HEU_WORKING_PATH);
	    if (!AssetDatabase.IsValidFolder(assetWorkingPath))
	    {
		AssetDatabase.CreateFolder(assetDBPath, HEU_Defines.HEU_WORKING_PATH);
	    }

	    string fileName = HEU_Platform.GetFileNameWithoutExtension(suggestedAssetPath);
	    if (string.IsNullOrEmpty(fileName))
	    {
		fileName = "AssetCache";
		HEU_Logger.LogWarningFormat("Unable to get file name from {0}. Using default value: {1}.", suggestedAssetPath, fileName);
	    }

	    if (HEU_PluginSettings.ShortenFolderPaths && fileName.Length >= 3 && hash != 0)
	    {
		fileName = fileName.Substring(0, 3) + hash;
	    }

	    string fullPath = HEU_Platform.BuildPath(assetWorkingPath, fileName);

	    // Gives us the unique folder path, which we then separate out to create this folder
	    fullPath = AssetDatabase.GenerateUniqueAssetPath(fullPath);

	    CreatePathWithFolders(fullPath);
	    if (!AssetDatabase.IsValidFolder(fullPath))
	    {
		HEU_Logger.LogErrorFormat("Unable to create a valid asset cache folder: {0}! Check directory permission or that enough space is available!", fullPath);
		fullPath = null;
	    }

	    return fullPath;
#else
			// TODO RUNTIME: AssetDatabase is not supported at runtime. Do we need to support this for runtime?
			HEU_Logger.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED);
			return null;
#endif
	}
	/// <summary>
	/// Load the preset file at the specified path into the specified asset and cook it.
	/// </summary>
	/// <param name="asset">Asset to load preset into</param>
	/// <param name="filePath">Full path to file containing preset. File must have been written out by SaveAssetPresetToFile.</param>
	public static void LoadPresetFileIntoAssetAndCook(HEU_HoudiniAsset asset, string filePath)
	{
	    try
	    {
		HEU_AssetPreset assetPreset = null;

		using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
		{
		    BinaryFormatter formatter = new BinaryFormatter();

		    HEU_Vector3SerializationSurrogate vector3S = new HEU_Vector3SerializationSurrogate();
		    HEU_Vector2SerializationSurrogate vector2S = new HEU_Vector2SerializationSurrogate();

		    SurrogateSelector surrogateSelector = new SurrogateSelector();
		    surrogateSelector.AddSurrogate(typeof(Vector3), new StreamingContext(StreamingContextStates.All), vector3S);
		    surrogateSelector.AddSurrogate(typeof(Vector2), new StreamingContext(StreamingContextStates.All), vector2S);
		    formatter.SurrogateSelector = surrogateSelector;

		    assetPreset = (HEU_AssetPreset)formatter.Deserialize(fs);
		}

		if (assetPreset != null)
		{
		    if (PRESET_IDENTIFIER.SequenceEqual(assetPreset._identifier))
		    {
			asset.LoadAssetPresetAndCook(assetPreset);
		    }
		    else
		    {
			HEU_Logger.LogErrorFormat("Unable to load preset. Specified file is not a saved HDA preset: {0}", filePath);
		    }
		}
		else
		{
		    HEU_Logger.LogErrorFormat("Failed to load preset file {0}.", filePath);
		}
	    }
	    catch (System.Exception ex)
	    {
		HEU_Logger.LogErrorFormat("Failed to load preset due to exception: " + ex.ToString());
	    }
	}
Esempio n. 24
0
 public static bool LoadFileIntoMemory(string path, out byte[] buffer)
 {
     buffer = null;
     try
     {
         if (File.Exists(path))
         {
             buffer = File.ReadAllBytes(path);
         }
         else
         {
             HEU_Logger.LogErrorFormat("Failed to open (0}. File doesn't exist!", path);
         }
     }
     catch (Exception ex)
     {
         HEU_Logger.LogErrorFormat("Failed to open (0}. Exception: {1}", path, ex.ToString());
     }
     return(buffer != null);
 }
	/// <summary>
	/// Save plugin data to disk.
	/// </summary>
	public bool SavePluginData()
	{
	    try
	    {
		string settingsFilePath = SettingsFilePath();
		using (StreamWriter writer = new StreamWriter(settingsFilePath, false))
		{
		    writer.WriteLine("Houdini Engine for Unity Plugin Settings");
		    writer.WriteLine("Version=" + PluginSettingsVersion);

		    foreach (KeyValuePair<string, StoreData> kvpair in _dataMap)
		    {
			// key(type)=value
			writer.WriteLine("{0}({1})={2}", kvpair.Key, kvpair.Value._type, kvpair.Value._valueStr);
		    }
		}

#if UNITY_EDITOR
		// Remove old keys from EditorPrefs as its the deprecated method of saving the plugin settings
		if (EditorPrefs.HasKey(HEU_Defines.PLUGIN_STORE_KEYS))
		{
		    EditorPrefs.DeleteKey(HEU_Defines.PLUGIN_STORE_KEYS);
		}
		if (EditorPrefs.HasKey(HEU_Defines.PLUGIN_STORE_DATA))
		{
		    EditorPrefs.DeleteKey(HEU_Defines.PLUGIN_STORE_DATA);
		}
#endif

		_requiresSave = false;
	    }
	    catch (System.Exception ex)
	    {
		HEU_Logger.LogErrorFormat("Exception when trying to save settings file: {0}", ex.ToString());
		return false;
	    }

	    return true;
	}
	/// <summary>
	/// Save the specified asset's preset data to file at specified path.
	/// </summary>
	/// <param name="asset">The asset's preset data will be saved</param>
	/// <param name="filePath">The file to save to</param>
	public static void SaveAssetPresetToFile(HEU_HoudiniAsset asset, string filePath)
	{
	    // This should return an object filled with preset data, and which we can serialize directly
	    HEU_AssetPreset assetPreset = asset.GetAssetPreset();

	    if (assetPreset != null)
	    {
		try
		{
		    int len = PRESET_IDENTIFIER.Length;

		    assetPreset._identifier = PRESET_IDENTIFIER;
		    assetPreset._version = PRESET_VERSION;

		    using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
		    {
			IFormatter formatter = new BinaryFormatter();

			HEU_Vector3SerializationSurrogate vector3S = new HEU_Vector3SerializationSurrogate();
			HEU_Vector2SerializationSurrogate vector2S = new HEU_Vector2SerializationSurrogate();

			SurrogateSelector surrogateSelector = new SurrogateSelector();
			surrogateSelector.AddSurrogate(typeof(Vector3), new StreamingContext(StreamingContextStates.All), vector3S);
			surrogateSelector.AddSurrogate(typeof(Vector2), new StreamingContext(StreamingContextStates.All), vector2S);
			formatter.SurrogateSelector = surrogateSelector;

			formatter.Serialize(fs, assetPreset);
		    }
		}
		catch (System.Exception ex)
		{
		    HEU_Logger.LogErrorFormat("Failed to save preset due to exception: " + ex.ToString());
		}
	    }
	    else
	    {
		HEU_Logger.LogErrorFormat("Failed to save preset due to unable to retrieve the preset buffer!");
	    }
	}
Esempio n. 27
0
	/// <summary>
	/// Generates a cube mesh using quad faces from given points, with vertex colours on selected and non-selected points.
	/// </summary>
	/// <param name="points">A cube will be created for each point in this list</param>
	/// <param name="selectedPtsFlag">Indices of selected points</param>
	/// <param name="defaultColor">Non-selected vertex color of cubes</param>
	/// <param name="selectedColor">Selected vertex color of cubes</param>
	/// <param name="size">Length of one side of cube</param>
	/// <returns>The generated mesh</returns>
	public static Mesh GenerateCubeMeshFromPoints(Vector3[] points, Color[] pointsColor, float size = 1f)
	{
	    float halfSize = size * 0.5f;

	    int totalPoints = points.Length;

	    // Each cube face will get unique vertices due to splitting the normals
	    int totalVertices = 24 * totalPoints;

	    Vector3[] vertices = new Vector3[totalVertices];
	    Color[] colors = new Color[totalVertices];
	    Vector3[] normals = new Vector3[totalVertices];
	    int[] indices = new int[totalVertices];

	    int ptIndex = 0;
	    int vertsPerPt = 24;

	    foreach (Vector3 pt in points)
	    {
		Vector3 v0 = new Vector3(pt.x - halfSize, pt.y + halfSize, pt.z + halfSize);
		Vector3 v1 = new Vector3(pt.x - halfSize, pt.y + halfSize, pt.z - halfSize);
		Vector3 v2 = new Vector3(pt.x + halfSize, pt.y + halfSize, pt.z - halfSize);
		Vector3 v3 = new Vector3(pt.x + halfSize, pt.y + halfSize, pt.z + halfSize);

		Vector3 v4 = new Vector3(pt.x - halfSize, pt.y - halfSize, pt.z + halfSize);
		Vector3 v5 = new Vector3(pt.x - halfSize, pt.y - halfSize, pt.z - halfSize);
		Vector3 v6 = new Vector3(pt.x + halfSize, pt.y - halfSize, pt.z - halfSize);
		Vector3 v7 = new Vector3(pt.x + halfSize, pt.y - halfSize, pt.z + halfSize);

		int vertIndex = ptIndex * vertsPerPt;

		// Top
		vertices[vertIndex + 0] = v0;
		vertices[vertIndex + 1] = v3;
		vertices[vertIndex + 2] = v2;
		vertices[vertIndex + 3] = v1;

		normals[vertIndex + 0] = Vector3.up;
		normals[vertIndex + 1] = Vector3.up;
		normals[vertIndex + 2] = Vector3.up;
		normals[vertIndex + 3] = Vector3.up;

		// Bottom
		vertices[vertIndex + 4] = v4;
		vertices[vertIndex + 5] = v5;
		vertices[vertIndex + 6] = v6;
		vertices[vertIndex + 7] = v7;

		normals[vertIndex + 4] = Vector3.down;
		normals[vertIndex + 5] = Vector3.down;
		normals[vertIndex + 6] = Vector3.down;
		normals[vertIndex + 7] = Vector3.down;

		// Front
		vertices[vertIndex + 8] = v0;
		vertices[vertIndex + 9] = v4;
		vertices[vertIndex + 10] = v7;
		vertices[vertIndex + 11] = v3;

		normals[vertIndex + 8] = Vector3.forward;
		normals[vertIndex + 9] = Vector3.forward;
		normals[vertIndex + 10] = Vector3.forward;
		normals[vertIndex + 11] = Vector3.forward;

		// Back
		vertices[vertIndex + 12] = v1;
		vertices[vertIndex + 13] = v2;
		vertices[vertIndex + 14] = v6;
		vertices[vertIndex + 15] = v5;

		normals[vertIndex + 12] = Vector3.back;
		normals[vertIndex + 13] = Vector3.back;
		normals[vertIndex + 14] = Vector3.back;
		normals[vertIndex + 15] = Vector3.back;

		// Left
		vertices[vertIndex + 16] = v0;
		vertices[vertIndex + 17] = v1;
		vertices[vertIndex + 18] = v5;
		vertices[vertIndex + 19] = v4;

		normals[vertIndex + 16] = Vector3.left;
		normals[vertIndex + 17] = Vector3.left;
		normals[vertIndex + 18] = Vector3.left;
		normals[vertIndex + 19] = Vector3.left;

		// Right
		vertices[vertIndex + 20] = v2;
		vertices[vertIndex + 21] = v3;
		vertices[vertIndex + 22] = v7;
		vertices[vertIndex + 23] = v6;

		normals[vertIndex + 20] = Vector3.right;
		normals[vertIndex + 21] = Vector3.right;
		normals[vertIndex + 22] = Vector3.right;
		normals[vertIndex + 23] = Vector3.right;

		// Vertex colors
		for (int i = 0; i < vertsPerPt; ++i)
		{
		    colors[ptIndex * vertsPerPt + i] = pointsColor[ptIndex];
		}

		// Indices
		for (int i = 0; i < vertsPerPt; ++i)
		{
		    indices[ptIndex * vertsPerPt + i] = ptIndex * vertsPerPt + i;
		}

		ptIndex++;
	    }

	    Mesh mesh = new Mesh();

	    if (indices.Length > ushort.MaxValue)
	    {
#if UNITY_2017_3_OR_NEWER
		mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#else
				HEU_Logger.LogErrorFormat("Unable to generate mesh from points due to larger than supported geometry (> {0} vertices). Use Unity 2017.3+ for large geometry.", ushort.MaxValue);
				return mesh;
#endif
	    }

	    mesh.vertices = vertices;
	    mesh.colors = colors;
	    mesh.normals = normals;
	    mesh.SetIndices(indices, MeshTopology.Quads, 0);

	    return mesh;
	}
Esempio n. 28
0
	/// <summary>
	/// Create an input node network and upload the given set of input objects.
	/// This creates a SOP/merge node, and input nodes for each object in inputObjects
	/// which are then connected to the merge node.
	/// It finds the input interface that supports each object in inputObjects for creating
	/// the input node and uploading the data based on the type of data.
	/// </summary>
	/// <param name="session">Session to create the input node in</param>
	/// <param name="assetID">Main asset ID</param>
	/// <param name="connectMergeID">Created SOP/merge node ID</param>
	/// <param name="inputObjects">List of input objects to upload</param>
	/// <param name="inputObjectsConnectedAssetIDs">List of input node IDs for the input nodes created</param>
	/// <param name="inputNode">The specified inputNode to create the node for (used for settings)</param>
	/// <returns>True if successfully uploading input nodes</returns>
	internal static bool CreateInputNodeWithMultiObjects(HEU_SessionBase session, HAPI_NodeId assetID,
		ref HAPI_NodeId connectMergeID, ref List<HEU_InputObjectInfo> inputObjects, ref List<HAPI_NodeId> inputObjectsConnectedAssetIDs, HEU_InputNode inputNode)
	{
	    bool bKeepWorldTransform = inputNode.KeepWorldTransform;
	    // Create the merge SOP node that the input nodes are going to connect to.
	    if (!session.CreateNode(-1, "SOP/merge", null, true, out connectMergeID))
	    {
		HEU_Logger.LogErrorFormat("Unable to create merge SOP node for connecting input assets.");
		return false;
	    }

	    int numObjects = inputObjects.Count;
	    for (int i = 0; i < numObjects; ++i)
	    {
		HAPI_NodeId newConnectInputID = HEU_Defines.HEU_INVALID_NODE_ID;
		inputObjectsConnectedAssetIDs.Add(newConnectInputID);

		// Skipping null gameobjects. Though if this causes issues, can always let it continue
		// to create input node, but not upload mesh data
		if (inputObjects[i]._gameObject == null)
		{
		    continue;
		}

		HEU_InputInterface inputInterface = GetInputInterface(inputObjects[i]);
		if (inputInterface == null)
		{
		    HEU_Logger.LogWarningFormat("No input interface found for gameobject: {0}. Skipping upload!", inputObjects[i]._gameObject.name);
		    continue;
		}

		// Apply settings based on the interface type.
		System.Type inputInterfaceType = inputInterface.GetType();
		if (inputInterfaceType == typeof(HEU_InputInterfaceMesh))
		{
		    HEU_InputInterfaceMesh meshInterface = inputInterface as HEU_InputInterfaceMesh;
		    meshInterface.Initialize(inputNode.MeshSettings);
		}
		if (inputInterfaceType == typeof(HEU_InputInterfaceTilemap))
		{
		    HEU_InputInterfaceTilemap tilemapInterface = inputInterface as HEU_InputInterfaceTilemap;
		    tilemapInterface.Initialize(inputNode.TilemapSettings);
		}

		bool bResult = inputInterface.CreateInputNodeWithDataUpload(session, connectMergeID, inputObjects[i]._gameObject, out newConnectInputID);
		if (!bResult || newConnectInputID == HEU_Defines.HEU_INVALID_NODE_ID)
		{
		    HEU_Logger.LogError("Failed to upload input.");
		    continue;
		}

		inputObjectsConnectedAssetIDs[i] = newConnectInputID;

		if (!session.ConnectNodeInput(connectMergeID, i, newConnectInputID))
		{
		    HEU_Logger.LogErrorFormat("Unable to connect input nodes!");
		    return false;
		}

		UploadInputObjectTransform(session, inputObjects[i], newConnectInputID, bKeepWorldTransform);
	    }

	    return true;
	}
        /// <summary>
        /// Process a PDG event. Notify the relevant HEU_PDGAssetLink object.
        /// </summary>
        /// <param name="session">Houdini Engine session</param>
        /// <param name="contextID">PDG graph context ID</param>
        /// <param name="eventInfo">PDG event info</param>
        private void ProcessPDGEvent(HEU_SessionBase session, HAPI_PDG_GraphContextId contextID, ref HAPI_PDG_EventInfo eventInfo)
        {
#if HOUDINIENGINEUNITY_ENABLED
            HEU_PDGAssetLink assetLink = null;
            HEU_TOPNodeData  topNode   = null;

            HAPI_PDG_EventType     evType       = (HAPI_PDG_EventType)eventInfo.eventType;
            HAPI_PDG_WorkitemState currentState = (HAPI_PDG_WorkitemState)eventInfo.currentState;
            HAPI_PDG_WorkitemState lastState    = (HAPI_PDG_WorkitemState)eventInfo.lastState;

            GetTOPAssetLinkAndNode(eventInfo.nodeId, out assetLink, out topNode);

            //string topNodeName = topNode != null ? string.Format("node={0}", topNode._nodeName) : string.Format("id={0}", eventInfo.nodeId);
            //HEU_Logger.LogFormat("PDG Event: {0}, type={1}, workitem={2}, curState={3}, lastState={4}", topNodeName, evType.ToString(),
            //	eventInfo.workitemId, currentState, lastState);

            if (assetLink == null || topNode == null || topNode._nodeID != eventInfo.nodeId)
            {
                return;
            }

            EventMessageColor msgColor = EventMessageColor.DEFAULT;

            // Events can be split into TOP node specific or work item specific

            if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_NULL)
            {
                SetTOPNodePDGState(assetLink, topNode, HEU_TOPNodeData.PDGState.NONE);
            }
            else if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_NODE_CLEAR)
            {
                NotifyTOPNodePDGStateClear(assetLink, topNode);
            }
            else if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_DIRTY_START)
            {
                SetTOPNodePDGState(assetLink, topNode, HEU_TOPNodeData.PDGState.DIRTYING);

                //HEU_PDGAssetLink.ClearTOPNodeWorkItemResults(topNode);
            }
            else if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_DIRTY_STOP)
            {
                SetTOPNodePDGState(assetLink, topNode, HEU_TOPNodeData.PDGState.DIRTIED);
            }
            else if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_COOK_ERROR)
            {
                SetTOPNodePDGState(assetLink, topNode, HEU_TOPNodeData.PDGState.COOK_FAILED);
                msgColor = EventMessageColor.ERROR;
            }
            else if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_COOK_WARNING)
            {
                msgColor = EventMessageColor.WARNING;
            }
            else if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_COOK_COMPLETE)
            {
                SetTOPNodePDGState(assetLink, topNode, HEU_TOPNodeData.PDGState.COOK_COMPLETE);
            }
            else
            {
                // Work item events

                HEU_TOPNodeData.PDGState currentTOPPDGState = topNode._pdgState;

                if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_WORKITEM_ADD)
                {
                    _totalNumItems++;
                    NotifyTOPNodeTotalWorkItem(assetLink, topNode, 1);
                }
                else if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_WORKITEM_REMOVE)
                {
                    NotifyTOPNodeTotalWorkItem(assetLink, topNode, -1);
                }
                else if (evType == HAPI_PDG_EventType.HAPI_PDG_EVENT_WORKITEM_STATE_CHANGE)
                {
                    // Last states
                    if (lastState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_WAITING && currentState != HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_WAITING)
                    {
                        NotifyTOPNodeWaitingWorkItem(assetLink, topNode, -1);
                    }
                    else if (lastState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_COOKING && currentState != HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_COOKING)
                    {
                        NotifyTOPNodeCookingWorkItem(assetLink, topNode, -1);
                    }
                    else if (lastState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_SCHEDULED && currentState != HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_SCHEDULED)
                    {
                        NotifyTOPNodeScheduledWorkItem(assetLink, topNode, -1);
                    }

                    // New states
                    if (currentState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_WAITING)
                    {
                        NotifyTOPNodeWaitingWorkItem(assetLink, topNode, 1);
                    }
                    else if (currentState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_UNCOOKED)
                    {
                    }
                    else if (currentState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_DIRTY)
                    {
                        //HEU_Logger.LogFormat("Dirty: id={0}", eventInfo.workitemId);

                        ClearWorkItemResult(session, contextID, eventInfo, topNode);
                    }
                    else if (currentState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_SCHEDULED)
                    {
                        NotifyTOPNodeScheduledWorkItem(assetLink, topNode, 1);
                    }
                    else if (currentState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_COOKING)
                    {
                        NotifyTOPNodeCookingWorkItem(assetLink, topNode, 1);
                    }
                    else if (currentState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_COOKED_SUCCESS || currentState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_COOKED_CACHE)
                    {
                        NotifyTOPNodeCookedWorkItem(assetLink, topNode);

                        // On cook success, handle results
                        if (topNode._tags._autoload)
                        {
                            HAPI_PDG_WorkitemInfo workItemInfo = new HAPI_PDG_WorkitemInfo();
                            if (!session.GetWorkItemInfo(contextID, eventInfo.workitemId, ref workItemInfo))
                            {
                                HEU_Logger.LogErrorFormat("Failed to get work item {1} info for {0}", topNode._nodeName, eventInfo.workitemId);
                                return;
                            }

                            if (workItemInfo.numResults > 0)
                            {
                                HAPI_PDG_WorkitemResultInfo[] resultInfos = new HAPI_PDG_WorkitemResultInfo[workItemInfo.numResults];
                                int resultCount = workItemInfo.numResults;
                                if (!session.GetWorkitemResultInfo(topNode._nodeID, eventInfo.workitemId, resultInfos, resultCount))
                                {
                                    HEU_Logger.LogErrorFormat("Failed to get work item {1} result info for {0}", topNode._nodeName, eventInfo.workitemId);
                                    return;
                                }

                                assetLink.LoadResults(session, topNode, workItemInfo, resultInfos, eventInfo.workitemId, OnWorkItemLoadResults);
                            }
                        }
                    }
                    else if (currentState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_COOKED_FAIL)
                    {
                        // TODO: on cook failure, get log path?
                        NotifyTOPNodeErrorWorkItem(assetLink, topNode);
                        msgColor = EventMessageColor.ERROR;
                    }
                    else if (currentState == HAPI_PDG_WorkitemState.HAPI_PDG_WORKITEM_COOKED_CANCEL)
                    {
                        // Ignore it because in-progress cooks can be cancelled when automatically recooking graph
                    }
                }

                if (currentTOPPDGState == HEU_TOPNodeData.PDGState.COOKING)
                {
                    if (topNode.AreAllWorkItemsComplete())
                    {
                        if (topNode.AnyWorkItemsFailed())
                        {
                            SetTOPNodePDGState(assetLink, topNode, HEU_TOPNodeData.PDGState.COOK_FAILED);
                        }
                        else
                        {
                            SetTOPNodePDGState(assetLink, topNode, HEU_TOPNodeData.PDGState.COOK_COMPLETE);
                        }
                    }
                }
                else if (topNode.AnyWorkItemsPending())
                {
                    SetTOPNodePDGState(assetLink, topNode, HEU_TOPNodeData.PDGState.COOKING);
                }
            }

            if (eventInfo.msgSH >= 0)
            {
                string eventMsg = HEU_SessionManager.GetString(eventInfo.msgSH, session);
                if (!string.IsNullOrEmpty(eventMsg))
                {
                    AddEventMessage(string.Format("<color={0}>{1} - {2}: {3}</color>\n",
                                                  _eventMessageColorCode[(int)msgColor],
                                                  evType,
                                                  topNode._nodeName,
                                                  eventMsg));
                }
            }
            CheckCallback(topNode);
#endif
        }
	/// <summary>
	/// Creates a mesh input node and uploads the mesh data from inputObject.
	/// </summary>
	/// <param name="session">Session that connectNodeID exists in</param>
	/// <param name="connectNodeID">The node to connect the network to. Most likely a SOP/merge node</param>
	/// <param name="inputObject">The gameobject containing the mesh components</param>
	/// <param name="inputNodeID">The created input node ID</param>
	/// <returns>True if created network and uploaded mesh data.</returns>
	public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, HAPI_NodeId connectNodeID, GameObject inputObject, out HAPI_NodeId inputNodeID)
	{
	    inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID;

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

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

	    bool bExportColliders = settings != null && settings.ExportColliders == true;

	    // Get upload meshes from input object
	    HEU_InputDataMeshes inputMeshes = GenerateMeshDatasFromGameObject(inputObject, bExportColliders);
	    if (inputMeshes == null || inputMeshes._inputMeshes == null || inputMeshes._inputMeshes.Count == 0)
	    {
		HEU_Logger.LogError("No valid meshes found on input objects.");
		return false;
	    }

	    string inputName = null;
	    HAPI_NodeId newNodeID = HEU_Defines.HEU_INVALID_NODE_ID;
	    session.CreateInputNode(out newNodeID, inputName);
	    if (newNodeID == HEU_Defines.HEU_INVALID_NODE_ID || !HEU_HAPIUtility.IsNodeValidInHoudini(session, newNodeID))
	    {
		HEU_Logger.LogError("Failed to create new input node in Houdini session!");
		return false;
	    }

	    inputNodeID = newNodeID;

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

		return false;
	    }

	    bool createMergeNode = false;
	    HAPI_NodeId mergeNodeId = HEU_Defines.HEU_INVALID_NODE_ID;

	    if (bExportColliders)
	    {
		createMergeNode = true;
	    }

	    if (!createMergeNode)
	    {
		return true;
	    }

	    HAPI_NodeId parentId = HEU_HAPIUtility.GetParentNodeID(session, newNodeID);

	    if (!session.CreateNode(parentId, "merge", null, false, out mergeNodeId))
	    {
		HEU_Logger.LogErrorFormat("Unable to create merge SOP node for connecting input assets.");
		return false;
	    }

	    if (!session.ConnectNodeInput(mergeNodeId, 0, newNodeID))
	    {
		HEU_Logger.LogErrorFormat("Unable to connect to input node!");
		return false;
	    }

	    if (!session.SetNodeDisplay(mergeNodeId, 1))
	    {
		HEU_Logger.LogWarningFormat("Unable to set display flag!");
	    }

	    inputNodeID = mergeNodeId;

	    if (bExportColliders)
	    {
		if (!UploadColliderData(session, mergeNodeId, inputMeshes, parentId))
		{
		    return false;
		}
	    }

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