You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

375 lines
16 KiB
C#

3 years ago
#if FLORA_PRESENT
using ProceduralWorlds.Flora;
using static ProceduralWorlds.Flora.CoreCommonFloraData;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using PWCommon5;
namespace Gaia
{
public class FloraUtils
{
public static void SaveSettingsFile(DetailScriptableObject detailScriptableObject, ref string assetGUID, ref int instanceID, bool allowOverWrite = false, string name = "", string path = "")
{
#if UNITY_EDITOR
//string path = GaiaDirectories.GetTerrainDetailsPath();
if (!allowOverWrite)
{
//We create a new settings asset for this resorurce for the first time.
//Make sure the name is unique, do not want to overwrite another settings file
DirectoryInfo directoryInfo = new DirectoryInfo(path);
//strip off the extension, we need an unique name BEFORE the extension
string[] existingFileNames = directoryInfo.GetFiles().Select(x => x.Name.Replace(".asset", "")).ToArray();
detailScriptableObject.Name = ObjectNames.GetUniqueName(existingFileNames, name);
}
else
{
detailScriptableObject.Name = name;
}
AssetDatabase.CreateAsset(detailScriptableObject, path + "/" + detailScriptableObject.Name + ".asset");
assetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(detailScriptableObject));
instanceID = detailScriptableObject.GetInstanceID();
#endif
}
public static void UpdateAllTerrainsWithNewSettingsSO(DetailScriptableObject oldObject, DetailScriptableObject newObject)
{
if (GaiaUtils.HasDynamicLoadedTerrains())
{
Action<Terrain> act = (terrain) => UpdateSingleTerrainWithNewSettingsSO(terrain, oldObject, newObject);
GaiaUtils.CallFunctionOnDynamicLoadedTerrains(act, true, null, "Updating Detail Settings");
}
else
{
foreach (Terrain t in Terrain.activeTerrains)
{
UpdateSingleTerrainWithNewSettingsSO(t, oldObject, newObject);
}
}
}
public static void SaveFloraLODs(List<FloraLOD> floraLODs, string name, string saveFilePath)
{
#if UNITY_EDITOR
if (floraLODs != null)
{
Dictionary<Material, int> createdMaterialInstanceIDs = new Dictionary<Material, int>();
foreach (FloraLOD floraLOD in floraLODs)
{
//Save the materials first
for (int i = 0; i < floraLOD.DetailerSettingsObject.Mat.Length; i++)
{
Material mat = floraLOD.DetailerSettingsObject.Mat[i];
if (createdMaterialInstanceIDs.ContainsKey(mat))
{
//this material has already been added - load it again and set it as the current material in use
foreach (UnityEngine.Object obj in AssetDatabase.LoadAllAssetsAtPath(saveFilePath))
{
if (obj.GetType() == typeof(Material))
{
int targetInstanceID = 0;
createdMaterialInstanceIDs.TryGetValue(mat, out targetInstanceID);
if (((Material)obj).GetInstanceID() == targetInstanceID)
{
floraLOD.DetailerSettingsObject.Mat[i] = (Material)obj;
break;
}
}
}
}
else
{
Material newMat = Material.Instantiate(mat);
newMat.name = mat.name;
AssetDatabase.AddObjectToAsset(newMat, saveFilePath);
//override the material in the detailer settings with the new material - the following reload of the settings will
//turn this back into the association with the files in the Gaia user directory
floraLOD.DetailerSettingsObject.Mat[i] = newMat;
createdMaterialInstanceIDs.Add(mat, newMat.GetInstanceID());
}
}
floraLOD.DetailerSettingsObject.Name = name + "_LOD" + floraLOD.m_index;
DetailScriptableObject newDSO = ScriptableObject.Instantiate(floraLOD.DetailerSettingsObject);
//Sanitize the name from any numbering and "(Clone)", just want a clean name of the original resource for storing it
newDSO.name = floraLOD.DetailerSettingsObject.Name;
AssetDatabase.AddObjectToAsset(newDSO, saveFilePath);
//For the saved spawner settings file, we need to assign the new instance that we just saved.
//The following reload will turn this back into the association with the files in the Gaia user directory
floraLOD.DetailerSettingsObject = newDSO;
}
}
#endif
}
public static void UpdateSingleTerrainWithNewSettingsSO(Terrain t, DetailScriptableObject oldObject, DetailScriptableObject newObject)
{
if (oldObject == null)
{
return;
}
if (newObject == null)
{
return;
}
FloraTerrainTile dtt = t.GetComponent<FloraTerrainTile>();
if (dtt == null)
{
return;
}
if (dtt.m_detailObjectList == null)
{
return;
}
//We match by the detail settings object, this should be precise enough to get the correct entry in the list
int listIndex = dtt.m_detailObjectList.FindIndex(x => x.DetailScriptableObject == oldObject);
if (listIndex == -1)
{
//not found, exit
return;
}
//DetailOverrideData is a struct, need to overwrite it in the list if it already exists
DetailOverrideData detailOverrideData = new DetailOverrideData();
detailOverrideData.DetailScriptableObject = newObject;
detailOverrideData.SourceDataType = SourceDataType.Detail;
detailOverrideData.SourceDataIndex = dtt.m_detailObjectList[listIndex].SourceDataIndex;
detailOverrideData.DebugColor = dtt.m_detailObjectList[listIndex].DebugColor;
dtt.m_detailObjectList[listIndex] = detailOverrideData;
}
public static void RemoveSettingsSOFromAllTerrains(List<FloraLOD> floraLODs)
{
if (GaiaUtils.HasDynamicLoadedTerrains())
{
Action<Terrain> act = (terrain) => RemoveSettingsSOFromSingleTerrain(floraLODs, terrain);
GaiaUtils.CallFunctionOnDynamicLoadedTerrains(act, true, null, "Removing Detail Setting");
}
else
{
foreach (Terrain t in Terrain.activeTerrains)
{
RemoveSettingsSOFromSingleTerrain(floraLODs, t);
}
}
}
public static void RemoveSettingsSOFromSingleTerrain(List<FloraLOD> floraLODs, Terrain t)
{
#if UNITY_EDITOR
if (floraLODs == null || floraLODs.Count <= 0)
{
return;
}
FloraTerrainTile dtt = t.GetComponent<FloraTerrainTile>();
if (dtt == null)
{
return;
}
if (dtt.m_detailObjectList == null)
{
return;
}
foreach (FloraLOD floraLOD in floraLODs)
{
//We match by the detail settings object, this should be precise enough to get the correct entry in the list
int listIndex = dtt.m_detailObjectList.FindIndex(x => x.DetailScriptableObject == floraLOD.DetailerSettingsObject);
if (listIndex == -1)
{
//not found, exit
return;
}
else
{
dtt.m_detailObjectList.RemoveAt(listIndex);
}
}
//if no detail configurations remaining, we can as well strip off the component
if (dtt.m_detailObjectList.Count == 0)
{
Component.DestroyImmediate(dtt);
}
#endif
}
public static void AddNewDetailerSettingsObject(List<FloraLOD> floraLODs, string name, GaiaConstants.SpawnerResourceType resourceType, int index = -99)
{
if (floraLODs == null)
{
floraLODs = new List<FloraLOD>();
}
FloraLOD newLOD = new FloraLOD()
{
DetailerSettingsObject = ScriptableObject.CreateInstance<DetailScriptableObject>(),
m_index = floraLODs.Count,
m_name = name,
m_spawnerResourceType = resourceType,
};
FloraLOD addedLOD = null;
if (index == -99)
{
floraLODs.Add(newLOD);
addedLOD = floraLODs.Last();
}
else
{
floraLODs.Insert(index, newLOD);
addedLOD = floraLODs[index];
}
FloraUtils.SaveSettingsFile(addedLOD.DetailerSettingsObject, ref addedLOD.m_detailerSettingsObjectAssetGUID, ref addedLOD.m_detailerSettingsObjectInstanceID, false, addedLOD.m_name, GaiaDirectories.GetFloraDataPath());
}
public static void AddFloraToTerrain(Terrain terrain, List<FloraLOD> floraLODs, int terrainSourceDataIndex, SourceDataType sourceDataType)
{
#if UNITY_EDITOR
//Set up the global manager for the detail system (if not present already)
GaiaUtils.GetOrCreateDetailGlobalManager();
FloraTerrainTile dtt = terrain.GetComponent<FloraTerrainTile>();
if (dtt == null)
{
Camera mainCamera = FloraAutomationAPI.GetCamera();
dtt = terrain.gameObject.AddComponent<FloraTerrainTile>();
if (mainCamera != null)
{
dtt.MaximumDrawDistance = mainCamera.farClipPlane;
}
}
if (dtt.m_detailObjectList == null)
{
dtt.m_detailObjectList = new List<DetailOverrideData>();
}
dtt.TerrainType = FloraTerrainType.UnityTerrain;
dtt.UnityTerrain = terrain;
//Remove all configs for the source data index first, to set up the current LOD setting anew.
dtt.m_detailObjectList.RemoveAll(x => x.SourceDataIndex == terrainSourceDataIndex && x.SourceDataType == sourceDataType);
//set the correct index
foreach (FloraLOD floraLOD in floraLODs.Where(x=>x.DetailerSettingsObject!=null))
{
floraLOD.DetailerSettingsObject.SourceDataIndex = terrainSourceDataIndex;
}
FloraAutomationAPI.AddToTerrain(dtt, floraLODs.Select(x => x.DetailerSettingsObject).ToList(), true);
FloraDefaults floraDefaults = FloraAutomationAPI.GetFloraDefaults();
FloraAutomationAPI.SetupTerrainCellCount(floraDefaults, dtt, terrain);
//Mark the scene as dirty - might not be saved otherwise!
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(terrain.gameObject.scene);
#endif
}
/// <summary>
/// Resets the detailer settings objects in a LOD list to force a reload by asset guid / instance ID
/// </summary>
/// <param name="floraLODs"></param>
public static void ResetSettingsObjects(List<FloraLOD> floraLODs, List<FloraLODIdOverrides> floraLODIdOverrides)
{
for (int i = 0; i < floraLODs.Count; i++)
{
FloraLOD floraLOD = floraLODs[i];
floraLOD.DetailerSettingsObject = null;
if (floraLODIdOverrides != null && i < floraLODIdOverrides.Count)
{
floraLOD.m_detailerSettingsObjectAssetGUID = floraLODIdOverrides[i].m_assetGUIDOverride;
floraLOD.m_detailerSettingsObjectInstanceID = floraLODIdOverrides[i].m_instanceIDOverride;
}
}
}
/// <summary>
/// Populates the list of flora LODs with the settings made by the flora automation API according to the prefab that is passed in.
/// </summary>
/// <param name="prefab"></param>
/// <param name="floraLODs"></param>
/// <param name="name"></param>
/// <param name="resourceType"></param>
public static void CreateConfigFromPrefab(GameObject prefab, List<FloraLOD> floraLODs, string name, GaiaConstants.SpawnerResourceType resourceType)
{
#if UNITY_EDITOR
if (prefab == null || floraLODs == null)
{
return;
}
SourceDataType sourceDataType = resourceType == GaiaConstants.SpawnerResourceType.TerrainTree ? SourceDataType.Tree : SourceDataType.Detail;
if (sourceDataType == SourceDataType.Tree && !FloraGlobalData.TreesEnabled)
{
return;
}
List<DetailScriptableObject> detailScriptableObjects = FloraAutomationAPI.CreateFloraRenderFromPrefab(Terrain.activeTerrain, prefab, true, sourceDataType, GaiaDirectories.GetFloraDataPath() + "/");
floraLODs.Clear();
for (int i = 0; i < detailScriptableObjects.Count; i++)
{
floraLODs.Add(new FloraLOD()
{
m_index = i,
m_name = name,
m_spawnerResourceType = resourceType,
DetailerSettingsObject = detailScriptableObjects[i],
});
}
#endif
}
/// <summary>
/// Populates the list of flora LODs with the settings made by the flora automation API according to the texture on the detail prototype that is passed in.
/// </summary>
/// <param name="texture"></param>
/// <param name="floraLODs"></param>
/// <param name="name"></param>
/// <param name="resourceType"></param>
public static void CreateConfigFromTexture(DetailPrototype detailProto, List<FloraLOD> floraLODs, string name, GaiaConstants.SpawnerResourceType resourceType)
{
#if UNITY_EDITOR
if (detailProto == null || detailProto.prototypeTexture == null || floraLODs == null)
{
return;
}
SourceDataType sourceDataType = resourceType == GaiaConstants.SpawnerResourceType.TerrainTree ? SourceDataType.Tree : SourceDataType.Detail;
List<DetailScriptableObject> detailScriptableObjects = FloraAutomationAPI.CreateFloraRenderFromTexture(detailProto, sourceDataType, GaiaDirectories.GetFloraDataPath() + "/");
floraLODs.Clear();
for (int i = 0; i < detailScriptableObjects.Count; i++)
{
floraLODs.Add(new FloraLOD()
{
m_index = i,
m_name = name,
m_spawnerResourceType = resourceType,
DetailerSettingsObject = detailScriptableObjects[i],
});
}
#endif
}
}
}
#endif