using System; using System.Collections.Generic; using System.IO; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif #if FLORA_PRESENT using ProceduralWorlds.Flora; using static ProceduralWorlds.Flora.CoreCommonFloraData; using System.Linq; #endif namespace Gaia { /// /// Used to serialise the tree prototypes /// [System.Serializable] public class ResourceProtoTree { [Tooltip("Resource name.")] public string m_name; [Tooltip("Desktop prefab.")] public GameObject m_desktopPrefab; [HideInInspector] public string m_desktopPrefabFileName; // Used for re-association //[Tooltip("Mobile prefab - future proofing here - not currently used.")] //public GameObject m_mobilePrefab; [HideInInspector] public string m_mobilePrefabFileName; // Used for re-association [Tooltip("How much the tree bends in the wind - only used by unity tree creator trees, ignored by SpeedTree trees.")] public float m_bendFactor; [Tooltip("The colour of healthy trees - only used by unity tree creator trees, ignored by SpeedTree trees.")] public Color m_healthyColour = Color.white; [Tooltip("The colour of dry trees - only used by unity tree creator trees, ignored by SpeedTree trees.")] public Color m_dryColour = Color.white; [Tooltip("Whether the tree should snap to the terrain height when being spawned - if not, it is possible to set a custom Y Offset")] public bool m_snapToTerrain = true; [Tooltip("The custom height used when the Y-Offset is based on a custom value.")] public float m_customOffset = 0.0f; [Tooltip("The min y-offset used for this tree")] public float m_minYOffset = -2.0f; [Tooltip("The min y-offset used for this tree")] public float m_maxYOffset = 2.0f; [Tooltip("The basis for the y-offset - the final y-position for this tree will be this basis plus the applied offset.")] public YOffsetBasedOn m_yOffsetBasedOn = YOffsetBasedOn.TerrainHeight; [Tooltip("The spawn scale used for this tree")] public SpawnScale m_spawnScale = SpawnScale.Fitness; [Tooltip("Minimum width in world units.")] public float m_minWidth = 0.75f; [Tooltip("Maximum width in world units.")] public float m_maxWidth = 1.5f; [Tooltip("Minimum height in world units.")] public float m_minHeight = 0.75f; //Height in world units [Tooltip("Maximum height in world units.")] public float m_maxHeight = 1.5f; //Height in world units public float m_widthRandomPercentage = 0.5f; public float m_heightRandomPercentage = 0.5f; /// The "last used fields" are filled when the tree is being spawned and can then be used to re-scle the tree when the user refreshes the prototype public SpawnScale m_lastUsedSpawnScale = SpawnScale.Fitness; public float m_lastUsedMinWidth = 0.75f; public float m_lastUsedMaxWidth = 1.5f; public float m_lastUsedMinHeight = 0.75f; //Height in world units public float m_lastUsedMaxHeight = 1.5f; //Height in world units public float m_lastUsedWidthRandomPercentage = 0.5f; public float m_lastUsedHeightRandomPercentage = 0.5f; [Tooltip("SPAWN CRITERIA - Spawn criteria are run against the terrain to assess its fitness in a range of 0..1 for use by this resource. If you add multiple criteria then the fittest one will be selected.")] public SpawnCritera[] m_spawnCriteria = new SpawnCritera[0]; [Tooltip("SPAWN EXTENSIONS - Spawn extensions allow fitness, spawning and post spawning extensions to be made to the spawning system.")] public SpawnRuleExtension[] m_spawnExtensions = new SpawnRuleExtension[0]; //[Tooltip("SPAWN MASKS - This list of masks can be used to determine where the terrain detail will appear on the terrain.")] //public ImageMask[] m_imageMasks = new ImageMask[0]; public bool m_instancesFoldOut; public bool m_dnaFoldedOut; public bool m_useFlora; public List m_floraLODs = new List(); //#if FLORA_PRESENT // public DetailScriptableObject DetailerSettingsObject // { // get // { // if (m_pwDetailerSettingsObject == null) // { // if (!string.IsNullOrEmpty(m_detailerSettingsObjectAssetGUID)) // { //#if UNITY_EDITOR // string assetPath = AssetDatabase.GUIDToAssetPath(m_detailerSettingsObjectAssetGUID); // //This can be multiple objects since the scriptable object can be embedded in a spawner settings file // UnityEngine.Object[] allObjects = AssetDatabase.LoadAllAssetsAtPath(assetPath); // //Looking for the object via the instance ID // foreach (UnityEngine.Object obj in allObjects) // { // if (obj.GetType() == typeof(DetailScriptableObject) && obj.GetInstanceID() == m_detailerSettingsObjectInstanceID) // { // //found it, cast it into the correct type to store it // m_pwDetailerSettingsObject = (DetailScriptableObject)obj; // //is this a file that was embedded within other assets at this path? // if (allObjects.Count() > 1) // { // //we need to create a copy and save it to not work with the embedded file // m_pwDetailerSettingsObject = ScriptableObject.Instantiate(m_pwDetailerSettingsObject); // FloraUtils.SaveSettingsFile(DetailerSettingsObject, ref m_detailerSettingsObjectAssetGUID, ref m_detailerSettingsObjectInstanceID, false, m_name, GaiaDirectories.GetTerrainDetailsPath()); // } // } // } //#endif // } // } // return m_pwDetailerSettingsObject; // } // set // { // if (value != m_pwDetailerSettingsObject) // { // m_pwDetailerSettingsObject = value; // if (value != null) // { //#if UNITY_EDITOR // m_detailerSettingsObjectAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(value)); // m_detailerSettingsObjectInstanceID = value.GetInstanceID(); //#endif // } // else // { // m_detailerSettingsObjectAssetGUID = ""; // m_detailerSettingsObjectInstanceID = -1; // } // } // } // } //#endif //This needs to be serialized still to survive recompiles, etc.! #if FLORA_PRESENT [SerializeField] private DetailScriptableObject m_pwDetailerSettingsObject; #endif public string m_detailerSettingsObjectAssetGUID; public int m_detailerSettingsObjectInstanceID; /// /// Initialise the tree /// /// The spawner it belongs to public void Initialise(Spawner spawner) { foreach (SpawnCritera criteria in m_spawnCriteria) { criteria.Initialise(spawner); } } /// /// Determine whether this has active criteria /// /// True if has actrive criteria public bool HasActiveCriteria() { for (int idx = 0; idx < m_spawnCriteria.Length; idx++) { if (m_spawnCriteria[idx].m_isActive) { return true; } } return false; } /// /// Set up the asset associations, return true if something changes. Can only be run when the editor is present. /// /// True if something changes public bool SetAssetAssociations() { bool isModified = false; #if UNITY_EDITOR if (m_desktopPrefab != null) { string fileName = Path.GetFileName(AssetDatabase.GetAssetPath(m_desktopPrefab)); if (fileName != m_desktopPrefabFileName) { m_desktopPrefabFileName = fileName; isModified = true; } } else { if (!string.IsNullOrEmpty(m_desktopPrefabFileName)) { m_desktopPrefabFileName = ""; isModified = true; } } //if (m_mobilePrefab != null) //{ // string fileName = Path.GetFileName(AssetDatabase.GetAssetPath(m_mobilePrefab)); // if (fileName != m_mobilePrefabFileName) // { // m_mobilePrefabFileName = fileName; // isModified = true; // } //} //else //{ // if (!string.IsNullOrEmpty(m_mobilePrefabFileName)) // { // m_mobilePrefabFileName = ""; // isModified = true; // } //} #endif return isModified; } /// /// Associate any unallocated assets to this resource. Return true if something changes. /// /// True if the prototype was in some way modified public bool AssociateAssets() { bool isModified = false; #if UNITY_EDITOR if (m_desktopPrefab == null) { if (!string.IsNullOrEmpty(m_desktopPrefabFileName)) { m_desktopPrefab = GaiaUtils.GetAsset(m_desktopPrefabFileName, typeof(UnityEngine.GameObject)) as GameObject; if (m_desktopPrefab != null) { isModified = true; } } } //if (m_mobilePrefab == null) //{ // if (!string.IsNullOrEmpty(m_mobilePrefabFileName)) // { // m_mobilePrefab = GaiaUtils.GetAsset(m_mobilePrefabFileName, typeof(UnityEngine.GameObject)) as GameObject; // if (m_mobilePrefab != null) // { // isModified = true; // } // } //} #endif return isModified; } /// /// Determine whether this has active criteria that checks textures /// /// True if has active criteria that checks textures public bool ChecksTextures() { for (int idx = 0; idx < m_spawnCriteria.Length; idx++) { if (m_spawnCriteria[idx].m_isActive && m_spawnCriteria[idx].m_checkTexture) { return true; } } return false; } /// /// Determine whether this has active criteria that checks proximity /// /// True if has active criteria that checks proximity public bool ChecksProximity() { for (int idx = 0; idx < m_spawnCriteria.Length; idx++) { if (m_spawnCriteria[idx].m_isActive && m_spawnCriteria[idx].m_checkProximity) { return true; } } return false; } /// /// Add tags to the list if they are not already there /// /// The list to add the tags to public void AddTags(ref List tagList) { for (int idx = 0; idx < m_spawnCriteria.Length; idx++) { if (m_spawnCriteria[idx].m_isActive && m_spawnCriteria[idx].m_checkProximity) { if (!tagList.Contains(m_spawnCriteria[idx].m_proximityTag)) { tagList.Add(m_spawnCriteria[idx].m_proximityTag); } } } } /// /// Stores the current spawn scale settings in the "last used" fields /// public void StoreLastUsedScaleSettings() { m_lastUsedSpawnScale = m_spawnScale; m_lastUsedMinWidth = m_minWidth; m_lastUsedMaxWidth = m_maxWidth; m_lastUsedMinHeight = m_minHeight; m_lastUsedMaxHeight = m_maxHeight; m_lastUsedWidthRandomPercentage = m_widthRandomPercentage; m_lastUsedHeightRandomPercentage = m_heightRandomPercentage; } /// /// Compares the current spawn scale settings agains the "last used" settings to determine whether the scaling was changed /// after the tree was spawned and the tree needs to be rescaled /// /// public bool NeedsRescale() { return m_lastUsedSpawnScale != m_spawnScale || m_lastUsedMinWidth != m_minWidth || m_lastUsedMaxWidth != m_maxWidth || m_lastUsedMinHeight != m_minHeight || m_lastUsedMaxHeight != m_maxHeight || m_lastUsedWidthRandomPercentage != m_widthRandomPercentage || m_lastUsedHeightRandomPercentage != m_heightRandomPercentage; } } }