diff --git a/com.unity.visualeffectgraph/CHANGELOG.md b/com.unity.visualeffectgraph/CHANGELOG.md index d2b250361af..cfa1dc06f74 100644 --- a/com.unity.visualeffectgraph/CHANGELOG.md +++ b/com.unity.visualeffectgraph/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Eye dropper in the color fields kept updating after pressing the Esc key - Automatically offset contexts when a new node is inserted to avoid overlapping - Compilation error while using not exposed texture in ShaderGraph [Case 1367167](https://issuetracker.unity3d.com/product/unity/issues/guid/1367167/) +- Texture picker lists only textures with expected dimensions (2D, 3D, Cubemap) ## [12.0.0] - 2021-01-11 ### Added diff --git a/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/CustomObjectPicker.cs b/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/CustomObjectPicker.cs new file mode 100644 index 00000000000..11c6d4ef59f --- /dev/null +++ b/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/CustomObjectPicker.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +using UnityEditor.Search; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Search; + +using Object = UnityEngine.Object; + +namespace UnityEditor.VFX.UI +{ + static class CustomObjectPicker + { + internal static void Pick(Type type, TextureDimension textureDimension, Action selectHandler) + { + var view = typeof(Texture).IsAssignableFrom(type) + ? GetTexturePickerView(type, textureDimension, selectHandler) + : GetGenericView(type, selectHandler); + + // Until the "viewState" API is made public (should be in 2022.1) we use reflection to remove the inspector button + var quickSearchType = typeof(Search.SearchService).Assembly.GetType("UnityEditor.Search.QuickSearch"); + var viewStateInfo = quickSearchType?.GetProperty("viewState", BindingFlags.Instance | BindingFlags.NonPublic); + var state = viewStateInfo?.GetValue(view); + if (state != null) + { + var flagsInfo = state.GetType().GetField("flags", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + flagsInfo?.SetValue(state, SearchViewFlags.DisableInspectorPreview); + } + } + + static ISearchView GetGenericView(Type type, Action selectHandler) + { + return Search.SearchService.ShowObjectPicker( + selectHandler, + null, + null, + type.Name, + type); + } + + static ISearchView GetTexturePickerView(Type type, TextureDimension textureDimension, Action selectHandler) + { + var view = Search.SearchService.ShowPicker( + Search.SearchService.CreateContext(CreateTextureProvider(type, textureDimension)), + (x, y) => selectHandler(x?.ToObject(), y), + null, + null, + null, + type.Name, + 5f); + view.itemIconSize = 5f; + + return view; + } + + static SearchProvider CreateTextureProvider(Type type, TextureDimension textureDimension) + { + return new SearchProvider("tex", "Texture", (context, _) => FetchTextures(type, textureDimension, context)); + } + + static IEnumerable FetchTextures(Type type, TextureDimension textureDimension, SearchContext context) + { + // This piece of code is meant to put RenderTextures in a separate tab + // But the display is right now buggy, so keep it for later use when display issue is fixed + //var createGroupProviderMethod = typeof(Search.SearchUtils).GetMethod("CreateGroupProvider", BindingFlags.NonPublic|BindingFlags.Static); + //SearchProvider textureGroupProvider = null; + //SearchProvider renderTextureGroupProvider = null; + //if (createGroupProviderMethod != null) + //{ + // textureGroupProvider = createGroupProviderMethod.Invoke(null, new object[] { adbProvider, type.Name, 0, true }) as SearchProvider; + // renderTextureGroupProvider = createGroupProviderMethod.Invoke(null, new object[] { adbProvider, "Render Textures", 1, true }) as SearchProvider;; + //} + + var userQuery = context.searchQuery; + var providers = new[] { Search.SearchService.GetProvider("adb") }; + + using (var query = Search.SearchService.CreateContext(providers, $"t:{type.Name} {userQuery}", context.options)) + using (var request = Search.SearchService.Request(query)) + { + foreach (var r in request) + { + //r.provider = textureGroupProvider; + yield return r; + } + } + + if (type != typeof(RenderTexture)) + { + using (var query = Search.SearchService.CreateContext(providers, $"t:{nameof(RenderTexture)} {userQuery}", context.options)) + using (var request = Search.SearchService.Request(query)) + { + foreach (var r in request) + { + if (r == null) continue; + var rt = r.ToObject(); + if (rt.dimension == textureDimension) + { + //r.provider = renderTextureGroupProvider; + yield return r; + } + } + } + } + } + } +} diff --git a/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/CustomObjectPicker.cs.meta b/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/CustomObjectPicker.cs.meta new file mode 100644 index 00000000000..8762993af72 --- /dev/null +++ b/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/CustomObjectPicker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 98b9e46b09f5f0c41b5a5aeac1a08222 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/ObjectPropertyRM.cs b/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/ObjectPropertyRM.cs index 23f47b29360..bb398324789 100644 --- a/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/ObjectPropertyRM.cs +++ b/com.unity.visualeffectgraph/Editor/GraphView/Views/Properties/ObjectPropertyRM.cs @@ -1,105 +1,62 @@ +using System; using System.Collections.Generic; -using System.Reflection; +using System.Linq; + using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UIElements; -using UnityEditor.UIElements; -using UnityEditor.VFX; -using UnityEditor.VFX.UIElements; -using UnityObject = UnityEngine.Object; -using Type = System.Type; -using ObjectField = UnityEditor.VFX.UI.VFXLabeledField; +using UnityObject = UnityEngine.Object; namespace UnityEditor.VFX.UI { class ObjectPropertyRM : PropertyRM { - public ObjectPropertyRM(IPropertyRMProvider controller, float labelWidth) : base(controller, labelWidth) + static readonly Dictionary s_TypeToDimensionMap = new() { - m_ObjectField = new ObjectField(m_Label); - if (controller.portType == typeof(Texture2D) || controller.portType == typeof(Texture3D) || controller.portType == typeof(Cubemap)) - m_ObjectField.control.objectType = typeof(Texture); - else - m_ObjectField.control.objectType = controller.portType; - - m_ObjectField.RegisterCallback>(OnValueChanged); - m_ObjectField.control.allowSceneObjects = false; - m_ObjectField.style.flexGrow = 1f; - m_ObjectField.style.flexShrink = 1f; - RegisterCallback(StopKeyPropagation); - Add(m_ObjectField); - } + { typeof(Texture2D), TextureDimension.Tex2D }, + { typeof(Texture3D), TextureDimension.Tex3D }, + { typeof(Cubemap), TextureDimension.Cube }, + }; - public override float GetPreferredControlWidth() - { - return 120; - } + readonly TextField m_TextField; + readonly Image m_ValueIcon; + readonly TextureDimension m_textureDimension; - void StopKeyPropagation(KeyDownEvent e) + public ObjectPropertyRM(IPropertyRMProvider controller, float labelWidth) : base(controller, labelWidth) { - e.StopPropagation(); - } + styleSheets.Add(VFXView.LoadStyleSheet("ObjectPropertyRM")); - public void OnValueChanged(ChangeEvent onObjectChanged) - { - UnityObject newValue = m_ObjectField.value; - if (typeof(Texture).IsAssignableFrom(m_Provider.portType)) - { - Texture tex = newValue as Texture; + m_TextField = new TextField { name = "PickLabel", isReadOnly = true }; + var button = new Button { name = "PickButton" }; + var icon = new VisualElement { name = "PickIcon" }; + m_ValueIcon = new Image { name = "TextureIcon" }; - if (tex != null) - { - if (m_Provider.portType == typeof(Texture2D)) - { - if (tex.dimension != TextureDimension.Tex2D) - { - Debug.LogError("Wrong Texture Dimension"); - - newValue = null; - } - } - else if (m_Provider.portType == typeof(Texture3D)) - { - if (tex.dimension != TextureDimension.Tex3D) - { - Debug.LogError("Wrong Texture Dimension"); - - newValue = null; - } - } - else if (m_Provider.portType == typeof(Cubemap)) - { - if (tex.dimension != TextureDimension.Cube) - { - Debug.LogError("Wrong Texture Dimension"); - - newValue = null; - } - } - } - } - m_Value = newValue; - NotifyValueChanged(); - } + button.clicked += OnPickObject; + button.Add(icon); + m_TextField.Add(m_ValueIcon); + m_TextField.Add(button); + Add(m_TextField); - ObjectField m_ObjectField; + m_TextField.RegisterCallback(OnClickToShow); + RegisterCallback(OnDragUpdate); + RegisterCallback(OnDragEnter); + RegisterCallback(OnDragPerformed); - protected override void UpdateEnabled() - { - m_ObjectField.SetEnabled(propertyEnabled); + if (!s_TypeToDimensionMap.TryGetValue(m_Provider.portType, out m_textureDimension)) + { + m_textureDimension = TextureDimension.Unknown; + } } - protected override void UpdateIndeterminate() - { - m_ObjectField.visible = !indeterminate; - } + public override float GetPreferredControlWidth() => 120; public override void UpdateGUI(bool force) { if (force) - m_ObjectField.SetValueWithoutNotify(null); - m_ObjectField.SetValueWithoutNotify(m_Value); + { + NotifyValueChanged(); + } } public override void SetValue(object obj) // object setvalue should accept null @@ -107,15 +64,76 @@ public override void SetValue(object obj) // object setvalue should accept null try { m_Value = (UnityObject)obj; + m_ValueIcon.image = obj != null + ? AssetPreview.GetMiniTypeThumbnail(m_Value) + : AssetPreview.GetMiniTypeThumbnail(m_Provider.portType); + m_TextField.value = m_Value?.name ?? $"None ({m_Provider.portType.Name})"; } - catch (System.Exception) + catch (Exception) { - Debug.Log("Error Trying to convert" + (obj != null ? obj.GetType().Name : "null") + " to " + typeof(UnityObject).Name); + Debug.Log($"Error Trying to convert {obj?.GetType().Name ?? "null"} to Object"); } - UpdateGUI(!object.ReferenceEquals(m_Value, obj)); + UpdateGUI(!ReferenceEquals(m_Value, obj)); } - public override bool showsEverything { get { return true; } } + public override bool showsEverything => true; + + protected override void UpdateEnabled() => SetEnabled(propertyEnabled); + + protected override void UpdateIndeterminate() => visible = !indeterminate; + + void OnPickObject() => CustomObjectPicker.Pick(m_Provider.portType, m_textureDimension, SelectHandler); + + void SelectHandler(UnityObject obj, bool isCanceled) + { + if (!isCanceled) + { + SetValue(obj); + NotifyValueChanged(); + } + } + + void OnClickToShow(ClickEvent evt) + { + EditorGUI.PingObjectOrShowPreviewOnClick(m_Value, Rect.zero); + } + + bool CanDrag() + { + if (DragAndDrop.objectReferences.Length == 1) + { + var type = DragAndDrop.objectReferences[0].GetType(); + if (m_Provider.portType.IsAssignableFrom(type)) + { + return true; + } + + if (m_textureDimension != TextureDimension.Unknown && DragAndDrop.objectReferences[0] is Texture texture) + { + return texture.dimension == m_textureDimension; + } + } + + return false; + } + + void OnDragEnter(DragEnterEvent evt) + { + DragAndDrop.visualMode = CanDrag() ? DragAndDropVisualMode.Link : DragAndDropVisualMode.Rejected; + evt.StopPropagation(); + } + + void OnDragUpdate(DragUpdatedEvent evt) + { + DragAndDrop.visualMode = CanDrag() ? DragAndDropVisualMode.Link : DragAndDropVisualMode.Rejected; + evt.StopPropagation(); + } + + void OnDragPerformed(DragPerformEvent evt) + { + var dragObject = DragAndDrop.objectReferences.First(); + SelectHandler(dragObject, false); + } } } diff --git a/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTexture2D.cs b/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTexture2D.cs index 74ae425528e..7fda6acd61b 100644 --- a/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTexture2D.cs +++ b/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTexture2D.cs @@ -1,14 +1,19 @@ -using System; -using System.Collections.Generic; using UnityEngine; - -using UnityObject = UnityEngine.Object; +using UnityEngine.Rendering; namespace UnityEditor.VFX { [VFXInfo(type = typeof(Texture2D))] class VFXSlotTexture2D : VFXSlotObject { + protected override void GenerateErrors(VFXInvalidateErrorReporter manager) + { + if (value is Texture texture && texture.dimension != TextureDimension.Tex2D) + manager.RegisterError("Slot_Value_Incorrect_Texture2D", VFXErrorType.Error, "This slot expects a Texture2D"); + + base.GenerateErrors(manager); + } + public override VFXValue DefaultExpression(VFXValue.Mode mode) { return new VFXTexture2DValue(0, mode); diff --git a/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTexture3D.cs b/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTexture3D.cs index b43f3180a7c..0e3fc4b452f 100644 --- a/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTexture3D.cs +++ b/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTexture3D.cs @@ -1,6 +1,4 @@ -using System; using UnityEngine; -using UnityEngine.VFX; using UnityEngine.Rendering; @@ -9,6 +7,14 @@ namespace UnityEditor.VFX [VFXInfo(type = typeof(Texture3D))] class VFXSlotTexture3D : VFXSlotObject { + protected override void GenerateErrors(VFXInvalidateErrorReporter manager) + { + if (value is Texture texture && texture.dimension != TextureDimension.Tex3D) + manager.RegisterError("Slot_Value_Incorrect_Texture3D", VFXErrorType.Error, "This slot expects a Texture3D"); + + base.GenerateErrors(manager); + } + public override VFXValue DefaultExpression(VFXValue.Mode mode) { return new VFXTexture3DValue(0, mode); diff --git a/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTextureCube.cs b/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTextureCube.cs index 525da6bf877..e4dbe55e5a3 100644 --- a/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTextureCube.cs +++ b/com.unity.visualeffectgraph/Editor/Models/Slots/Implementations/VFXSlotTextureCube.cs @@ -8,6 +8,14 @@ namespace UnityEditor.VFX [VFXInfo(type = typeof(Cubemap))] class VFXSlotTextureCube : VFXSlotObject { + protected override void GenerateErrors(VFXInvalidateErrorReporter manager) + { + if (value is Texture texture && texture.dimension != TextureDimension.Cube) + manager.RegisterError("Slot_Value_Incorrect_TextureCube", VFXErrorType.Error, "This slot expects a Cubemap"); + + base.GenerateErrors(manager); + } + public override VFXValue DefaultExpression(VFXValue.Mode mode) { return new VFXTextureCubeValue(0, mode); diff --git a/com.unity.visualeffectgraph/Editor/UIResources/uss/ObjectPropertyRM.uss b/com.unity.visualeffectgraph/Editor/UIResources/uss/ObjectPropertyRM.uss new file mode 100644 index 00000000000..0962f1b6400 --- /dev/null +++ b/com.unity.visualeffectgraph/Editor/UIResources/uss/ObjectPropertyRM.uss @@ -0,0 +1,44 @@ +#PickIcon +{ + width: 12px; + height: 12px; + background-image: var(--unity-icons-picker); +} + +#PickButton +{ + margin: 0px 1px 0px -21px; + padding: 0; + border-width: 0; + border-bottom-left-radius: 0px; + border-top-left-radius: 0px; + width:16px; + height:15px; + justify-content: center; + align-items: center; + align-self: center; + background-color: var(--unity-colors-object_field_button-background); +} + +#PickLabel +{ + flex-grow: 1; + flex-shrink: 1; + margin: 1px 0; +} + + +#TextureIcon +{ + margin-left: 2px; + position: absolute; + align-self: center; + width: 12px; + height: 12px; +} + +#PickLabel > TextInput +{ + margin-right: 4px; + padding: 0 22px 0 18px; +} diff --git a/com.unity.visualeffectgraph/Editor/UIResources/uss/ObjectPropertyRM.uss.meta b/com.unity.visualeffectgraph/Editor/UIResources/uss/ObjectPropertyRM.uss.meta new file mode 100644 index 00000000000..124da0506ed --- /dev/null +++ b/com.unity.visualeffectgraph/Editor/UIResources/uss/ObjectPropertyRM.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b40791d0947b6d94fbabbb8da581185f +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0