using System.Collections.Generic; using System.IO; using UnityEditor.AnimatedValues; using UnityEngine; using UnityEngine.Rendering; namespace UnityEditor.TerrainTools { internal class TerrainToolboxSettings { List m_Terrains = new List(); Terrain m_SelectedTerrain = null; TerrainSettings m_Settings = ScriptableObject.CreateInstance(); TerrainSettings m_SelectedPreset = null; readonly TerrainSettings m_DefaultSettings = ScriptableObject.CreateInstance(); string m_MaterialPath = string.Empty; AnimBool m_ShowCustomMaterial = new AnimBool(); AnimBool m_ShowBuiltinSpecular = new AnimBool(); AnimBool m_ShowReflectionProbes = new AnimBool(); const int kBaseMapDistMax = 20000; static class Styles { public static readonly GUIContent BasicTerrainSettings = EditorGUIUtility.TrTextContent("Basic Settings", "Toggle to enable or disable changing the settings."); public static readonly GUIContent MeshResolutionSettings = EditorGUIUtility.TrTextContent("Mesh Resolutions", "Toggle to enable or disable changing the settings."); public static readonly GUIContent TextureResolutionSettings = EditorGUIUtility.TrTextContent("Texture Resolutions", "Toggle to enable or disable changing the settings."); public static readonly GUIContent TreeAndDetailSettings = EditorGUIUtility.TrTextContent("Trees & Details", "Toggle to enable or disable changing the settings."); public static readonly GUIContent GrassWindSettings = EditorGUIUtility.TrTextContent("Wind for Grass", "Toggle to enable or disable changing the settings."); public static readonly GUIStyle LableStyle = EditorStyles.wordWrappedMiniLabel; public static readonly GUIStyle ToggleButtonStyle = "LargeButton"; public static readonly GUIContent LoadDefaultBtn = EditorGUIUtility.TrTextContent("Default", "Load settings from default values."); public static readonly GUIContent LoadSelectedBtn = EditorGUIUtility.TrTextContent("Selected", "Load settings from selected terrain."); public static readonly GUIContent LoadPresetBtn = EditorGUIUtility.TrTextContent("Preset", "Load settings from selected preset."); // Settings public static readonly GUIContent GroupingID = EditorGUIUtility.TrTextContent("Grouping ID", "Grouping ID for auto connection"); public static readonly GUIContent AllowAutoConnect = EditorGUIUtility.TrTextContent("Auto connect", "Allow the current terrain tile automatically connect to neighboring tiles sharing the same grouping ID."); public static readonly GUIContent DrawTerrain = EditorGUIUtility.TrTextContent("Draw", "Toggle the rendering of terrain"); public static readonly GUIContent DrawInstancedTerrain = EditorGUIUtility.TrTextContent("Draw Instanced", "Toggle terrain instancing rendering"); public static readonly GUIContent PixelError = EditorGUIUtility.TrTextContent("Pixel Error", "The accuracy of the mapping between the terrain maps (heightmap, textures, etc) and the generated terrain; higher values indicate lower accuracy but lower rendering overhead."); public static readonly GUIContent BaseMapDist = EditorGUIUtility.TrTextContent("Base Map Dist.", "The maximum distance at which terrain textures will be displayed at full resolution. Beyond this distance, a lower resolution composite image will be used for efficiency."); public static readonly GUIContent CastShadows = EditorGUIUtility.TrTextContent("Cast Shadows", "Does the terrain cast shadows?"); public static readonly GUIContent Material = EditorGUIUtility.TrTextContent("Material", "The material used to render the terrain. This will affect how the color channels of a terrain texture are interpreted."); public static readonly GUIContent ReflectionProbes = EditorGUIUtility.TrTextContent("Reflection Probes", "How reflection probes are used on terrain. Only effective when using built-in standard material or a custom material which supports rendering with reflection."); public static readonly GUIContent PreserveTreePrototypeLayers = EditorGUIUtility.TrTextContent("Preserve Tree Prototype Layers|Enable this option if you want your tree instances to take on the layer values of their prototype prefabs, rather than the terrain GameObject's layer."); public static readonly GUIContent DrawTrees = EditorGUIUtility.TrTextContent("Draw", "Should trees, grass and details be drawn?"); public static readonly GUIContent DetailObjectDistance = EditorGUIUtility.TrTextContent("Detail Distance", "The distance (from camera) beyond which details will be culled."); public static readonly GUIContent CollectDetailPatches = EditorGUIUtility.TrTextContent("Collect Detail Patches", "Should detail patches in the Terrain be removed from memory when not visible?"); public static readonly GUIContent DetailObjectDensity = EditorGUIUtility.TrTextContent("Detail Density", "The number of detail/grass objects in a given unit of area. The value can be set lower to reduce rendering overhead."); public static readonly GUIContent TreeDistance = EditorGUIUtility.TrTextContent("Tree Distance", "The distance (from camera) beyond which trees will be culled."); public static readonly GUIContent TreeBillboardDistance = EditorGUIUtility.TrTextContent("Billboard Start", "The distance (from camera) at which 3D tree objects will be replaced by billboard images."); public static readonly GUIContent TreeCrossFadeLength = EditorGUIUtility.TrTextContent("Fade Length", "Distance over which trees will transition between 3D objects and billboards."); public static readonly GUIContent TreeMaximumFullLODCount = EditorGUIUtility.TrTextContent("Max Mesh Trees", "The maximum number of visible trees that will be represented as solid 3D meshes. Beyond this limit, trees will be replaced with billboards."); public static readonly GUIContent Thickness = EditorGUIUtility.TrTextContent("Thickness", "How much the terrain collision volume should extend along the negative Y-axis. Objects are considered colliding with the terrain from the surface to a depth equal to the thickness. This helps prevent high-speed moving objects from penetrating into the terrain without using expensive continuous collision detection."); public static readonly GUIContent WavingGrassStrength = EditorGUIUtility.TrTextContent("Speed", "The speed of the wind as it blows grass."); public static readonly GUIContent WavingGrassSpeed = EditorGUIUtility.TrTextContent("Size", "The size of the 'ripples' on grassy areas as the wind blows over them."); public static readonly GUIContent WavingGrassAmount = EditorGUIUtility.TrTextContent("Bending", "The degree to which grass objects are bent over by the wind."); public static readonly GUIContent WavingGrassTint = EditorGUIUtility.TrTextContent("Grass Tint", "Overall color tint applied to grass objects."); public static readonly GUIContent BaseTextureRes = EditorGUIUtility.TrTextContent("Base Texture Resolution", "Resolution of the composite texture used on the terrain when viewed from a distance greater than the Basemap Distance."); public static readonly GUIContent ControlTextureRes = EditorGUIUtility.TrTextContent("Control Texture Resolution", "Resolution of the \"splatmap\" that controls the blending of the different terrain textures."); public static readonly GUIContent HeightTextureRes = EditorGUIUtility.TrTextContent("Heightmap Resolution", "Pixel resolution of the terrain's heightmap (should be a power of two plus one, eg, 513 = 512 + 1)."); // warnings public static readonly GUIContent DetailResolutionWarning = EditorGUIUtility.TrTextContent("You may reduce CPU draw call overhead by setting the detail resolution per patch as high as possible, relative to detail resolution."); public static readonly GUIContent TerrainMaterialWarning = EditorGUIUtility.TrTextContent("Built in or default materials are not supported in scriptable rendering pipeline. Please use custom terrain material."); // presets public static readonly GUIContent Preset = EditorGUIUtility.TrTextContent("Preset", "Preset setting file in use. Select a pre-saved preset asset or create a new preset."); public static readonly GUIContent SavePreset = EditorGUIUtility.TrTextContent("Save", "Save current settings to selected preset."); public static readonly GUIContent SaveAsPreset = EditorGUIUtility.TrTextContent("Save As", "Save the current preset as a new preset asset file."); public static readonly GUIContent RefreshPreset = EditorGUIUtility.TrTextContent("Refresh", "Load selected preset and apply to current settings"); // apply button public static readonly GUIContent ApplySettingsBtn = EditorGUIUtility.TrTextContent("Apply to Selected Terrain(s)", "Start applying enabled settings to selected terrain(s)."); public static readonly GUIContent ApplySettingsToAllBtn = EditorGUIUtility.TrTextContent("Apply to All Terrain(s) in Scene", "Start applying enabled settings to all terrain(s) in scene."); } public void OnEnable() { m_Terrains.AddRange(GameObject.FindObjectsOfType()); } void Refresh() { m_Terrains.AddRange(GameObject.FindObjectsOfType()); } bool GetSelectedTerrain() { var result = false; var selectedTerrains = Selection.GetFiltered(typeof(Terrain), SelectionMode.Unfiltered); if (selectedTerrains != null && selectedTerrains.Length > 0) { m_SelectedTerrain = selectedTerrains[0] as Terrain; result = true; } return result; } public void OnDisable() { } public void OnGUI() { // scroll view of settings EditorGUIUtility.hierarchyMode = true; TerrainToolboxUtilities.DrawSeperatorLine(); EditorGUILayout.LabelField("Load Settings", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button(Styles.LoadDefaultBtn)) { ResetToDefaultSettings(); } if (GUILayout.Button(Styles.LoadSelectedBtn)) { LoadSettingsFromSelectedTerrain(); } if (GUILayout.Button(Styles.LoadPresetBtn)) { LoadPresetToSettings(); } EditorGUILayout.EndHorizontal(); // Presets TerrainToolboxUtilities.DrawSeperatorLine(); EditorGUILayout.LabelField(Styles.Preset, EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); m_SelectedPreset = (TerrainSettings)EditorGUILayout.ObjectField(m_SelectedPreset, typeof(TerrainSettings), false); EditorGUILayout.BeginHorizontal(); if (EditorGUI.EndChangeCheck() && m_SelectedPreset != null) { if (EditorUtility.DisplayDialog("Confirm", "Load terrain settings from selected preset?", "OK", "Cancel")) { LoadPresetToSettings(); } } if (GUILayout.Button(Styles.SavePreset)) { if (m_SelectedPreset == null) { if (EditorUtility.DisplayDialog("Confirm", "No preset selected. Create a new preset?", "Continue", "Cancel")) { CreateNewPreset(); } } else { SavePresetSettings(); } } if (GUILayout.Button(Styles.SaveAsPreset)) { CreateNewPreset(); } if (GUILayout.Button(Styles.RefreshPreset)) { LoadPresetToSettings(); } EditorGUILayout.EndHorizontal(); // basic settings TerrainToolboxUtilities.DrawSeperatorLine(); bool basicSettingToggled = m_Settings.EnableBasicSettings; m_Settings.ShowBasicTerrainSettings = TerrainToolGUIHelper.DrawToggleHeaderFoldout(Styles.BasicTerrainSettings, m_Settings.ShowBasicTerrainSettings, ref basicSettingToggled, 0f); ++EditorGUI.indentLevel; if (m_Settings.ShowBasicTerrainSettings) { EditorGUI.BeginDisabledGroup(!m_Settings.EnableBasicSettings); ShowBasicSettings(); EditorGUI.EndDisabledGroup(); } --EditorGUI.indentLevel; m_Settings.EnableBasicSettings = basicSettingToggled; // mesh resolution settings TerrainToolboxUtilities.DrawSeperatorLine(); bool meshSettingToggled = m_Settings.EnableMeshResSettings; m_Settings.ShowMeshResolutionSettings = TerrainToolGUIHelper.DrawToggleHeaderFoldout(Styles.MeshResolutionSettings, m_Settings.ShowMeshResolutionSettings, ref meshSettingToggled, 0f); ++EditorGUI.indentLevel; if (m_Settings.ShowMeshResolutionSettings) { EditorGUI.BeginDisabledGroup(!m_Settings.EnableMeshResSettings); ShowMeshResolutionSettings(); EditorGUI.EndDisabledGroup(); } --EditorGUI.indentLevel; m_Settings.EnableMeshResSettings = meshSettingToggled; // texture resolution settings TerrainToolboxUtilities.DrawSeperatorLine(); bool textureSettingToggled = m_Settings.EnableTextureResSettings; m_Settings.ShowTextureResolutionSettings = TerrainToolGUIHelper.DrawToggleHeaderFoldout(Styles.TextureResolutionSettings, m_Settings.ShowTextureResolutionSettings, ref textureSettingToggled, 0f); ++EditorGUI.indentLevel; if (m_Settings.ShowTextureResolutionSettings) { EditorGUI.BeginDisabledGroup(!m_Settings.EnableTextureResSettings); ShowTextureResolutionSettings(); EditorGUI.EndDisabledGroup(); } --EditorGUI.indentLevel; m_Settings.EnableTextureResSettings = textureSettingToggled; // trees and details TerrainToolboxUtilities.DrawSeperatorLine(); bool treeSettingToggled = m_Settings.EnableTreeSettings; m_Settings.ShowTreeAndDetailSettings = TerrainToolGUIHelper.DrawToggleHeaderFoldout(Styles.TreeAndDetailSettings, m_Settings.ShowTreeAndDetailSettings, ref treeSettingToggled, 0f); ++EditorGUI.indentLevel; if (m_Settings.ShowTreeAndDetailSettings) { EditorGUI.BeginDisabledGroup(!m_Settings.EnableTreeSettings); ShowTreeAndDetailSettings(); EditorGUI.EndDisabledGroup(); } --EditorGUI.indentLevel; m_Settings.EnableTreeSettings = treeSettingToggled; // grass wind TerrainToolboxUtilities.DrawSeperatorLine(); bool grassSettingToggled = m_Settings.EnableWindSettings; m_Settings.ShowGrassWindSettings = TerrainToolGUIHelper.DrawToggleHeaderFoldout(Styles.GrassWindSettings, m_Settings.ShowGrassWindSettings, ref grassSettingToggled, 0f); ++EditorGUI.indentLevel; if (m_Settings.ShowGrassWindSettings) { EditorGUI.BeginDisabledGroup(!m_Settings.EnableWindSettings); ShowGrassWindSettings(); EditorGUI.EndDisabledGroup(); } --EditorGUI.indentLevel; m_Settings.EnableWindSettings = grassSettingToggled; --EditorGUI.indentLevel; EditorGUILayout.Space(); // buttons TerrainToolboxUtilities.DrawSeperatorLine(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button(Styles.ApplySettingsBtn, GUILayout.Height(40))) { ApplySettingsToSelectedTerrains(); } if (GUILayout.Button(Styles.ApplySettingsToAllBtn, GUILayout.Height(40))) { ApplySettingsToAllTerrains("This operation will apply settings to all terrains in scene."); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); } void ShowBasicSettings() { m_Settings.GroupingID = EditorGUILayout.IntField(Styles.GroupingID, m_Settings.GroupingID); m_Settings.AutoConnect = EditorGUILayout.Toggle(Styles.AllowAutoConnect, m_Settings.AutoConnect); m_Settings.DrawHeightmap = EditorGUILayout.Toggle(Styles.DrawTerrain, m_Settings.DrawHeightmap); m_Settings.DrawInstanced = EditorGUILayout.Toggle(Styles.DrawInstancedTerrain, m_Settings.DrawInstanced); m_Settings.PixelError = EditorGUILayout.Slider(Styles.PixelError, m_Settings.PixelError, 1, 200); m_Settings.BaseMapDistance = EditorGUILayout.Slider(Styles.BaseMapDist, m_Settings.BaseMapDistance, 0, kBaseMapDistMax); m_Settings.ShadowCastingMode = (ShadowCastingMode)EditorGUILayout.EnumPopup(Styles.CastShadows, m_Settings.ShadowCastingMode); #if UNITY_2019_2_OR_NEWER m_Settings.MaterialTemplate = EditorGUILayout.ObjectField("Material", m_Settings.MaterialTemplate, typeof(Material), false) as Material; #if UNITY_2021_2_OR_NEWER TerrainInspectorUtility.TerrainShaderValidationGUI(m_Settings.MaterialTemplate); #endif m_Settings.ReflectionProbeUsage = (ReflectionProbeUsage)EditorGUILayout.EnumPopup(Styles.ReflectionProbes, m_Settings.ReflectionProbeUsage); #else m_Settings.MaterialType = (Terrain.MaterialType)EditorGUILayout.EnumPopup(Styles.Material, m_Settings.MaterialType); if (m_Settings.MaterialType == Terrain.MaterialType.Custom) { m_Settings.MaterialTemplate = EditorGUILayout.ObjectField("Material", m_Settings.MaterialTemplate, typeof(Material), false) as Material; m_Settings.ReflectionProbeUsage = (ReflectionProbeUsage)EditorGUILayout.EnumPopup(Styles.ReflectionProbes, m_Settings.ReflectionProbeUsage); } if (m_Settings.MaterialType == Terrain.MaterialType.BuiltInLegacySpecular) { m_Settings.LegacySpecular = EditorGUILayout.ColorField("Specular Color", m_Settings.LegacySpecular); m_Settings.LegacyShininess = EditorGUILayout.Slider("Shininess", m_Settings.LegacyShininess, 0, 1); } #endif } void ShowMeshResolutionSettings() { // max size defined in TerrainInspector.cs const int maxTerrainSize = 100000; const int maxTerrainHeight = 10000; m_Settings.TerrainWidth = Mathf.Clamp(EditorGUILayout.FloatField("Terrain Width", m_Settings.TerrainWidth), 1.0f, maxTerrainSize); m_Settings.TerrainLength = Mathf.Clamp(EditorGUILayout.FloatField("Terrain Length", m_Settings.TerrainLength), 1.0f, maxTerrainSize); m_Settings.TerrainHeight = Mathf.Clamp(EditorGUILayout.FloatField("Terrain Height", m_Settings.TerrainHeight), 1.0f, maxTerrainHeight); m_Settings.DetailResolutionPerPatch = EditorGUILayout.IntField("Detail Resolution Per Patch", m_Settings.DetailResolutionPerPatch); m_Settings.DetailResolutionPerPatch = Mathf.Clamp(m_Settings.DetailResolutionPerPatch, 8, 128); m_Settings.DetailResolutaion = EditorGUILayout.IntField("Detail Resolution", m_Settings.DetailResolutaion); m_Settings.DetailResolutaion = Mathf.Clamp(m_Settings.DetailResolutaion, 0, 4048); } void ShowTextureResolutionSettings() { m_Settings.BaseTextureResolution = EditorGUILayout.IntPopup(Styles.BaseTextureRes.text, m_Settings.BaseTextureResolution, ToolboxHelper.GUITextureResolutionNames, ToolboxHelper.GUITextureResolutions); m_Settings.AlphaMapResolution = EditorGUILayout.IntPopup(Styles.ControlTextureRes.text, m_Settings.AlphaMapResolution, ToolboxHelper.GUITextureResolutionNames, ToolboxHelper.GUITextureResolutions); m_Settings.HeightMapResolution = EditorGUILayout.IntPopup(Styles.HeightTextureRes, m_Settings.HeightMapResolution, ToolboxHelper.GUIHeightmapResolutionNames, ToolboxHelper.GUIHeightmapResolutions); } void ShowTreeAndDetailSettings() { m_Settings.DrawTreesAndFoliage = EditorGUILayout.Toggle(Styles.DrawTrees, m_Settings.DrawTreesAndFoliage); m_Settings.BakeLightProbesForTrees = EditorGUILayout.Toggle("Bake Light Probes For Trees", m_Settings.BakeLightProbesForTrees); m_Settings.DeringLightProbesForTrees = EditorGUILayout.Toggle("Remove Light Probe Ringing", m_Settings.DeringLightProbesForTrees); m_Settings.PreserveTreePrototypeLayers = EditorGUILayout.Toggle("Preserve Tree Prototype Layers", m_Settings.PreserveTreePrototypeLayers); m_Settings.DetailObjectDistance = EditorGUILayout.Slider("Detail Distance", m_Settings.DetailObjectDistance, 0, 250); m_Settings.DetailObjectDensity = EditorGUILayout.Slider("Detail Density", m_Settings.DetailObjectDensity, 0.0f, 1.0f); m_Settings.TreeDistance = EditorGUILayout.Slider("Tree Distance", m_Settings.TreeDistance, 0, 5000); m_Settings.TreeBillboardDistance = EditorGUILayout.Slider("Billboard Start", m_Settings.TreeBillboardDistance, 5, 2000); m_Settings.TreeCrossFadeLength = EditorGUILayout.Slider("Fade Length", m_Settings.TreeCrossFadeLength, 0, 200); m_Settings.TreeMaximumFullLODCount = EditorGUILayout.IntSlider("Max Mesh Trees", m_Settings.TreeMaximumFullLODCount, 0, 10000); } void ShowGrassWindSettings() { m_Settings.WavingGrassStrength = EditorGUILayout.Slider("Speed", m_Settings.WavingGrassStrength, 0, 1); m_Settings.WavingGrassSpeed = EditorGUILayout.Slider("Size", m_Settings.WavingGrassSpeed, 0, 1); m_Settings.WavingGrassAmount = EditorGUILayout.Slider("Bending", m_Settings.WavingGrassAmount, 0, 1); m_Settings.WavingGrassTint = EditorGUILayout.ColorField("Grass Tint", m_Settings.WavingGrassTint); } void ResetToDefaultSettings() { // Copy everything to our current settings. // Since UI settings are not copied, they remain unchanged. m_Settings.CopySettingsFrom(m_DefaultSettings); } void ApplySettingsToAllTerrains(string errorContext) { var terrains = Object.FindObjectsOfType(); if (terrains != null && terrains.Length > 0) { ApplySettingsToTerrains(terrains, errorContext); } } void ApplySettingsToSelectedTerrains() { var terrainObjs = Selection.GetFiltered(typeof(Terrain), SelectionMode.Unfiltered); if (terrainObjs != null && terrainObjs.Length > 0) { Terrain[] terrains = new Terrain[terrainObjs.Length]; for (var i = 0; i < terrainObjs.Length; i++) { terrains[i] = terrainObjs[i] as Terrain; } ApplySettingsToTerrains(terrains); } } void ApplySettingsToTerrains(Terrain[] terrains, string errorContext = "") { int index = 0; try { bool continueEdit = true; // Only show this warning if the user has Mesh Settings enabled. if (m_Settings.EnableMeshResSettings) { var newSize = new Vector3(m_Settings.TerrainWidth, m_Settings.TerrainHeight, m_Settings.TerrainLength); foreach (var terrain in terrains) { // If any terrain has a size that's different from the specified settings, let's confirm // the action. if (terrain.terrainData.size != newSize) { var message = "Some terrains have different sizes than the settings specified. This operation will resize the terrains potentially resulting in gaps and/or overlaps."; if (string.IsNullOrEmpty(errorContext)) { continueEdit = EditorUtility.DisplayDialog("Confirm", $"{message}\nAre you sure you want to continue?", "Continue", "Cancel"); } else { continueEdit = EditorUtility.DisplayDialog("Confirm", $"1. {errorContext}\n2. {message}\n\nAre you sure you want to continue?", "Continue", "Cancel"); } break; } } } if (continueEdit) { foreach (var terrain in terrains) { EditorUtility.DisplayProgressBar("Applying settings changes on terrains", string.Format("Updating terrain tile {0}", terrain.name), ((float)index / terrains.Length)); if (m_Settings.EnableBasicSettings) { Undo.RecordObject(terrain, "Terrain property change"); terrain.groupingID = m_Settings.GroupingID; terrain.allowAutoConnect = m_Settings.AutoConnect; terrain.drawHeightmap = m_Settings.DrawHeightmap; terrain.drawInstanced = m_Settings.DrawInstanced; terrain.heightmapPixelError = m_Settings.PixelError; terrain.basemapDistance = m_Settings.BaseMapDistance; terrain.shadowCastingMode = m_Settings.ShadowCastingMode; terrain.materialTemplate = m_Settings.MaterialTemplate; terrain.reflectionProbeUsage = m_Settings.ReflectionProbeUsage; #if UNITY_2019_2_OR_NEWER #else terrain.materialType = m_Settings.MaterialType; if (m_Settings.MaterialType != Terrain.MaterialType.Custom) { terrain.legacySpecular = m_Settings.LegacySpecular; terrain.legacyShininess = m_Settings.LegacyShininess; } #endif } if (m_Settings.EnableMeshResSettings) { Undo.RecordObject(terrain.terrainData, "TerrainData property change"); Vector3 size = new Vector3(m_Settings.TerrainWidth, m_Settings.TerrainHeight, m_Settings.TerrainLength); terrain.terrainData.size = size; terrain.terrainData.SetDetailResolution(m_Settings.DetailResolutaion, m_Settings.DetailResolutionPerPatch); } if (m_Settings.EnableTextureResSettings) { terrain.terrainData.baseMapResolution = m_Settings.BaseTextureResolution; if (m_Settings.AlphaMapResolution != terrain.terrainData.alphamapResolution) { ToolboxHelper.ResizeControlTexture(terrain.terrainData, m_Settings.AlphaMapResolution); } if (m_Settings.HeightMapResolution != terrain.terrainData.heightmapResolution) { ToolboxHelper.ResizeHeightmap(terrain.terrainData, m_Settings.HeightMapResolution); } } if (m_Settings.EnableTreeSettings) { Undo.RecordObject(terrain, "Terrain property change"); terrain.drawTreesAndFoliage = m_Settings.DrawTreesAndFoliage; terrain.bakeLightProbesForTrees = m_Settings.BakeLightProbesForTrees; terrain.deringLightProbesForTrees = m_Settings.DeringLightProbesForTrees; terrain.preserveTreePrototypeLayers = m_Settings.PreserveTreePrototypeLayers; terrain.detailObjectDistance = m_Settings.DetailObjectDistance; terrain.collectDetailPatches = m_Settings.CollectDetailPatches; terrain.detailObjectDensity = m_Settings.DetailObjectDensity; terrain.treeDistance = m_Settings.TreeDistance; terrain.treeBillboardDistance = m_Settings.TreeBillboardDistance; terrain.treeCrossFadeLength = m_Settings.TreeCrossFadeLength; terrain.treeMaximumFullLODCount = m_Settings.TreeMaximumFullLODCount; } if (m_Settings.EnableWindSettings) { Undo.RecordObject(terrain.terrainData, "TerrainData property change"); terrain.terrainData.wavingGrassStrength = m_Settings.WavingGrassStrength; terrain.terrainData.wavingGrassSpeed = m_Settings.WavingGrassSpeed; terrain.terrainData.wavingGrassAmount = m_Settings.WavingGrassAmount; terrain.terrainData.wavingGrassTint = m_Settings.WavingGrassTint; } index++; } } } finally { AssetDatabase.Refresh(); EditorUtility.ClearProgressBar(); } } void SavePresetSettings() { m_SelectedPreset.CopySettingsFrom(m_Settings); m_SelectedPreset.CopyUISettingsFrom(m_Settings); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } void CreateNewPreset() { string filePath = EditorUtility.SaveFilePanelInProject("Save Terrain Setting Preset", "New Terrain Settings Preset.asset", "asset", ""); if (string.IsNullOrEmpty(filePath)) { return; } if (!File.Exists(filePath)) { m_SelectedPreset = null; var newPreset = ScriptableObject.CreateInstance(); newPreset.CopySettingsFrom(m_Settings); newPreset.CopyUISettingsFrom(m_Settings); AssetDatabase.CreateAsset(newPreset, filePath); } m_SelectedPreset = AssetDatabase.LoadAssetAtPath(filePath); SavePresetSettings(); } bool GetSettingsPreset() { if (m_SelectedPreset == null) { if (EditorUtility.DisplayDialog("Error", "No terrain settings preset found, create a new one?", "OK", "Cancel")) { CreateNewPreset(); return true; } return false; } return true; } void LoadPresetToSettings() { if (m_SelectedPreset == null) { EditorUtility.DisplayDialog("Error", "No selected preset found. Select one to continue.", "OK"); return; } m_Settings.CopySettingsFrom(m_SelectedPreset); m_Settings.CopyUISettingsFrom(m_SelectedPreset); } void LoadSettingsFromSelectedTerrain() { if (!GetSelectedTerrain()) { return; } m_Settings.CopySettingsFrom(m_SelectedTerrain); } public void SaveSettings() { if (m_SelectedPreset != null) { m_Settings.PresetPath = AssetDatabase.GetAssetPath(m_SelectedPreset); } else { m_Settings.PresetPath = string.Empty; } string filePath = ToolboxHelper.GetPrefFilePath(ToolboxHelper.ToolboxPrefsSettings); string settingsData = JsonUtility.ToJson(m_Settings); File.WriteAllText(filePath, settingsData); } public void LoadSettings() { string filePath = ToolboxHelper.GetPrefFilePath(ToolboxHelper.ToolboxPrefsSettings); if (File.Exists(filePath)) { string settingsData = File.ReadAllText(filePath); JsonUtility.FromJsonOverwrite(settingsData, m_Settings); } if (m_Settings.PresetPath == string.Empty) { m_SelectedPreset = null; } else { m_SelectedPreset = AssetDatabase.LoadAssetAtPath(m_Settings.PresetPath, typeof(TerrainSettings)) as TerrainSettings; } } } }