/// <summary>
		/// Creates all folders in the given path if they don't exist.
		/// </summary>
		/// <param name="inPath">The path to create folders for</param>
		public static void CreatePathWithFolders(string inPath)
		{
#if UNITY_EDITOR
			string pathBuild = "";
			string[] folders = inPath.Split(HEU_Platform.DirectorySeparator);
			foreach(string folder in folders)
			{
				if(string.IsNullOrEmpty(folder))
				{
					break;
				}

				string nextPath = "";
				if (string.IsNullOrEmpty(pathBuild))
				{
					nextPath = folder;
				}
				else
				{
					nextPath = HEU_Platform.BuildPath(pathBuild, folder);
				}

				if (!AssetDatabase.IsValidFolder(nextPath))
				{
					//Debug.LogFormat("{0}: Creating folder: {1}/{2}", HEU_Defines.HEU_NAME, pathBuild, folder);
					AssetDatabase.CreateFolder(pathBuild, folder);
				}

				pathBuild = nextPath;
			}
#else
			Debug.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED);
#endif
		}
		public static string GetAssetRootPath(Object asset)
		{
#if UNITY_EDITOR
			string assetPath = GetAssetPath(asset);
			if(!string.IsNullOrEmpty(assetPath))
			{
				// We'll strip the path until we're under AssetCache/Baked/assetName or AssetCache/Working/assetName

				string assetTypePath = GetAssetBakedPath();
				if(!assetPath.StartsWith(assetTypePath))
				{
					assetTypePath = GetAssetWorkingPath();
					if(!assetPath.StartsWith(assetTypePath))
					{
						return null;
					}
				}

				string removedBakedPath = assetPath.Replace(assetTypePath + HEU_Platform.DirectorySeparator, "");
				string[] splits = removedBakedPath.Split(HEU_Platform.DirectorySeparator);
				if (!string.IsNullOrEmpty(splits[0]))
				{
					string rootPath = HEU_Platform.BuildPath(assetTypePath, splits[0]);
					Debug.AssertFormat(AssetDatabase.IsValidFolder(rootPath), "Calculated root path {0} is invalid for asset at {1}.", rootPath, assetPath);
					return rootPath;
				}
			}
			return null;
#else
			Debug.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED);
			return null; 
#endif
		}
		/// <summary>
		/// Create the given object inside the asset cache folder path.
		/// Depending on type, it might store in a subfolder for organizational purposes.
		/// </summary>
		/// <param name="objectToCreate"></param>
		/// <param name="assetCacheFolderPath"></param>
		/// <param name="assetFileName"></param>
		/// <param name="type"></param>
		public static void CreateObjectInAssetCacheFolder(Object objectToCreate, string assetCacheFolderPath, string assetFileName, System.Type type)
		{
#if UNITY_EDITOR
			Debug.Assert(!string.IsNullOrEmpty(assetCacheFolderPath), "Must give valid assetCacheFolderPath to create object at");

			string subFolderPath = assetCacheFolderPath;
			if(type == typeof(Mesh))
			{
				subFolderPath = AppendMeshesPathToAssetFolder(assetCacheFolderPath);
			}
			else if (type == typeof(Material))
			{
				subFolderPath = AppendMaterialsPathToAssetFolder(assetCacheFolderPath);
			}
			else if (type == typeof(TerrainData))
			{
				subFolderPath = AppendTerrainPathToAssetFolder(assetCacheFolderPath);
			}

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

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

			AssetDatabase.CreateAsset(objectToCreate, subFolderPath);

			// 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?
			Debug.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED);
#endif
		}
示例#4
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
				{
					Debug.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?
			Debug.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED);
			return null;
#endif
		}
	public static string GetHoudiniPathOnMacOS(string houdiniPath)
	{
#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
	    return houdiniPath;
	}
		public static string GetAssetBakedPathWithAssetName(string assetName)
		{
#if UNITY_EDITOR
			return HEU_Platform.BuildPath(GetAssetBakedPath(), assetName);
#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
		}
示例#7
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);
	    }

	    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?
			Debug.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED);
#endif
	}
		public static Texture2D RenderAndExtractImageToTexture(HEU_SessionBase session, HAPI_MaterialInfo materialInfo, HAPI_ParmId textureParmID, string textureName, string assetCacheFolderPath)
		{
			//Debug.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;

					// 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 = textureName + ".png";
					}

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

					byte[] encodedBytes;
					if(textureName.EndsWith(".jpg"))
					{
						encodedBytes = texture.EncodeToJPG();
					}
					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);

					// 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;
		}
	public void Setup(HEU_HoudiniAsset hdaAsset)
	{
	    _heu = hdaAsset;
	    _assetGO = _heu.RootGameObject;
	    _assetPath = _heu.AssetPath;
	    _assetName = _heu.AssetName;

	    // Use the HDAs cache folder for generating output files
	    string hdaCachePath = _heu.GetValidAssetCacheFolderPath();
	    _outputCachePathRoot = HEU_Platform.BuildPath(hdaCachePath, "PDGCache");

	    Reset();
	    Refresh();
	}
		/// <summary>
		/// Loads a copy of the given asset. Creates the copy if not found.
		/// </summary>
		/// <param name="srcAsset">Source asset whose copy will be loaded (and created if no copy exists).</param>
		/// <param name="newAssetFolderPath">Folder of to look for copy or create in</param>
		/// <param name="type">Type of asset</param>
		/// <returns>Loaded copy of the asset</returns>
		public static Object LoadAssetCopy(Object srcAsset, string newAssetFolderPath, System.Type type, bool bOverwriteExisting)
		{
#if UNITY_EDITOR
			string srcAssetPath = GetAssetPath(srcAsset);
			if (!string.IsNullOrEmpty(srcAssetPath) && IsPathInAssetCache(srcAssetPath))
			{
				string subFolderPath = newAssetFolderPath;
				if(type == typeof(Material))
				{
					subFolderPath = AppendMaterialsPathToAssetFolder(newAssetFolderPath);
				}
				else if(type == typeof(Texture))
				{
					subFolderPath = AppendTexturesPathToAssetFolder(newAssetFolderPath);
				}
				else if (type == typeof(Mesh))
				{
					subFolderPath = AppendMeshesPathToAssetFolder(newAssetFolderPath);
				}
				else if (type == typeof(TerrainData))
				{
					subFolderPath = AppendTerrainPathToAssetFolder(newAssetFolderPath);
				}

				CreatePathWithFolders(subFolderPath);

				string fileName = HEU_Platform.GetFileName(srcAssetPath);
				string newAssetPath = HEU_Platform.BuildPath(subFolderPath, 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
		}
		private static string GetAssetCachePath()
		{
#if UNITY_EDITOR
			string rootPath = HEU_Platform.BuildPath("Assets", HEU_PluginSettings.AssetCachePath);
			if (!AssetDatabase.IsValidFolder(rootPath))
			{
				AssetDatabase.CreateFolder("Assets", HEU_PluginSettings.AssetCachePath);
			}

			return rootPath;
#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 ""; 
#endif
		}
	// PUBLIC FUNCTIONS =========================================================================================


	public void Setup(HEU_HoudiniAsset hdaAsset)
	{
	    _heu = hdaAsset;
	    _assetGO = _heu.RootGameObject;
	    _assetPath = _heu.AssetPath;
	    _assetName = _heu.AssetName;
	    _bUseTOPNodeFilter = true;
	    _bUseTOPOutputFilter = true;
	    _topNodeFilter = HEU_Defines.DEFAULT_TOP_NODE_FILTER;
	    _topOutputFilter = HEU_Defines.DEFAULT_TOP_OUTPUT_FILTER;

	    // Use the HDAs cache folder for generating output files
	    string hdaCachePath = _heu.GetValidAssetCacheFolderPath();
	    _outputCachePathRoot = HEU_Platform.BuildPath(hdaCachePath, "PDGCache");

	    Reset();
	    Refresh();
	}
		public static string GetAssetBakedPath()
		{
#if UNITY_EDITOR
			string dbRoot = GetAssetCachePath();
			string bakedPath = HEU_Platform.BuildPath(dbRoot, HEU_Defines.HEU_BAKED_PATH);

			if (!AssetDatabase.IsValidFolder(bakedPath))
			{
				AssetDatabase.CreateFolder(dbRoot, HEU_Defines.HEU_BAKED_PATH);
			}

			return bakedPath;
#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
		}
示例#14
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
	}
		public static string AppendPrefabPath(string inAssetCacheFolder, string assetName)
		{
			string prefabPath = HEU_Platform.BuildPath(inAssetCacheFolder, assetName);
			return prefabPath + ".prefab";
		}
		public static string AppendTerrainPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, "Terrain");
		}
		public static string AppendMaterialsPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, "Materials");
		}
示例#18
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;
        }
示例#19
0
		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);
					TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(newGameObject);

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

						terrain.terrainData = HEU_AssetDatabase.CopyUniqueAndLoadAssetAtAnyPath(sourceTerrainData, outputTerrainpath, 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();
					}
					TerrainData terrainData = terrain.terrainData;
					collider.terrainData = terrainData;

					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 && terrainLayerIndex >= 0)
								{
									// Copy the TerrainLayer file
									TerrainLayer prevTerrainLayer = terrainlayer;
									terrainlayer = HEU_AssetDatabase.CopyAndLoadAssetAtAnyPath(terrainlayer, outputTerrainpath, typeof(TerrainLayer), true) as TerrainLayer;
									if (terrainlayer != null)
									{
										// Update the TerrainLayer reference in the list with this copy
										finalTerrainLayers[terrainLayerIndex] = 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.
									}
								}
							}

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

			// 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);
			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[]> heightFields = new List<float[]>();
			// Corresponding list of HF volume layers to process as splatmaps
			List<HEU_VolumeLayer> volumeLayersToProcess = new List<HEU_VolumeLayer>();

			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.
			for(int i = 1; i < numLayers; ++i)
			{
				float[] normalizedHF = HEU_TerrainUtility.GetNormalizedHeightmapFromPartWithMinMax(session, _ownerNode.GeoID, _layers[i]._part.PartID, 
					_layers[i]._xLength, _layers[i]._yLength, ref minHeight, ref maxHeight, ref heightRange);
				if (normalizedHF != null && normalizedHF.Length > 0)
				{
					heightFields.Add(normalizedHF);
					volumeLayersToProcess.Add(_layers[i]);
				}
			}

			int numVolumeLayers = volumeLayersToProcess.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 < numVolumeLayers; ++m)
			{
				bNewTerrainLayer = false;
				bSetTerrainLayerProperties = true;

				layer = volumeLayersToProcess[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 < numVolumeLayers; ++m)
			{
				bNewSplat = false;

				layer = volumeLayersToProcess[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 && volumeLayersToProcess.Count > 0)
			{
				// Convert the heightfields into alpha maps with layer strengths
				float[] strengths = new float[volumeLayersToProcess.Count];
				for (int m = 0; m < volumeLayersToProcess.Count; ++m)
				{
					strengths[m] = volumeLayersToProcess[m]._strength;
				}

				alphamap = HEU_TerrainUtility.AppendConvertedHeightFieldToAlphaMap(
					volumeLayersToProcess[0]._xLength, volumeLayersToProcess[0]._yLength, existingAlphaMaps,
					heightFields, 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 = volumeLayersToProcess[0]._xLength;
				terrainData.alphamapResolution = alphamapResolution;

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

			// Tree instances for scattering
			HEU_TerrainUtility.ApplyScatter(terrainData, _scatterTrees);

			// 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();
		}
示例#21
0
		public static string AppendMeshesPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, HEU_Defines.HEU_FOLDER_MESHES);
		}
示例#22
0
		public static string AppendTexturesPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, HEU_Defines.HEU_FOLDER_TEXTURES);
		}
示例#23
0
		public static string AppendMaterialsPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, HEU_Defines.HEU_FOLDER_MATERIALS);
		}
示例#24
0
		public static string AppendTerrainPathToAssetFolder(string inAssetCacheFolder)
		{
			return HEU_Platform.BuildPath(inAssetCacheFolder, HEU_Defines.HEU_FOLDER_TERRAIN);
		}