// Copyright © 2018 Procedural Worlds Pty Limited. All Rights Reserved. using UnityEngine; using System.Collections.Generic; using static Gaia.GaiaConstants; using UnityEditor; using System.Linq; using System; using System.IO; /* * Scriptable Object containing settings for a Spawner */ namespace Gaia { /// Contains information about a Sequence of clips to play and how [CreateAssetMenu(menuName = "Procedural Worlds/Gaia/Spawner Settings")] [System.Serializable] public class SpawnerSettings : ScriptableObject, ISerializationCallbackReceiver { #region Public Variables /// /// The resources associated with the spawner /// public GaiaResource m_resources = new GaiaResource(); ///// ///// Spanwer x location - done this way to expose in the editor as a simple slider ///// public float m_x = 0f; ///// ///// Spawner y location - done this way to expose in the editor as a simple slider ///// public float m_y = 50f; ///// ///// Spawner z location - done this way to expose in the editor as a simple slider ///// public float m_z = 0f; ///// ///// Spawner width - this is the horizontal scaling factor - applied to both x & z ///// public float m_width = 10f; ///// ///// Spawner height - this is the vertical scaling factor ///// public float m_height = 10f; ///// ///// Spawner rotation ///// public float m_rotation = 0f; /// /// Is this spawner intended to be used on world map terrains? /// public bool m_isWorldmapSpawner = false; /// /// Range of the spawn area /// public float m_spawnRange = 500f; /// /// Should the random seed be generated anew for each spawn? /// public bool m_generateRandomSeed = true; /// /// Seed value for the random number generator /// public int m_randomSeed = 0; /// /// The spawn Density which controls the global object density for all spawners. This value is managed in the session /// for the scene, but also needs to be stored in the spawner settings for correct session playback. /// public float m_spawnDensity; /// /// The path this resources file came from /// public string m_resourcesPath; /// /// The GUID of the last used resources file; Used while saving and loading to save / load the resource file reference /// //public string m_resourcesGUID; /// /// The prefabs that can be spawned and their settings /// public List m_spawnerRules = new List(); /// /// Whether or not to show gizmos /// public bool m_showGizmos = true; /// /// Whether or not to show debug messages /// public bool m_showDebug = false; /// /// Whether or not to show the terrain helper /// public bool m_showTerrainHelper = true; public SpawnMode spawnMode = SpawnMode.Replace; [SerializeField] private ImageMask[] imageMasks = new ImageMask[0]; /// /// Toggle for trees in the spawn clear controls /// public bool m_clearSpawnsToggleTrees = false; /// /// Toggle for terrain details in the spawn clear controls /// public bool m_clearSpawnsToggleDetails = false; /// /// Toggle for game objects in the spawn clear controls /// public bool m_clearSpawnsToggleGOs = false; /// /// Toggle for spawn extensions in the spawn clear controls /// public bool m_clearSpawnsToggleSpawnExtensions = false; /// /// Toggle for probes in the spawn clear controls /// public bool m_clearSpawnsToggleProbes = false; /// /// Setting to determine which terrains should be affected by a clearing action /// public ClearSpawnFor m_clearSpawnsFor = ClearSpawnFor.AllTerrains; /// /// Setting to determine which prototypes should be deleted in a clearing action /// public ClearSpawnFrom m_clearSpawnsFrom = ClearSpawnFrom.AnySource; /// /// The last GUID of the settings file used to save these settings. /// public string lastGUIDSaved = ""; //Using a property to make sure the image mask list is always initialized //All image filters that are being applied in this spawning process public ImageMask[] m_imageMasks { get { if (imageMasks == null) { imageMasks = new ImageMask[0]; } return imageMasks; } set { imageMasks = value; } } //public float m_powerOf; #endregion #region Serialization public void OnBeforeSerialize() { } public void OnAfterDeserialize() { } #endregion /// /// Removes References to Texture2Ds in Image masks. The image mask will still remember the GUID of that texture to load it when needed. /// Call this when you are "done" with the spawner settings to free up memory caused by these references. /// public void ClearImageMaskTextures() { foreach (ImageMask im in m_imageMasks) { im.FreeTextureReferences(); } foreach (SpawnRule sr in m_spawnerRules) { foreach (ImageMask im in sr.m_imageMasks) { im.FreeTextureReferences(); } } Resources.UnloadUnusedAssets(); } public Spawner CreateSpawner(bool autoAddResources = false, Transform targetTransform = null) { //Find or create gaia GameObject gaiaObj = GaiaUtils.GetGaiaGameObject(); GameObject spawnerObj = new GameObject(this.name); spawnerObj.AddComponent(); if (targetTransform != null) { spawnerObj.transform.parent = targetTransform; } else { spawnerObj.transform.parent = gaiaObj.transform; } Spawner spawner = spawnerObj.GetComponent(); spawner.LoadSettings(this); //spawner.m_settings.m_resources = (GaiaResource)AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(this.m_resourcesGUID), typeof(GaiaResource)); if (autoAddResources) { TerrainLayer[] terrainLayers = new TerrainLayer[0]; DetailPrototype[] terrainDetails = new DetailPrototype[0]; TreePrototype[] terrainTrees = new TreePrototype[0]; GaiaDefaults.GetPrototypes(new List() { new BiomeSpawnerListEntry() {m_spawnerSettings = this, m_autoAssignPrototypes=true } }, ref terrainLayers, ref terrainDetails, ref terrainTrees, Terrain.activeTerrain); foreach (Terrain t in Terrain.activeTerrains) { GaiaDefaults.ApplyPrototypesToTerrain(t, terrainLayers, terrainDetails, terrainTrees); } } //We need to check the texture prototypes in this spawner against the already created terrain layers for this session //- otherwise the spawner will not know about those in subsequent spawns and might create unneccessary additional layers //Get a list of all exisiting Terrain Layers for this session string path = GaiaDirectories.GetTerrainLayerPath(); #if UNITY_EDITOR AssetDatabase.ImportAsset(path); if (Directory.Exists(path)) { string[] allLayerGuids = AssetDatabase.FindAssets("t:TerrainLayer", new string[1] { path }); List existingTerrainLayers = new List(); foreach (string guid in allLayerGuids) { try { TerrainLayer layer = (TerrainLayer)AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid), typeof(TerrainLayer)); if (layer != null) { existingTerrainLayers.Add(layer); } } catch (Exception ex) { if (ex.Message == "") { } } } foreach (SpawnRule sr in spawner.m_settings.m_spawnerRules) { if (sr.m_resourceType == SpawnerResourceType.TerrainTexture) { ResourceProtoTexture protoTexture = spawner.m_settings.m_resources.m_texturePrototypes[sr.m_resourceIdx]; //if a terrainLayer with these properties exist we can assume it fits to the given spawn rule TerrainLayer terrainLayer = existingTerrainLayers.FirstOrDefault(x => x.diffuseTexture == protoTexture.m_texture && x.normalMapTexture == protoTexture.m_normal && x.tileOffset == new Vector2(protoTexture.m_offsetX, protoTexture.m_offsetY) && x.tileSize == new Vector2(protoTexture.m_sizeX, protoTexture.m_sizeY) ); if (terrainLayer != null) { protoTexture.m_LayerGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(terrainLayer)); } } } } #endif foreach (SpawnRule rule in spawner.m_settings.m_spawnerRules) { rule.m_spawnedInstances = 0; } if (Terrain.activeTerrains.Where(x=>!TerrainHelper.IsWorldMapTerrain(x)).Count() > 0) { spawner.FitToAllTerrains(); } //else //{ // spawner.FitToTerrain(); //} return spawner; } } }