using System; using System.Collections.Generic; using System.IO; using UnityEditor.Rendering.Analytics; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UIElements; namespace UnityEditor.Rendering { [CustomEditor(typeof(Volume))] [CanEditMultipleObjects] sealed class VolumeEditor : Editor { const string k_TemplatePath = "Packages/com.unity.render-pipelines.core/Editor/UXML/VolumeEditor.uxml"; static class Styles { public static readonly string isGlobalDropdownTooltip = L10n.Tr("Global Volumes affect the Camera wherever the Camera is in the Scene. Local Volumes affect the Camera if they encapsulate the Camera within the bounds of their Collider."); public static readonly GUIContent addBoxCollider = EditorGUIUtility.TrTextContent("Add a Box Collider"); public static readonly GUIContent sphereBoxCollider = EditorGUIUtility.TrTextContent("Add a Sphere Collider"); public static readonly GUIContent capsuleBoxCollider = EditorGUIUtility.TrTextContent("Add a Capsule Collider"); public static readonly GUIContent meshBoxCollider = EditorGUIUtility.TrTextContent("Add a Mesh Collider"); public static readonly GUIContent addColliderFixMessage = EditorGUIUtility.TrTextContentWithIcon("Add a Collider to this GameObject to set boundaries for the local Volume.", CoreEditorStyles.iconWarn); public static readonly GUIContent disableColliderFixMessage = EditorGUIUtility.TrTextContentWithIcon("Global Volumes do not need a collider. Disable or remove the collider.", CoreEditorStyles.iconWarn); public static readonly GUIContent enableColliderFixMessage = EditorGUIUtility.TrTextContentWithIcon("Local Volumes need a collider enabled. Enable the collider.", CoreEditorStyles.iconWarn); public static readonly GUIContent newLabel = EditorGUIUtility.TrTextContent("New", "Create a new profile."); public static readonly GUIContent saveLabel = EditorGUIUtility.TrTextContent("Save", "Save the instantiated profile"); public static readonly GUIContent cloneLabel = EditorGUIUtility.TrTextContent("Clone", "Create a new profile and copy the content of the currently assigned profile."); public static readonly GUIContent enableAll = EditorGUIUtility.TrTextContent("Enable All"); public static readonly GUIContent disableAll = EditorGUIUtility.TrTextContent("Disable All"); public static readonly GUIContent removeAll = EditorGUIUtility.TrTextContent("Remove All"); } SerializedProperty m_IsGlobal; SerializedProperty m_Profile; VolumeComponentListEditor m_ComponentList; Volume targetVolume => target as Volume; VolumeProfile profileRef => targetVolume.HasInstantiatedProfile() ? targetVolume.profile : targetVolume.sharedProfile; void OnEnable() { var o = new PropertyFetcher(serializedObject); m_IsGlobal = o.Find(x => x.isGlobal); m_Profile = o.Find(x => x.sharedProfile); } void OnDisable() { m_ComponentList?.Clear(); } public override VisualElement CreateInspectorGUI() { var template = AssetDatabase.LoadAssetAtPath(k_TemplatePath); var root = template.Instantiate(); string ModeIntToString(int modeInt) => modeInt == 1 ? "Global" : "Local"; var modeList = new List { 0, 1 }; var isGlobalDropdown = new PopupField("Mode", modeList, m_IsGlobal.boolValue ? 1 : 0, ModeIntToString, ModeIntToString); isGlobalDropdown.tooltip = Styles.isGlobalDropdownTooltip; isGlobalDropdown.AddToClassList("unity-base-field__aligned"); isGlobalDropdown.RegisterValueChangedCallback(evt => { m_IsGlobal.boolValue = evt.newValue != 0; serializedObject.ApplyModifiedProperties(); }); // This is required to get notified when the property changes through Undo/Redo isGlobalDropdown.TrackPropertyValue(m_IsGlobal, property => { isGlobalDropdown.SetValueWithoutNotify(property.boolValue ? 1 : 0); }); // Note: Must insert directly into root at the appropriate index, using a container VisualElement breaks // alignment to other PropertyFields (achieved via the "unity-base-field__aligned" class). root.Insert(0, isGlobalDropdown); // Fix me boxes var blendDistancePropertyField = root.Q("volume-profile-blend-distance"); root.Q("collider-fixme-box__container").Add(new IMGUIContainer(() => { bool hasCollider = targetVolume.TryGetComponent(out var collider); if (m_IsGlobal.boolValue) // Blend radius is not needed for global volumes { if (hasCollider && collider.enabled) CoreEditorUtils.DrawFixMeBox(Styles.disableColliderFixMessage, () => SetColliderEnabledWithUndo(collider, false)); blendDistancePropertyField.style.display = DisplayStyle.None; } else { if (hasCollider) { if (!collider.enabled) CoreEditorUtils.DrawFixMeBox(Styles.enableColliderFixMessage, () => SetColliderEnabledWithUndo(collider, true)); } else { CoreEditorUtils.DrawFixMeBox(Styles.addColliderFixMessage, AddColliderWithUndo); } blendDistancePropertyField.style.display = DisplayStyle.Flex; } })); var assetIcon = AssetDatabase.LoadAssetAtPath( "Packages/com.unity.render-pipelines.core/Editor/Icons/Processed/d_VolumeProfile Icon.asset"); root.Q("volume-profile-header__asset-icon").image = assetIcon; root.Q("volume-profile-objectfield__contextmenu-image").image = CoreEditorStyles.paneOptionsIcon; root.Q