Пример #1
0
        /// <summary>
        /// Open given session in a new Houdini instance.
        /// </summary>
        /// <param name="session">Session to open. If null, will use default session.</param>
        /// <returns>True if successfully loaded session</returns>
        public static bool OpenSessionInHoudini(HEU_SessionBase session = null)
        {
            if (session == null || !session.IsSessionValid())
            {
                session = GetOrCreateDefaultSession();
                if (session == null || !session.IsSessionValid())
                {
                    session.SetSessionErrorMsg("No valid session found. Unable to open session in Houdini!", true);
                    return false;
                }
            }

            string HIPName = string.Format("hscene_{0}.hip", System.IO.Path.GetRandomFileName().Replace(".", ""));
            string HIPPath = Application.temporaryCachePath + HEU_Platform.DirectorySeparatorStr + HIPName;

            if (!session.SaveHIPFile(HIPPath, false))
            {
                session.SetSessionErrorMsg("Unable to save session to .hip file at: " + HIPPath, true);
                return false;
            }
            Debug.Log("Saved session to " + HIPPath);

            string HoudiniPath = HEU_PluginSettings.HoudiniDebugLaunchPath;

#if UNITY_EDITOR_OSX
			// On macOS, need to find the actual executable, which is within one of .app folders
			// that Houdini ships with, depending on the installation type.
			// HoudiniPath should by default be pointing to the HFS (Houdini install) folder.
			// Or user should have selected one of the .app folders within.
			
			// If not set to .app, then set it based on what app is available
			if (!HoudiniPath.EndsWith(".app", System.StringComparison.InvariantCulture))
			{
				string[] appNames = { "FX", "Core", "Indie", "Apprentice", "Indie", "Indie Steam Edition" };
				string tryPath;
				foreach(string name in appNames)
				{
					tryPath = HEU_Platform.BuildPath(HoudiniPath, string.Format("Houdini {0} {1}.app", 
						name,
						HEU_HoudiniVersion.HOUDINI_VERSION_STRING));
					if (HEU_Platform.DoesPathExist(tryPath))
					{
						HoudiniPath = tryPath;
						break;
					}
				}
			}

			if (HoudiniPath.EndsWith(".app", System.StringComparison.InvariantCulture))
			{
				// Get the executable name inside the .app, but the executable
				// name is based on the license type, so need to query the 
				// license type and map it to executable name:
				// 	Houdini Apprenctice 18.0.100
				// 	Houdini Core 18.0.100
				// 	Houdini FX 18.0.100
				// 	Houdini Indie 18.0.100
				// 	Houdini Indie Steam Edition 18.0.100
				//HoudiniPath = "/Applications/Houdini/Houdini18.0.100/Houdini Indie 18.0.100.app";
				string hexecutable = "";
				string pattern = @"(.*)/Houdini (.*) (.*).app$";
				Regex reg = new Regex(pattern); 
				Match match = reg.Match(HoudiniPath);
				if (match.Success && match.Groups.Count > 2)
				{
					switch(match.Groups[2].Value)
					{
						case "Apprentice": hexecutable = "happrentice"; break;
						case "Core": hexecutable = "houdinicore"; break;
						case "FX": hexecutable = "houdini"; break;
						case "Indie": hexecutable = "hindie"; break;
						case "Indie Steam Edition": hexecutable = "hindie.steam"; break;
						default: break;
					}
				}

				HoudiniPath += "/Contents/MacOS/" + hexecutable;
			}
#endif

            var HoudiniProcess = new System.Diagnostics.Process();
            HoudiniProcess.StartInfo.FileName = HoudiniPath;
            HoudiniProcess.StartInfo.Arguments = string.Format("\"{0}\"", HIPPath);
            if (!HoudiniProcess.Start())
            {
                session.SetSessionErrorMsg("Unable to start Houdini. Check that the Houdini Debug Exectable path is valid in Plugin Settings.", true);
                HEU_EditorUtility.RevealInFinder(HIPPath);
                return false;
            }

            return true;
        }
        public static void ExportAssetsToGeoFiles(HEU_HoudiniAssetRoot[] rootAssets)
        {
            // Open a Dialog to get user settings:
            //	-directory to write to
            //	-file name with extension (determines file format)

            string             exportExt      = "bgeo.sc";
            List <HEU_GeoNode> outputGeoNodes = new List <HEU_GeoNode>();
            int numNodes = 0;

            int numAssets = rootAssets.Length;

            if (numAssets == 0)
            {
                return;
            }

            string exportDir = EditorSaveFolderPanel("Export Geo to Folder", HEU_PluginSettings.LastExportPath, "");

            if (string.IsNullOrEmpty(exportDir))
            {
                return;
            }

            // Save latest folder choice
            HEU_PluginSettings.LastExportPath = exportDir;

            if (string.IsNullOrEmpty(exportExt))
            {
                Debug.LogErrorFormat("Export extension cannot be empty.");
                return;
            }

            if (!HEU_Platform.DoesDirectoryExist(exportDir) && HEU_Platform.CreateDirectory(exportDir))
            {
                Debug.LogErrorFormat("Error creating directory at {0}.", exportDir);
                return;
            }

            for (int i = 0; i < numAssets; ++i)
            {
                if (rootAssets[i] != null && rootAssets[i]._houdiniAsset != null)
                {
                    HEU_HoudiniAsset asset = rootAssets[i]._houdiniAsset;

                    HEU_SessionBase session = asset.GetAssetSession(true);
                    if (session == null || !session.IsSessionValid())
                    {
                        continue;
                    }

                    if (string.IsNullOrEmpty(asset.AssetName))
                    {
                        Debug.LogErrorFormat("Unable to export output of asset at {0} due to empty name.", asset.AssetPath);
                        continue;
                    }

                    outputGeoNodes.Clear();
                    asset.GetOutputGeoNodes(outputGeoNodes);

                    numNodes = outputGeoNodes.Count;
                    for (int j = 0; j < numNodes; ++j)
                    {
                        string exportPath = string.Format("{0}/{1}_{2}.{3}", exportDir, asset.RootGameObject.name, outputGeoNodes[j].GeoName, exportExt);

                        if (!session.SaveGeoToFile(outputGeoNodes[j].GeoID, exportPath))
                        {
                            Debug.LogErrorFormat("Failed to export output geo of asset with path: {0}", exportPath);
                        }
                        else
                        {
                            Debug.LogFormat("Exported output geo {0} of {1} at: {2}", outputGeoNodes[j].GeoName, asset.RootGameObject.name, exportPath);
                        }
                    }
                }
            }
        }
        private void GenerateTerrain(List <HEU_LoadBufferVolume> terrainBuffers)
        {
            Transform parent = this.gameObject.transform;

            // Directory to store generated terrain files.
            string outputTerrainpath = GetOutputCacheDirectory();

            outputTerrainpath = HEU_Platform.BuildPath(outputTerrainpath, "Terrain");

            int numVolumes = terrainBuffers.Count;

            for (int t = 0; t < numVolumes; ++t)
            {
                if (terrainBuffers[t]._heightMap != null)
                {
                    GameObject newGameObject = new GameObject("heightfield_" + terrainBuffers[t]._tileIndex);
                    Transform  newTransform  = newGameObject.transform;
                    newTransform.parent = parent;

                    HEU_GeneratedOutput generatedOutput = new HEU_GeneratedOutput();
                    generatedOutput._outputData._gameObject = newGameObject;

                    Terrain terrain = HEU_GeneralUtility.GetOrCreateComponent <Terrain>(newGameObject);

#if !HEU_TERRAIN_COLLIDER_DISABLED
                    TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent <TerrainCollider>(newGameObject);
#endif
                    // The TerrainData and TerrainLayer files needs to be saved out if we create them.
                    // Try user specified path, otherwise use the cache folder
                    string exportTerrainDataPath = terrainBuffers[t]._terrainDataExportPath;
                    if (string.IsNullOrEmpty(exportTerrainDataPath))
                    {
                        // This creates the relative folder path from the Asset's cache folder: {assetCache}/{geo name}/Terrain/Tile{tileIndex}/...
                        exportTerrainDataPath = HEU_Platform.BuildPath(outputTerrainpath, HEU_Defines.HEU_FOLDER_TERRAIN, HEU_Defines.HEU_FOLDER_TILE + terrainBuffers[t]._tileIndex);
                    }

                    bool bFullExportTerrainDataPath = HEU_Platform.DoesFileExist(exportTerrainDataPath);

                    if (!string.IsNullOrEmpty(terrainBuffers[t]._terrainDataPath))
                    {
                        // Load the source TerrainData, then make a unique copy of it in the cache folder

                        TerrainData sourceTerrainData = HEU_AssetDatabase.LoadAssetAtPath(terrainBuffers[t]._terrainDataPath, typeof(TerrainData)) as TerrainData;
                        if (sourceTerrainData == null)
                        {
                            Debug.LogWarningFormat("TerrainData, set via attribute, not found at: {0}", terrainBuffers[t]._terrainDataPath);
                        }

                        if (bFullExportTerrainDataPath)
                        {
                            terrain.terrainData = HEU_AssetDatabase.CopyAndLoadAssetAtGivenPath(sourceTerrainData, exportTerrainDataPath, typeof(TerrainData)) as TerrainData;
                        }
                        else
                        {
                            terrain.terrainData = HEU_AssetDatabase.CopyUniqueAndLoadAssetAtAnyPath(sourceTerrainData, exportTerrainDataPath, typeof(TerrainData)) as TerrainData;
                        }

                        if (terrain.terrainData != null)
                        {
                            // Store path so that it can be deleted on clean up
                            AddGeneratedOutputFilePath(HEU_AssetDatabase.GetAssetPath(terrain.terrainData));
                        }
                    }

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

                        if (bFullExportTerrainDataPath)
                        {
                            string folderPath = HEU_Platform.GetFolderPath(exportTerrainDataPath, true);
                            HEU_AssetDatabase.CreatePathWithFolders(folderPath);
                            HEU_AssetDatabase.CreateAsset(terrain.terrainData, exportTerrainDataPath);
                        }
                        else
                        {
                            string assetPathName = "TerrainData" + HEU_Defines.HEU_EXT_ASSET;
                            HEU_AssetDatabase.CreateObjectInAssetCacheFolder(terrain.terrainData, exportTerrainDataPath, null, assetPathName, typeof(TerrainData));
                        }
                    }
                    TerrainData terrainData = terrain.terrainData;

#if !HEU_TERRAIN_COLLIDER_DISABLED
                    collider.terrainData = terrainData;
#endif

                    HEU_TerrainUtility.SetTerrainMaterial(terrain, terrainBuffers[t]._specifiedTerrainMaterialName);

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

                    int heightMapSize = terrainBuffers[t]._heightMapWidth;

                    terrainData.heightmapResolution = heightMapSize;
                    if (terrainData.heightmapResolution != heightMapSize)
                    {
                        Debug.LogErrorFormat("Unsupported terrain size: {0}", heightMapSize);
                        continue;
                    }

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

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

                    terrainData.SetHeights(0, 0, terrainBuffers[t]._heightMap);

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

                    terrainData.size = new Vector3(terrainBuffers[t]._terrainSizeX, heightRange, terrainBuffers[t]._terrainSizeY);

                    terrain.Flush();

                    // Set position
                    HAPI_Transform hapiTransformVolume = new HAPI_Transform(true);
                    hapiTransformVolume.position[0] += terrainBuffers[t]._position[0];
                    hapiTransformVolume.position[1] += terrainBuffers[t]._position[1];
                    hapiTransformVolume.position[2] += terrainBuffers[t]._position[2];
                    HEU_HAPIUtility.ApplyLocalTransfromFromHoudiniToUnity(ref hapiTransformVolume, newTransform);

                    // Set layers
                    Texture2D defaultTexture = HEU_VolumeCache.LoadDefaultSplatTexture();
                    int       numLayers      = terrainBuffers[t]._splatLayers.Count;

#if UNITY_2018_3_OR_NEWER
                    // Create TerrainLayer for each heightfield layer.
                    // Note that height and mask layers are ignored (i.e. not created as TerrainLayers).
                    // Since height layer is first, only process layers from 2nd index onwards.
                    if (numLayers > 1)
                    {
                        // Keep existing TerrainLayers, and either update or append to them
                        TerrainLayer[] existingTerrainLayers = terrainData.terrainLayers;

                        // Total layers are existing layers + new alpha maps
                        List <TerrainLayer> finalTerrainLayers = new List <TerrainLayer>(existingTerrainLayers);

                        for (int m = 1; m < numLayers; ++m)
                        {
                            TerrainLayer terrainlayer = null;

                            int terrainLayerIndex = -1;

                            bool bSetTerrainLayerProperties = true;

                            HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._splatLayers[m];

                            // Look up TerrainLayer file via attribute if user has set it
                            if (!string.IsNullOrEmpty(layer._layerPath))
                            {
                                terrainlayer = HEU_AssetDatabase.LoadAssetAtPath(layer._layerPath, typeof(TerrainLayer)) as TerrainLayer;
                                if (terrainlayer == null)
                                {
                                    Debug.LogWarningFormat("TerrainLayer, set via attribute, not found at: {0}", layer._layerPath);
                                    continue;
                                }
                                else
                                {
                                    // Always check if its part of existing list so as not to add it again
                                    terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(terrainlayer, existingTerrainLayers);
                                }
                            }

                            if (terrainlayer == null)
                            {
                                terrainlayer      = new TerrainLayer();
                                terrainLayerIndex = finalTerrainLayers.Count;
                                finalTerrainLayers.Add(terrainlayer);
                            }
                            else
                            {
                                // For existing TerrainLayer, make a copy of it if it has custom layer attributes
                                // because we don't want to change the original TerrainLayer.
                                if (layer._hasLayerAttributes)
                                {
                                    // Copy the TerrainLayer file
                                    TerrainLayer prevTerrainLayer = terrainlayer;
                                    terrainlayer = HEU_AssetDatabase.CopyAndLoadAssetAtAnyPath(terrainlayer, outputTerrainpath, typeof(TerrainLayer), true) as TerrainLayer;
                                    if (terrainlayer != null)
                                    {
                                        if (terrainLayerIndex >= 0)
                                        {
                                            // Update the TerrainLayer reference in the list with this copy
                                            finalTerrainLayers[terrainLayerIndex] = terrainlayer;
                                        }
                                        else
                                        {
                                            // Newly added
                                            terrainLayerIndex = finalTerrainLayers.Count;
                                            finalTerrainLayers.Add(terrainlayer);
                                        }

                                        // Store path for clean up later
                                        AddGeneratedOutputFilePath(HEU_AssetDatabase.GetAssetPath(terrainlayer));
                                    }
                                    else
                                    {
                                        Debug.LogErrorFormat("Unable to copy TerrainLayer '{0}' for generating Terrain. "
                                                             + "Using original TerrainLayer. Will not be able to set any TerrainLayer properties.", layer._layerName);
                                        terrainlayer = prevTerrainLayer;
                                        bSetTerrainLayerProperties = false;
                                        // Again, continuing on to keep proper indexing.
                                    }
                                }
                                else
                                {
                                    // Could be a layer in Assets/ but not part of existing layers in TerrainData
                                    terrainLayerIndex = finalTerrainLayers.Count;
                                    finalTerrainLayers.Add(terrainlayer);
                                    bSetTerrainLayerProperties = false;
                                }
                            }

                            if (bSetTerrainLayerProperties)
                            {
                                if (!string.IsNullOrEmpty(layer._diffuseTexturePath))
                                {
                                    terrainlayer.diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath);
                                }
                                if (terrainlayer.diffuseTexture == null)
                                {
                                    terrainlayer.diffuseTexture = defaultTexture;
                                }

                                terrainlayer.diffuseRemapMin = Vector4.zero;
                                terrainlayer.diffuseRemapMax = Vector4.one;

                                if (!string.IsNullOrEmpty(layer._maskTexturePath))
                                {
                                    terrainlayer.maskMapTexture = HEU_MaterialFactory.LoadTexture(layer._maskTexturePath);
                                }

                                terrainlayer.maskMapRemapMin = Vector4.zero;
                                terrainlayer.maskMapRemapMax = Vector4.one;

                                terrainlayer.metallic = layer._metallic;

                                if (!string.IsNullOrEmpty(layer._normalTexturePath))
                                {
                                    terrainlayer.normalMapTexture = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath);
                                }

                                terrainlayer.normalScale = layer._normalScale;

                                terrainlayer.smoothness = layer._smoothness;
                                terrainlayer.specular   = layer._specularColor;
                                terrainlayer.tileOffset = layer._tileOffset;

                                if (layer._tileSize.magnitude == 0f && terrainlayer.diffuseTexture != null)
                                {
                                    // Use texture size if tile size is 0
                                    layer._tileSize = new Vector2(terrainlayer.diffuseTexture.width, terrainlayer.diffuseTexture.height);
                                }
                                terrainlayer.tileSize = layer._tileSize;
                            }
                        }
                        terrainData.terrainLayers = finalTerrainLayers.ToArray();
                    }
#else
                    // Need to create SplatPrototype for each layer in heightfield, representing the textures.
                    SplatPrototype[] splatPrototypes = new SplatPrototype[numLayers];
                    for (int m = 0; m < numLayers; ++m)
                    {
                        splatPrototypes[m] = new SplatPrototype();

                        HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._splatLayers[m];

                        Texture2D diffuseTexture = null;
                        if (!string.IsNullOrEmpty(layer._diffuseTexturePath))
                        {
                            diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath);
                        }
                        if (diffuseTexture == null)
                        {
                            diffuseTexture = defaultTexture;
                        }
                        splatPrototypes[m].texture = diffuseTexture;

                        splatPrototypes[m].tileOffset = layer._tileOffset;
                        if (layer._tileSize.magnitude == 0f && diffuseTexture != null)
                        {
                            // Use texture size if tile size is 0
                            layer._tileSize = new Vector2(diffuseTexture.width, diffuseTexture.height);
                        }
                        splatPrototypes[m].tileSize = layer._tileSize;

                        splatPrototypes[m].metallic   = layer._metallic;
                        splatPrototypes[m].smoothness = layer._smoothness;

                        if (!string.IsNullOrEmpty(layer._normalTexturePath))
                        {
                            splatPrototypes[m].normalMap = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath);
                        }
                    }
                    terrainData.splatPrototypes = splatPrototypes;
#endif

                    // Set the splatmaps
                    if (terrainBuffers[t]._splatMaps != null)
                    {
                        // Set the alphamap size before setting the alphamaps to get correct scaling
                        // The alphamap size comes from the first alphamap layer
                        int alphamapResolution = terrainBuffers[t]._heightMapWidth;
                        if (numLayers > 1)
                        {
                            alphamapResolution = terrainBuffers[t]._splatLayers[1]._heightMapWidth;
                        }
                        terrainData.alphamapResolution = alphamapResolution;

                        terrainData.SetAlphamaps(0, 0, terrainBuffers[t]._splatMaps);
                    }

                    // Set the tree scattering
                    if (terrainBuffers[t]._scatterTrees != null)
                    {
                        HEU_TerrainUtility.ApplyScatterTrees(terrainData, terrainBuffers[t]._scatterTrees);
                    }

                    // Set the detail layers
                    if (terrainBuffers[t]._detailPrototypes != null)
                    {
                        HEU_TerrainUtility.ApplyDetailLayers(terrain, terrainData, terrainBuffers[t]._detailProperties,
                                                             terrainBuffers[t]._detailPrototypes, terrainBuffers[t]._detailMaps);
                    }

                    terrainBuffers[t]._generatedOutput = generatedOutput;
                    _generatedOutputs.Add(generatedOutput);

                    SetOutputVisiblity(terrainBuffers[t]);
                }
            }
        }
		public static string AppendPrefabPath(string inAssetCacheFolder, string assetName)
		{
			string prefabPath = HEU_Platform.BuildPath(inAssetCacheFolder, assetName);
			return prefabPath + ".prefab";
		}
Пример #5
0
		public static string AppendMaterialsPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, "Materials");
		}
		public static string AppendMeshesPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, HEU_Defines.HEU_FOLDER_MESHES);
		}
		public static string AppendMaterialsPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, HEU_Defines.HEU_FOLDER_MATERIALS);
		}
		public void GenerateTerrainWithAlphamaps(HEU_SessionBase session, HEU_HoudiniAsset houdiniAsset, bool bRebuild)
		{
			if(_layers == null || _layers.Count == 0)
			{
				Debug.LogError("Unable to generate terrain due to lack of heightfield layers!");
				return;
			}

			HEU_VolumeLayer heightLayer = _layers[0];

			HAPI_VolumeInfo heightVolumeInfo = new HAPI_VolumeInfo();
			bool bResult = session.GetVolumeInfo(_ownerNode.GeoID, heightLayer._part.PartID, ref heightVolumeInfo);
			if (!bResult)
			{
				Debug.LogErrorFormat("Unable to get volume info for height layer: {0}!", heightLayer._layerName);
				return;
			}

			// Special handling of volume cache presets. It is applied here (if exists) because it might pertain to TerrainData that exists
			// in the AssetDatabase. If we don't apply here but rather create a new one, the existing file will get overwritten.
			// Applying the preset here for terrain ensures the TerrainData is reused.
			// Get the volume preset for this part
			HEU_VolumeCachePreset volumeCachePreset = houdiniAsset.GetVolumeCachePreset(_ownerNode.ObjectNode.ObjectName, _ownerNode.GeoName, TileIndex);
			if (volumeCachePreset != null)
			{
				ApplyPreset(volumeCachePreset);

				// Remove it so that it doesn't get applied when doing the recook step
				houdiniAsset.RemoveVolumeCachePreset(volumeCachePreset);
			}

			// The TerrainData and TerrainLayer files needs to be saved out if we create them. This creates the relative folder
			// path from the Asset's cache folder: {assetCache}/{geo name}/Terrain/Tile{tileIndex}/...
			string relativeFolderPath = HEU_Platform.BuildPath(_ownerNode.GeoName, HEU_Defines.HEU_FOLDER_TERRAIN, HEU_Defines.HEU_FOLDER_TILE + TileIndex);

			if (bRebuild)
			{
				// For full rebuild, re-create the TerrainData instead of using previous
				_terrainData = null;
			}

			//Debug.Log("Generating Terrain with AlphaMaps: " + (_terrainData != null ? _terrainData.name : "NONE"));
			TerrainData terrainData = _terrainData;
			Vector3 terrainOffsetPosition = Vector3.zero;

			// Look up TerrainData file via attribute if user has set it
			string terrainDataFile = HEU_GeneralUtility.GetAttributeStringValueSingle(session, _ownerNode.GeoID, heightLayer._part.PartID,
				HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TERRAINDATA_FILE_ATTR, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM);
			if (!string.IsNullOrEmpty(terrainDataFile))
			{
				TerrainData loadedTerrainData = HEU_AssetDatabase.LoadAssetAtPath(terrainDataFile, typeof(TerrainData)) as TerrainData;
				if (loadedTerrainData == null)
				{
					Debug.LogWarningFormat("TerrainData, set via attribute, not found at: {0}", terrainDataFile);
				}
				else
				{
					// In the case that the specified TerrainData belongs to another Terrain (i.e. input Terrain), 
					// make a copy of it and store it in our cache. Note that this overwrites existing TerrainData in our cache
					// because the workflow is such that attributes will always override local setting.
					string bakedTerrainPath = houdiniAsset.GetValidAssetCacheFolderPath();
					bakedTerrainPath = HEU_Platform.BuildPath(bakedTerrainPath, relativeFolderPath);
					terrainData = HEU_AssetDatabase.CopyAndLoadAssetAtAnyPath(loadedTerrainData, bakedTerrainPath, typeof(TerrainData), true) as TerrainData;
					if (terrainData == null)
					{
						Debug.LogErrorFormat("Unable to copy TerrainData from {0} for generating Terrain.", terrainDataFile);
					}
				}
			}

			Terrain terrain = null;

			// Generate the terrain and terrain data from the height layer. This applies height values.
			bResult = HEU_TerrainUtility.GenerateTerrainFromVolume(session, ref heightVolumeInfo, heightLayer._part.ParentGeoNode.GeoID,
				heightLayer._part.PartID, heightLayer._part.OutputGameObject, ref terrainData, out terrainOffsetPosition,
				ref terrain);
			if (!bResult || terrainData == null)
			{
				return;
			}

			if (_terrainData != terrainData)
			{
				_terrainData = terrainData;
				heightLayer._part.SetTerrainData(terrainData, relativeFolderPath);
			}

			heightLayer._part.SetTerrainOffsetPosition(terrainOffsetPosition);

			int terrainSize = terrainData.heightmapResolution;

			// Now process TerrainLayers and alpha maps

			// First, preprocess all layers to get heightfield arrays, converted to proper size
			List<float[]> normalizedHeightfields = new List<float[]>();
			// Corresponding list of HF volume layers to process as splatmaps
			List<HEU_VolumeLayer> terrainLayersToProcess = new List<HEU_VolumeLayer>();

			List<int[,]> convertedDetailMaps = new List<int[,]>();
			List<HEU_DetailPrototype> detailPrototypes = new List<HEU_DetailPrototype>();

			int numLayers = _layers.Count;
			float minHeight = 0;
			float maxHeight = 0;
			float heightRange = 0;
			// This skips the height layer, and processes all other layers.
			// Note that mask shouldn't be part of _layers at this point.
			// The layers are normalized and split into detail and terrain layers.
			for(int i = 1; i < numLayers; ++i)
			{
				if (_layers[i]._layerType == HFLayerType.DETAIL)
				{
					if (_detailProperties == null)
					{
						_detailProperties = new HEU_DetailProperties();
					}

					// Convert to detail map, and add to list to set later
					int[,] normalizedDetail = HEU_TerrainUtility.GetDetailMapFromPart(
						session, _ownerNode.GeoID, _layers[i]._part.PartID, out _detailProperties._detailResolution);
					if (normalizedDetail != null && normalizedDetail.Length > 0)
					{
						convertedDetailMaps.Add(normalizedDetail);
						detailPrototypes.Add(_layers[i]._detailPrototype);
					}
				}
				else
				{
					// Convert to normalized heightfield
					float[] normalizedHF = HEU_TerrainUtility.GetNormalizedHeightmapFromPartWithMinMax(
						session, _ownerNode.GeoID, _layers[i]._part.PartID, _layers[i]._xLength, 
						_layers[i]._yLength, ref minHeight, ref maxHeight, ref heightRange, false);
					if (normalizedHF != null && normalizedHF.Length > 0)
					{
						normalizedHeightfields.Add(normalizedHF);
						terrainLayersToProcess.Add(_layers[i]);
					}
				}
			}

			int numTerrainLayersToProcess = terrainLayersToProcess.Count;

			HAPI_NodeId geoID;
			HAPI_PartId partID;

			Texture2D defaultTexture = LoadDefaultSplatTexture();

#if UNITY_2018_3_OR_NEWER

			// Create or update the terrain layers based on heightfield layers.

			// Keep existing TerrainLayers, and either update or append to them
			TerrainLayer[] existingTerrainLayers = terrainData.terrainLayers;

			// Total layers are existing layers + new alpha maps
			List<TerrainLayer> finalTerrainLayers = new List<TerrainLayer>(existingTerrainLayers);

			// This holds the alpha map indices for each layer that will be added to the TerrainData.
			// The alpha maps could be a mix of existing and new values, so need to know which to use
			// Initially set to use existing alpha maps, then override later on if specified via HF layers
			List<int> alphaMapIndices = new List<int>();
			for (int a = 0; a < existingTerrainLayers.Length; ++a)
			{
				// Negative indices for existing alpha map (offset by -1)
				alphaMapIndices.Add(-a - 1);
			}

			bool bNewTerrainLayer = false;
			HEU_VolumeLayer layer = null;
			TerrainLayer terrainLayer = null;
			bool bSetTerrainLayerProperties = true;
			for (int m = 0; m < numTerrainLayersToProcess; ++m)
			{
				bNewTerrainLayer = false;
				bSetTerrainLayerProperties = true;

				layer = terrainLayersToProcess[m];

				geoID = _ownerNode.GeoID;
				partID = layer._part.PartID;

				terrainLayer = null;

				int terrainLayerIndex = -1;

				// The TerrainLayer attribute overrides existing TerrainLayer. So if its set, load and use it.
				string terrainLayerFile = HEU_GeneralUtility.GetAttributeStringValueSingle(session, geoID, partID,
						HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TERRAINLAYER_FILE_ATTR, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM);
				if (!string.IsNullOrEmpty(terrainLayerFile))
				{
					terrainLayer = HEU_AssetDatabase.LoadAssetAtPath(terrainLayerFile, typeof(TerrainLayer)) as TerrainLayer;
					if (terrainLayer == null)
					{
						Debug.LogWarningFormat("TerrainLayer, set via attribute, not found at: {0}", terrainLayerFile);
						// Not earlying out or skipping this layer due to error because we want to keep proper indexing
						// by creating a new TerrainLayer.
					}
					else
					{
						// TerrainLayer loaded from attribute. 
						// It could be an existing TerrainLayer that is already part of finalTerrainLayers 
						// or could be a new one which needs to be added.

						// If its a different TerrainLayer than existing, update the finalTerrainLayers, and index.
						if (layer._terrainLayer != null && layer._terrainLayer != terrainLayer)
						{
							terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(layer._terrainLayer, existingTerrainLayers);
							if (terrainLayerIndex >= 0)
							{
								finalTerrainLayers[terrainLayerIndex] = terrainLayer;
							}
						}

						if (terrainLayerIndex == -1)
						{
							// Always check if its part of existing list so as not to add it again
							terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(terrainLayer, existingTerrainLayers);
						}
					}
				}

				// No terrain layer specified, so try using existing if we have it
				if (terrainLayer == null)
				{
					terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(layer._terrainLayer, existingTerrainLayers);
					if (terrainLayerIndex >= 0)
					{
						// Note the terrainLayerIndex is same for finalTerrainLayers as existingTerrainLayers
						terrainLayer = existingTerrainLayers[terrainLayerIndex];
					}
				}

				// Still not found, so just create a new one
				if (terrainLayer == null)
				{
					terrainLayer = new TerrainLayer();
					terrainLayer.name = layer._layerName;
					//Debug.LogFormat("Created new TerrainLayer with name: {0} ", terrainLayer.name);
					bNewTerrainLayer = true;
				}

				if (terrainLayerIndex == -1)
				{
					// Adding to the finalTerrainLayers if this is indeed a newly created or loaded TerrainLayer
					// (i.e. isn't already part of the TerrainLayers for this Terrain).
					// Save this layer's index for later on if we make a copy.
					terrainLayerIndex = finalTerrainLayers.Count;
					finalTerrainLayers.Add(terrainLayer);

					// Positive index for alpha map from heightfield (starting at 1)
					alphaMapIndices.Add(m + 1);
				}
				else
				{
					// Positive index for alpha map from heightfield (starting at 1)
					alphaMapIndices[terrainLayerIndex] = m + 1;
				}

				// For existing TerrainLayer, make a copy of it if it has custom layer attributes
				// because we don't want to change the original TerrainLayer.
				if (!bNewTerrainLayer && layer._hasLayerAttributes)
				{
					string bakedTerrainPath = houdiniAsset.GetValidAssetCacheFolderPath();
					bakedTerrainPath = HEU_Platform.BuildPath(bakedTerrainPath, relativeFolderPath);
					TerrainLayer prevTerrainLayer = terrainLayer;
					terrainLayer = HEU_AssetDatabase.CopyAndLoadAssetAtAnyPath(terrainLayer, bakedTerrainPath, typeof(TerrainLayer), true) as TerrainLayer;
					if (terrainLayer != null)
					{
						// Update the TerrainLayer reference in the list with this copy
						finalTerrainLayers[terrainLayerIndex] = terrainLayer;
					}
					else
					{
						Debug.LogErrorFormat("Unable to copy TerrainLayer '{0}' for generating Terrain. "
							+ "Using original TerrainLayer. Will not be able to set any TerrainLayer properties.", layer._layerName);
						terrainLayer = prevTerrainLayer;
						bSetTerrainLayerProperties = false;
						// Again, continuing on to keep proper indexing.
					}
				}

				// Now override layer properties if they have been set via attributes
				if (bSetTerrainLayerProperties)
				{
					LoadLayerPropertiesFromAttributes(session, geoID, partID, terrainLayer, bNewTerrainLayer, defaultTexture);
				}

				if (bNewTerrainLayer)
				{
					// In order to retain the new TerrainLayer, it must be saved to the AssetDatabase.
					Object savedObject = null;
					string layerFileNameWithExt = terrainLayer.name;
					if (!layerFileNameWithExt.EndsWith(HEU_Defines.HEU_EXT_TERRAINLAYER))
					{
						layerFileNameWithExt += HEU_Defines.HEU_EXT_TERRAINLAYER;
					}
					houdiniAsset.AddToAssetDBCache(layerFileNameWithExt, terrainLayer, relativeFolderPath, ref savedObject);
				}

				// Store reference
				layer._terrainLayer = terrainLayer;
			}

			// Get existing alpha maps so we can reuse the values if needed
			float[,,] existingAlphaMaps = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight);

			terrainData.terrainLayers = finalTerrainLayers.ToArray();

			int numTotalAlphaMaps = finalTerrainLayers.Count;

#else
			// Create or update the SplatPrototype based on heightfield layers.

			// Need to create or reuse SplatPrototype for each layer in heightfield, representing the textures.
			SplatPrototype[] existingSplats = terrainData.splatPrototypes;

			// A full rebuild clears out existing splats, but a regular cook keeps them.
			List<SplatPrototype> finalSplats = new List<SplatPrototype>(existingSplats);

			// This holds the alpha map indices for each layer that will be added to the TerrainData
			// The alpha maps could be a mix of existing and new values, so need to know which to use
			List<int> alphaMapIndices = new List<int>();

			// Initially set to use existing alpha maps, then override later on if specified via HF layers.
			for (int a = 0; a < existingSplats.Length; ++a)
			{
				// Negative indices for existing alpha map (offset by -1)
				alphaMapIndices.Add(-a - 1);
			}

			bool bNewSplat = false;
			HEU_VolumeLayer layer = null;
			SplatPrototype splatPrototype = null;

			for (int m = 0; m < numTerrainLayersToProcess; ++m)
			{
				bNewSplat = false;

				layer = terrainLayersToProcess[m];

				geoID = _ownerNode.GeoID;
				partID = layer._part.PartID;

				// Try to find existing SplatPrototype for reuse. But not for full rebuild.
				splatPrototype = null;
				if (layer._splatPrototypeIndex >= 0 && layer._splatPrototypeIndex < existingSplats.Length)
				{
					splatPrototype = existingSplats[layer._splatPrototypeIndex];

					// Positive index for alpha map from heightfield (starting at 1)
					alphaMapIndices[layer._splatPrototypeIndex] = m + 1;
				}

				if (splatPrototype == null)
				{
					splatPrototype = new SplatPrototype();
					layer._splatPrototypeIndex = finalSplats.Count;
					finalSplats.Add(splatPrototype);

					// Positive index for alpha map from heightfield (starting at 1)
					alphaMapIndices.Add(m + 1);
				}

				// Now override splat properties if they have been set via attributes
				LoadLayerPropertiesFromAttributes(session, geoID, partID, splatPrototype, bNewSplat, defaultTexture);
			}

			// On regular cook, get existing alpha maps so we can reuse the values if needed.
			float[,,] existingAlphaMaps = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight);

			terrainData.splatPrototypes = finalSplats.ToArray();

			int numTotalAlphaMaps = finalSplats.Count;
#endif

			// Set alpha maps by combining with existing alpha maps, and appending new heightfields

			float[,,] alphamap = null;
			if (numTotalAlphaMaps > 0 && terrainLayersToProcess.Count > 0)
			{
				// Convert the heightfields into alpha maps with layer strengths
				float[] strengths = new float[terrainLayersToProcess.Count];
				for (int m = 0; m < terrainLayersToProcess.Count; ++m)
				{
					strengths[m] = terrainLayersToProcess[m]._strength;
				}

				alphamap = HEU_TerrainUtility.AppendConvertedHeightFieldToAlphaMap(
					terrainLayersToProcess[0]._xLength, terrainLayersToProcess[0]._yLength, existingAlphaMaps,
					normalizedHeightfields, strengths, alphaMapIndices);

				// Update the alphamap resolution to the actual size of the first 
				// heightfield layer used for the alphamaps.
				// Setting the size before setting the alphamas applies proper scaling.
				int alphamapResolution = terrainLayersToProcess[0]._xLength;
				terrainData.alphamapResolution = alphamapResolution;

				terrainData.SetAlphamaps(0, 0, alphamap);
			}

			// Scattering - trees and details
			HEU_TerrainUtility.ApplyScatterTrees(terrainData, _scatterTrees);
			HEU_TerrainUtility.ApplyDetailLayers(terrain, terrainData, _detailProperties, detailPrototypes, convertedDetailMaps);

			// If the layers were writen out, this saves the asset DB. Otherwise user has to save it themselves.
			// Not 100% sure this is needed, but without this the editor doesn't know the terrain asset has been updated
			// and therefore doesn't import and show the terrain layer.
			HEU_AssetDatabase.SaveAssetDatabase();
		}
	public static Texture2D RenderAndExtractImageToTexture(HEU_SessionBase session, HAPI_MaterialInfo materialInfo, HAPI_ParmId textureParmID, string textureName, string assetCacheFolderPath, bool isNormalMap, bool invertTexture = false)
	{
	    //HEU_Logger.LogFormat("Rendering texture {0} with name {1} for material {2} at path {3}", textureParmID, textureName, materialInfo.nodeId, assetCacheFolderPath);

	    Texture2D texture = null;

	    // First we get Houdini to render the texture to an image buffer, then query the buffer over HAPI
	    // Next we convert to PNG, and write out to file in our Assets directory
	    // The reason for querying as a buffer is to workaround a bug with ExtractHoudiniImageToTextureFile 
	    // Note: intentionly ignoring any errors as sometimes there aren't any textures
	    if (session.RenderTextureToImage(materialInfo.nodeId, textureParmID, false))
	    {
		texture = HEU_MaterialFactory.ExtractHoudiniImageToTextureRaw(session, materialInfo, "C A");
		if (texture != null)
		{
		    texture.name = textureName;

		    // Note: Should I make this a plugin option for roughness?
		    if (invertTexture)
		    {
			Color[] pixels = texture.GetPixels();
			for (int i = 0; i < pixels.Length; i++)
			{
			    pixels[i].r = 1 - pixels[i].r;
			    pixels[i].g = 1 - pixels[i].g;
			    pixels[i].b = 1 - pixels[i].b;
			}

			texture.SetPixels(pixels);

			texture.Apply();
		    }


		    // Get the Textures folder in the assetCacheFolderPath. Make sure it exists.
		    assetCacheFolderPath = HEU_AssetDatabase.AppendTexturesPathToAssetFolder(assetCacheFolderPath);
		    HEU_AssetDatabase.CreatePathWithFolders(assetCacheFolderPath);

		    // We are defaulting to PNG here if no extension already set. This forces it to use PNG format below.
		    if (!textureName.EndsWith(".png") && !textureName.EndsWith(".jpg") && !textureName.EndsWith(".tga") && !textureName.EndsWith(".exr"))
		    {
			textureName = textureName + ".png";
		    }

		    string textureFileName = HEU_Platform.BuildPath(assetCacheFolderPath, string.Format("{0}", textureName));

		    byte[] encodedBytes;
		    if (textureName.EndsWith(".jpg"))
		    {
			encodedBytes = texture.EncodeToJPG();
		    }
		    else if (textureName.EndsWith(".tga"))
		    {
			encodedBytes = texture.EncodeToTGA();
		    }
		    else if (textureName.EndsWith(".exr"))
		    {
			encodedBytes = texture.EncodeToEXR();
		    }
		    else // Use PNG otherwise
		    {
			encodedBytes = texture.EncodeToPNG();
		    }
		    HEU_Platform.WriteBytes(textureFileName, encodedBytes);

		    // Re-import for project to recognize the new texture file
		    HEU_AssetDatabase.ImportAsset(textureFileName, HEU_AssetDatabase.HEU_ImportAssetOptions.Default);

		    if (isNormalMap)
		    {
			HEU_EditorUtility.SetTextureToNormalMap(textureFileName);
		    }

		    // Load the new texture file
		    texture = HEU_AssetDatabase.LoadAssetAtPath(textureFileName, typeof(Texture2D)) as Texture2D;
		}

		//texture = HEU_MaterialFactory.ExtractHoudiniImageToTextureFile(session, materialInfo, "C A", assetCacheFolderPath);
	    }
	    return texture;
	}
Пример #10
0
	/// <summary>
	/// Given relative path to an asset (with Assets/ or Packages/ in the path), this returns the full path to it.
	/// </summary>
	/// <param name="inPath">Relative path to parse</param>
	/// <returns>Returns full path to asset, or null if invalid input path</returns>
	public static string GetAssetFullPath(string inPath)
	{
	    return HEU_Platform.GetFullPath(inPath);
	}
Пример #11
0
		public static bool LoadToolFromJsonString(string json)
		{
			// Get environment variable for tool path
			string envValue = HEU_Platform.GetEnvironmentValue(HEU_Defines.HEU_PATH_KEY_TOOL);
			string envKey = string.Format("<{0}>", HEU_Defines.HEU_PATH_KEY_TOOL);

			if (!string.IsNullOrEmpty(json))
			{
				HEU_ShelfToolData toolData = JsonUtility.FromJson<HEU_ShelfToolData>(json);

				if(toolData != null 
					&& !string.IsNullOrEmpty(toolData.name) 
					&& !string.IsNullOrEmpty(toolData.assetPath)
					&& !string.IsNullOrEmpty(toolData.iconPath))
				{
					// Make sure this tool targets Unity (must have "all" or "unity" set in target field)
					bool bCompatiple = false;
					if(toolData.target != null)
					{
						int numTargets = toolData.target.Length;
						for(int i = 0; i < numTargets; ++i)
						{
							if (toolData.target[i].Equals(TARGET_ALL))
							{
								bCompatiple = true;
								break;
							}
							else if(toolData.target[i].Equals(TARGET_UNITY))
							{
								bCompatiple = true;
								break;
							}
						}
					}

					if (bCompatiple)
					{
						toolData.assetPath = toolData.assetPath.Replace(HEU_Defines.HEU_PATH_KEY_PROJECT + "/", "");
						toolData.iconPath = toolData.iconPath.Replace(HEU_Defines.HEU_PATH_KEY_PROJECT + "/", "");

						if(toolData.assetPath.Contains(envKey))
						{
							if(string.IsNullOrEmpty(envValue))
							{
								Debug.LogErrorFormat("Environment value {0} used but not set in environment.", HEU_Defines.HEU_PATH_KEY_TOOL);
							}
							else
							{
								toolData.assetPath = toolData.assetPath.Replace(envKey, envValue);
							}
						}

						if (toolData.iconPath.Contains(envKey))
						{
							if (string.IsNullOrEmpty(envValue))
							{
								Debug.LogErrorFormat("Environment value {0} used but not set in environment.", HEU_Defines.HEU_PATH_KEY_TOOL);
							}
							else
							{
								toolData.iconPath = toolData.iconPath.Replace(envKey, envValue);
							}
						}

						_tools.Add(toolData);
						Debug.LogFormat("Added tool: {0}", toolData.name);
					}
				}

				return true;
			}

			return false;
		}
Пример #12
0
	/// <summary>
	/// Create the given object inside the asset cache folder path, with relative folder path.
	/// Depending on type, it might store in a subfolder for organizational purposes.
	/// </summary>
	/// <param name="objectToCreate">The object to create inside the asset cache</param>
	/// <param name="assetCacheRoot">The target path in the asset cache</param>
	/// <param name="relativeFolderPath">If not null or empty, the relative path to append to the assetCacheRoot. 
	/// Otherwise uses type of asset to get subfolder name.</param>
	/// <param name="assetFileName">The asset's file name</param>
	/// <param name="type">The type of asset</param>
	/// <param name="bOverwriteExisting">Whether or not to overwrite if there is an existing file</param>
	public static void CreateObjectInAssetCacheFolder(Object objectToCreate, string assetCacheRoot, string relativeFolderPath, string assetFileName, System.Type type, bool bOverwriteExisting)
	{
#if UNITY_EDITOR
	    Debug.Assert(!string.IsNullOrEmpty(assetCacheRoot), "Must give valid assetCacheFolderPath to create object at");

	    string subFolderPath = assetCacheRoot;

	    if (!string.IsNullOrEmpty(relativeFolderPath))
	    {
		subFolderPath = HEU_Platform.BuildPath(subFolderPath, relativeFolderPath);
	    }
	    else
	    {
		if (type == typeof(Mesh))
		{
		    subFolderPath = AppendMeshesPathToAssetFolder(assetCacheRoot);
		}
		else if (type == typeof(Material))
		{
		    subFolderPath = AppendMaterialsPathToAssetFolder(assetCacheRoot);
		}
		else if (type == typeof(TerrainData)
#if UNITY_2018_3_OR_NEWER
				|| (type == typeof(TerrainLayer))
#else
				|| (type == typeof(SplatPrototype))
#endif
				)
		{
		    subFolderPath = AppendTerrainPathToAssetFolder(assetCacheRoot);
		}
	    }

	    // Make sure subfolders exist
	    HEU_AssetDatabase.CreatePathWithFolders(subFolderPath);

	    // Add file name
	    string finalAssetPath = HEU_Platform.BuildPath(subFolderPath, assetFileName);

	    if (HEU_Platform.DoesFileExist(finalAssetPath) && !bOverwriteExisting)
	    {
		finalAssetPath = AssetDatabase.GenerateUniqueAssetPath(finalAssetPath);
	    }

	    if (AssetDatabase.Contains(objectToCreate))
	    {
		AssetDatabase.SaveAssets();
	    }
	    else
	    {
	        AssetDatabase.CreateAsset(objectToCreate, finalAssetPath);
	    }

	    // Commented out AssetDatabase.Refresh() below because its slow and seems to be unnecessary.
	    // Leaving it commented in case need to revisit due to problems with asset creation.
	    //RefreshAssetDatabase();
#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);
#endif
	}
        /// <summary>
        /// Returns true if file or folder at inPath exists. Supports inPath with environment mapped values
        /// such as <HFS> or $.
        /// </summary>
        /// <param name="inPath">Path to check. Could be either file or folder.</param>
        /// <returns>True if file or folder exists</returns>
        public static bool DoesMappedPathExist(string inPath)
        {
            string realPath = HEU_PluginStorage.Instance.ConvertEnvKeyedPathToReal(inPath);

            return(HEU_Platform.DoesPathExist(realPath));
        }
Пример #14
0
		public static string AppendTerrainPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, "Terrain");
		}
Пример #15
0
		public void OnGUI()
		{
			if (!_initializedUI)
			{
				// Creating of UI elements must happen in OnGUI
				InitializeUIElements();
			}

			bool bChanged = false;

			Color originalBGColor = GUI.backgroundColor;

			bool bRequiresLoad = !HEU_ShelfTools.AreShelvesLoaded();
			if(!bRequiresLoad)
			{
				// Sanity check that textures are still valid. When scene changes, these get invalidated.
				if (_guiContents != null && _guiContents.Length > 0)
				{
					bRequiresLoad = (_guiContents[0].image == null);
				}
			}
			
			if(bRequiresLoad)
			{
				LoadShelves();
			}

			int numTools = 0;

			using (new EditorGUILayout.VerticalScope())
			{
				using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
				{
					if (HEU_ShelfTools.AreShelvesLoaded())
					{
						int currentShelfIndex = HEU_ShelfTools.GetCurrentShelfIndex();

						HEU_Shelf shelf = null;

						using (new EditorGUILayout.HorizontalScope())
						{
							GUILayout.FlexibleSpace();

							if (GUILayout.Button(_addButton, _buttonStyle, GUILayout.MaxWidth(_buttonWidth), GUILayout.MaxHeight(_buttonHeight)))
							{
								string newShelfPath = UnityEditor.EditorUtility.OpenFolderPanel("Add Shelf Folder", "", "");
								if (!string.IsNullOrEmpty(newShelfPath) && HEU_Platform.DoesDirectoryExist(newShelfPath))
								{
									AddNewShelfWindow(newShelfPath);
									bChanged = true;
								}
							}
						}

						using (new EditorGUILayout.HorizontalScope())
						{
							GUILayout.Label("Active Shelf");

							int newShelfIndex = EditorGUILayout.Popup(currentShelfIndex, _shelfNames, _popupStyle);
							if (currentShelfIndex != newShelfIndex)
							{
								// Change shelf
								currentShelfIndex = newShelfIndex;
								HEU_ShelfTools.SetCurrentShelf(currentShelfIndex);
								SelectShelf(currentShelfIndex);
							}

							shelf = HEU_ShelfTools.GetShelf(currentShelfIndex);
							numTools = shelf._tools.Count;

							using (new EditorGUI.DisabledGroupScope(shelf._defaultShelf))
							{
								if (GUILayout.Button(_removeButton, _buttonStyle, GUILayout.MaxWidth(_buttonWidth)))
								{
									HEU_ShelfTools.RemoveShelf(currentShelfIndex);

									HEU_ShelfTools.SaveShelf();
									HEU_ShelfTools.SetReloadShelves();
									bChanged = true;
								}
							}
						}


						HEU_EditorUI.DrawSeparator();

						if (!bChanged)
						{
							using (EditorGUILayout.ScrollViewScope scroll = new EditorGUILayout.ScrollViewScope(_toolButtonScrollPos))
							{
								if (numTools > 0)
								{
									int numXElements = numTools < _toolGridXElements ? numTools : _toolGridXElements;

									_selectedToolIndex = GUILayout.SelectionGrid(_selectedToolIndex, _guiContents, numXElements, _toolGridStyle);
								}
								else
								{
									EditorGUILayout.LabelField("No tools found!");
								}

								_toolButtonScrollPos = scroll.scrollPosition;
							}
						}
					}
				}
				
				bool bValidSelection = (_selectedToolIndex >= 0 && _selectedToolIndex < numTools);
				using (new EditorGUI.DisabledGroupScope(!bValidSelection))
				{
					if(!bValidSelection)
					{
						_applyButton.text = "Select a Tool!";
					}
					else
					{
						GameObject[] selectedObjects = HEU_EditorUtility.GetSelectedObjects();
						if(selectedObjects.Length == 0)
						{
							_applyButton.text = "Create Tool (no input selected)!";
						}
						else
						{
							_applyButton.text = "Create Tool (selected objects as inputs)!";
						}
					}

					if (GUILayout.Button(_applyButton, _buttonStyle, GUILayout.MaxHeight(_buttonHeight)))
					{
						ProcessUserSelection(_selectedToolIndex);
					}
				}
			}
		}
Пример #16
0
	/// <summary>
	/// Load the saved plugin settings from disk.
	/// </summary>
	/// <returns>True if successfully loaded.</returns>
	public bool LoadPluginData()
	{
	    // First check if settings pref file exists
	    string settingsFilePath = SettingsFilePath();
	    if (!HEU_Platform.DoesFileExist(settingsFilePath))
	    {
		// Try reading from EditorPrefs to see if this is still using the old method
		return ReadFromEditorPrefs();
	    }

	    // Open file and read each line to create the settings entry
	    using (StreamReader reader = new StreamReader(settingsFilePath))
	    {
		// Must match first line
		string line = reader.ReadLine();
		if (string.IsNullOrEmpty(line) || !line.Equals(PluginSettingsLine1))
		{
		    Debug.LogWarningFormat("Unable to load Plugin settings file. {0} should have line 1: {1}", settingsFilePath, PluginSettingsLine1);
		    return false;
		}

		// Must match 2nd line
		line = reader.ReadLine();
		if (string.IsNullOrEmpty(line) || !line.StartsWith(PluginSettingsLine2))
		{
		    Debug.LogWarningFormat("Unable to load Plugin settings file. {0} should start line 2 with: {1}", settingsFilePath, PluginSettingsLine2);
		    return false;
		}

		Dictionary<string, StoreData> storeMap = new Dictionary<string, StoreData>();
		string keyStr;
		string typeStr;
		string valueStr;
		DataType dataType;
		// "key(type)=value"
		System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"^(\w+)\((\w+)\)=(.*)");
		while ((line = reader.ReadLine()) != null)
		{
		    System.Text.RegularExpressions.Match match = regex.Match(line);
		    if (match.Success && match.Groups.Count >= 4)
		    {
			keyStr = match.Groups[1].Value;
			typeStr = match.Groups[2].Value;
			valueStr = match.Groups[3].Value;

			if (!string.IsNullOrEmpty(keyStr) && !string.IsNullOrEmpty(typeStr)
				&& !string.IsNullOrEmpty(valueStr))
			{
			    try
			    {
				dataType = (DataType)System.Enum.Parse(typeof(DataType), typeStr);

				StoreData store = new StoreData();
				store._type = dataType;
				store._valueStr = valueStr;
				storeMap.Add(keyStr, store);
			    }
			    catch (System.Exception ex)
			    {
				Debug.LogErrorFormat("Invalid data type found in settings: {0}. Exception: {1}", typeStr, ex.ToString());
			    }
			}
		    }
		}

		_dataMap = storeMap;
	    }

	    return true;
	}
		/// <summary>
		/// Loads a copy of the srcAsset, or if copy is not found, creates the copy and loads it.
		/// The copy is expected to be located at newAssetFolderPath/relativePath.
		/// If relativePath is null or empty, uses the srcAsset type to acquire the subfolder if the type requires it.
		/// </summary>
		/// <param name="srcAsset">Source asset whose copy will be loaded (and created if no copy exists).</param>
		/// <param name="copyAssetFolder">Asset's root folder to look for the copy or create in</param>
		/// <param name="relativePath">If not null or empty, the relative path to append to the newAssetFolderPath. 
		/// Otherwise uses type of asset to subfolder name.</param>
		/// <param name="type">Type of asset</param>
		/// <returns>Returns loaded copy if exists or created, otherwise null</returns>
		public static Object CopyAndLoadAssetWithRelativePath(Object srcAsset, string copyAssetFolder, string relativePath, System.Type type, bool bOverwriteExisting)
		{
#if UNITY_EDITOR
			string srcAssetPath = GetAssetPath(srcAsset);
			if (!string.IsNullOrEmpty(srcAssetPath) && IsPathInAssetCache(srcAssetPath))
			{
				string copyAssetFullPath = copyAssetFolder;

				if (!string.IsNullOrEmpty(relativePath))
				{
					copyAssetFullPath = HEU_Platform.BuildPath(copyAssetFullPath, relativePath);
				}
				else
				{
					if (type == typeof(Material))
					{
						copyAssetFullPath = AppendMaterialsPathToAssetFolder(copyAssetFolder);
					}
					else if (type == typeof(Texture))
					{
						copyAssetFullPath = AppendTexturesPathToAssetFolder(copyAssetFolder);
					}
					else if (type == typeof(Mesh))
					{
						copyAssetFullPath = AppendMeshesPathToAssetFolder(copyAssetFolder);
					}
					else if (type == typeof(TerrainData)
#if UNITY_2018_3_OR_NEWER
						|| (type == typeof(TerrainLayer))
#else
						|| (type == typeof(SplatPrototype))
#endif
					)
					{
						copyAssetFullPath = AppendTerrainPathToAssetFolder(copyAssetFolder);
					}
				}

				CreatePathWithFolders(copyAssetFullPath);

				string fileName = HEU_Platform.GetFileName(srcAssetPath);
				string newAssetPath = HEU_Platform.BuildPath(copyAssetFullPath, fileName);

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

					return LoadAssetAtPath(newAssetPath, type);
				}
				else
				{
					Debug.LogErrorFormat("Failed to copy and load asset from {0} to {1}!", srcAssetPath, newAssetPath);
				}
			}
			return null;
#else
			// TODO RUNTIME: AssetDatabase is not supported at runtime. Do we need to support this for runtime?
			Debug.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED);
			return null;
#endif
		}
Пример #18
0
        /// <summary>
        /// Return Houdini Engine installation and session information.
        /// Tries to use existing or creates new session to find information.
        /// </summary>
        /// <returns>String containing installation and session information.</returns>
        public static string GetHoudiniEngineInstallationInfo()
        {
#if HOUDINIENGINEUNITY_ENABLED
            StringBuilder sb = new StringBuilder();

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

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

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

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

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

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

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

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

                    sb.AppendLine();
                }

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

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

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

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

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

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

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

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

            Debug.Log(sb.ToString());

            return(sb.ToString());
#else
            return("");
#endif
        }
		public static string AppendTexturesPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, HEU_Defines.HEU_FOLDER_TEXTURES);
		}
	public static void LoadShelves()
	{
	    bool bSaveShelf = false;

	    _shelves.Clear();

	    // Always add the default shelf
	    HEU_Shelf defaultShelf = AddShelf(HEU_Defines.HEU_HENGINE_SHIPPED_SHELF, HEU_Defines.HEU_HENGINE_TOOLS_SHIPPED_FOLDER);
	    if (defaultShelf == null)
	    {
		return;
	    }
	    
	    defaultShelf._defaultShelf = true;

	    List<string> shelfEntries = HEU_PluginSettings.HEngineToolsShelves;
	    if (shelfEntries == null || shelfEntries.Count == 0)
	    {
		shelfEntries = new List<string>();
	    }

	    // Convert shelf path + name to actual shelf objects
	    int numShelves = shelfEntries.Count;
	    for (int i = 0; i < numShelves; i++)
	    {
		string shelfName = "";
		string shelfPath = "";

		GetSplitShelfEntry(shelfEntries[i], out shelfName, out shelfPath);

		// Ignore default shelf because we added it already
		if (shelfPath.Equals(HEU_Defines.HEU_HENGINE_TOOLS_SHIPPED_FOLDER))
		{
		    continue;
		}

		if (!string.IsNullOrEmpty(shelfName) && !string.IsNullOrEmpty(shelfPath))
		{
		    HEU_Shelf newShelf = new HEU_Shelf();
		    newShelf._shelfName = shelfName;
		    newShelf._shelfPath = shelfPath;

		    _shelves.Add(newShelf);
		}
		else
		{
		    HEU_Logger.LogWarningFormat("Found invalid shelf with entry: {0}", shelfEntries[i]);
		    shelfEntries.RemoveAt(i);
		    i--;
		    bSaveShelf = true;
		}
	    }

	    foreach (HEU_Shelf shelf in _shelves)
	    {
		string realShelfPath = HEU_HAPIUtility.GetRealPathFromHFSPath(shelf._shelfPath);

		if (!HEU_Platform.DoesPathExist(realShelfPath))
		{
		    HEU_Logger.LogWarningFormat("Shelf path does not exist: {0}", realShelfPath);
		}
		else
		{
		    bool bShelfLoaded = LoadToolsFromDirectory(realShelfPath, out shelf._tools);
		    if (!bShelfLoaded)
		    {
			HEU_Logger.LogWarningFormat("Failed to load shelf {0} at path {1}", shelf._shelfName, realShelfPath);
		    }
		}
	    }

	    _currentSelectedShelf = HEU_PluginSettings.HEngineShelfSelectedIndex;
	    if (_currentSelectedShelf < 0 || _currentSelectedShelf >= _shelves.Count)
	    {
		_currentSelectedShelf = 0;
		HEU_PluginSettings.HEngineShelfSelectedIndex = _currentSelectedShelf;
	    }

	    if (bSaveShelf)
	    {
		SaveShelf();
	    }

	    _shelvesLoaded = true;
	}
		public static string AppendTerrainPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, HEU_Defines.HEU_FOLDER_TERRAIN);
		}
	public static HEU_ShelfToolData LoadToolFromJsonString(string json, string jsonFilePath)
	{
	    //HEU_Logger.Log("Loading json: " + jsonFilePath);

	    // Get environment variable for tool path
	    string envValue = HEU_Platform.GetEnvironmentValue(HEU_Defines.HEU_PATH_KEY_TOOL);
	    string envKey = string.Format("<{0}>", HEU_Defines.HEU_PATH_KEY_TOOL);

	    HEU_ShelfToolData toolData = null;

	    if (!string.IsNullOrEmpty(json))
	    {
		try
		{
		    JSONNode jsonShelfNode = JSON.Parse(json);
		    if (jsonShelfNode != null)
		    {
			bool isObject = jsonShelfNode.IsObject;
			bool isArray = jsonShelfNode.IsArray;

			toolData = new HEU_ShelfToolData();

			toolData._name = jsonShelfNode["name"];

			toolData._toolType = (HEU_ShelfToolData.ToolType)System.Enum.Parse(typeof(HEU_ShelfToolData.ToolType), jsonShelfNode["toolType"]);

			toolData._toolTip = jsonShelfNode["toolTip"];

			toolData._iconPath = jsonShelfNode["iconPath"];

			toolData._assetPath = jsonShelfNode["assetPath"];

			toolData._helpURL = jsonShelfNode["helpURL"];

			JSONArray targetArray = jsonShelfNode["target"].AsArray;
			if (targetArray != null)
			{
			    int targetCount = targetArray.Count;
			    toolData._targets = new string[targetCount];
			    for (int j = 0; j < targetCount; ++j)
			    {
				toolData._targets[j] = targetArray[j];
			    }
			}
		    }
		}
		catch (System.Exception ex)
		{
		    HEU_Logger.LogErrorFormat("Exception when trying to parse shelf json file at path: {0}. Exception: {1}", jsonFilePath, ex.ToString());
		    return null;
		}

		toolData._jsonPath = jsonFilePath;

		if (toolData != null && !string.IsNullOrEmpty(toolData._name))
		{
		    // Make sure this tool targets Unity (must have "all" or "unity" set in target field)
		    bool bCompatiple = false;
		    if (toolData._targets != null)
		    {
			int numTargets = toolData._targets.Length;
			for (int i = 0; i < numTargets; ++i)
			{
			    if (toolData._targets[i].Equals(TARGET_ALL) || toolData._targets[i].Equals(TARGET_UNITY))
			    {
				bCompatiple = true;
				break;
			    }
			}
		    }

		    if (bCompatiple)
		    {
			if (!string.IsNullOrEmpty(toolData._assetPath))
			{
			    toolData._assetPath = toolData._assetPath.Replace(HEU_Defines.HEU_PATH_KEY_PROJECT + "/", "");
			    if (toolData._assetPath.Contains(envKey))
			    {
				if (string.IsNullOrEmpty(envValue))
				{
				    HEU_Logger.LogErrorFormat("Environment value {0} used but not set in environment.", HEU_Defines.HEU_PATH_KEY_TOOL);
				}
				else
				{
				    toolData._assetPath = toolData._assetPath.Replace(envKey, envValue);
				}
			    }
			}
			else
			{
			    toolData._assetPath = GetToolAssetPath(toolData, toolData._assetPath);
			}

			string realPath = HEU_PluginStorage.Instance.ConvertEnvKeyedPathToReal(toolData._assetPath);
			if (!HEU_Platform.DoesFileExist(realPath))
			{
			    HEU_Logger.LogErrorFormat("Houdini Engine shelf tool at {0} does not exist!", realPath);
			    return null;
			}

			if (!string.IsNullOrEmpty(toolData._iconPath))
			{
			    toolData._iconPath = toolData._iconPath.Replace(HEU_Defines.HEU_PATH_KEY_PROJECT + "/", "");
			    if (toolData._iconPath.Contains(envKey))
			    {
				if (string.IsNullOrEmpty(envValue))
				{
				    HEU_Logger.LogErrorFormat("Environment value {0} used but not set in environment.", HEU_Defines.HEU_PATH_KEY_TOOL);
				}
				else
				{
				    toolData._iconPath = toolData._iconPath.Replace(envKey, envValue);
				}
			    }
			}
			else
			{
			    toolData._iconPath = GetToolIconPath(toolData, toolData._iconPath);
			}

			return toolData;
		    }
		}
	    }

	    return null;
	}
		/// <summary>
		/// Do the geometry loading in Houdini in a thread.
		/// Creates a file node, loads the bgeo, then retrives the geometry into local buffers.
		/// </summary>
		protected override void DoWork()
		{
			_loadData._loadStatus = HEU_LoadData.LoadStatus.STARTED;

			//Debug.LogFormat("DoWork: Loading {0}", _filePath);

			if (_session == null || !_session.IsSessionValid())
			{
				SetLog(HEU_LoadData.LoadStatus.ERROR, "Invalid session!");
				return;
			}

			// Check file path
			if (!HEU_Platform.DoesPathExist(_filePath))
			{
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("File not found at {0}", _filePath));
				return;
			}

#if true
			// Make sure file type is supported
			//if (!_filePath.EndsWith(".bgeo") && !_filePath.EndsWith(".bgeo.sc"))
			//{
			//	SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Only .bgeo or .bgeo.sc files are supported."));
			//	return;
			//}

			// Create file SOP
			if (_loadData._fileNodeID == HEU_Defines.HEU_INVALID_NODE_ID)
			{
				if (!CreateFileNode(out _loadData._fileNodeID))
				{
					SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to create file node in Houdini."));
					return;
				}
			}

            Sleep();

			HAPI_NodeId displayNodeID = GetDisplayNodeID(_loadData._fileNodeID);
			if (displayNodeID == HEU_Defines.HEU_INVALID_NODE_ID)
			{
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to get display node of file geo node."));
				return;
			}

			// Set the file parameter
			if (!SetFileParm(displayNodeID, _filePath))
			{
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to set file path parm."));
				return;
			}

			// Cooking it will load the bgeo
			if (!_session.CookNode(_loadData._fileNodeID, false))
			{
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to cook node."));
				return;
			}

			// Wait until cooking has finished
			bool bResult = true;
			HAPI_State statusCode = HAPI_State.HAPI_STATE_STARTING_LOAD;
			while (bResult && statusCode > HAPI_State.HAPI_STATE_MAX_READY_STATE)
			{
				bResult = _session.GetStatus(HAPI_StatusType.HAPI_STATUS_COOK_STATE, out statusCode);

				Sleep();
			}

			// Check cook results for any errors
			if (statusCode == HAPI_State.HAPI_STATE_READY_WITH_COOK_ERRORS || statusCode == HAPI_State.HAPI_STATE_READY_WITH_FATAL_ERRORS)
			{
				string statusString = _session.GetStatusString(HAPI_StatusType.HAPI_STATUS_COOK_RESULT, HAPI_StatusVerbosity.HAPI_STATUSVERBOSITY_ERRORS);
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Cook failed: {0}.", statusString));
				return;
			}

#else

            // Load the file using HAPI_LoadGeoFromFile
            if (_loadData._fileNodeID == HEU_Defines.HEU_INVALID_NODE_ID)
            {
				Debug.Log("Creating file node with path: " + _filePath);
                if (!_session.CreateNode(-1, "SOP/file", "loadfile", true, out _loadData._fileNodeID))
                {
                    SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to create geo SOP in Houdini."));
                    return;
                }

                if (!_session.LoadGeoFromFile(_loadData._fileNodeID, _filePath))
                {
                    SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to create file node in Houdini."));
                    return;
                }
            }
			HAPI_NodeId displayNodeID = GetDisplayNodeID(_loadData._fileNodeID);

#endif

			// Note that object instancing is not supported. Instancers currently supported are
			// part and point instancing.

			// Get the various types of geometry (parts) from the display node
			List<HAPI_PartInfo> meshParts = new List<HAPI_PartInfo>();
			List<HAPI_PartInfo> volumeParts = new List<HAPI_PartInfo>();
			List<HAPI_PartInfo> instancerParts = new List<HAPI_PartInfo>();
			List<HAPI_PartInfo> curveParts = new List<HAPI_PartInfo>();
			if (!QueryParts(displayNodeID, ref meshParts, ref volumeParts, ref instancerParts, ref curveParts))
			{
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to query parts on node."));
				return;
			}

			Sleep();

			// Create Unity mesh buffers
            if (!GenerateMeshBuffers(_session, displayNodeID, meshParts, _generateOptions._splitPoints, _generateOptions._useLODGroups, 
				_generateOptions._generateUVs, _generateOptions._generateTangents, _generateOptions._generateNormals, 
				out _loadData._meshBuffers))
            {
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to generate mesh data from parts."));
				return;
			}

			// Create Unity terrain buffers
			if (!GenerateTerrainBuffers(_session, displayNodeID, volumeParts, out _loadData._terrainBuffers))
			{
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to generate terrain data from volume parts."));
				return;
			}

			// Create instancers (should come after normal geometry has been generated above)
			if (!GenerateInstancerBuffers(_session, displayNodeID, instancerParts, out _loadData._instancerBuffers))
			{
				SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to generate data from instancer parts."));
				return;
			}

			SetLog(HEU_LoadData.LoadStatus.SUCCESS, "Completed!");
		}
		private bool DrawDetailsGeneral()
		{
			bool bChanged = false;
			{
				string oldPath = HEU_PluginSettings.AssetCachePath;
				EditorGUILayout.LabelField(new GUIContent("Houdini Asset Cache Path:", "Files generated by this plugin will be stored in this folder path relative to Assets/."));
				string newPath = EditorGUILayout.TextField("", oldPath);
				if (!newPath.Equals(oldPath))
				{
					HEU_PluginSettings.AssetCachePath = newPath;
					bChanged = true;
				}
			}
			HEU_EditorUI.DrawSeparator();
			{
				string oldPath = HEU_PluginSettings.HoudiniInstallPath;
				string fileExt = "";

				EditorGUILayout.LabelField(new GUIContent("Override Houdini Install Path:", 
					"Set a specific Houdini installation to use for this plugin. The plugin's default version of Houdini will be ignored."));
				using (new EditorGUILayout.HorizontalScope())
				{
					string newPath = EditorGUILayout.DelayedTextField(oldPath);

					GUIStyle buttonStyle = HEU_EditorUI.GetNewButtonStyle_MarginPadding(0, 0);
					if (GUILayout.Button("...", buttonStyle, GUILayout.Width(30), GUILayout.Height(18)))
					{
						string panelMsg = "Select Houdini Install Path";
#if UNITY_EDITOR_OSX
						panelMsg += " (.app)";
#endif

						string openFilePath = UnityEditor.EditorUtility.OpenFolderPanel(panelMsg, newPath, fileExt);
						if (!string.IsNullOrEmpty(openFilePath))
						{
							newPath = openFilePath;
						}
					}

					if (!newPath.Equals(oldPath))
					{
						string msgPath = !string.IsNullOrEmpty(newPath) ? newPath : HEU_Platform.GetHoudiniEngineDefaultPath();

						string confirmMsg = string.Format(
							"Change the Houdini install path?\n"
							+ "  New path: {0}\n\n"
							+ "You will need to restart Unity to use this path!", msgPath);

						bool result = HEU_EditorUtility.DisplayDialog("Houdini Install Path Changed", confirmMsg, "Confirm", "Cancel");
						if (result)
						{
							HEU_PluginSettings.HoudiniInstallPath = newPath;
							bChanged = true;
						}
					}
				}
#if UNITY_EDITOR_OSX
				GUIStyle labelStyle = new GUIStyle(GUI.skin.label);
				labelStyle.wordWrap = true;
				EditorGUILayout.LabelField("  On macOS, you'll need to select the path to the .app folder.\n  E.g. /Applications/Houdini/Houdini16.5.616/Houdini Core 16.5.616.app", labelStyle);
#endif
			}
			HEU_EditorUI.DrawSeparator();
			{
				string oldPath = HEU_PluginSettings.HoudiniDebugLaunchPath;
				string fileExt = "";

				EditorGUILayout.LabelField(new GUIContent("Houdini Debug Executable:", "Set Houdini executable to launch when opening debug scenes."));
				using (new EditorGUILayout.HorizontalScope())
				{
					string newPath = EditorGUILayout.DelayedTextField(oldPath);

					GUIStyle buttonStyle = HEU_EditorUI.GetNewButtonStyle_MarginPadding(0, 0);
					if (GUILayout.Button("...", buttonStyle, GUILayout.Width(30), GUILayout.Height(18)))
					{
						string panelMsg = "Select Houdini Executable";
#if UNITY_EDITOR_OSX
						panelMsg += " (.app)";
#endif

						string openFilePath = UnityEditor.EditorUtility.OpenFilePanel(panelMsg, newPath, fileExt);
						if (!string.IsNullOrEmpty(openFilePath))
						{
							newPath = openFilePath;
						}
					}

					if (!newPath.Equals(oldPath))
					{
						HEU_PluginSettings.HoudiniDebugLaunchPath = newPath;
						bChanged = true;
					}
				}
#if UNITY_EDITOR_OSX
				GUIStyle labelStyle = new GUIStyle(GUI.skin.label);
				labelStyle.wordWrap = true;
				EditorGUILayout.LabelField("  On macOS, you'll need to select the path to the .app folder.\n  E.g. /Applications/Houdini/Houdini16.5.616/Houdini Core 16.5.616.app", labelStyle);
#endif
			}
			HEU_EditorUI.DrawSeparator();
			{
				bool oldValue = HEU_PluginSettings.UseFullPathNamesForOutput;
				bool newValue = HEU_EditorUI.DrawToggleLeft(oldValue, "Use Full Path Names For Output");
				if (!newValue.Equals(oldValue))
				{
					HEU_PluginSettings.UseFullPathNamesForOutput = newValue;
					bChanged = true;
				}
			}
			HEU_EditorUI.DrawSeparator();
			{
				bool oldValue = HEU_PluginSettings.SetCurrentThreadToInvariantCulture;
				bool newValue = HEU_EditorUI.DrawToggleLeft(oldValue, "Set Current Thread To Invariant Culture", "Enabling this sets to use InvariantCutulre which fixes locale-specific parsing issues such as using comma instead of dot for decimals.");
				if (!newValue.Equals(oldValue))
				{
					HEU_PluginSettings.SetCurrentThreadToInvariantCulture = newValue;
					bChanged = true;
				}
			}

			return bChanged;
		}