initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 465f456071d65c648a34adddf33057f7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
class BlackboardInputInfo : Attribute
|
||||
{
|
||||
public float priority;
|
||||
public string name;
|
||||
|
||||
/// <summary>
|
||||
/// Provide additional information to provide the blackboard for order and name of the ShaderInput item.
|
||||
/// </summary>
|
||||
/// <param name="priority">Priority of the item, higher values will result in lower positions in the menu.</param>
|
||||
/// <param name="name">Name of the item. If null, the class name of the item will be used instead.</param>
|
||||
public BlackboardInputInfo(float priority, string name = null)
|
||||
{
|
||||
this.priority = priority;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ca3677c253614a948be3cf8f0d17e254
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
static class BlackboardUtils
|
||||
{
|
||||
internal static int GetInsertionIndex(VisualElement owner, Vector2 position, IEnumerable<VisualElement> children)
|
||||
{
|
||||
var index = -1;
|
||||
if (owner.ContainsPoint(position))
|
||||
{
|
||||
index = 0;
|
||||
foreach (VisualElement child in children)
|
||||
{
|
||||
Rect rect = child.layout;
|
||||
|
||||
if (position.y > (rect.y + rect.height / 2))
|
||||
{
|
||||
++index;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
internal static string FormatPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return "—";
|
||||
return path;
|
||||
}
|
||||
|
||||
internal static string SanitizePath(string path)
|
||||
{
|
||||
var splitString = path.Split('/');
|
||||
List<string> newStrings = new List<string>();
|
||||
foreach (string s in splitString)
|
||||
{
|
||||
var str = s.Trim();
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
{
|
||||
newStrings.Add(str);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join("/", newStrings.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 956e673aa3cb4690b9c44d970ffd8e65
|
||||
timeCreated: 1611700923
|
|
@ -0,0 +1,582 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Drawing.Views;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.ShaderGraph;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class BlackboardGroupInfo
|
||||
{
|
||||
[SerializeField]
|
||||
SerializableGuid m_Guid = new SerializableGuid();
|
||||
|
||||
internal Guid guid => m_Guid.guid;
|
||||
|
||||
[SerializeField]
|
||||
string m_GroupName;
|
||||
|
||||
internal string GroupName
|
||||
{
|
||||
get => m_GroupName;
|
||||
set => m_GroupName = value;
|
||||
}
|
||||
|
||||
BlackboardGroupInfo()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class SGBlackboard : GraphSubWindow, ISGControlledElement<BlackboardController>
|
||||
{
|
||||
VisualElement m_ScrollBoundaryTop;
|
||||
VisualElement m_ScrollBoundaryBottom;
|
||||
VisualElement m_BottomResizer;
|
||||
TextField m_PathLabelTextField;
|
||||
public VisualElement m_VariantExceededHelpBox;
|
||||
|
||||
// --- Begin ISGControlledElement implementation
|
||||
public void OnControllerChanged(ref SGControllerChangedEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnControllerEvent(SGControllerEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetCurrentVariantUsage(int currentVariantCount, int maxVariantCount)
|
||||
{
|
||||
if (currentVariantCount < maxVariantCount && m_VariantExceededHelpBox != null)
|
||||
{
|
||||
RemoveAt(0);
|
||||
m_VariantExceededHelpBox = null;
|
||||
}
|
||||
else if (maxVariantCount <= currentVariantCount && m_VariantExceededHelpBox == null)
|
||||
{
|
||||
var helpBox = HelpBoxRow.CreateVariantLimitHelpBox(currentVariantCount, maxVariantCount);
|
||||
m_VariantExceededHelpBox = helpBox;
|
||||
Insert(0, helpBox);
|
||||
}
|
||||
}
|
||||
|
||||
public BlackboardController controller
|
||||
{
|
||||
get => m_Controller;
|
||||
set
|
||||
{
|
||||
if (m_Controller != value)
|
||||
{
|
||||
if (m_Controller != null)
|
||||
{
|
||||
m_Controller.UnregisterHandler(this);
|
||||
}
|
||||
m_Controller = value;
|
||||
|
||||
if (m_Controller != null)
|
||||
{
|
||||
m_Controller.RegisterHandler(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SGController ISGControlledElement.controller => m_Controller;
|
||||
|
||||
// --- ISGControlledElement implementation
|
||||
|
||||
BlackboardController m_Controller;
|
||||
|
||||
BlackboardViewModel m_ViewModel;
|
||||
|
||||
BlackboardViewModel ViewModel
|
||||
{
|
||||
get => m_ViewModel;
|
||||
set => m_ViewModel = value;
|
||||
}
|
||||
|
||||
// List of user-made blackboard category views
|
||||
IList<SGBlackboardCategory> m_BlackboardCategories = new List<SGBlackboardCategory>();
|
||||
|
||||
bool m_ScrollToTop = false;
|
||||
bool m_ScrollToBottom = false;
|
||||
bool m_EditPathCancelled = false;
|
||||
bool m_IsUserDraggingItems = false;
|
||||
int m_InsertIndex = -1;
|
||||
|
||||
const int k_DraggedPropertyScrollSpeed = 6;
|
||||
|
||||
public override string windowTitle => "Blackboard";
|
||||
public override string elementName => "SGBlackboard";
|
||||
public override string styleName => "SGBlackboard";
|
||||
public override string UxmlName => "Blackboard/SGBlackboard";
|
||||
public override string layoutKey => "UnityEditor.ShaderGraph.Blackboard";
|
||||
|
||||
Action addItemRequested { get; set; }
|
||||
|
||||
internal Action hideDragIndicatorAction { get; set; }
|
||||
|
||||
GenericMenu m_AddBlackboardItemMenu;
|
||||
internal GenericMenu addBlackboardItemMenu => m_AddBlackboardItemMenu;
|
||||
|
||||
VisualElement m_DragIndicator;
|
||||
|
||||
public SGBlackboard(BlackboardViewModel viewModel, BlackboardController controller) : base(viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
this.controller = controller;
|
||||
|
||||
InitializeAddBlackboardItemMenu();
|
||||
|
||||
// By default dock blackboard to left of graph window
|
||||
windowDockingLayout.dockingLeft = true;
|
||||
|
||||
if (m_MainContainer.Q(name: "addButton") is Button addButton)
|
||||
addButton.clickable.clicked += () =>
|
||||
{
|
||||
InitializeAddBlackboardItemMenu();
|
||||
addItemRequested?.Invoke();
|
||||
ShowAddPropertyMenu();
|
||||
};
|
||||
|
||||
ParentView.RegisterCallback<FocusOutEvent>(evt => OnDragExitedEvent(new DragExitedEvent()));
|
||||
|
||||
m_TitleLabel.text = ViewModel.title;
|
||||
|
||||
m_SubTitleLabel.RegisterCallback<MouseDownEvent>(OnMouseDownEvent);
|
||||
m_SubTitleLabel.text = ViewModel.subtitle;
|
||||
|
||||
m_PathLabelTextField = this.Q<TextField>("subTitleTextField");
|
||||
m_PathLabelTextField.value = ViewModel.subtitle;
|
||||
m_PathLabelTextField.visible = false;
|
||||
m_PathLabelTextField.Q("unity-text-input").RegisterCallback<FocusOutEvent>(e => { OnEditPathTextFinished(); });
|
||||
m_PathLabelTextField.Q("unity-text-input").RegisterCallback<KeyDownEvent>(OnPathTextFieldKeyPressed);
|
||||
|
||||
// These callbacks make sure the scroll boundary regions and drag indicator don't show up user is not dragging/dropping properties/categories
|
||||
RegisterCallback<MouseUpEvent>(OnMouseUpEvent);
|
||||
RegisterCallback<DragExitedEvent>(OnDragExitedEvent);
|
||||
|
||||
// Register drag callbacks
|
||||
RegisterCallback<DragUpdatedEvent>(OnDragUpdatedEvent);
|
||||
RegisterCallback<DragPerformEvent>(OnDragPerformEvent);
|
||||
RegisterCallback<DragLeaveEvent>(OnDragLeaveEvent);
|
||||
RegisterCallback<DragExitedEvent>(OnDragExitedEvent);
|
||||
|
||||
// This callback makes sure the drag indicator is shown again if user exits and then re-enters blackboard while dragging
|
||||
RegisterCallback<MouseEnterEvent>(OnMouseEnterEvent);
|
||||
|
||||
m_ScrollBoundaryTop = m_MainContainer.Q(name: "scrollBoundaryTop");
|
||||
m_ScrollBoundaryTop.RegisterCallback<MouseEnterEvent>(ScrollRegionTopEnter);
|
||||
m_ScrollBoundaryTop.RegisterCallback<DragUpdatedEvent>(OnFieldDragUpdate);
|
||||
m_ScrollBoundaryTop.RegisterCallback<MouseLeaveEvent>(ScrollRegionTopLeave);
|
||||
|
||||
m_ScrollBoundaryBottom = m_MainContainer.Q(name: "scrollBoundaryBottom");
|
||||
m_ScrollBoundaryBottom.RegisterCallback<MouseEnterEvent>(ScrollRegionBottomEnter);
|
||||
m_ScrollBoundaryBottom.RegisterCallback<DragUpdatedEvent>(OnFieldDragUpdate);
|
||||
m_ScrollBoundaryBottom.RegisterCallback<MouseLeaveEvent>(ScrollRegionBottomLeave);
|
||||
|
||||
m_BottomResizer = m_MainContainer.Q("bottom-resize");
|
||||
|
||||
HideScrollBoundaryRegions();
|
||||
|
||||
// Sets delegate association so scroll boundary regions are hidden when a blackboard property is dropped into graph
|
||||
if (ParentView is MaterialGraphView materialGraphView)
|
||||
materialGraphView.blackboardFieldDropDelegate = HideScrollBoundaryRegions;
|
||||
|
||||
isWindowScrollable = true;
|
||||
isWindowResizable = true;
|
||||
focusable = true;
|
||||
|
||||
m_DragIndicator = new VisualElement();
|
||||
m_DragIndicator.name = "categoryDragIndicator";
|
||||
m_DragIndicator.style.position = Position.Absolute;
|
||||
hierarchy.Add(m_DragIndicator);
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
}
|
||||
|
||||
void SetCategoryDragIndicatorVisible(bool visible)
|
||||
{
|
||||
if (visible && (m_DragIndicator.parent == null))
|
||||
{
|
||||
hierarchy.Add(m_DragIndicator);
|
||||
m_DragIndicator.visible = true;
|
||||
}
|
||||
else if ((visible == false) && (m_DragIndicator.parent != null))
|
||||
{
|
||||
hierarchy.Remove(m_DragIndicator);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDragEnterEvent(DragEnterEvent evt)
|
||||
{
|
||||
if (!m_IsUserDraggingItems)
|
||||
{
|
||||
m_IsUserDraggingItems = true;
|
||||
|
||||
if (scrollableHeight > 0)
|
||||
{
|
||||
// Interferes with scrolling functionality of properties with the bottom scroll boundary
|
||||
m_BottomResizer.style.visibility = Visibility.Hidden;
|
||||
|
||||
var contentElement = m_MainContainer.Q(name: "content");
|
||||
scrollViewIndex = contentElement.IndexOf(m_ScrollView);
|
||||
contentElement.Insert(scrollViewIndex, m_ScrollBoundaryTop);
|
||||
scrollViewIndex = contentElement.IndexOf(m_ScrollView);
|
||||
contentElement.Insert(scrollViewIndex + 1, m_ScrollBoundaryBottom);
|
||||
}
|
||||
|
||||
// If there are any categories in the selection, show drag indicator, otherwise hide
|
||||
SetCategoryDragIndicatorVisible(selection.OfType<SGBlackboardCategory>().Any());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDragExitedEvent(DragExitedEvent evt)
|
||||
{
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
HideScrollBoundaryRegions();
|
||||
}
|
||||
|
||||
void OnMouseEnterEvent(MouseEnterEvent evt)
|
||||
{
|
||||
if (m_IsUserDraggingItems && selection.OfType<SGBlackboardCategory>().Any())
|
||||
SetCategoryDragIndicatorVisible(true);
|
||||
}
|
||||
|
||||
void HideScrollBoundaryRegions()
|
||||
{
|
||||
m_BottomResizer.style.visibility = Visibility.Visible;
|
||||
m_IsUserDraggingItems = false;
|
||||
m_ScrollBoundaryTop.RemoveFromHierarchy();
|
||||
m_ScrollBoundaryBottom.RemoveFromHierarchy();
|
||||
}
|
||||
|
||||
int InsertionIndex(Vector2 pos)
|
||||
{
|
||||
VisualElement owner = contentContainer != null ? contentContainer : this;
|
||||
Vector2 localPos = this.ChangeCoordinatesTo(owner, pos);
|
||||
|
||||
int index = BlackboardUtils.GetInsertionIndex(owner, localPos, Children());
|
||||
|
||||
// Clamps the index between the min and max of the child indices based on the mouse position relative to the categories on the y-axis (up/down)
|
||||
// Checking for at least 2 children to make sure Children.First() and Children.Last() don't throw an exception
|
||||
if (index == -1 && childCount >= 2)
|
||||
{
|
||||
index = localPos.y < Children().First().layout.yMin ? 0 :
|
||||
localPos.y > Children().Last().layout.yMax ? childCount : -1;
|
||||
}
|
||||
|
||||
// Don't allow the default category to be displaced
|
||||
return Mathf.Clamp(index, 1, index);
|
||||
}
|
||||
|
||||
void OnDragUpdatedEvent(DragUpdatedEvent evt)
|
||||
{
|
||||
var selection = DragAndDrop.GetGenericData("DragSelection") as List<ISelectable>;
|
||||
if (selection == null)
|
||||
{
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ISelectable selectedElement in selection)
|
||||
{
|
||||
var sourceItem = selectedElement as VisualElement;
|
||||
// Don't allow user to move the default category
|
||||
if (sourceItem is SGBlackboardCategory blackboardCategory && blackboardCategory.controller.Model.IsNamedCategory() == false)
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 localPosition = evt.localMousePosition;
|
||||
m_InsertIndex = InsertionIndex(localPosition);
|
||||
|
||||
if (m_InsertIndex != -1)
|
||||
{
|
||||
float indicatorY = 0;
|
||||
if (m_InsertIndex == childCount)
|
||||
{
|
||||
if (childCount > 0)
|
||||
{
|
||||
VisualElement lastChild = this[childCount - 1];
|
||||
indicatorY = lastChild.ChangeCoordinatesTo(this, new Vector2(0, lastChild.layout.height + lastChild.resolvedStyle.marginBottom)).y;
|
||||
}
|
||||
else
|
||||
{
|
||||
indicatorY = this.contentRect.height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualElement childAtInsertIndex = this[m_InsertIndex];
|
||||
indicatorY = childAtInsertIndex.ChangeCoordinatesTo(this, new Vector2(0, -childAtInsertIndex.resolvedStyle.marginTop)).y;
|
||||
}
|
||||
|
||||
m_DragIndicator.style.top = indicatorY - m_DragIndicator.resolvedStyle.height * 0.5f;
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Move;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
}
|
||||
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
void OnDragPerformEvent(DragPerformEvent evt)
|
||||
{
|
||||
// Don't bubble up drop operations onto blackboard upto the graph view, as it leads to nodes being created without users knowledge behind the blackboard
|
||||
evt.StopPropagation();
|
||||
|
||||
var selection = DragAndDrop.GetGenericData("DragSelection") as List<ISelectable>;
|
||||
if (selection == null || selection.Count == 0)
|
||||
{
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the category drag indicator if no categories in selection
|
||||
if (!selection.OfType<SGBlackboardCategory>().Any())
|
||||
{
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
}
|
||||
|
||||
Vector2 localPosition = evt.localMousePosition;
|
||||
m_InsertIndex = InsertionIndex(localPosition);
|
||||
|
||||
// Any categories in the selection that are from other graphs, would have to be copied as opposed to moving the categories within the same graph
|
||||
foreach (var item in selection.ToList())
|
||||
{
|
||||
if (item is SGBlackboardCategory category)
|
||||
{
|
||||
var selectedCategoryData = category.controller.Model;
|
||||
bool doesCategoryExistInGraph = controller.Model.ContainsCategory(selectedCategoryData);
|
||||
if (doesCategoryExistInGraph == false)
|
||||
{
|
||||
var copyCategoryAction = new CopyCategoryAction();
|
||||
copyCategoryAction.categoryToCopyReference = selectedCategoryData;
|
||||
ViewModel.requestModelChangeAction(copyCategoryAction);
|
||||
selection.Remove(item);
|
||||
|
||||
// Remove any child inputs that belong to this category from the selection, to prevent duplicates from being copied onto the graph
|
||||
foreach (var otherItem in selection.ToList())
|
||||
{
|
||||
if (otherItem is SGBlackboardField blackboardField && category.Contains(blackboardField))
|
||||
selection.Remove(otherItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above, but for blackboard items (properties, keywords, dropdowns)
|
||||
foreach (var item in selection.ToList())
|
||||
{
|
||||
if (item is SGBlackboardField blackboardField)
|
||||
{
|
||||
var selectedBlackboardItem = blackboardField.controller.Model;
|
||||
bool doesInputExistInGraph = controller.Model.ContainsInput(selectedBlackboardItem);
|
||||
if (doesInputExistInGraph == false)
|
||||
{
|
||||
var copyShaderInputAction = new CopyShaderInputAction();
|
||||
copyShaderInputAction.shaderInputToCopy = selectedBlackboardItem;
|
||||
ViewModel.requestModelChangeAction(copyShaderInputAction);
|
||||
selection.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var moveCategoryAction = new MoveCategoryAction();
|
||||
moveCategoryAction.newIndexValue = m_InsertIndex;
|
||||
moveCategoryAction.categoryGuids = selection.OfType<SGBlackboardCategory>().OrderBy(sgcat => sgcat.GetPosition().y).Select(cat => cat.viewModel.associatedCategoryGuid).ToList();
|
||||
ViewModel.requestModelChangeAction(moveCategoryAction);
|
||||
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
}
|
||||
|
||||
void OnDragLeaveEvent(DragLeaveEvent evt)
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
m_InsertIndex = -1;
|
||||
}
|
||||
|
||||
int scrollViewIndex { get; set; }
|
||||
|
||||
void ScrollRegionTopEnter(MouseEnterEvent mouseEnterEvent)
|
||||
{
|
||||
if (m_IsUserDraggingItems)
|
||||
{
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
m_ScrollToTop = true;
|
||||
m_ScrollToBottom = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollRegionTopLeave(MouseLeaveEvent mouseLeaveEvent)
|
||||
{
|
||||
if (m_IsUserDraggingItems)
|
||||
{
|
||||
m_ScrollToTop = false;
|
||||
// If there are any categories in the selection, show drag indicator, otherwise hide
|
||||
SetCategoryDragIndicatorVisible(selection.OfType<SGBlackboardCategory>().Any());
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollRegionBottomEnter(MouseEnterEvent mouseEnterEvent)
|
||||
{
|
||||
if (m_IsUserDraggingItems)
|
||||
{
|
||||
SetCategoryDragIndicatorVisible(false);
|
||||
m_ScrollToBottom = true;
|
||||
m_ScrollToTop = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollRegionBottomLeave(MouseLeaveEvent mouseLeaveEvent)
|
||||
{
|
||||
if (m_IsUserDraggingItems)
|
||||
{
|
||||
m_ScrollToBottom = false;
|
||||
// If there are any categories in the selection, show drag indicator, otherwise hide
|
||||
SetCategoryDragIndicatorVisible(selection.OfType<SGBlackboardCategory>().Any());
|
||||
}
|
||||
}
|
||||
|
||||
void OnFieldDragUpdate(DragUpdatedEvent dragUpdatedEvent)
|
||||
{
|
||||
if (m_ScrollToTop)
|
||||
m_ScrollView.scrollOffset = new Vector2(m_ScrollView.scrollOffset.x, Mathf.Clamp(m_ScrollView.scrollOffset.y - k_DraggedPropertyScrollSpeed, 0, scrollableHeight));
|
||||
else if (m_ScrollToBottom)
|
||||
m_ScrollView.scrollOffset = new Vector2(m_ScrollView.scrollOffset.x, Mathf.Clamp(m_ScrollView.scrollOffset.y + k_DraggedPropertyScrollSpeed, 0, scrollableHeight));
|
||||
}
|
||||
|
||||
void InitializeAddBlackboardItemMenu()
|
||||
{
|
||||
m_AddBlackboardItemMenu = new GenericMenu();
|
||||
|
||||
if (ViewModel == null)
|
||||
{
|
||||
AssertHelpers.Fail("SGBlackboard: View Model is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add category at top, followed by separator
|
||||
m_AddBlackboardItemMenu.AddItem(new GUIContent("Category"), false, () => ViewModel.requestModelChangeAction(ViewModel.addCategoryAction));
|
||||
m_AddBlackboardItemMenu.AddSeparator($"/");
|
||||
|
||||
var selectedCategoryGuid = controller.GetFirstSelectedCategoryGuid();
|
||||
foreach (var nameToAddActionTuple in ViewModel.propertyNameToAddActionMap)
|
||||
{
|
||||
string propertyName = nameToAddActionTuple.Key;
|
||||
AddShaderInputAction addAction = nameToAddActionTuple.Value as AddShaderInputAction;
|
||||
addAction.categoryToAddItemToGuid = selectedCategoryGuid;
|
||||
m_AddBlackboardItemMenu.AddItem(new GUIContent(propertyName), false, () => ViewModel.requestModelChangeAction(addAction));
|
||||
}
|
||||
m_AddBlackboardItemMenu.AddSeparator($"/");
|
||||
|
||||
foreach (var nameToAddActionTuple in ViewModel.defaultKeywordNameToAddActionMap)
|
||||
{
|
||||
string defaultKeywordName = nameToAddActionTuple.Key;
|
||||
AddShaderInputAction addAction = nameToAddActionTuple.Value as AddShaderInputAction;
|
||||
addAction.categoryToAddItemToGuid = selectedCategoryGuid;
|
||||
m_AddBlackboardItemMenu.AddItem(new GUIContent($"Keyword/{defaultKeywordName}"), false, () => ViewModel.requestModelChangeAction(addAction));
|
||||
}
|
||||
m_AddBlackboardItemMenu.AddSeparator($"Keyword/");
|
||||
|
||||
foreach (var nameToAddActionTuple in ViewModel.builtInKeywordNameToAddActionMap)
|
||||
{
|
||||
string builtInKeywordName = nameToAddActionTuple.Key;
|
||||
AddShaderInputAction addAction = nameToAddActionTuple.Value as AddShaderInputAction;
|
||||
addAction.categoryToAddItemToGuid = selectedCategoryGuid;
|
||||
m_AddBlackboardItemMenu.AddItem(new GUIContent($"Keyword/{builtInKeywordName}"), false, () => ViewModel.requestModelChangeAction(addAction));
|
||||
}
|
||||
|
||||
foreach (string disabledKeywordName in ViewModel.disabledKeywordNameList)
|
||||
{
|
||||
m_AddBlackboardItemMenu.AddDisabledItem(new GUIContent($"Keyword/{disabledKeywordName}"));
|
||||
}
|
||||
|
||||
if (ViewModel.defaultDropdownNameToAdd != null)
|
||||
{
|
||||
string defaultDropdownName = ViewModel.defaultDropdownNameToAdd.Item1;
|
||||
AddShaderInputAction addAction = ViewModel.defaultDropdownNameToAdd.Item2 as AddShaderInputAction;
|
||||
addAction.categoryToAddItemToGuid = selectedCategoryGuid;
|
||||
m_AddBlackboardItemMenu.AddItem(new GUIContent($"{defaultDropdownName}"), false, () => ViewModel.requestModelChangeAction(addAction));
|
||||
}
|
||||
|
||||
foreach (string disabledDropdownName in ViewModel.disabledDropdownNameList)
|
||||
{
|
||||
m_AddBlackboardItemMenu.AddDisabledItem(new GUIContent(disabledDropdownName));
|
||||
}
|
||||
}
|
||||
|
||||
void ShowAddPropertyMenu()
|
||||
{
|
||||
m_AddBlackboardItemMenu.ShowAsContext();
|
||||
}
|
||||
|
||||
void OnMouseUpEvent(MouseUpEvent evt)
|
||||
{
|
||||
this.HideScrollBoundaryRegions();
|
||||
}
|
||||
|
||||
void OnMouseDownEvent(MouseDownEvent evt)
|
||||
{
|
||||
if (evt.clickCount == 2 && evt.button == (int)MouseButton.LeftMouse)
|
||||
{
|
||||
StartEditingPath();
|
||||
evt.PreventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
void StartEditingPath()
|
||||
{
|
||||
m_SubTitleLabel.visible = false;
|
||||
m_PathLabelTextField.visible = true;
|
||||
m_PathLabelTextField.value = m_SubTitleLabel.text;
|
||||
m_PathLabelTextField.Q("unity-text-input").Focus();
|
||||
m_PathLabelTextField.SelectAll();
|
||||
}
|
||||
|
||||
void OnPathTextFieldKeyPressed(KeyDownEvent evt)
|
||||
{
|
||||
switch (evt.keyCode)
|
||||
{
|
||||
case KeyCode.Escape:
|
||||
m_EditPathCancelled = true;
|
||||
m_PathLabelTextField.Q("unity-text-input").Blur();
|
||||
break;
|
||||
case KeyCode.Return:
|
||||
case KeyCode.KeypadEnter:
|
||||
m_PathLabelTextField.Q("unity-text-input").Blur();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnEditPathTextFinished()
|
||||
{
|
||||
m_SubTitleLabel.visible = true;
|
||||
m_PathLabelTextField.visible = false;
|
||||
|
||||
var newPath = m_PathLabelTextField.text;
|
||||
if (!m_EditPathCancelled && (newPath != m_SubTitleLabel.text))
|
||||
{
|
||||
newPath = BlackboardUtils.SanitizePath(newPath);
|
||||
}
|
||||
|
||||
// Request graph path change action
|
||||
var pathChangeAction = new ChangeGraphPathAction();
|
||||
pathChangeAction.NewGraphPath = newPath;
|
||||
ViewModel.requestModelChangeAction(pathChangeAction);
|
||||
|
||||
m_SubTitleLabel.text = BlackboardUtils.FormatPath(newPath);
|
||||
m_EditPathCancelled = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ce596a3c8ef34fc6bcd82c059ad53e57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,769 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Drawing.Views;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using System.Linq;
|
||||
|
||||
using ContextualMenuManipulator = UnityEngine.UIElements.ContextualMenuManipulator;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
sealed class SGBlackboardCategory : GraphElement, ISGControlledElement<BlackboardCategoryController>, ISelection, IComparable<SGBlackboardCategory>
|
||||
{
|
||||
// --- Begin ISGControlledElement implementation
|
||||
public void OnControllerChanged(ref SGControllerChangedEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnControllerEvent(SGControllerEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public BlackboardCategoryController controller
|
||||
{
|
||||
get => m_Controller;
|
||||
set
|
||||
{
|
||||
if (m_Controller != value)
|
||||
{
|
||||
if (m_Controller != null)
|
||||
{
|
||||
m_Controller.UnregisterHandler(this);
|
||||
}
|
||||
m_Controller = value;
|
||||
|
||||
if (m_Controller != null)
|
||||
{
|
||||
m_Controller.RegisterHandler(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SGController ISGControlledElement.controller => m_Controller;
|
||||
|
||||
// --- ISGControlledElement implementation
|
||||
|
||||
BlackboardCategoryController m_Controller;
|
||||
|
||||
BlackboardCategoryViewModel m_ViewModel;
|
||||
public BlackboardCategoryViewModel viewModel => m_ViewModel;
|
||||
|
||||
const string k_StylePath = "Styles/SGBlackboard";
|
||||
const string k_UxmlPath = "UXML/Blackboard/SGBlackboardCategory";
|
||||
|
||||
VisualElement m_DragIndicator;
|
||||
VisualElement m_MainContainer;
|
||||
VisualElement m_Header;
|
||||
Label m_TitleLabel;
|
||||
Foldout m_Foldout;
|
||||
TextField m_TextField;
|
||||
internal TextField textField => m_TextField;
|
||||
VisualElement m_RowsContainer;
|
||||
int m_InsertIndex;
|
||||
SGBlackboard blackboard => m_ViewModel.parentView as SGBlackboard;
|
||||
|
||||
bool m_IsDragInProgress;
|
||||
bool m_WasHoverExpanded;
|
||||
|
||||
bool m_DroppedOnBottomEdge;
|
||||
bool m_DroppedOnTopEdge;
|
||||
|
||||
bool m_RenameInProgress;
|
||||
|
||||
public delegate bool CanAcceptDropDelegate(ISelectable selected);
|
||||
|
||||
public CanAcceptDropDelegate canAcceptDrop { get; set; }
|
||||
|
||||
int InsertionIndex(Vector2 pos)
|
||||
{
|
||||
// For an empty category can always just insert at the start
|
||||
if (this.childCount == 0)
|
||||
return 0;
|
||||
|
||||
var blackboardRows = this.Query<SGBlackboardRow>().ToList();
|
||||
for (int index = 0; index < blackboardRows.Count; index++)
|
||||
{
|
||||
var blackboardRow = blackboardRows[index];
|
||||
var localPosition = this.ChangeCoordinatesTo(blackboardRow, pos);
|
||||
if (blackboardRow.ContainsPoint(localPosition))
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static VisualElement FindCategoryDirectChild(SGBlackboardCategory blackboardCategory, VisualElement element)
|
||||
{
|
||||
VisualElement directChild = element;
|
||||
|
||||
while ((directChild != null) && (directChild != blackboardCategory))
|
||||
{
|
||||
if (directChild.parent == blackboardCategory)
|
||||
{
|
||||
return directChild;
|
||||
}
|
||||
directChild = directChild.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal SGBlackboardCategory(BlackboardCategoryViewModel categoryViewModel, BlackboardCategoryController inController)
|
||||
{
|
||||
m_ViewModel = categoryViewModel;
|
||||
controller = inController;
|
||||
userData = controller.Model;
|
||||
|
||||
// Setup VisualElement from Stylesheet and UXML file
|
||||
var tpl = Resources.Load(k_UxmlPath) as VisualTreeAsset;
|
||||
m_MainContainer = tpl.Instantiate();
|
||||
m_MainContainer.AddToClassList("mainContainer");
|
||||
|
||||
m_Header = m_MainContainer.Q("categoryHeader");
|
||||
m_TitleLabel = m_MainContainer.Q<Label>("categoryTitleLabel");
|
||||
m_Foldout = m_MainContainer.Q<Foldout>("categoryTitleFoldout");
|
||||
m_RowsContainer = m_MainContainer.Q("rowsContainer");
|
||||
m_TextField = m_MainContainer.Q<TextField>("textField");
|
||||
m_TextField.style.display = DisplayStyle.None;
|
||||
|
||||
hierarchy.Add(m_MainContainer);
|
||||
|
||||
m_DragIndicator = m_MainContainer.Q("dragIndicator");
|
||||
m_DragIndicator.visible = false;
|
||||
|
||||
hierarchy.Add(m_DragIndicator);
|
||||
|
||||
capabilities |= Capabilities.Selectable | Capabilities.Movable | Capabilities.Droppable | Capabilities.Deletable | Capabilities.Renamable | Capabilities.Copiable;
|
||||
|
||||
ClearClassList();
|
||||
AddToClassList("blackboardCategory");
|
||||
|
||||
// add the right click context menu
|
||||
IManipulator contextMenuManipulator = new ContextualMenuManipulator(AddContextMenuOptions);
|
||||
this.AddManipulator(contextMenuManipulator);
|
||||
|
||||
// add drag and drop manipulator
|
||||
var selectionDropperManipulator = new SelectionDropper();
|
||||
this.AddManipulator(selectionDropperManipulator);
|
||||
|
||||
RegisterCallback<MouseDownEvent>(OnMouseDownEvent);
|
||||
var textInputElement = m_TextField.Q(TextField.textInputUssName);
|
||||
textInputElement.RegisterCallback<FocusOutEvent>(e => { OnEditTextFinished(); });
|
||||
// Register hover callbacks
|
||||
RegisterCallback<MouseEnterEvent>(OnHoverStartEvent);
|
||||
RegisterCallback<MouseLeaveEvent>(OnHoverEndEvent);
|
||||
// Register drag callbacks
|
||||
RegisterCallback<DragUpdatedEvent>(OnDragUpdatedEvent);
|
||||
RegisterCallback<DragPerformEvent>(OnDragPerformEvent);
|
||||
RegisterCallback<DragLeaveEvent>(OnDragLeaveEvent);
|
||||
|
||||
var styleSheet = Resources.Load<StyleSheet>(k_StylePath);
|
||||
styleSheets.Add(styleSheet);
|
||||
|
||||
m_InsertIndex = -1;
|
||||
|
||||
// Update category title from view model
|
||||
title = m_ViewModel.name;
|
||||
this.viewDataKey = viewModel.associatedCategoryGuid;
|
||||
|
||||
if (String.IsNullOrEmpty(title))
|
||||
{
|
||||
m_Foldout.visible = false;
|
||||
m_Foldout.RemoveFromHierarchy();
|
||||
}
|
||||
else
|
||||
{
|
||||
TryDoFoldout(m_ViewModel.isExpanded);
|
||||
m_Foldout.RegisterCallback<ChangeEvent<bool>>(OnFoldoutToggle);
|
||||
}
|
||||
|
||||
// Remove the header element if this is the default category
|
||||
if (!controller.Model.IsNamedCategory())
|
||||
headerVisible = false;
|
||||
}
|
||||
|
||||
public override VisualElement contentContainer { get { return m_RowsContainer; } }
|
||||
|
||||
public override string title
|
||||
{
|
||||
get => m_TitleLabel.text;
|
||||
set
|
||||
{
|
||||
m_TitleLabel.text = value;
|
||||
if (m_TitleLabel.text == String.Empty)
|
||||
{
|
||||
AddToClassList("unnamed");
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveFromClassList("unnamed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool headerVisible
|
||||
{
|
||||
get { return m_Header.parent != null; }
|
||||
set
|
||||
{
|
||||
if (value == (m_Header.parent != null))
|
||||
return;
|
||||
|
||||
if (value)
|
||||
{
|
||||
m_MainContainer.Add(m_Header);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_MainContainer.Remove(m_Header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetDragIndicatorVisible(bool visible)
|
||||
{
|
||||
m_DragIndicator.visible = visible;
|
||||
}
|
||||
|
||||
public bool CategoryContains(List<ISelectable> selection)
|
||||
{
|
||||
if (selection == null)
|
||||
return false;
|
||||
|
||||
// Look for at least one selected element in this category to accept drop
|
||||
foreach (ISelectable selected in selection)
|
||||
{
|
||||
VisualElement selectedElement = selected as VisualElement;
|
||||
|
||||
if (selected != null && Contains(selectedElement))
|
||||
{
|
||||
if (canAcceptDrop == null || canAcceptDrop(selected))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnFoldoutToggle(ChangeEvent<bool> evt)
|
||||
{
|
||||
if (evt.previousValue != evt.newValue)
|
||||
{
|
||||
var isExpandedAction = new ChangeCategoryIsExpandedAction();
|
||||
if (selection.Contains(this)) // expand all selected if the foldout is part of a selection
|
||||
isExpandedAction.categoryGuids = selection.OfType<SGBlackboardCategory>().Select(s => s.viewModel.associatedCategoryGuid).ToList();
|
||||
else
|
||||
isExpandedAction.categoryGuids = new List<string>() { viewModel.associatedCategoryGuid };
|
||||
|
||||
isExpandedAction.isExpanded = evt.newValue;
|
||||
isExpandedAction.editorPrefsBaseKey = blackboard.controller.editorPrefsBaseKey;
|
||||
viewModel.requestModelChangeAction(isExpandedAction);
|
||||
}
|
||||
}
|
||||
|
||||
internal void TryDoFoldout(bool expand)
|
||||
{
|
||||
m_Foldout.SetValueWithoutNotify(expand);
|
||||
if (!expand)
|
||||
{
|
||||
m_DragIndicator.visible = true;
|
||||
m_RowsContainer.RemoveFromHierarchy();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DragIndicator.visible = false;
|
||||
m_MainContainer.Add(m_RowsContainer);
|
||||
}
|
||||
|
||||
var key = $"{blackboard.controller.editorPrefsBaseKey}.{viewDataKey}.{ChangeCategoryIsExpandedAction.kEditorPrefKey}";
|
||||
EditorPrefs.SetBool(key, expand);
|
||||
}
|
||||
|
||||
void OnMouseDownEvent(MouseDownEvent e)
|
||||
{
|
||||
// Handles double-click with left mouse, which should trigger a rename action on this category
|
||||
if ((e.clickCount == 2) && e.button == (int)MouseButton.LeftMouse && IsRenamable())
|
||||
{
|
||||
OpenTextEditor();
|
||||
e.PreventDefault();
|
||||
}
|
||||
else if (e.clickCount == 1 && e.button == (int)MouseButton.LeftMouse && IsRenamable())
|
||||
{
|
||||
// Select the child elements within this category (the field views)
|
||||
var fieldViews = this.Query<SGBlackboardField>();
|
||||
foreach (var child in fieldViews.ToList())
|
||||
{
|
||||
this.AddToSelection(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void OpenTextEditor()
|
||||
{
|
||||
m_TextField.SetValueWithoutNotify(title);
|
||||
m_TextField.style.display = DisplayStyle.Flex;
|
||||
m_TitleLabel.visible = false;
|
||||
m_TextField.Q(TextField.textInputUssName).Focus();
|
||||
m_TextField.SelectAll();
|
||||
|
||||
m_RenameInProgress = true;
|
||||
}
|
||||
|
||||
void OnEditTextFinished()
|
||||
{
|
||||
m_TitleLabel.visible = true;
|
||||
m_TextField.style.display = DisplayStyle.None;
|
||||
|
||||
if (title != m_TextField.text && String.IsNullOrWhiteSpace(m_TextField.text) == false)
|
||||
{
|
||||
var changeCategoryNameAction = new ChangeCategoryNameAction();
|
||||
changeCategoryNameAction.newCategoryNameValue = m_TextField.text;
|
||||
changeCategoryNameAction.categoryGuid = m_ViewModel.associatedCategoryGuid;
|
||||
m_ViewModel.requestModelChangeAction(changeCategoryNameAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset text field to original name
|
||||
m_TextField.value = title;
|
||||
}
|
||||
|
||||
m_RenameInProgress = false;
|
||||
}
|
||||
|
||||
void OnHoverStartEvent(MouseEnterEvent evt)
|
||||
{
|
||||
AddToClassList("hovered");
|
||||
if (selection.OfType<SGBlackboardField>().Any()
|
||||
&& controller.Model.IsNamedCategory()
|
||||
&& m_IsDragInProgress
|
||||
&& !viewModel.isExpanded)
|
||||
{
|
||||
m_WasHoverExpanded = true;
|
||||
TryDoFoldout(true);
|
||||
}
|
||||
}
|
||||
|
||||
void OnHoverEndEvent(MouseLeaveEvent evt)
|
||||
{
|
||||
if (m_WasHoverExpanded && m_IsDragInProgress)
|
||||
{
|
||||
m_WasHoverExpanded = false;
|
||||
TryDoFoldout(false);
|
||||
}
|
||||
RemoveFromClassList("hovered");
|
||||
}
|
||||
|
||||
void OnDragUpdatedEvent(DragUpdatedEvent evt)
|
||||
{
|
||||
var selection = DragAndDrop.GetGenericData("DragSelection") as List<ISelectable>;
|
||||
|
||||
// Don't show drag indicator if selection has categories,
|
||||
// We don't want category drag & drop to be ambiguous with shader input drag & drop
|
||||
if (selection.OfType<SGBlackboardCategory>().Any())
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// If can't find at least one blackboard field in the selection, don't update drag indicator
|
||||
if (selection.OfType<SGBlackboardField>().Any() == false)
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_IsDragInProgress = true;
|
||||
|
||||
Vector2 localPosition = evt.localMousePosition;
|
||||
|
||||
m_InsertIndex = InsertionIndex(localPosition);
|
||||
if (m_InsertIndex != -1)
|
||||
{
|
||||
float indicatorY = 0;
|
||||
bool inMoveRange = false;
|
||||
// When category is empty
|
||||
if (this.childCount == 0)
|
||||
{
|
||||
// This moves the indicator to the bottom of the category in case of an empty category
|
||||
indicatorY = this.layout.height * 0.9f;
|
||||
m_DragIndicator.style.marginBottom = 8;
|
||||
inMoveRange = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DragIndicator.style.marginBottom = 0;
|
||||
|
||||
var relativePosition = new Vector2();
|
||||
var childHeight = 0.0f;
|
||||
VisualElement childAtInsertIndex = this[m_InsertIndex];
|
||||
childHeight = childAtInsertIndex.layout.height;
|
||||
|
||||
relativePosition = this.ChangeCoordinatesTo(childAtInsertIndex, localPosition);
|
||||
|
||||
if (relativePosition.y > 0 && relativePosition.y < childHeight * 0.25f)
|
||||
{
|
||||
// Top Edge
|
||||
inMoveRange = true;
|
||||
indicatorY = childAtInsertIndex.ChangeCoordinatesTo(this, new Vector2(0, 0)).y;
|
||||
m_DragIndicator.style.rotate = new StyleRotate(Rotate.None());
|
||||
m_DroppedOnBottomEdge = false;
|
||||
m_DroppedOnTopEdge = true;
|
||||
}
|
||||
else if (relativePosition.y > 0.75f * childHeight && relativePosition.y < childHeight)
|
||||
{
|
||||
// Bottom Edge
|
||||
inMoveRange = true;
|
||||
indicatorY = childAtInsertIndex.ChangeCoordinatesTo(this, new Vector2(0, 0)).y + childAtInsertIndex.layout.height;
|
||||
//m_DragIndicator.style.rotate = new StyleRotate(new Rotate(-180));
|
||||
m_DroppedOnBottomEdge = true;
|
||||
m_DroppedOnTopEdge = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (inMoveRange)
|
||||
{
|
||||
SetDragIndicatorVisible(true);
|
||||
|
||||
m_DragIndicator.style.width = layout.width;
|
||||
var newPosition = indicatorY;
|
||||
m_DragIndicator.style.top = newPosition;
|
||||
}
|
||||
else
|
||||
SetDragIndicatorVisible(true);
|
||||
}
|
||||
else
|
||||
SetDragIndicatorVisible(false);
|
||||
|
||||
if (m_InsertIndex != -1)
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Move;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDragPerformEvent(DragPerformEvent evt)
|
||||
{
|
||||
var selection = DragAndDrop.GetGenericData("DragSelection") as List<ISelectable>;
|
||||
|
||||
m_IsDragInProgress = false;
|
||||
|
||||
// Don't show drag indicator if selection has categories,
|
||||
// We don't want category drag & drop to be ambiguous with shader input drag & drop
|
||||
if (selection.OfType<SGBlackboardCategory>().Any())
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_InsertIndex == -1)
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Map of containing categories to the actual dragged elements within them
|
||||
SortedDictionary<SGBlackboardCategory, List<VisualElement>> draggedElements = new SortedDictionary<SGBlackboardCategory, List<VisualElement>>();
|
||||
|
||||
foreach (ISelectable selectedElement in selection)
|
||||
{
|
||||
var draggedElement = selectedElement as VisualElement;
|
||||
if (draggedElement == null)
|
||||
continue;
|
||||
|
||||
if (this.Contains(draggedElement))
|
||||
{
|
||||
if (draggedElements.ContainsKey(this))
|
||||
draggedElements[this].Add(FindCategoryDirectChild(this, draggedElement));
|
||||
else
|
||||
draggedElements.Add(this, new List<VisualElement> { FindCategoryDirectChild(this, draggedElement) });
|
||||
}
|
||||
else
|
||||
{
|
||||
var otherCategory = draggedElement.GetFirstAncestorOfType<SGBlackboardCategory>();
|
||||
if (otherCategory != null)
|
||||
{
|
||||
if (draggedElements.ContainsKey(otherCategory))
|
||||
draggedElements[otherCategory].Add(FindCategoryDirectChild(otherCategory, draggedElement));
|
||||
else
|
||||
draggedElements.Add(otherCategory, new List<VisualElement> { FindCategoryDirectChild(otherCategory, draggedElement) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (draggedElements.Count == 0)
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var categoryToChildrenTuple in draggedElements)
|
||||
{
|
||||
var containingCategory = categoryToChildrenTuple.Key;
|
||||
var childList = categoryToChildrenTuple.Value;
|
||||
// Sorts the dragged elements from their relative order in their parent
|
||||
childList.Sort((item1, item2) => containingCategory.IndexOf(item1).CompareTo(containingCategory.IndexOf(item2)));
|
||||
}
|
||||
|
||||
int insertIndex = Mathf.Clamp(m_InsertIndex, 0, m_InsertIndex);
|
||||
|
||||
bool adjustedInsertIndex = false;
|
||||
VisualElement lastInsertedElement = null;
|
||||
/* Handles moving elements within a category */
|
||||
foreach (var categoryToChildrenTuple in draggedElements)
|
||||
{
|
||||
var childList = categoryToChildrenTuple.Value;
|
||||
VisualElement firstChild = childList.First();
|
||||
foreach (var draggedElement in childList)
|
||||
{
|
||||
var blackboardField = draggedElement.Q<SGBlackboardField>();
|
||||
ShaderInput shaderInput = null;
|
||||
if (blackboardField != null)
|
||||
shaderInput = blackboardField.controller.Model;
|
||||
|
||||
// Skip if this field is not contained by this category as we handle that in the next loop below
|
||||
if (shaderInput == null || !this.Contains(blackboardField))
|
||||
continue;
|
||||
|
||||
VisualElement categoryDirectChild = draggedElement;
|
||||
int indexOfDraggedElement = IndexOf(categoryDirectChild);
|
||||
|
||||
bool listEndInsertion = false;
|
||||
// Only find index for the first item
|
||||
if (draggedElement == firstChild)
|
||||
{
|
||||
adjustedInsertIndex = true;
|
||||
// Handles case of inserting after last item in list
|
||||
if (insertIndex == childCount - 1 && m_DroppedOnBottomEdge)
|
||||
{
|
||||
listEndInsertion = true;
|
||||
}
|
||||
// Handles case of inserting after any item except the last in list
|
||||
else if (m_DroppedOnBottomEdge)
|
||||
insertIndex++;
|
||||
|
||||
insertIndex = Mathf.Clamp(insertIndex, 0, childCount - 1);
|
||||
|
||||
if (insertIndex != indexOfDraggedElement)
|
||||
{
|
||||
// If ever placing it at end of list, make sure to place after last item
|
||||
if (listEndInsertion)
|
||||
{
|
||||
categoryDirectChild.PlaceInFront(this[insertIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
categoryDirectChild.PlaceBehind(this[insertIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
lastInsertedElement = firstChild;
|
||||
}
|
||||
// Place every subsequent row after that use PlaceInFront(), this prevents weird re-ordering issues as long as we can get the first index right
|
||||
else
|
||||
{
|
||||
var indexOfFirstChild = this.IndexOf(lastInsertedElement);
|
||||
categoryDirectChild.PlaceInFront(this[indexOfFirstChild]);
|
||||
lastInsertedElement = categoryDirectChild;
|
||||
}
|
||||
|
||||
if (insertIndex > childCount - 1 || listEndInsertion)
|
||||
insertIndex = -1;
|
||||
|
||||
var moveShaderInputAction = new MoveShaderInputAction();
|
||||
moveShaderInputAction.associatedCategoryGuid = viewModel.associatedCategoryGuid;
|
||||
moveShaderInputAction.shaderInputReference = shaderInput;
|
||||
moveShaderInputAction.newIndexValue = insertIndex;
|
||||
m_ViewModel.requestModelChangeAction(moveShaderInputAction);
|
||||
|
||||
// Make sure to remove the element from the selection so it doesn't get re-handled by the blackboard as well, leads to duplicates
|
||||
selection.Remove(blackboardField);
|
||||
|
||||
if (insertIndex > indexOfDraggedElement)
|
||||
continue;
|
||||
|
||||
// If adding to the end of the list, we no longer need to increment the index
|
||||
if (insertIndex != -1)
|
||||
insertIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handles moving elements from one category to another (including between different graph windows) */
|
||||
// Handles case of inserting after item in list
|
||||
if (!adjustedInsertIndex)
|
||||
{
|
||||
if (m_DroppedOnBottomEdge)
|
||||
{
|
||||
insertIndex++;
|
||||
}
|
||||
// Only ever do this for the first item
|
||||
else if (m_DroppedOnTopEdge && insertIndex == 0)
|
||||
{
|
||||
insertIndex = Mathf.Clamp(insertIndex - 1, 0, childCount - 1);
|
||||
}
|
||||
}
|
||||
else if (lastInsertedElement != null)
|
||||
{
|
||||
insertIndex = this.IndexOf(lastInsertedElement) + 1;
|
||||
}
|
||||
|
||||
foreach (var categoryToChildrenTuple in draggedElements)
|
||||
{
|
||||
var childList = categoryToChildrenTuple.Value;
|
||||
foreach (var draggedElement in childList)
|
||||
{
|
||||
var blackboardField = draggedElement.Q<SGBlackboardField>();
|
||||
ShaderInput shaderInput = null;
|
||||
if (blackboardField != null)
|
||||
shaderInput = blackboardField.controller.Model;
|
||||
if (shaderInput == null)
|
||||
continue;
|
||||
|
||||
// If the blackboard field is contained by this category its already been handled above, skip
|
||||
if (this.Contains(blackboardField))
|
||||
continue;
|
||||
|
||||
var addItemToCategoryAction = new AddItemToCategoryAction();
|
||||
addItemToCategoryAction.categoryGuid = viewModel.associatedCategoryGuid;
|
||||
addItemToCategoryAction.addActionSource = AddItemToCategoryAction.AddActionSource.DragDrop;
|
||||
addItemToCategoryAction.itemToAdd = shaderInput;
|
||||
|
||||
// If adding to end of list, make the insert index -1 to ensure op goes through as expected
|
||||
if (insertIndex > childCount - 1)
|
||||
insertIndex = -1;
|
||||
|
||||
addItemToCategoryAction.indexToAddItemAt = insertIndex;
|
||||
m_ViewModel.requestModelChangeAction(addItemToCategoryAction);
|
||||
|
||||
// Make sure to remove the element from the selection so it doesn't get re-handled by the blackboard as well, leads to duplicates
|
||||
selection.Remove(blackboardField);
|
||||
|
||||
// If adding to the end of the list, we no longer need to increment the index
|
||||
if (insertIndex != -1)
|
||||
insertIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
SetDragIndicatorVisible(false);
|
||||
}
|
||||
|
||||
void OnDragLeaveEvent(DragLeaveEvent evt)
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
}
|
||||
|
||||
internal void OnDragActionCanceled()
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
m_IsDragInProgress = false;
|
||||
}
|
||||
|
||||
public override void Select(VisualElement selectionContainer, bool additive)
|
||||
{
|
||||
// Don't add the un-named/default category to graph selections
|
||||
if (controller.Model.IsNamedCategory())
|
||||
{
|
||||
base.Select(selectionContainer, additive);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSelected()
|
||||
{
|
||||
AddToClassList("selected");
|
||||
}
|
||||
|
||||
public override void OnUnselected()
|
||||
{
|
||||
RemoveFromClassList("selected");
|
||||
}
|
||||
|
||||
public void AddToSelection(ISelectable selectable)
|
||||
{
|
||||
// Don't add the un-named/default category to graph selections,
|
||||
if (controller.Model.IsNamedCategory() == false && selectable == this)
|
||||
return;
|
||||
|
||||
// Don't add to selection if a rename op is in progress
|
||||
if (m_RenameInProgress)
|
||||
{
|
||||
RemoveFromSelection(this);
|
||||
return;
|
||||
}
|
||||
|
||||
var materialGraphView = m_ViewModel.parentView.GetFirstAncestorOfType<MaterialGraphView>();
|
||||
materialGraphView?.AddToSelection(selectable);
|
||||
}
|
||||
|
||||
public void RemoveFromSelection(ISelectable selectable)
|
||||
{
|
||||
var materialGraphView = m_ViewModel.parentView.GetFirstAncestorOfType<MaterialGraphView>();
|
||||
|
||||
// If we're de-selecting the category itself
|
||||
if (selectable == this)
|
||||
{
|
||||
materialGraphView?.RemoveFromSelection(selectable);
|
||||
// Also deselect the child elements within this category (the field views)
|
||||
var fieldViews = this.Query<SGBlackboardField>();
|
||||
foreach (var child in fieldViews.ToList())
|
||||
{
|
||||
materialGraphView?.RemoveFromSelection(child);
|
||||
}
|
||||
}
|
||||
// If a category is unselected, only then can the children beneath it be deselected
|
||||
else if (selection.Contains(this) == false)
|
||||
{
|
||||
materialGraphView?.RemoveFromSelection(selectable);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
RemoveFromClassList("selected");
|
||||
var materialGraphView = m_ViewModel.parentView.GetFirstAncestorOfType<MaterialGraphView>();
|
||||
materialGraphView?.ClearSelection();
|
||||
}
|
||||
|
||||
public List<ISelectable> selection
|
||||
{
|
||||
get
|
||||
{
|
||||
var selectionProvider = m_ViewModel.parentView.GetFirstAncestorOfType<ISelectionProvider>();
|
||||
if (selectionProvider?.GetSelection != null)
|
||||
return selectionProvider.GetSelection;
|
||||
|
||||
return new List<ISelectable>();
|
||||
}
|
||||
}
|
||||
|
||||
void RequestCategoryDelete()
|
||||
{
|
||||
var materialGraphView = blackboard.ParentView as MaterialGraphView;
|
||||
materialGraphView?.deleteSelection?.Invoke("Delete", GraphView.AskUser.DontAskUser);
|
||||
}
|
||||
|
||||
void AddContextMenuOptions(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
evt.menu.AppendAction("Delete", evt => RequestCategoryDelete());
|
||||
evt.menu.AppendAction("Rename", (a) => OpenTextEditor(), DropdownMenuAction.AlwaysEnabled);
|
||||
// Don't allow the default/un-named category to have right-click menu options
|
||||
if (controller.Model.IsNamedCategory())
|
||||
{
|
||||
evt.menu.AppendAction("Delete", evt => RequestCategoryDelete());
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(SGBlackboardCategory other)
|
||||
{
|
||||
if (other == null)
|
||||
return 1;
|
||||
|
||||
var thisBlackboard = this.blackboard;
|
||||
var otherBlackboard = other.blackboard;
|
||||
|
||||
return thisBlackboard.IndexOf(this).CompareTo(otherBlackboard.IndexOf(other));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5e118fcc48ee4d78b7ea01a1d8011b1b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,387 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Drawing.Controls;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
using ContextualMenuManipulator = UnityEngine.UIElements.ContextualMenuManipulator;
|
||||
using GraphDataStore = UnityEditor.ShaderGraph.DataStore<UnityEditor.ShaderGraph.GraphData>;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class SGBlackboardField : GraphElement, IInspectable, ISGControlledElement<ShaderInputViewController>
|
||||
{
|
||||
static readonly Texture2D k_ExposedIcon = Resources.Load<Texture2D>("GraphView/Nodes/BlackboardFieldExposed");
|
||||
static readonly string k_UxmlTemplatePath = "UXML/Blackboard/SGBlackboardField";
|
||||
static readonly string k_StyleSheetPath = "Styles/SGBlackboard";
|
||||
|
||||
ShaderInputViewModel m_ViewModel;
|
||||
|
||||
ShaderInputViewModel ViewModel
|
||||
{
|
||||
get => m_ViewModel;
|
||||
set => m_ViewModel = value;
|
||||
}
|
||||
|
||||
VisualElement m_ContentItem;
|
||||
Pill m_Pill;
|
||||
Label m_TypeLabel;
|
||||
TextField m_TextField;
|
||||
internal TextField textField => m_TextField;
|
||||
|
||||
Action m_ResetReferenceNameTrigger;
|
||||
List<Node> m_SelectedNodes = new List<Node>();
|
||||
|
||||
public string text
|
||||
{
|
||||
get { return m_Pill.text; }
|
||||
set { m_Pill.text = value; }
|
||||
}
|
||||
|
||||
public string typeText
|
||||
{
|
||||
get { return m_TypeLabel.text; }
|
||||
set { m_TypeLabel.text = value; }
|
||||
}
|
||||
|
||||
public Texture icon
|
||||
{
|
||||
get { return m_Pill.icon; }
|
||||
set { m_Pill.icon = value; }
|
||||
}
|
||||
|
||||
public bool highlighted
|
||||
{
|
||||
get { return m_Pill.highlighted; }
|
||||
set { m_Pill.highlighted = value; }
|
||||
}
|
||||
|
||||
internal SGBlackboardField(ShaderInputViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
// Store ShaderInput in userData object
|
||||
userData = ViewModel.model;
|
||||
if (userData == null)
|
||||
{
|
||||
AssertHelpers.Fail("Could not initialize blackboard field as shader input was null.");
|
||||
return;
|
||||
}
|
||||
// Store the Model guid as viewDataKey as that is persistent
|
||||
viewDataKey = ViewModel.model.guid.ToString();
|
||||
|
||||
var visualTreeAsset = Resources.Load<VisualTreeAsset>(k_UxmlTemplatePath);
|
||||
Assert.IsNotNull(visualTreeAsset);
|
||||
|
||||
VisualElement mainContainer = visualTreeAsset.Instantiate();
|
||||
var styleSheet = Resources.Load<StyleSheet>(k_StyleSheetPath);
|
||||
Assert.IsNotNull(styleSheet);
|
||||
styleSheets.Add(styleSheet);
|
||||
|
||||
mainContainer.AddToClassList("mainContainer");
|
||||
mainContainer.pickingMode = PickingMode.Ignore;
|
||||
|
||||
m_ContentItem = mainContainer.Q("contentItem");
|
||||
m_Pill = mainContainer.Q<Pill>("pill");
|
||||
m_TypeLabel = mainContainer.Q<Label>("typeLabel");
|
||||
m_TextField = mainContainer.Q<TextField>("textField");
|
||||
m_TextField.style.display = DisplayStyle.None;
|
||||
|
||||
// Update the Pill text if shader input name is changed
|
||||
// we handle this in controller if we change it through SGBlackboardField, but its possible to change through PropertyNodeView as well
|
||||
shaderInput.displayNameUpdateTrigger += newDisplayName => text = newDisplayName;
|
||||
|
||||
// Handles the upgrade fix for the old color property deprecation
|
||||
if (shaderInput is AbstractShaderProperty property)
|
||||
{
|
||||
property.onAfterVersionChange += () =>
|
||||
{
|
||||
this.typeText = property.GetPropertyTypeString();
|
||||
this.m_InspectorUpdateDelegate();
|
||||
};
|
||||
}
|
||||
|
||||
Add(mainContainer);
|
||||
|
||||
RegisterCallback<MouseDownEvent>(OnMouseDownEvent);
|
||||
|
||||
capabilities |= Capabilities.Selectable | Capabilities.Droppable | Capabilities.Deletable | Capabilities.Renamable;
|
||||
|
||||
ClearClassList();
|
||||
AddToClassList("blackboardField");
|
||||
|
||||
this.name = "SGBlackboardField";
|
||||
UpdateFromViewModel();
|
||||
|
||||
// add the right click context menu
|
||||
IManipulator contextMenuManipulator = new ContextualMenuManipulator(AddContextMenuOptions);
|
||||
this.AddManipulator(contextMenuManipulator);
|
||||
this.AddManipulator(new SelectionDropper());
|
||||
this.AddManipulator(new ContextualMenuManipulator(BuildFieldContextualMenu));
|
||||
|
||||
// When a display name is changed through the BlackboardPill, bind this callback to handle it with appropriate change action
|
||||
var textInputElement = m_TextField.Q(TextField.textInputUssName);
|
||||
textInputElement.RegisterCallback<FocusOutEvent>(e => { OnEditTextFinished(); });
|
||||
|
||||
ShaderGraphPreferences.onAllowDeprecatedChanged += UpdateTypeText;
|
||||
|
||||
RegisterCallback<MouseEnterEvent>(evt => OnMouseHover(evt, ViewModel.model));
|
||||
RegisterCallback<MouseLeaveEvent>(evt => OnMouseHover(evt, ViewModel.model));
|
||||
RegisterCallback<DragUpdatedEvent>(OnDragUpdatedEvent);
|
||||
|
||||
var blackboard = ViewModel.parentView.GetFirstAncestorOfType<SGBlackboard>();
|
||||
if (blackboard != null)
|
||||
{
|
||||
// These callbacks are used for the property dragging scroll behavior
|
||||
RegisterCallback<DragEnterEvent>(blackboard.OnDragEnterEvent);
|
||||
RegisterCallback<DragExitedEvent>(blackboard.OnDragExitedEvent);
|
||||
|
||||
// These callbacks are used for the property dragging scroll behavior
|
||||
RegisterCallback<DragEnterEvent>(blackboard.OnDragEnterEvent);
|
||||
RegisterCallback<DragExitedEvent>(blackboard.OnDragExitedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
~SGBlackboardField()
|
||||
{
|
||||
ShaderGraphPreferences.onAllowDeprecatedChanged -= UpdateTypeText;
|
||||
}
|
||||
|
||||
void AddContextMenuOptions(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
// Checks if the reference name has been overridden and appends menu action to reset it, if so
|
||||
if (shaderInput.isRenamable &&
|
||||
!string.IsNullOrEmpty(shaderInput.overrideReferenceName))
|
||||
{
|
||||
evt.menu.AppendAction(
|
||||
"Reset Reference",
|
||||
e =>
|
||||
{
|
||||
var resetReferenceNameAction = new ResetReferenceNameAction();
|
||||
resetReferenceNameAction.shaderInputReference = shaderInput;
|
||||
ViewModel.requestModelChangeAction(resetReferenceNameAction);
|
||||
m_ResetReferenceNameTrigger();
|
||||
},
|
||||
DropdownMenuAction.AlwaysEnabled);
|
||||
}
|
||||
|
||||
if (shaderInput is ColorShaderProperty colorProp)
|
||||
{
|
||||
PropertyNodeView.AddMainColorMenuOptions(evt, colorProp, controller.graphData, m_InspectorUpdateDelegate);
|
||||
}
|
||||
|
||||
|
||||
if (shaderInput is Texture2DShaderProperty texProp)
|
||||
{
|
||||
PropertyNodeView.AddMainTextureMenuOptions(evt, texProp, controller.graphData, m_InspectorUpdateDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateFromViewModel()
|
||||
{
|
||||
this.text = ViewModel.inputName;
|
||||
this.icon = ViewModel.isInputExposed ? k_ExposedIcon : null;
|
||||
this.typeText = ViewModel.inputTypeName;
|
||||
}
|
||||
|
||||
ShaderInputViewController m_Controller;
|
||||
|
||||
// --- Begin ISGControlledElement implementation
|
||||
public void OnControllerChanged(ref SGControllerChangedEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnControllerEvent(SGControllerEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public ShaderInputViewController controller
|
||||
{
|
||||
get => m_Controller;
|
||||
set
|
||||
{
|
||||
if (m_Controller != value)
|
||||
{
|
||||
if (m_Controller != null)
|
||||
{
|
||||
m_Controller.UnregisterHandler(this);
|
||||
}
|
||||
|
||||
m_Controller = value;
|
||||
|
||||
if (m_Controller != null)
|
||||
{
|
||||
m_Controller.RegisterHandler(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SGController ISGControlledElement.controller => m_Controller;
|
||||
|
||||
// --- ISGControlledElement implementation
|
||||
|
||||
[Inspectable("Shader Input", null)]
|
||||
public ShaderInput shaderInput => ViewModel.model;
|
||||
|
||||
public string inspectorTitle => ViewModel.inputName + " " + ViewModel.inputTypeName;
|
||||
|
||||
public object GetObjectToInspect()
|
||||
{
|
||||
return shaderInput;
|
||||
}
|
||||
|
||||
Action m_InspectorUpdateDelegate;
|
||||
|
||||
public void SupplyDataToPropertyDrawer(IPropertyDrawer propertyDrawer, Action inspectorUpdateDelegate)
|
||||
{
|
||||
if (propertyDrawer is ShaderInputPropertyDrawer shaderInputPropertyDrawer)
|
||||
{
|
||||
// We currently need to do a halfway measure between the old way of handling stuff for property drawers (how FieldView and NodeView handle it)
|
||||
// and how we want to handle it with the new style of controllers and views. Ideally we'd just hand the property drawer a view model and thats it.
|
||||
// We've maintained all the old callbacks as they are in the PropertyDrawer to reduce possible halo changes and support PropertyNodeView functionality
|
||||
// Instead we supply different underlying methods for the callbacks in the new SGBlackboardField,
|
||||
// that way both code paths should work until we can refactor PropertyNodeView
|
||||
shaderInputPropertyDrawer.GetViewModel(
|
||||
ViewModel,
|
||||
controller.graphData,
|
||||
((triggerInspectorUpdate, modificationScope) =>
|
||||
{
|
||||
controller.DirtyNodes(modificationScope);
|
||||
if (triggerInspectorUpdate)
|
||||
inspectorUpdateDelegate();
|
||||
}));
|
||||
|
||||
m_ResetReferenceNameTrigger = shaderInputPropertyDrawer.ResetReferenceName;
|
||||
m_InspectorUpdateDelegate = inspectorUpdateDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseDownEvent(MouseDownEvent e)
|
||||
{
|
||||
if ((e.clickCount == 2) && e.button == (int)MouseButton.LeftMouse && IsRenamable())
|
||||
{
|
||||
OpenTextEditor();
|
||||
e.PreventDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
e.StopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
void OnDragUpdatedEvent(DragUpdatedEvent evt)
|
||||
{
|
||||
if (m_SelectedNodes.Any())
|
||||
{
|
||||
foreach (var node in m_SelectedNodes)
|
||||
{
|
||||
node.RemoveFromClassList("hovered");
|
||||
}
|
||||
m_SelectedNodes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move to controller? Feels weird for this to be directly communicating with PropertyNodes etc.
|
||||
// Better way would be to send event to controller that notified of hover enter/exit and have other controllers be sent those events in turn
|
||||
void OnMouseHover(EventBase evt, ShaderInput input)
|
||||
{
|
||||
var graphView = ViewModel.parentView.GetFirstAncestorOfType<MaterialGraphView>();
|
||||
if (evt.eventTypeId == MouseEnterEvent.TypeId())
|
||||
{
|
||||
foreach (var node in graphView.nodes.ToList())
|
||||
{
|
||||
if (input is AbstractShaderProperty property)
|
||||
{
|
||||
if (node.userData is PropertyNode propertyNode)
|
||||
{
|
||||
if (propertyNode.property == input)
|
||||
{
|
||||
m_SelectedNodes.Add(node);
|
||||
node.AddToClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (input is ShaderKeyword keyword)
|
||||
{
|
||||
if (node.userData is KeywordNode keywordNode)
|
||||
{
|
||||
if (keywordNode.keyword == input)
|
||||
{
|
||||
m_SelectedNodes.Add(node);
|
||||
node.AddToClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (input is ShaderDropdown dropdown)
|
||||
{
|
||||
if (node.userData is DropdownNode dropdownNode)
|
||||
{
|
||||
if (dropdownNode.dropdown == input)
|
||||
{
|
||||
m_SelectedNodes.Add(node);
|
||||
node.AddToClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (evt.eventTypeId == MouseLeaveEvent.TypeId() && m_SelectedNodes.Any())
|
||||
{
|
||||
foreach (var node in m_SelectedNodes)
|
||||
{
|
||||
node.RemoveFromClassList("hovered");
|
||||
}
|
||||
m_SelectedNodes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateTypeText()
|
||||
{
|
||||
if (shaderInput is AbstractShaderProperty asp)
|
||||
{
|
||||
typeText = asp.GetPropertyTypeString();
|
||||
}
|
||||
}
|
||||
|
||||
internal void OpenTextEditor()
|
||||
{
|
||||
m_TextField.SetValueWithoutNotify(text);
|
||||
m_TextField.style.display = DisplayStyle.Flex;
|
||||
m_ContentItem.visible = false;
|
||||
m_TextField.Q(TextField.textInputUssName).Focus();
|
||||
m_TextField.SelectAll();
|
||||
}
|
||||
|
||||
void OnEditTextFinished()
|
||||
{
|
||||
m_ContentItem.visible = true;
|
||||
m_TextField.style.display = DisplayStyle.None;
|
||||
|
||||
if (text != m_TextField.text && String.IsNullOrWhiteSpace(m_TextField.text) == false && String.IsNullOrEmpty(m_TextField.text) == false)
|
||||
{
|
||||
var changeDisplayNameAction = new ChangeDisplayNameAction();
|
||||
changeDisplayNameAction.shaderInputReference = shaderInput;
|
||||
changeDisplayNameAction.newDisplayNameValue = m_TextField.text;
|
||||
ViewModel.requestModelChangeAction(changeDisplayNameAction);
|
||||
m_InspectorUpdateDelegate?.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset text field to original name
|
||||
m_TextField.value = text;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void BuildFieldContextualMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
evt.menu.AppendAction("Rename", (a) => OpenTextEditor(), DropdownMenuAction.AlwaysEnabled);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 04f0848d35cb46cabeec016d7ccd5169
|
||||
timeCreated: 1611267123
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class SGBlackboardRow : VisualElement
|
||||
{
|
||||
static readonly string k_UxmlTemplatePath = "UXML/Blackboard/SGBlackboardRow";
|
||||
static readonly string k_StyleSheetPath = "Styles/SGBlackboard";
|
||||
|
||||
VisualElement m_Root;
|
||||
Button m_ExpandButton;
|
||||
VisualElement m_ItemContainer;
|
||||
VisualElement m_PropertyViewContainer;
|
||||
bool m_Expanded = true;
|
||||
|
||||
public bool expanded
|
||||
{
|
||||
get { return m_Expanded; }
|
||||
set
|
||||
{
|
||||
if (m_Expanded == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Expanded = value;
|
||||
|
||||
if (m_Expanded)
|
||||
{
|
||||
m_Root.Add(m_PropertyViewContainer);
|
||||
AddToClassList("expanded");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Root.Remove(m_PropertyViewContainer);
|
||||
RemoveFromClassList("expanded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SGBlackboardRow(VisualElement item, VisualElement propertyView)
|
||||
{
|
||||
var stylesheet = Resources.Load(k_StyleSheetPath) as StyleSheet;
|
||||
Assert.IsNotNull(stylesheet);
|
||||
styleSheets.Add(stylesheet);
|
||||
|
||||
var uxmlTemplate = Resources.Load(k_UxmlTemplatePath) as VisualTreeAsset;
|
||||
Assert.IsNotNull(uxmlTemplate);
|
||||
|
||||
VisualElement mainContainer = null;
|
||||
mainContainer = uxmlTemplate.Instantiate();
|
||||
Assert.IsNotNull(mainContainer);
|
||||
mainContainer.AddToClassList("mainContainer");
|
||||
|
||||
m_Root = mainContainer.Q("root");
|
||||
m_ItemContainer = mainContainer.Q("itemContainer");
|
||||
m_PropertyViewContainer = mainContainer.Q("propertyViewContainer");
|
||||
|
||||
m_ExpandButton = mainContainer.Q<Button>("expandButton");
|
||||
m_ExpandButton.clickable.clicked += () => expanded = !expanded;
|
||||
m_ExpandButton.RemoveFromHierarchy();
|
||||
|
||||
Add(mainContainer);
|
||||
|
||||
ClearClassList();
|
||||
AddToClassList("blackboardRow");
|
||||
|
||||
name = "SGBlackboardRow";
|
||||
m_ItemContainer.Add(item);
|
||||
m_PropertyViewContainer.Add(propertyView);
|
||||
|
||||
expanded = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7481cf2e22074dbdb915ebd086bd0652
|
||||
timeCreated: 1611611278
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ff4538fd9b634ffd8b591bd96663d06
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
class CategoryColors : ColorProviderFromStyleSheet
|
||||
{
|
||||
public override string GetTitle() => "Category";
|
||||
|
||||
public override bool AllowCustom() => false;
|
||||
public override bool ClearOnDirty() => false;
|
||||
|
||||
protected override bool GetClassFromNode(AbstractMaterialNode node, out string ussClass)
|
||||
{
|
||||
ussClass = string.Empty;
|
||||
if (!(node.GetType().GetCustomAttributes(typeof(TitleAttribute), false).FirstOrDefault() is TitleAttribute title))
|
||||
return false;
|
||||
|
||||
ussClass = title.title[0];
|
||||
|
||||
return !string.IsNullOrEmpty(ussClass);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 93e3298dd3de748f18fe0a2c486c15f5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
// Use this to set colors on your node titles.
|
||||
// There are 2 methods of setting colors - direct Color objects via code (such as data saved in the node itself),
|
||||
// or setting classes on a VisualElement, allowing the colors themselves to be defined in USS. See notes on
|
||||
// IColorProvider for how to use these different methods.
|
||||
class ColorManager
|
||||
{
|
||||
static string DefaultProvider = NoColors.Title;
|
||||
|
||||
List<IColorProvider> m_Providers;
|
||||
|
||||
int m_ActiveIndex = 0;
|
||||
public int activeIndex
|
||||
{
|
||||
get => m_ActiveIndex;
|
||||
private set
|
||||
{
|
||||
if (!IsValidIndex(value))
|
||||
return;
|
||||
|
||||
m_ActiveIndex = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ColorManager(string activeProvider)
|
||||
{
|
||||
m_Providers = new List<IColorProvider>();
|
||||
|
||||
if (string.IsNullOrEmpty(activeProvider))
|
||||
activeProvider = DefaultProvider;
|
||||
|
||||
foreach (var colorType in TypeCache.GetTypesDerivedFrom<IColorProvider>().Where(t => !t.IsAbstract))
|
||||
{
|
||||
var provider = (IColorProvider)Activator.CreateInstance(colorType);
|
||||
m_Providers.Add(provider);
|
||||
}
|
||||
|
||||
m_Providers.Sort((p1, p2) => string.Compare(p1.GetTitle(), p2.GetTitle(), StringComparison.InvariantCulture));
|
||||
activeIndex = m_Providers.FindIndex(provider => provider.GetTitle() == activeProvider);
|
||||
}
|
||||
|
||||
public void SetNodesDirty(IEnumerable<IShaderNodeView> nodeViews)
|
||||
{
|
||||
if (activeProvider.ClearOnDirty())
|
||||
{
|
||||
foreach (var view in nodeViews)
|
||||
{
|
||||
activeProvider.ClearColor(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActiveProvider(int newIndex, IEnumerable<IShaderNodeView> nodeViews)
|
||||
{
|
||||
if (newIndex == activeIndex || !IsValidIndex(newIndex))
|
||||
return;
|
||||
|
||||
var oldProvider = activeProvider;
|
||||
activeIndex = newIndex;
|
||||
|
||||
foreach (var view in nodeViews)
|
||||
{
|
||||
oldProvider.ClearColor(view);
|
||||
activeProvider.ApplyColor(view);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateNodeViews(IEnumerable<IShaderNodeView> nodeViews)
|
||||
{
|
||||
foreach (var view in nodeViews)
|
||||
{
|
||||
UpdateNodeView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateNodeView(IShaderNodeView nodeView)
|
||||
{
|
||||
activeProvider.ApplyColor(nodeView);
|
||||
}
|
||||
|
||||
public IEnumerable<string> providerNames => m_Providers.Select(p => p.GetTitle());
|
||||
|
||||
public string activeProviderName => activeProvider.GetTitle();
|
||||
|
||||
public bool activeSupportsCustom => activeProvider.AllowCustom();
|
||||
|
||||
IColorProvider activeProvider => m_Providers[activeIndex];
|
||||
|
||||
bool IsValidIndex(int index) => index >= 0 && index < m_Providers.Count;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 739266dbe5679492f90aba99b9265731
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
[Serializable]
|
||||
class SerializableUserColor
|
||||
{
|
||||
public string Key = String.Empty;
|
||||
public Color Value = Color.black;
|
||||
|
||||
public SerializableUserColor(KeyValuePair<string, Color> pair) { Key = pair.Key; Value = pair.Value; }
|
||||
|
||||
// Empty constructor required by serialization system
|
||||
public SerializableUserColor() { }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CustomColorData : ISerializationCallbackReceiver
|
||||
{
|
||||
Dictionary<string, Color> m_CustomColors = new Dictionary<string, Color>();
|
||||
[SerializeField]
|
||||
List<SerializationHelper.JSONSerializedElement> m_SerializableColors = new List<SerializationHelper.JSONSerializedElement>();
|
||||
|
||||
public bool TryGetColor(string provider, out Color color)
|
||||
{
|
||||
return m_CustomColors.TryGetValue(provider, out color);
|
||||
}
|
||||
|
||||
public void Set(string provider, Color color)
|
||||
{
|
||||
m_CustomColors[provider] = color;
|
||||
}
|
||||
|
||||
public void Remove(string provider)
|
||||
{
|
||||
m_CustomColors.Remove(provider);
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
m_SerializableColors.Clear();
|
||||
foreach (var customColorKvp in m_CustomColors)
|
||||
{
|
||||
m_SerializableColors.Add(SerializationHelper.Serialize(new SerializableUserColor(customColorKvp)));
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
List<SerializableUserColor> colors = SerializationHelper.Deserialize<SerializableUserColor>(m_SerializableColors, null);
|
||||
foreach (var colorPair in colors)
|
||||
{
|
||||
m_CustomColors.Add(colorPair.Key, colorPair.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 77e837a1c5a3a4f3f873a99e6287a220
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,72 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
// Defines how the ColorManager interacts with various providers
|
||||
interface IColorProvider
|
||||
{
|
||||
string GetTitle();
|
||||
|
||||
bool AllowCustom();
|
||||
|
||||
bool ClearOnDirty();
|
||||
|
||||
void ApplyColor(IShaderNodeView nodeView);
|
||||
void ClearColor(IShaderNodeView nodeView);
|
||||
}
|
||||
|
||||
internal abstract class ColorProviderFromCode : IColorProvider
|
||||
{
|
||||
protected abstract bool GetColorFromNode(AbstractMaterialNode node, out Color color);
|
||||
|
||||
public abstract string GetTitle();
|
||||
|
||||
public abstract bool AllowCustom();
|
||||
|
||||
public abstract bool ClearOnDirty();
|
||||
|
||||
public virtual void ApplyColor(IShaderNodeView nodeView)
|
||||
{
|
||||
if (GetColorFromNode(nodeView.node, out var color))
|
||||
{
|
||||
nodeView.SetColor(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeView.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearColor(IShaderNodeView nodeView)
|
||||
{
|
||||
nodeView.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class ColorProviderFromStyleSheet : IColorProvider
|
||||
{
|
||||
protected abstract bool GetClassFromNode(AbstractMaterialNode node, out string ussClass);
|
||||
|
||||
public abstract string GetTitle();
|
||||
|
||||
public abstract bool AllowCustom();
|
||||
|
||||
public abstract bool ClearOnDirty();
|
||||
|
||||
public virtual void ApplyColor(IShaderNodeView nodeView)
|
||||
{
|
||||
if (GetClassFromNode(nodeView.node, out var ussClass))
|
||||
{
|
||||
nodeView.colorElement.AddToClassList(ussClass);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearColor(IShaderNodeView nodeView)
|
||||
{
|
||||
if (GetClassFromNode(nodeView.node, out var ussClass))
|
||||
{
|
||||
nodeView.colorElement.RemoveFromClassList(ussClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8fede14600fd343d592ea0747a5fd164
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,22 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
internal class NoColors : IColorProvider
|
||||
{
|
||||
public const string Title = "<None>";
|
||||
public string GetTitle() => Title;
|
||||
|
||||
public bool AllowCustom() => false;
|
||||
public bool ClearOnDirty() => false;
|
||||
|
||||
public void ApplyColor(IShaderNodeView nodeView)
|
||||
{
|
||||
}
|
||||
|
||||
public void ClearColor(IShaderNodeView nodeView)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a22ff676828b743cc8de233f626453ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
class PrecisionColors : ColorProviderFromStyleSheet
|
||||
{
|
||||
public override string GetTitle() => "Precision";
|
||||
|
||||
public override bool AllowCustom() => false;
|
||||
|
||||
public override bool ClearOnDirty() => true;
|
||||
|
||||
protected override bool GetClassFromNode(AbstractMaterialNode node, out string ussClass)
|
||||
{
|
||||
var graphPrecision = node.graphPrecision.GraphFallback(node.owner.graphDefaultPrecision);
|
||||
ussClass = graphPrecision.ToString();
|
||||
return !string.IsNullOrEmpty(ussClass);
|
||||
}
|
||||
|
||||
public override void ClearColor(IShaderNodeView nodeView)
|
||||
{
|
||||
foreach (var type in GraphPrecision.GetValues(typeof(GraphPrecision)))
|
||||
{
|
||||
nodeView.colorElement.RemoveFromClassList(type.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0656a3ae06559c940a3ba33cef303f99
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
class UserColors : ColorProviderFromCode
|
||||
{
|
||||
const string m_Title = "User Defined";
|
||||
public override string GetTitle() => m_Title;
|
||||
|
||||
public override bool AllowCustom() => true;
|
||||
public override bool ClearOnDirty() => false;
|
||||
|
||||
protected override bool GetColorFromNode(AbstractMaterialNode node, out Color color)
|
||||
{
|
||||
color = Color.black;
|
||||
return node.TryGetColor(m_Title, ref color);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bca71e015fbd848c39feff083689afca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b1b1a4fb9c392ef49acffada0059f01e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,299 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.UIElements;
|
||||
using GraphDataStore = UnityEditor.ShaderGraph.DataStore<UnityEditor.ShaderGraph.GraphData>;
|
||||
using BlackboardItem = UnityEditor.ShaderGraph.Internal.ShaderInput;
|
||||
using BlackboardItemController = UnityEditor.ShaderGraph.Drawing.ShaderInputViewController;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class MoveShaderInputAction : IGraphDataAction
|
||||
{
|
||||
void MoveShaderInput(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out MoveShaderInputAction");
|
||||
AssertHelpers.IsNotNull(shaderInputReference, "ShaderInputReference is null while carrying out MoveShaderInputAction");
|
||||
graphData.owner.RegisterCompleteObjectUndo("Move Graph Input");
|
||||
graphData.MoveItemInCategory(shaderInputReference, newIndexValue, associatedCategoryGuid);
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => MoveShaderInput;
|
||||
|
||||
internal string associatedCategoryGuid { get; set; }
|
||||
|
||||
// Reference to the shader input being modified
|
||||
internal ShaderInput shaderInputReference { get; set; }
|
||||
|
||||
internal int newIndexValue { get; set; }
|
||||
}
|
||||
|
||||
class DeleteCategoryAction : IGraphDataAction
|
||||
{
|
||||
void RemoveCategory(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out DeleteCategoryAction");
|
||||
AssertHelpers.IsNotNull(categoriesToRemoveGuids, "CategoryToRemove is null while carrying out DeleteCategoryAction");
|
||||
|
||||
// This is called by MaterialGraphView currently, no need to repeat it here, though ideally it would live here
|
||||
//graphData.owner.RegisterCompleteObjectUndo("Delete Category");
|
||||
|
||||
foreach (var categoryGUID in categoriesToRemoveGuids)
|
||||
{
|
||||
graphData.RemoveCategory(categoryGUID);
|
||||
}
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => RemoveCategory;
|
||||
|
||||
// Reference to the guid(s) of categories being deleted
|
||||
public HashSet<string> categoriesToRemoveGuids { get; set; } = new HashSet<string>();
|
||||
}
|
||||
|
||||
class ChangeCategoryIsExpandedAction : IGraphDataAction
|
||||
{
|
||||
internal const string kEditorPrefKey = ".isCategoryExpanded";
|
||||
|
||||
void ChangeIsExpanded(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeIsExpanded on Category");
|
||||
foreach (var catid in categoryGuids)
|
||||
{
|
||||
var key = $"{editorPrefsBaseKey}.{catid}.{kEditorPrefKey}";
|
||||
var currentValue = EditorPrefs.GetBool(key, true);
|
||||
|
||||
if (currentValue != isExpanded)
|
||||
{
|
||||
EditorPrefs.SetBool(key, isExpanded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string editorPrefsBaseKey;
|
||||
public List<string> categoryGuids { get; set; }
|
||||
public bool isExpanded { get; set; }
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => ChangeIsExpanded;
|
||||
}
|
||||
|
||||
class ChangeCategoryNameAction : IGraphDataAction
|
||||
{
|
||||
void ChangeCategoryName(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeCategoryNameAction");
|
||||
graphData.owner.RegisterCompleteObjectUndo("Change Category Name");
|
||||
graphData.ChangeCategoryName(categoryGuid, newCategoryNameValue);
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => ChangeCategoryName;
|
||||
|
||||
// Guid of the category being modified
|
||||
public string categoryGuid { get; set; }
|
||||
|
||||
internal string newCategoryNameValue { get; set; }
|
||||
}
|
||||
|
||||
class BlackboardCategoryController : SGViewController<CategoryData, BlackboardCategoryViewModel>
|
||||
{
|
||||
internal SGBlackboardCategory blackboardCategoryView => m_BlackboardCategoryView;
|
||||
SGBlackboardCategory m_BlackboardCategoryView;
|
||||
Dictionary<string, BlackboardItemController> m_BlackboardItemControllers = new Dictionary<string, ShaderInputViewController>();
|
||||
SGBlackboard blackboard { get; set; }
|
||||
|
||||
internal BlackboardCategoryController(CategoryData categoryData, BlackboardCategoryViewModel categoryViewModel, GraphDataStore dataStore)
|
||||
: base(categoryData, categoryViewModel, dataStore)
|
||||
{
|
||||
m_BlackboardCategoryView = new SGBlackboardCategory(categoryViewModel, this);
|
||||
blackboard = categoryViewModel.parentView as SGBlackboard;
|
||||
if (blackboard == null)
|
||||
return;
|
||||
|
||||
blackboard.Add(m_BlackboardCategoryView);
|
||||
// These make sure that the drag indicators are disabled whenever a drag action is cancelled without completing a drop
|
||||
blackboard.RegisterCallback<MouseUpEvent>(evt =>
|
||||
{
|
||||
m_BlackboardCategoryView.OnDragActionCanceled();
|
||||
});
|
||||
blackboard.hideDragIndicatorAction += m_BlackboardCategoryView.OnDragActionCanceled;
|
||||
|
||||
foreach (var categoryItem in categoryData.Children)
|
||||
{
|
||||
if (categoryItem == null)
|
||||
{
|
||||
AssertHelpers.Fail("Failed to insert blackboard row into category due to shader input being null.");
|
||||
continue;
|
||||
}
|
||||
InsertBlackboardRow(categoryItem);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RequestModelChange(IGraphDataAction changeAction)
|
||||
{
|
||||
DataStore.Dispatch(changeAction);
|
||||
}
|
||||
|
||||
// Called by GraphDataStore.Subscribe after the model has been changed
|
||||
protected override void ModelChanged(GraphData graphData, IGraphDataAction changeAction)
|
||||
{
|
||||
// If categoryData associated with this controller is removed by an operation, destroy controller and views associated
|
||||
if (graphData.ContainsCategory(Model) == false)
|
||||
{
|
||||
this.Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (changeAction)
|
||||
{
|
||||
case AddShaderInputAction addBlackboardItemAction:
|
||||
if (addBlackboardItemAction.shaderInputReference != null && IsInputInCategory(addBlackboardItemAction.shaderInputReference))
|
||||
{
|
||||
var blackboardRow = FindBlackboardRow(addBlackboardItemAction.shaderInputReference);
|
||||
if (blackboardRow == null)
|
||||
blackboardRow = InsertBlackboardRow(addBlackboardItemAction.shaderInputReference);
|
||||
// Rows should auto-expand when an input is first added
|
||||
// blackboardRow.expanded = true;
|
||||
var propertyView = blackboardRow.Q<SGBlackboardField>();
|
||||
if (addBlackboardItemAction.addInputActionType == AddShaderInputAction.AddActionSource.AddMenu)
|
||||
propertyView.OpenTextEditor();
|
||||
}
|
||||
break;
|
||||
|
||||
case CopyShaderInputAction copyShaderInputAction:
|
||||
// In the specific case of only-one keywords like Material Quality and Raytracing, they can get copied, but because only one can exist, the output copied value is null
|
||||
if (copyShaderInputAction.copiedShaderInput != null && IsInputInCategory(copyShaderInputAction.copiedShaderInput))
|
||||
{
|
||||
var blackboardRow = InsertBlackboardRow(copyShaderInputAction.copiedShaderInput, copyShaderInputAction.insertIndex);
|
||||
if (blackboardRow != null)
|
||||
{
|
||||
var graphView = ViewModel.parentView.GetFirstAncestorOfType<MaterialGraphView>();
|
||||
var propertyView = blackboardRow.Q<SGBlackboardField>();
|
||||
graphView?.AddToSelectionNoUndoRecord(propertyView);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AddItemToCategoryAction addItemToCategoryAction:
|
||||
// If item was added to category that this controller manages, then add blackboard row to represent that item
|
||||
if (addItemToCategoryAction.itemToAdd != null && addItemToCategoryAction.categoryGuid == ViewModel.associatedCategoryGuid)
|
||||
{
|
||||
InsertBlackboardRow(addItemToCategoryAction.itemToAdd, addItemToCategoryAction.indexToAddItemAt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the added input has been added to a category other than this one, and it used to belong to this category,
|
||||
// Then cleanup the controller and view that used to represent that input
|
||||
foreach (var key in m_BlackboardItemControllers.Keys)
|
||||
{
|
||||
var blackboardItemController = m_BlackboardItemControllers[key];
|
||||
if (blackboardItemController.Model == addItemToCategoryAction.itemToAdd)
|
||||
{
|
||||
RemoveBlackboardRow(addItemToCategoryAction.itemToAdd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DeleteCategoryAction deleteCategoryAction:
|
||||
if (deleteCategoryAction.categoriesToRemoveGuids.Contains(ViewModel.associatedCategoryGuid))
|
||||
{
|
||||
this.Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ChangeCategoryIsExpandedAction changeIsExpandedAction:
|
||||
if (changeIsExpandedAction.categoryGuids.Contains(ViewModel.associatedCategoryGuid))
|
||||
{
|
||||
ViewModel.isExpanded = changeIsExpandedAction.isExpanded;
|
||||
m_BlackboardCategoryView.TryDoFoldout(changeIsExpandedAction.isExpanded);
|
||||
}
|
||||
break;
|
||||
|
||||
case ChangeCategoryNameAction changeCategoryNameAction:
|
||||
if (changeCategoryNameAction.categoryGuid == ViewModel.associatedCategoryGuid)
|
||||
{
|
||||
ViewModel.name = Model.name;
|
||||
m_BlackboardCategoryView.title = ViewModel.name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsInputInCategory(ShaderInput shaderInput)
|
||||
{
|
||||
return Model != null && Model.IsItemInCategory(shaderInput);
|
||||
}
|
||||
|
||||
internal SGBlackboardRow FindBlackboardRow(ShaderInput shaderInput)
|
||||
{
|
||||
m_BlackboardItemControllers.TryGetValue(shaderInput.objectId, out var associatedController);
|
||||
return associatedController?.BlackboardItemView;
|
||||
}
|
||||
|
||||
// Creates controller, view and view model for a blackboard item and adds the view to the specified index in the category
|
||||
// By default adds it to the end of the list if no insertionIndex specified
|
||||
internal SGBlackboardRow InsertBlackboardRow(BlackboardItem shaderInput, int insertionIndex = -1)
|
||||
{
|
||||
var shaderInputViewModel = new ShaderInputViewModel()
|
||||
{
|
||||
model = shaderInput,
|
||||
parentView = blackboardCategoryView,
|
||||
};
|
||||
var blackboardItemController = new BlackboardItemController(shaderInput, shaderInputViewModel, DataStore);
|
||||
|
||||
m_BlackboardItemControllers.TryGetValue(shaderInput.objectId, out var existingItemController);
|
||||
if (existingItemController == null)
|
||||
{
|
||||
m_BlackboardItemControllers.Add(shaderInput.objectId, blackboardItemController);
|
||||
// If no index specified, add to end of category
|
||||
if (insertionIndex == -1)
|
||||
blackboardCategoryView.Add(blackboardItemController.BlackboardItemView);
|
||||
else
|
||||
blackboardCategoryView.Insert(insertionIndex, blackboardItemController.BlackboardItemView);
|
||||
|
||||
blackboardCategoryView.MarkDirtyRepaint();
|
||||
|
||||
return blackboardItemController.BlackboardItemView;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertHelpers.Fail("Tried to add blackboard item that already exists to category.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveBlackboardRow(BlackboardItem shaderInput)
|
||||
{
|
||||
m_BlackboardItemControllers.TryGetValue(shaderInput.objectId, out var associatedBlackboardItemController);
|
||||
if (associatedBlackboardItemController != null)
|
||||
{
|
||||
associatedBlackboardItemController.Destroy();
|
||||
m_BlackboardItemControllers.Remove(shaderInput.objectId);
|
||||
}
|
||||
else
|
||||
AssertHelpers.Fail("Failed to find associated blackboard item controller for shader input that was just deleted. Cannot clean up view associated with input.");
|
||||
}
|
||||
|
||||
void ClearBlackboardRows()
|
||||
{
|
||||
foreach (var shaderInputViewController in m_BlackboardItemControllers.Values)
|
||||
shaderInputViewController.Destroy();
|
||||
|
||||
m_BlackboardItemControllers.Clear();
|
||||
}
|
||||
|
||||
public override void Destroy()
|
||||
{
|
||||
Cleanup();
|
||||
m_BlackboardCategoryView?.RemoveFromHierarchy();
|
||||
ClearBlackboardRows();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 415e66e3c4ba4ffd834a7d90349db989
|
||||
timeCreated: 1612206426
|
|
@ -0,0 +1,818 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine.UIElements;
|
||||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using GraphDataStore = UnityEditor.ShaderGraph.DataStore<UnityEditor.ShaderGraph.GraphData>;
|
||||
using BlackboardItem = UnityEditor.ShaderGraph.Internal.ShaderInput;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
struct BlackboardShaderInputOrder
|
||||
{
|
||||
public bool isKeyword;
|
||||
public bool isDropdown;
|
||||
public KeywordType keywordType;
|
||||
public ShaderKeyword builtInKeyword;
|
||||
public string deprecatedPropertyName;
|
||||
public int version;
|
||||
}
|
||||
class BlackboardShaderInputFactory
|
||||
{
|
||||
static public ShaderInput GetShaderInput(BlackboardShaderInputOrder order)
|
||||
{
|
||||
ShaderInput output;
|
||||
if (order.isKeyword)
|
||||
{
|
||||
if (order.builtInKeyword == null)
|
||||
{
|
||||
output = new ShaderKeyword(order.keywordType);
|
||||
}
|
||||
else
|
||||
{
|
||||
output = order.builtInKeyword;
|
||||
}
|
||||
}
|
||||
else if (order.isDropdown)
|
||||
{
|
||||
output = new ShaderDropdown();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (order.deprecatedPropertyName)
|
||||
{
|
||||
case "Color":
|
||||
output = new ColorShaderProperty(order.version);
|
||||
break;
|
||||
default:
|
||||
output = null;
|
||||
AssertHelpers.Fail("BlackboardShaderInputFactory: Unknown deprecated property type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
class AddShaderInputAction : IGraphDataAction
|
||||
{
|
||||
public enum AddActionSource
|
||||
{
|
||||
Default,
|
||||
AddMenu
|
||||
}
|
||||
|
||||
void AddShaderInput(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out AddShaderInputAction");
|
||||
|
||||
// If type property is valid, create instance of that type
|
||||
if (blackboardItemType != null && blackboardItemType.IsSubclassOf(typeof(BlackboardItem)))
|
||||
{
|
||||
shaderInputReference = (BlackboardItem)Activator.CreateInstance(blackboardItemType, true);
|
||||
}
|
||||
else if (m_ShaderInputReferenceGetter != null)
|
||||
{
|
||||
shaderInputReference = m_ShaderInputReferenceGetter();
|
||||
}
|
||||
// If type is null a direct override object must have been provided or else we are in an error-state
|
||||
else if (shaderInputReference == null)
|
||||
{
|
||||
AssertHelpers.Fail("BlackboardController: Unable to complete Add Shader Input action.");
|
||||
return;
|
||||
}
|
||||
|
||||
shaderInputReference.generatePropertyBlock = shaderInputReference.isExposable;
|
||||
|
||||
if (graphData.owner != null)
|
||||
graphData.owner.RegisterCompleteObjectUndo("Add Shader Input");
|
||||
else
|
||||
AssertHelpers.Fail("GraphObject is null while carrying out AddShaderInputAction");
|
||||
|
||||
graphData.AddGraphInput(shaderInputReference);
|
||||
|
||||
// If no categoryToAddItemToGuid is provided, add the input to the default category
|
||||
if (categoryToAddItemToGuid == String.Empty)
|
||||
{
|
||||
var defaultCategory = graphData.categories.FirstOrDefault();
|
||||
AssertHelpers.IsNotNull(defaultCategory, "Default category reference is null.");
|
||||
if (defaultCategory != null)
|
||||
{
|
||||
var addItemToCategoryAction = new AddItemToCategoryAction();
|
||||
addItemToCategoryAction.categoryGuid = defaultCategory.categoryGuid;
|
||||
addItemToCategoryAction.itemToAdd = shaderInputReference;
|
||||
graphData.owner.graphDataStore.Dispatch(addItemToCategoryAction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var addItemToCategoryAction = new AddItemToCategoryAction();
|
||||
addItemToCategoryAction.categoryGuid = categoryToAddItemToGuid;
|
||||
addItemToCategoryAction.itemToAdd = shaderInputReference;
|
||||
graphData.owner.graphDataStore.Dispatch(addItemToCategoryAction);
|
||||
}
|
||||
}
|
||||
|
||||
public static AddShaderInputAction AddDeprecatedPropertyAction(BlackboardShaderInputOrder order)
|
||||
{
|
||||
return new() { shaderInputReference = BlackboardShaderInputFactory.GetShaderInput(order), addInputActionType = AddShaderInputAction.AddActionSource.AddMenu };
|
||||
}
|
||||
|
||||
public static AddShaderInputAction AddDropdownAction(BlackboardShaderInputOrder order)
|
||||
{
|
||||
return new() { shaderInputReference = BlackboardShaderInputFactory.GetShaderInput(order), addInputActionType = AddShaderInputAction.AddActionSource.AddMenu };
|
||||
}
|
||||
|
||||
public static AddShaderInputAction AddKeywordAction(BlackboardShaderInputOrder order)
|
||||
{
|
||||
return new() { shaderInputReference = BlackboardShaderInputFactory.GetShaderInput(order), addInputActionType = AddShaderInputAction.AddActionSource.AddMenu };
|
||||
}
|
||||
|
||||
public static AddShaderInputAction AddPropertyAction(Type shaderInputType)
|
||||
{
|
||||
return new() { blackboardItemType = shaderInputType, addInputActionType = AddShaderInputAction.AddActionSource.AddMenu };
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => AddShaderInput;
|
||||
// If this is a subclass of ShaderInput and is not null, then an object of this type is created to add to blackboard
|
||||
// If the type field above is null and this is provided, then it is directly used as the item to add to blackboard
|
||||
public BlackboardItem shaderInputReference { get; set; }
|
||||
public AddActionSource addInputActionType { get; set; }
|
||||
public string categoryToAddItemToGuid { get; set; } = String.Empty;
|
||||
|
||||
Type blackboardItemType { get; set; }
|
||||
|
||||
Func<BlackboardItem> m_ShaderInputReferenceGetter = null;
|
||||
}
|
||||
|
||||
class ChangeGraphPathAction : IGraphDataAction
|
||||
{
|
||||
void ChangeGraphPath(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeGraphPathAction");
|
||||
graphData.path = NewGraphPath;
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => ChangeGraphPath;
|
||||
|
||||
public string NewGraphPath { get; set; }
|
||||
}
|
||||
|
||||
class CopyShaderInputAction : IGraphDataAction
|
||||
{
|
||||
void CopyShaderInput(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out CopyShaderInputAction");
|
||||
AssertHelpers.IsNotNull(shaderInputToCopy, "ShaderInputToCopy is null while carrying out CopyShaderInputAction");
|
||||
|
||||
// Don't handle undo here as there are different contexts in which this action is used, that define the undo action
|
||||
// TODO: Perhaps a sign that each of those need to be made their own actions instead of conflating intent into a single action
|
||||
|
||||
switch (shaderInputToCopy)
|
||||
{
|
||||
case AbstractShaderProperty property:
|
||||
insertIndex = Mathf.Clamp(insertIndex, -1, graphData.properties.Count() - 1);
|
||||
|
||||
var copiedProperty = (AbstractShaderProperty)graphData.AddCopyOfShaderInput(property, insertIndex);
|
||||
if (copiedProperty != null) // some property types cannot be duplicated (unknown types)
|
||||
{
|
||||
// Update the property nodes that depends on the copied node
|
||||
foreach (var node in dependentNodeList)
|
||||
{
|
||||
if (node is PropertyNode propertyNode)
|
||||
{
|
||||
propertyNode.owner = graphData;
|
||||
propertyNode.property = copiedProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
copiedShaderInput = copiedProperty;
|
||||
break;
|
||||
|
||||
case ShaderKeyword shaderKeyword:
|
||||
// InsertIndex gets passed in relative to the blackboard position of an item overall,
|
||||
// and not relative to the array sizes of the properties/keywords/dropdowns
|
||||
var keywordInsertIndex = insertIndex - graphData.properties.Count();
|
||||
keywordInsertIndex = Mathf.Clamp(keywordInsertIndex, -1, graphData.keywords.Count() - 1);
|
||||
|
||||
// Don't duplicate built-in keywords within the same graph
|
||||
if (shaderKeyword.isBuiltIn && graphData.keywords.Any(p => p.referenceName == shaderInputToCopy.referenceName))
|
||||
return;
|
||||
|
||||
var copiedKeyword = (ShaderKeyword)graphData.AddCopyOfShaderInput(shaderKeyword, keywordInsertIndex);
|
||||
|
||||
// Update the keyword nodes that depends on the copied node
|
||||
foreach (var node in dependentNodeList)
|
||||
{
|
||||
if (node is KeywordNode propertyNode)
|
||||
{
|
||||
propertyNode.owner = graphData;
|
||||
propertyNode.keyword = copiedKeyword;
|
||||
}
|
||||
}
|
||||
|
||||
copiedShaderInput = copiedKeyword;
|
||||
break;
|
||||
|
||||
case ShaderDropdown shaderDropdown:
|
||||
// InsertIndex gets passed in relative to the blackboard position of an item overall,
|
||||
// and not relative to the array sizes of the properties/keywords/dropdowns
|
||||
var dropdownInsertIndex = insertIndex - graphData.properties.Count() - graphData.keywords.Count();
|
||||
dropdownInsertIndex = Mathf.Clamp(dropdownInsertIndex, -1, graphData.dropdowns.Count() - 1);
|
||||
|
||||
var copiedDropdown = (ShaderDropdown)graphData.AddCopyOfShaderInput(shaderDropdown, dropdownInsertIndex);
|
||||
|
||||
// Update the dropdown nodes that depends on the copied node
|
||||
foreach (var node in dependentNodeList)
|
||||
{
|
||||
if (node is DropdownNode propertyNode)
|
||||
{
|
||||
propertyNode.owner = graphData;
|
||||
propertyNode.dropdown = copiedDropdown;
|
||||
}
|
||||
}
|
||||
|
||||
copiedShaderInput = copiedDropdown;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (copiedShaderInput != null)
|
||||
{
|
||||
// If specific category to copy to is provided, find and use it
|
||||
foreach (var category in graphData.categories)
|
||||
{
|
||||
if (category.categoryGuid == containingCategoryGuid)
|
||||
{
|
||||
// Ensures that the new item gets added after the item it was duplicated from
|
||||
insertIndex += 1;
|
||||
// If the source item was already the last item in list, just add to end of list
|
||||
if (insertIndex >= category.childCount)
|
||||
insertIndex = -1;
|
||||
graphData.InsertItemIntoCategory(category.objectId, copiedShaderInput, insertIndex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Else, add to default category
|
||||
graphData.categories.First().InsertItemIntoCategory(copiedShaderInput);
|
||||
}
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => CopyShaderInput;
|
||||
|
||||
public IEnumerable<AbstractMaterialNode> dependentNodeList { get; set; } = new List<AbstractMaterialNode>();
|
||||
|
||||
public BlackboardItem shaderInputToCopy { get; set; }
|
||||
|
||||
public BlackboardItem copiedShaderInput { get; set; }
|
||||
|
||||
public string containingCategoryGuid { get; set; }
|
||||
|
||||
public int insertIndex { get; set; } = -1;
|
||||
}
|
||||
|
||||
class AddCategoryAction : IGraphDataAction
|
||||
{
|
||||
void AddCategory(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out AddCategoryAction");
|
||||
graphData.owner.RegisterCompleteObjectUndo("Add Category");
|
||||
// If categoryDataReference is not null, directly add it to graphData
|
||||
if (categoryDataReference == null)
|
||||
categoryDataReference = new CategoryData(categoryName, childObjects);
|
||||
graphData.AddCategory(categoryDataReference);
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => AddCategory;
|
||||
|
||||
// Direct reference to the categoryData to use if it is specified
|
||||
public CategoryData categoryDataReference { get; set; }
|
||||
public string categoryName { get; set; } = String.Empty;
|
||||
public List<ShaderInput> childObjects { get; set; }
|
||||
}
|
||||
|
||||
class MoveCategoryAction : IGraphDataAction
|
||||
{
|
||||
void MoveCategory(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out MoveCategoryAction");
|
||||
graphData.owner.RegisterCompleteObjectUndo("Move Category");
|
||||
// Handling for out of range moves is slightly different, but otherwise we need to reverse for insertion order.
|
||||
var guids = newIndexValue >= graphData.categories.Count() ? categoryGuids : categoryGuids.Reverse<string>();
|
||||
foreach (var guid in categoryGuids)
|
||||
{
|
||||
var cat = graphData.categories.FirstOrDefault(c => c.categoryGuid == guid);
|
||||
graphData.MoveCategory(cat, newIndexValue);
|
||||
}
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => MoveCategory;
|
||||
|
||||
// Reference to the shader input being modified
|
||||
internal List<string> categoryGuids { get; set; }
|
||||
|
||||
internal int newIndexValue { get; set; }
|
||||
}
|
||||
|
||||
class AddItemToCategoryAction : IGraphDataAction
|
||||
{
|
||||
public enum AddActionSource
|
||||
{
|
||||
Default,
|
||||
DragDrop
|
||||
}
|
||||
|
||||
void AddItemsToCategory(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out AddItemToCategoryAction");
|
||||
graphData.owner.RegisterCompleteObjectUndo("Add Item to Category");
|
||||
graphData.InsertItemIntoCategory(categoryGuid, itemToAdd, indexToAddItemAt);
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => AddItemsToCategory;
|
||||
|
||||
public string categoryGuid { get; set; }
|
||||
|
||||
public ShaderInput itemToAdd { get; set; }
|
||||
|
||||
// By default an item is always added to the end of a category, if this value is set to something other than -1, will insert the item at that position within the category
|
||||
public int indexToAddItemAt { get; set; } = -1;
|
||||
|
||||
public AddActionSource addActionSource { get; set; }
|
||||
}
|
||||
|
||||
class CopyCategoryAction : IGraphDataAction
|
||||
{
|
||||
void CopyCategory(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out CopyCategoryAction");
|
||||
AssertHelpers.IsNotNull(categoryToCopyReference, "CategoryToCopyReference is null while carrying out CopyCategoryAction");
|
||||
|
||||
// This is called by MaterialGraphView currently, no need to repeat it here, though ideally it would live here
|
||||
//graphData.owner.RegisterCompleteObjectUndo("Copy Category");
|
||||
|
||||
newCategoryDataReference = graphData.CopyCategory(categoryToCopyReference);
|
||||
}
|
||||
|
||||
// Reference to the new category created as a copy
|
||||
public CategoryData newCategoryDataReference { get; set; }
|
||||
|
||||
// After category has been copied, store reference to it
|
||||
public CategoryData categoryToCopyReference { get; set; }
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => CopyCategory;
|
||||
}
|
||||
|
||||
class ShaderVariantLimitAction : IGraphDataAction
|
||||
{
|
||||
public int currentVariantCount { get; set; } = 0;
|
||||
public int maxVariantCount { get; set; } = 0;
|
||||
|
||||
public ShaderVariantLimitAction(int currentVariantCount, int maxVariantCount)
|
||||
{
|
||||
this.maxVariantCount = maxVariantCount;
|
||||
this.currentVariantCount = currentVariantCount;
|
||||
}
|
||||
|
||||
// There's no action actually performed on the graph, but we need to implement this as a valid function
|
||||
public Action<GraphData> modifyGraphDataAction => Empty;
|
||||
|
||||
void Empty(GraphData graphData)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class BlackboardController : SGViewController<GraphData, BlackboardViewModel>
|
||||
{
|
||||
// Type changes (adds/removes of Types) only happen after a full assembly reload so its safe to make this static
|
||||
static IList<Type> s_ShaderInputTypes;
|
||||
|
||||
static BlackboardController()
|
||||
{
|
||||
var shaderInputTypes = TypeCache.GetTypesWithAttribute<BlackboardInputInfo>().ToList();
|
||||
// Sort the ShaderInput by priority using the BlackboardInputInfo attribute
|
||||
shaderInputTypes.Sort((s1, s2) =>
|
||||
{
|
||||
var info1 = Attribute.GetCustomAttribute(s1, typeof(BlackboardInputInfo)) as BlackboardInputInfo;
|
||||
var info2 = Attribute.GetCustomAttribute(s2, typeof(BlackboardInputInfo)) as BlackboardInputInfo;
|
||||
|
||||
if (info1.priority == info2.priority)
|
||||
return (info1.name ?? s1.Name).CompareTo(info2.name ?? s2.Name);
|
||||
else
|
||||
return info1.priority.CompareTo(info2.priority);
|
||||
});
|
||||
|
||||
s_ShaderInputTypes = shaderInputTypes.ToList();
|
||||
}
|
||||
|
||||
BlackboardCategoryController m_DefaultCategoryController = null;
|
||||
Dictionary<string, BlackboardCategoryController> m_BlackboardCategoryControllers = new Dictionary<string, BlackboardCategoryController>();
|
||||
|
||||
SGBlackboard m_Blackboard;
|
||||
|
||||
internal SGBlackboard blackboard
|
||||
{
|
||||
get => m_Blackboard;
|
||||
private set => m_Blackboard = value;
|
||||
}
|
||||
public string GetFirstSelectedCategoryGuid()
|
||||
{
|
||||
if (m_Blackboard == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
var copiedSelectionList = new List<ISelectable>(m_Blackboard.selection);
|
||||
var selectedCategories = new List<SGBlackboardCategory>();
|
||||
var selectedCategoryGuid = String.Empty;
|
||||
for (int i = 0; i < copiedSelectionList.Count; i++)
|
||||
{
|
||||
var selectable = copiedSelectionList[i];
|
||||
if (selectable is SGBlackboardCategory category)
|
||||
{
|
||||
selectedCategories.Add(selectable as SGBlackboardCategory);
|
||||
}
|
||||
}
|
||||
if (selectedCategories.Any())
|
||||
{
|
||||
selectedCategoryGuid = selectedCategories[0].viewModel.associatedCategoryGuid;
|
||||
}
|
||||
return selectedCategoryGuid;
|
||||
}
|
||||
|
||||
void InitializeViewModel(bool useDropdowns)
|
||||
{
|
||||
// Clear the view model
|
||||
ViewModel.ResetViewModelData();
|
||||
ViewModel.subtitle = BlackboardUtils.FormatPath(Model.path);
|
||||
BlackboardShaderInputOrder propertyTypesOrder = new BlackboardShaderInputOrder();
|
||||
|
||||
// Property data first
|
||||
foreach (var shaderInputType in s_ShaderInputTypes)
|
||||
{
|
||||
if (shaderInputType.IsAbstract)
|
||||
continue;
|
||||
|
||||
var info = Attribute.GetCustomAttribute(shaderInputType, typeof(BlackboardInputInfo)) as BlackboardInputInfo;
|
||||
string name = info?.name ?? ObjectNames.NicifyVariableName(shaderInputType.Name.Replace("ShaderProperty", ""));
|
||||
|
||||
// QUICK FIX TO DEAL WITH DEPRECATED COLOR PROPERTY
|
||||
if (name.Equals("Color", StringComparison.InvariantCultureIgnoreCase) && ShaderGraphPreferences.allowDeprecatedBehaviors)
|
||||
{
|
||||
propertyTypesOrder.isKeyword = false;
|
||||
propertyTypesOrder.deprecatedPropertyName = name;
|
||||
propertyTypesOrder.version = ColorShaderProperty.deprecatedVersion;
|
||||
ViewModel.propertyNameToAddActionMap.Add("Color (Deprecated)", AddShaderInputAction.AddDeprecatedPropertyAction(propertyTypesOrder));
|
||||
ViewModel.propertyNameToAddActionMap.Add(name, AddShaderInputAction.AddPropertyAction(shaderInputType));
|
||||
}
|
||||
else
|
||||
ViewModel.propertyNameToAddActionMap.Add(name, AddShaderInputAction.AddPropertyAction(shaderInputType));
|
||||
}
|
||||
|
||||
// Default Keywords next
|
||||
BlackboardShaderInputOrder keywordTypesOrder = new BlackboardShaderInputOrder();
|
||||
keywordTypesOrder.isKeyword = true;
|
||||
keywordTypesOrder.keywordType = KeywordType.Boolean;
|
||||
ViewModel.defaultKeywordNameToAddActionMap.Add("Boolean", AddShaderInputAction.AddKeywordAction(keywordTypesOrder));
|
||||
keywordTypesOrder.keywordType = KeywordType.Enum;
|
||||
ViewModel.defaultKeywordNameToAddActionMap.Add("Enum", AddShaderInputAction.AddKeywordAction(keywordTypesOrder));
|
||||
|
||||
// Built-In Keywords after that
|
||||
foreach (var builtinKeywordDescriptor in KeywordUtil.GetBuiltinKeywordDescriptors())
|
||||
{
|
||||
var keyword = ShaderKeyword.CreateBuiltInKeyword(builtinKeywordDescriptor);
|
||||
// Do not allow user to add built-in keywords that conflict with user-made keywords that have the same reference name or display name
|
||||
if (Model.keywords.Any(x => x.referenceName == keyword.referenceName || x.displayName == keyword.displayName))
|
||||
{
|
||||
ViewModel.disabledKeywordNameList.Add(keyword.displayName);
|
||||
}
|
||||
else
|
||||
{
|
||||
keywordTypesOrder.builtInKeyword = (ShaderKeyword)keyword.Copy();
|
||||
ViewModel.builtInKeywordNameToAddActionMap.Add(keyword.displayName, AddShaderInputAction.AddKeywordAction(keywordTypesOrder));
|
||||
}
|
||||
}
|
||||
|
||||
if (useDropdowns)
|
||||
{
|
||||
BlackboardShaderInputOrder dropdownsOrder = new BlackboardShaderInputOrder();
|
||||
dropdownsOrder.isDropdown = true;
|
||||
ViewModel.defaultDropdownNameToAdd = new Tuple<string, IGraphDataAction>("Dropdown", AddShaderInputAction.AddDropdownAction(dropdownsOrder));
|
||||
}
|
||||
|
||||
// Category data last
|
||||
var defaultNewCategoryReference = new CategoryData("Category");
|
||||
ViewModel.addCategoryAction = new AddCategoryAction() { categoryDataReference = defaultNewCategoryReference };
|
||||
|
||||
ViewModel.requestModelChangeAction = this.RequestModelChange;
|
||||
ViewModel.categoryInfoList.AddRange(DataStore.State.categories.ToList());
|
||||
}
|
||||
|
||||
internal BlackboardController(GraphData model, BlackboardViewModel inViewModel, GraphDataStore graphDataStore)
|
||||
: base(model, inViewModel, graphDataStore)
|
||||
{
|
||||
// TODO: hide this more generically for category types.
|
||||
bool useDropdowns = model.isSubGraph;
|
||||
InitializeViewModel(useDropdowns);
|
||||
|
||||
blackboard = new SGBlackboard(ViewModel, this);
|
||||
|
||||
// Add default category at the top of the blackboard (create it if it doesn't exist already)
|
||||
var existingDefaultCategory = DataStore.State.categories.FirstOrDefault();
|
||||
if (existingDefaultCategory != null && existingDefaultCategory.IsNamedCategory() == false)
|
||||
{
|
||||
AddBlackboardCategory(graphDataStore, existingDefaultCategory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Any properties that don't already have a category (for example, if this graph is being loaded from an older version that doesn't have category data)
|
||||
var uncategorizedBlackboardItems = new List<ShaderInput>();
|
||||
foreach (var shaderProperty in DataStore.State.properties)
|
||||
if (IsInputUncategorized(shaderProperty))
|
||||
uncategorizedBlackboardItems.Add(shaderProperty);
|
||||
|
||||
foreach (var shaderKeyword in DataStore.State.keywords)
|
||||
if (IsInputUncategorized(shaderKeyword))
|
||||
uncategorizedBlackboardItems.Add(shaderKeyword);
|
||||
|
||||
if (useDropdowns)
|
||||
{
|
||||
foreach (var shaderDropdown in DataStore.State.dropdowns)
|
||||
if (IsInputUncategorized(shaderDropdown))
|
||||
uncategorizedBlackboardItems.Add(shaderDropdown);
|
||||
}
|
||||
|
||||
var addCategoryAction = new AddCategoryAction();
|
||||
addCategoryAction.categoryDataReference = CategoryData.DefaultCategory(uncategorizedBlackboardItems);
|
||||
graphDataStore.Dispatch(addCategoryAction);
|
||||
}
|
||||
|
||||
// Get the reference to default category controller after its been added
|
||||
m_DefaultCategoryController = m_BlackboardCategoryControllers.Values.FirstOrDefault();
|
||||
AssertHelpers.IsNotNull(m_DefaultCategoryController, "Failed to instantiate default category.");
|
||||
|
||||
// Handle loaded-in categories from graph first, skipping the first/default category
|
||||
foreach (var categoryData in ViewModel.categoryInfoList.Skip(1))
|
||||
{
|
||||
AddBlackboardCategory(graphDataStore, categoryData);
|
||||
}
|
||||
}
|
||||
|
||||
internal string editorPrefsBaseKey => "unity.shadergraph." + DataStore.State.objectId;
|
||||
|
||||
BlackboardCategoryController AddBlackboardCategory(GraphDataStore graphDataStore, CategoryData categoryInfo)
|
||||
{
|
||||
var blackboardCategoryViewModel = new BlackboardCategoryViewModel();
|
||||
blackboardCategoryViewModel.parentView = blackboard;
|
||||
blackboardCategoryViewModel.requestModelChangeAction = ViewModel.requestModelChangeAction;
|
||||
blackboardCategoryViewModel.name = categoryInfo.name;
|
||||
blackboardCategoryViewModel.associatedCategoryGuid = categoryInfo.categoryGuid;
|
||||
blackboardCategoryViewModel.isExpanded = EditorPrefs.GetBool($"{editorPrefsBaseKey}.{categoryInfo.categoryGuid}.{ChangeCategoryIsExpandedAction.kEditorPrefKey}", true);
|
||||
|
||||
var blackboardCategoryController = new BlackboardCategoryController(categoryInfo, blackboardCategoryViewModel, graphDataStore);
|
||||
if (m_BlackboardCategoryControllers.ContainsKey(categoryInfo.categoryGuid) == false)
|
||||
{
|
||||
m_BlackboardCategoryControllers.Add(categoryInfo.categoryGuid, blackboardCategoryController);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertHelpers.Fail("Failed to add category controller due to category with same GUID already having been added.");
|
||||
return null;
|
||||
}
|
||||
return blackboardCategoryController;
|
||||
}
|
||||
|
||||
// Creates controller, view and view model for a blackboard item and adds the view to the specified index in the category
|
||||
SGBlackboardRow InsertBlackboardRow(BlackboardItem shaderInput, int insertionIndex = -1)
|
||||
{
|
||||
return m_DefaultCategoryController.InsertBlackboardRow(shaderInput, insertionIndex);
|
||||
}
|
||||
|
||||
public void UpdateBlackboardTitle(string newTitle)
|
||||
{
|
||||
ViewModel.title = newTitle;
|
||||
blackboard.title = ViewModel.title;
|
||||
}
|
||||
|
||||
protected override void RequestModelChange(IGraphDataAction changeAction)
|
||||
{
|
||||
DataStore.Dispatch(changeAction);
|
||||
}
|
||||
|
||||
// Called by GraphDataStore.Subscribe after the model has been changed
|
||||
protected override void ModelChanged(GraphData graphData, IGraphDataAction changeAction)
|
||||
{
|
||||
// Reconstruct view-model first
|
||||
// TODO: hide this more generically for category types.
|
||||
bool useDropdowns = graphData.isSubGraph;
|
||||
InitializeViewModel(useDropdowns);
|
||||
|
||||
var graphView = ViewModel.parentView as MaterialGraphView;
|
||||
|
||||
switch (changeAction)
|
||||
{
|
||||
// If newly added input doesn't belong to any of the user-made categories, add it to the default category at top of blackboard
|
||||
case AddShaderInputAction addBlackboardItemAction:
|
||||
if (IsInputUncategorized(addBlackboardItemAction.shaderInputReference))
|
||||
{
|
||||
var blackboardRow = InsertBlackboardRow(addBlackboardItemAction.shaderInputReference);
|
||||
if (blackboardRow != null)
|
||||
{
|
||||
var propertyView = blackboardRow.Q<SGBlackboardField>();
|
||||
if (addBlackboardItemAction.addInputActionType == AddShaderInputAction.AddActionSource.AddMenu)
|
||||
propertyView.OpenTextEditor();
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Need to handle deletion of shader inputs here as opposed to BlackboardCategoryController, as currently,
|
||||
// once removed from the categories there is no way to associate an input with the category that owns it
|
||||
case DeleteShaderInputAction deleteShaderInputAction:
|
||||
foreach (var shaderInput in deleteShaderInputAction.shaderInputsToDelete)
|
||||
RemoveInputFromBlackboard(shaderInput);
|
||||
break;
|
||||
|
||||
case HandleUndoRedoAction handleUndoRedoAction:
|
||||
ClearBlackboardCategories();
|
||||
|
||||
foreach (var categoryData in graphData.addedCategories)
|
||||
AddBlackboardCategory(DataStore, categoryData);
|
||||
|
||||
m_DefaultCategoryController = m_BlackboardCategoryControllers.Values.FirstOrDefault();
|
||||
|
||||
break;
|
||||
case CopyShaderInputAction copyShaderInputAction:
|
||||
// In the specific case of only-one keywords like Material Quality and Raytracing, they can get copied, but because only one can exist, the output copied value is null
|
||||
if (copyShaderInputAction.copiedShaderInput != null && IsInputUncategorized(copyShaderInputAction.copiedShaderInput))
|
||||
{
|
||||
var blackboardRow = InsertBlackboardRow(copyShaderInputAction.copiedShaderInput, copyShaderInputAction.insertIndex);
|
||||
var propertyView = blackboardRow.Q<SGBlackboardField>();
|
||||
graphView?.AddToSelectionNoUndoRecord(propertyView);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AddCategoryAction addCategoryAction:
|
||||
AddBlackboardCategory(DataStore, addCategoryAction.categoryDataReference);
|
||||
// Iterate through anything that is selected currently
|
||||
foreach (var selectedElement in blackboard.selection.ToList())
|
||||
{
|
||||
if (selectedElement is SGBlackboardField { userData: ShaderInput shaderInput })
|
||||
{
|
||||
// If a blackboard item is selected, first remove it from the blackboard
|
||||
RemoveInputFromBlackboard(shaderInput);
|
||||
|
||||
// Then add input to the new category
|
||||
var addItemToCategoryAction = new AddItemToCategoryAction();
|
||||
addItemToCategoryAction.categoryGuid = addCategoryAction.categoryDataReference.categoryGuid;
|
||||
addItemToCategoryAction.itemToAdd = shaderInput;
|
||||
DataStore.Dispatch(addItemToCategoryAction);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DeleteCategoryAction deleteCategoryAction:
|
||||
// Clean up deleted categories
|
||||
foreach (var categoryGUID in deleteCategoryAction.categoriesToRemoveGuids)
|
||||
{
|
||||
RemoveBlackboardCategory(categoryGUID);
|
||||
}
|
||||
break;
|
||||
|
||||
case MoveCategoryAction moveCategoryAction:
|
||||
ClearBlackboardCategories();
|
||||
foreach (var categoryData in ViewModel.categoryInfoList)
|
||||
AddBlackboardCategory(graphData.owner.graphDataStore, categoryData);
|
||||
break;
|
||||
|
||||
case CopyCategoryAction copyCategoryAction:
|
||||
var blackboardCategory = AddBlackboardCategory(graphData.owner.graphDataStore, copyCategoryAction.newCategoryDataReference);
|
||||
if (blackboardCategory != null)
|
||||
graphView?.AddToSelectionNoUndoRecord(blackboardCategory.blackboardCategoryView);
|
||||
break;
|
||||
case ShaderVariantLimitAction shaderVariantLimitAction:
|
||||
blackboard.SetCurrentVariantUsage(shaderVariantLimitAction.currentVariantCount, shaderVariantLimitAction.maxVariantCount);
|
||||
break;
|
||||
}
|
||||
|
||||
// Lets all event handlers this controller owns/manages know that the model has changed
|
||||
// Usually this is to update views and make them reconstruct themself from updated view-model
|
||||
//NotifyChange(changeAction);
|
||||
|
||||
// Let child controllers know about changes to this controller so they may update themselves in turn
|
||||
//ApplyChanges();
|
||||
}
|
||||
|
||||
void RemoveInputFromBlackboard(ShaderInput shaderInput)
|
||||
{
|
||||
// Check if input is in one of the categories
|
||||
foreach (var controller in m_BlackboardCategoryControllers.Values)
|
||||
{
|
||||
var blackboardRow = controller.FindBlackboardRow(shaderInput);
|
||||
if (blackboardRow != null)
|
||||
{
|
||||
controller.RemoveBlackboardRow(shaderInput);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInputUncategorized(ShaderInput shaderInput)
|
||||
{
|
||||
// Skip the first category controller as that is guaranteed to be the default category
|
||||
foreach (var categoryController in m_BlackboardCategoryControllers.Values.Skip(1))
|
||||
{
|
||||
if (categoryController.IsInputInCategory(shaderInput))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public SGBlackboardCategory GetBlackboardCategory(string inputGuid)
|
||||
{
|
||||
foreach (var categoryController in m_BlackboardCategoryControllers.Values)
|
||||
{
|
||||
if (categoryController.Model.categoryGuid == inputGuid)
|
||||
return categoryController.blackboardCategoryView;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public SGBlackboardRow GetBlackboardRow(ShaderInput blackboardItem)
|
||||
{
|
||||
foreach (var categoryController in m_BlackboardCategoryControllers.Values)
|
||||
{
|
||||
var blackboardRow = categoryController.FindBlackboardRow(blackboardItem);
|
||||
if (blackboardRow != null)
|
||||
return blackboardRow;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
int numberOfCategories => m_BlackboardCategoryControllers.Count;
|
||||
|
||||
// Gets the index after the currently selected shader input for pasting properties into this graph
|
||||
internal int GetInsertionIndexForPaste()
|
||||
{
|
||||
if (blackboard?.selection == null || blackboard.selection.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach (ISelectable selection in blackboard.selection)
|
||||
{
|
||||
if (selection is SGBlackboardField blackboardPropertyView)
|
||||
{
|
||||
SGBlackboardRow row = blackboardPropertyView.GetFirstAncestorOfType<SGBlackboardRow>();
|
||||
SGBlackboardCategory category = blackboardPropertyView.GetFirstAncestorOfType<SGBlackboardCategory>();
|
||||
if (row == null || category == null)
|
||||
continue;
|
||||
int blackboardFieldIndex = category.IndexOf(row);
|
||||
|
||||
return blackboardFieldIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RemoveBlackboardCategory(string categoryGUID)
|
||||
{
|
||||
m_BlackboardCategoryControllers.TryGetValue(categoryGUID, out var blackboardCategoryController);
|
||||
if (blackboardCategoryController != null)
|
||||
{
|
||||
blackboardCategoryController.Destroy();
|
||||
m_BlackboardCategoryControllers.Remove(categoryGUID);
|
||||
}
|
||||
else
|
||||
AssertHelpers.Fail("Tried to remove a category that doesn't exist. ");
|
||||
}
|
||||
|
||||
void ClearBlackboardCategories()
|
||||
{
|
||||
foreach (var categoryController in m_BlackboardCategoryControllers.Values)
|
||||
{
|
||||
categoryController.Destroy();
|
||||
}
|
||||
m_BlackboardCategoryControllers.Clear();
|
||||
}
|
||||
|
||||
// Meant to be used by UI testing in order to clear blackboard state
|
||||
internal void ResetBlackboardState()
|
||||
{
|
||||
ClearBlackboardCategories();
|
||||
var addCategoryAction = new AddCategoryAction();
|
||||
addCategoryAction.categoryDataReference = CategoryData.DefaultCategory();
|
||||
DataStore.Dispatch(addCategoryAction);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8b25321bfcc6454b9c673c127a84bb0c
|
||||
timeCreated: 1608602455
|
|
@ -0,0 +1,199 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
|
||||
using GraphDataStore = UnityEditor.ShaderGraph.DataStore<UnityEditor.ShaderGraph.GraphData>;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
class DummyChangeAction : IGraphDataAction
|
||||
{
|
||||
void OnDummyChangeAction(GraphData m_GraphData)
|
||||
{
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => OnDummyChangeAction;
|
||||
}
|
||||
|
||||
struct SGControllerChangedEvent
|
||||
{
|
||||
public ISGControlledElement target;
|
||||
public SGController controller;
|
||||
public IGraphDataAction change;
|
||||
|
||||
private bool m_PropagationStopped;
|
||||
void StopPropagation()
|
||||
{
|
||||
m_PropagationStopped = true;
|
||||
}
|
||||
|
||||
public bool isPropagationStopped => m_PropagationStopped;
|
||||
}
|
||||
|
||||
class SGControllerEvent
|
||||
{
|
||||
ISGControlledElement target = null;
|
||||
|
||||
SGControllerEvent(ISGControlledElement controlledTarget)
|
||||
{
|
||||
target = controlledTarget;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SGController
|
||||
{
|
||||
public bool m_DisableCalled = false;
|
||||
|
||||
protected IGraphDataAction DummyChange = new DummyChangeAction();
|
||||
|
||||
public virtual void OnDisable()
|
||||
{
|
||||
if (m_DisableCalled)
|
||||
Debug.LogError(GetType().Name + ".Disable called twice");
|
||||
|
||||
m_DisableCalled = true;
|
||||
foreach (var element in allChildren)
|
||||
{
|
||||
UnityEngine.Profiling.Profiler.BeginSample(element.GetType().Name + ".OnDisable");
|
||||
element.OnDisable();
|
||||
UnityEngine.Profiling.Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
|
||||
internal void RegisterHandler(ISGControlledElement handler)
|
||||
{
|
||||
//Debug.Log("RegisterHandler of " + handler.GetType().Name + " on " + GetType().Name );
|
||||
|
||||
if (m_EventHandlers.Contains(handler))
|
||||
Debug.LogError("Handler registered twice");
|
||||
else
|
||||
{
|
||||
m_EventHandlers.Add(handler);
|
||||
|
||||
NotifyEventHandler(handler, DummyChange);
|
||||
}
|
||||
}
|
||||
|
||||
internal void UnregisterHandler(ISGControlledElement handler)
|
||||
{
|
||||
m_EventHandlers.Remove(handler);
|
||||
}
|
||||
|
||||
protected void NotifyChange(IGraphDataAction changeAction)
|
||||
{
|
||||
var eventHandlers = m_EventHandlers.ToArray(); // Some notification may trigger Register/Unregister so duplicate the collection.
|
||||
|
||||
foreach (var eventHandler in eventHandlers)
|
||||
{
|
||||
UnityEngine.Profiling.Profiler.BeginSample("NotifyChange:" + eventHandler.GetType().Name);
|
||||
NotifyEventHandler(eventHandler, changeAction);
|
||||
UnityEngine.Profiling.Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyEventHandler(ISGControlledElement eventHandler, IGraphDataAction changeAction)
|
||||
{
|
||||
SGControllerChangedEvent e = new SGControllerChangedEvent();
|
||||
e.controller = this;
|
||||
e.target = eventHandler;
|
||||
e.change = changeAction;
|
||||
eventHandler.OnControllerChanged(ref e);
|
||||
if (e.isPropagationStopped)
|
||||
return;
|
||||
if (eventHandler is VisualElement)
|
||||
{
|
||||
var element = eventHandler as VisualElement;
|
||||
eventHandler = element.GetFirstOfType<ISGControlledElement>();
|
||||
while (eventHandler != null)
|
||||
{
|
||||
eventHandler.OnControllerChanged(ref e);
|
||||
if (e.isPropagationStopped)
|
||||
break;
|
||||
eventHandler = (eventHandler as VisualElement).GetFirstAncestorOfType<ISGControlledElement>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendEvent(SGControllerEvent e)
|
||||
{
|
||||
var eventHandlers = m_EventHandlers.ToArray(); // Some notification may trigger Register/Unregister so duplicate the collection.
|
||||
|
||||
foreach (var eventHandler in eventHandlers)
|
||||
{
|
||||
eventHandler.OnControllerEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void ApplyChanges();
|
||||
|
||||
public virtual IEnumerable<SGController> allChildren
|
||||
{
|
||||
get { return Enumerable.Empty<SGController>(); }
|
||||
}
|
||||
|
||||
List<ISGControlledElement> m_EventHandlers = new List<ISGControlledElement>();
|
||||
}
|
||||
|
||||
abstract class SGController<T> : SGController
|
||||
{
|
||||
GraphDataStore m_DataStore;
|
||||
protected GraphDataStore DataStore => m_DataStore;
|
||||
|
||||
protected SGController(T model, GraphDataStore dataStore)
|
||||
{
|
||||
m_Model = model;
|
||||
m_DataStore = dataStore;
|
||||
DataStore.Subscribe += ModelChanged;
|
||||
}
|
||||
|
||||
protected abstract void RequestModelChange(IGraphDataAction changeAction);
|
||||
|
||||
protected abstract void ModelChanged(GraphData graphData, IGraphDataAction changeAction);
|
||||
|
||||
T m_Model;
|
||||
public T Model => m_Model;
|
||||
|
||||
// Cleanup delegate association before destruction
|
||||
public void Cleanup()
|
||||
{
|
||||
DataStore.Subscribe -= ModelChanged;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SGViewController<ModelType, ViewModelType> : SGController<ModelType>
|
||||
{
|
||||
protected SGViewController(ModelType model, ViewModelType viewModel, GraphDataStore graphDataStore) : base(model, graphDataStore)
|
||||
{
|
||||
m_ViewModel = viewModel;
|
||||
try
|
||||
{
|
||||
// Need ViewModel to be initialized before we call ModelChanged() [as view model might need to update]
|
||||
ModelChanged(DataStore.State, DummyChange);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log("Failed to initialize View Controller of type: " + this.GetType() + " due to exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// Holds data specific to the views this controller is responsible for
|
||||
ViewModelType m_ViewModel;
|
||||
public ViewModelType ViewModel => m_ViewModel;
|
||||
|
||||
public override void ApplyChanges()
|
||||
{
|
||||
foreach (var controller in allChildren)
|
||||
{
|
||||
controller.ApplyChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Destroy() { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1dae36c782bd4d8babb267707f7596f3
|
||||
timeCreated: 1608256298
|
|
@ -0,0 +1,355 @@
|
|||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph.Drawing.Controls;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.UIElements;
|
||||
using GraphDataStore = UnityEditor.ShaderGraph.DataStore<UnityEditor.ShaderGraph.GraphData>;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class ChangeExposedFlagAction : IGraphDataAction
|
||||
{
|
||||
internal ChangeExposedFlagAction(ShaderInput shaderInput, bool newIsExposed)
|
||||
{
|
||||
this.shaderInputReference = shaderInput;
|
||||
this.newIsExposedValue = newIsExposed;
|
||||
this.oldIsExposedValue = shaderInput.generatePropertyBlock;
|
||||
}
|
||||
|
||||
void ChangeExposedFlag(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeExposedFlagAction");
|
||||
AssertHelpers.IsNotNull(shaderInputReference, "ShaderInputReference is null while carrying out ChangeExposedFlagAction");
|
||||
// The Undos are currently handled in ShaderInputPropertyDrawer but we want to move that out from there and handle here
|
||||
//graphData.owner.RegisterCompleteObjectUndo("Change Exposed Toggle");
|
||||
shaderInputReference.generatePropertyBlock = newIsExposedValue;
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => ChangeExposedFlag;
|
||||
|
||||
// Reference to the shader input being modified
|
||||
internal ShaderInput shaderInputReference { get; private set; }
|
||||
|
||||
// New value of whether the shader input should be exposed to the material inspector
|
||||
internal bool newIsExposedValue { get; private set; }
|
||||
internal bool oldIsExposedValue { get; private set; }
|
||||
}
|
||||
|
||||
class ChangePropertyValueAction : IGraphDataAction
|
||||
{
|
||||
void ChangePropertyValue(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangePropertyValueAction");
|
||||
AssertHelpers.IsNotNull(shaderInputReference, "ShaderPropertyReference is null while carrying out ChangePropertyValueAction");
|
||||
// The Undos are currently handled in ShaderInputPropertyDrawer but we want to move that out from there and handle here
|
||||
//graphData.owner.RegisterCompleteObjectUndo("Change Property Value");
|
||||
switch (shaderInputReference)
|
||||
{
|
||||
case BooleanShaderProperty booleanProperty:
|
||||
booleanProperty.value = ((ToggleData)newShaderInputValue).isOn;
|
||||
break;
|
||||
case Vector1ShaderProperty vector1Property:
|
||||
vector1Property.value = (float)newShaderInputValue;
|
||||
break;
|
||||
case Vector2ShaderProperty vector2Property:
|
||||
vector2Property.value = (Vector2)newShaderInputValue;
|
||||
break;
|
||||
case Vector3ShaderProperty vector3Property:
|
||||
vector3Property.value = (Vector3)newShaderInputValue;
|
||||
break;
|
||||
case Vector4ShaderProperty vector4Property:
|
||||
vector4Property.value = (Vector4)newShaderInputValue;
|
||||
break;
|
||||
case ColorShaderProperty colorProperty:
|
||||
colorProperty.value = (Color)newShaderInputValue;
|
||||
break;
|
||||
case Texture2DShaderProperty texture2DProperty:
|
||||
texture2DProperty.value.texture = (Texture)newShaderInputValue;
|
||||
break;
|
||||
case Texture2DArrayShaderProperty texture2DArrayProperty:
|
||||
texture2DArrayProperty.value.textureArray = (Texture2DArray)newShaderInputValue;
|
||||
break;
|
||||
case Texture3DShaderProperty texture3DProperty:
|
||||
texture3DProperty.value.texture = (Texture3D)newShaderInputValue;
|
||||
break;
|
||||
case CubemapShaderProperty cubemapProperty:
|
||||
cubemapProperty.value.cubemap = (Cubemap)newShaderInputValue;
|
||||
break;
|
||||
case Matrix2ShaderProperty matrix2Property:
|
||||
matrix2Property.value = (Matrix4x4)newShaderInputValue;
|
||||
break;
|
||||
case Matrix3ShaderProperty matrix3Property:
|
||||
matrix3Property.value = (Matrix4x4)newShaderInputValue;
|
||||
break;
|
||||
case Matrix4ShaderProperty matrix4Property:
|
||||
matrix4Property.value = (Matrix4x4)newShaderInputValue;
|
||||
break;
|
||||
case SamplerStateShaderProperty samplerStateProperty:
|
||||
samplerStateProperty.value = (TextureSamplerState)newShaderInputValue;
|
||||
break;
|
||||
case GradientShaderProperty gradientProperty:
|
||||
gradientProperty.value = (Gradient)newShaderInputValue;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => ChangePropertyValue;
|
||||
|
||||
// Reference to the shader input being modified
|
||||
internal ShaderInput shaderInputReference { get; set; }
|
||||
|
||||
// New value of the shader property
|
||||
|
||||
internal object newShaderInputValue { get; set; }
|
||||
}
|
||||
|
||||
class ChangeDisplayNameAction : IGraphDataAction
|
||||
{
|
||||
void ChangeDisplayName(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeDisplayNameAction");
|
||||
AssertHelpers.IsNotNull(shaderInputReference, "ShaderInputReference is null while carrying out ChangeDisplayNameAction");
|
||||
graphData.owner.RegisterCompleteObjectUndo("Change Display Name");
|
||||
if (newDisplayNameValue != shaderInputReference.displayName)
|
||||
{
|
||||
shaderInputReference.SetDisplayNameAndSanitizeForGraph(graphData, newDisplayNameValue);
|
||||
}
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => ChangeDisplayName;
|
||||
|
||||
// Reference to the shader input being modified
|
||||
internal ShaderInput shaderInputReference { get; set; }
|
||||
|
||||
internal string newDisplayNameValue { get; set; }
|
||||
}
|
||||
|
||||
class ChangeReferenceNameAction : IGraphDataAction
|
||||
{
|
||||
void ChangeReferenceName(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeReferenceNameAction");
|
||||
AssertHelpers.IsNotNull(shaderInputReference, "ShaderInputReference is null while carrying out ChangeReferenceNameAction");
|
||||
// The Undos are currently handled in ShaderInputPropertyDrawer but we want to move that out from there and handle here
|
||||
//graphData.owner.RegisterCompleteObjectUndo("Change Reference Name");
|
||||
if (newReferenceNameValue != shaderInputReference.overrideReferenceName)
|
||||
{
|
||||
graphData.SanitizeGraphInputReferenceName(shaderInputReference, newReferenceNameValue);
|
||||
}
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => ChangeReferenceName;
|
||||
|
||||
// Reference to the shader input being modified
|
||||
internal ShaderInput shaderInputReference { get; set; }
|
||||
|
||||
internal string newReferenceNameValue { get; set; }
|
||||
}
|
||||
|
||||
class ResetReferenceNameAction : IGraphDataAction
|
||||
{
|
||||
void ResetReferenceName(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ResetReferenceNameAction");
|
||||
AssertHelpers.IsNotNull(shaderInputReference, "ShaderInputReference is null while carrying out ResetReferenceNameAction");
|
||||
graphData.owner.RegisterCompleteObjectUndo("Reset Reference Name");
|
||||
shaderInputReference.overrideReferenceName = null;
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => ResetReferenceName;
|
||||
|
||||
// Reference to the shader input being modified
|
||||
internal ShaderInput shaderInputReference { get; set; }
|
||||
}
|
||||
|
||||
class DeleteShaderInputAction : IGraphDataAction
|
||||
{
|
||||
void DeleteShaderInput(GraphData graphData)
|
||||
{
|
||||
AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out DeleteShaderInputAction");
|
||||
AssertHelpers.IsNotNull(shaderInputsToDelete, "ShaderInputsToDelete is null while carrying out DeleteShaderInputAction");
|
||||
// This is called by MaterialGraphView currently, no need to repeat it here, though ideally it would live here
|
||||
//graphData.owner.RegisterCompleteObjectUndo("Delete Graph Input(s)");
|
||||
|
||||
foreach (var shaderInput in shaderInputsToDelete)
|
||||
{
|
||||
graphData.RemoveGraphInput(shaderInput);
|
||||
}
|
||||
}
|
||||
|
||||
public Action<GraphData> modifyGraphDataAction => DeleteShaderInput;
|
||||
|
||||
// Reference to the shader input(s) being deleted
|
||||
internal IList<ShaderInput> shaderInputsToDelete { get; set; } = new List<ShaderInput>();
|
||||
}
|
||||
|
||||
class ShaderInputViewController : SGViewController<ShaderInput, ShaderInputViewModel>
|
||||
{
|
||||
// Exposed for PropertyView
|
||||
internal GraphData graphData => DataStore.State;
|
||||
|
||||
internal ShaderInputViewController(ShaderInput shaderInput, ShaderInputViewModel inViewModel, GraphDataStore graphDataStore)
|
||||
: base(shaderInput, inViewModel, graphDataStore)
|
||||
{
|
||||
InitializeViewModel();
|
||||
|
||||
m_SgBlackboardField = new SGBlackboardField(ViewModel);
|
||||
m_SgBlackboardField.controller = this;
|
||||
|
||||
m_BlackboardRowView = new SGBlackboardRow(m_SgBlackboardField, null);
|
||||
m_BlackboardRowView.expanded = SessionState.GetBool($"Unity.ShaderGraph.Input.{shaderInput.objectId}.isExpanded", false);
|
||||
}
|
||||
|
||||
void InitializeViewModel()
|
||||
{
|
||||
if (Model == null)
|
||||
{
|
||||
AssertHelpers.Fail("Could not initialize shader input view model as shader input was null.");
|
||||
return;
|
||||
}
|
||||
ViewModel.model = Model;
|
||||
ViewModel.isSubGraph = DataStore.State.isSubGraph;
|
||||
ViewModel.isInputExposed = (DataStore.State.isSubGraph || (Model.isExposable && Model.generatePropertyBlock));
|
||||
ViewModel.inputName = Model.displayName;
|
||||
switch (Model)
|
||||
{
|
||||
case AbstractShaderProperty shaderProperty:
|
||||
ViewModel.inputTypeName = shaderProperty.GetPropertyTypeString();
|
||||
// Handles upgrade fix for deprecated old Color property
|
||||
shaderProperty.onBeforeVersionChange += (_) => graphData.owner.RegisterCompleteObjectUndo($"Change {shaderProperty.displayName} Version");
|
||||
break;
|
||||
case ShaderKeyword shaderKeyword:
|
||||
ViewModel.inputTypeName = shaderKeyword.keywordType + " Keyword";
|
||||
ViewModel.inputTypeName = shaderKeyword.isBuiltIn ? "Built-in " + ViewModel.inputTypeName : ViewModel.inputTypeName;
|
||||
break;
|
||||
case ShaderDropdown shaderDropdown:
|
||||
ViewModel.inputTypeName = "Dropdown";
|
||||
break;
|
||||
}
|
||||
|
||||
ViewModel.requestModelChangeAction = this.RequestModelChange;
|
||||
}
|
||||
|
||||
SGBlackboardRow m_BlackboardRowView;
|
||||
SGBlackboardField m_SgBlackboardField;
|
||||
|
||||
internal SGBlackboardRow BlackboardItemView => m_BlackboardRowView;
|
||||
|
||||
protected override void RequestModelChange(IGraphDataAction changeAction)
|
||||
{
|
||||
DataStore.Dispatch(changeAction);
|
||||
}
|
||||
|
||||
// Called by GraphDataStore.Subscribe after the model has been changed
|
||||
protected override void ModelChanged(GraphData graphData, IGraphDataAction changeAction)
|
||||
{
|
||||
switch (changeAction)
|
||||
{
|
||||
case ChangeExposedFlagAction changeExposedFlagAction:
|
||||
// ModelChanged is called overzealously on everything
|
||||
// but we only care if the action pertains to our Model
|
||||
if (changeExposedFlagAction.shaderInputReference == Model)
|
||||
{
|
||||
ViewModel.isInputExposed = Model.generatePropertyBlock;
|
||||
if (changeExposedFlagAction.oldIsExposedValue != changeExposedFlagAction.newIsExposedValue)
|
||||
DirtyNodes(ModificationScope.Graph);
|
||||
m_SgBlackboardField.UpdateFromViewModel();
|
||||
}
|
||||
break;
|
||||
|
||||
case ChangePropertyValueAction changePropertyValueAction:
|
||||
if (changePropertyValueAction.shaderInputReference == Model)
|
||||
{
|
||||
DirtyNodes(ModificationScope.Graph);
|
||||
m_SgBlackboardField.MarkDirtyRepaint();
|
||||
}
|
||||
break;
|
||||
|
||||
case ResetReferenceNameAction resetReferenceNameAction:
|
||||
if (resetReferenceNameAction.shaderInputReference == Model)
|
||||
{
|
||||
DirtyNodes(ModificationScope.Graph);
|
||||
}
|
||||
break;
|
||||
|
||||
case ChangeReferenceNameAction changeReferenceNameAction:
|
||||
if (changeReferenceNameAction.shaderInputReference == Model)
|
||||
{
|
||||
DirtyNodes(ModificationScope.Graph);
|
||||
}
|
||||
break;
|
||||
|
||||
case ChangeDisplayNameAction changeDisplayNameAction:
|
||||
if (changeDisplayNameAction.shaderInputReference == Model)
|
||||
{
|
||||
ViewModel.inputName = Model.displayName;
|
||||
DirtyNodes(ModificationScope.Topological);
|
||||
m_SgBlackboardField.UpdateFromViewModel();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should communicate to node controllers instead of searching for the nodes themselves everytime, but that's going to take a while...
|
||||
internal void DirtyNodes(ModificationScope modificationScope = ModificationScope.Node)
|
||||
{
|
||||
switch (Model)
|
||||
{
|
||||
case AbstractShaderProperty property:
|
||||
var graphEditorView = m_BlackboardRowView.GetFirstAncestorOfType<GraphEditorView>();
|
||||
if (graphEditorView == null)
|
||||
return;
|
||||
var colorManager = graphEditorView.colorManager;
|
||||
var nodes = graphEditorView.graphView.Query<MaterialNodeView>().ToList();
|
||||
|
||||
colorManager.SetNodesDirty(nodes);
|
||||
colorManager.UpdateNodeViews(nodes);
|
||||
|
||||
foreach (var node in DataStore.State.GetNodes<PropertyNode>())
|
||||
{
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
break;
|
||||
case ShaderKeyword keyword:
|
||||
foreach (var node in DataStore.State.GetNodes<KeywordNode>())
|
||||
{
|
||||
node.UpdateNode();
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
|
||||
// Cant determine if Sub Graphs contain the keyword so just update them
|
||||
foreach (var node in DataStore.State.GetNodes<SubGraphNode>())
|
||||
{
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
break;
|
||||
case ShaderDropdown dropdown:
|
||||
foreach (var node in DataStore.State.GetNodes<DropdownNode>())
|
||||
{
|
||||
node.UpdateNode();
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
|
||||
// Cant determine if Sub Graphs contain the dropdown so just update them
|
||||
foreach (var node in DataStore.State.GetNodes<SubGraphNode>())
|
||||
{
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Destroy()
|
||||
{
|
||||
Cleanup();
|
||||
BlackboardItemView.RemoveFromHierarchy();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8e48f61a99c146b1a223fe8ad61a6b55
|
||||
timeCreated: 1611265477
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 29309be0e1004fad967256187775c528
|
||||
timeCreated: 1507642261
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ButtonControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
public ButtonControlAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ButtonControlView(node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
struct ButtonConfig
|
||||
{
|
||||
public string text;
|
||||
public Action action;
|
||||
}
|
||||
|
||||
class ButtonControlView : VisualElement
|
||||
{
|
||||
public ButtonControlView(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
|
||||
m_Node = node;
|
||||
|
||||
Type type = propertyInfo.PropertyType;
|
||||
if (type != typeof(ButtonConfig))
|
||||
{
|
||||
throw new ArgumentException("Property must be a ButtonConfig.", "propertyInfo");
|
||||
}
|
||||
var value = (ButtonConfig)propertyInfo.GetValue(m_Node, null);
|
||||
|
||||
Add(new Button(value.action) { text = value.text });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7a22fecc3eba0844810adaf43e45f0f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ChannelEnumControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
int m_SlotId;
|
||||
|
||||
public ChannelEnumControlAttribute(string label = null, int slotId = 0)
|
||||
{
|
||||
m_Label = label;
|
||||
m_SlotId = slotId;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ChannelEnumControlView(m_Label, m_SlotId, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelEnumControlView : VisualElement, AbstractMaterialNodeModificationListener
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
int m_SlotId;
|
||||
|
||||
PopupField<string> m_PopupField;
|
||||
string[] m_ValueNames;
|
||||
|
||||
int m_PreviousChannelCount = -1;
|
||||
|
||||
public ChannelEnumControlView(string label, int slotId, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ChannelEnumControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
m_SlotId = slotId;
|
||||
if (!propertyInfo.PropertyType.IsEnum)
|
||||
throw new ArgumentException("Property must be an enum.", "propertyInfo");
|
||||
Add(new Label(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name)));
|
||||
|
||||
var value = (Enum)m_PropertyInfo.GetValue(m_Node, null);
|
||||
m_ValueNames = Enum.GetNames(value.GetType());
|
||||
|
||||
CreatePopup();
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<string> evt)
|
||||
{
|
||||
var index = m_PopupField.index;
|
||||
var value = (int)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (!index.Equals(value))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, index, null);
|
||||
}
|
||||
|
||||
CreatePopup();
|
||||
}
|
||||
|
||||
public void OnNodeModified(ModificationScope scope)
|
||||
{
|
||||
if (scope == ModificationScope.Node)
|
||||
{
|
||||
CreatePopup();
|
||||
m_PopupField.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
void CreatePopup()
|
||||
{
|
||||
int channelCount = SlotValueHelper.GetChannelCount(m_Node.FindSlot<MaterialSlot>(m_SlotId).concreteValueType);
|
||||
|
||||
if (m_PopupField != null)
|
||||
{
|
||||
if (channelCount == m_PreviousChannelCount)
|
||||
return;
|
||||
|
||||
Remove(m_PopupField);
|
||||
}
|
||||
|
||||
m_PreviousChannelCount = channelCount;
|
||||
List<string> popupEntries = new List<string>();
|
||||
for (int i = 0; i < channelCount; i++)
|
||||
popupEntries.Add(m_ValueNames[i]);
|
||||
|
||||
var value = (int)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (value >= channelCount)
|
||||
value = 0;
|
||||
|
||||
m_PopupField = new PopupField<string>(popupEntries, value);
|
||||
m_PopupField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(m_PopupField);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 74fdde12d8253bd4c874acc555be0585
|
||||
timeCreated: 1507817885
|
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ChannelEnumMaskControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
int m_SlotId;
|
||||
|
||||
public ChannelEnumMaskControlAttribute(string label = null, int slotId = 0)
|
||||
{
|
||||
m_Label = label;
|
||||
m_SlotId = slotId;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ChannelEnumMaskControlView(m_Label, m_SlotId, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelEnumMaskControlView : VisualElement, AbstractMaterialNodeModificationListener
|
||||
{
|
||||
string m_Label;
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
IMGUIContainer m_Container;
|
||||
int m_SlotId;
|
||||
|
||||
public ChannelEnumMaskControlView(string label, int slotId, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ChannelEnumMaskControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
m_SlotId = slotId;
|
||||
//if (!propertyInfo.PropertyType.IsEnum)
|
||||
//throw new ArgumentException("Property must be an enum.", "propertyInfo");
|
||||
m_Label = label;
|
||||
m_Container = new IMGUIContainer(OnGUIHandler);
|
||||
Add(m_Container);
|
||||
}
|
||||
|
||||
void OnGUIHandler()
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(m_Label);
|
||||
UpdatePopup();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public void OnNodeModified(ModificationScope scope)
|
||||
{
|
||||
if (scope == ModificationScope.Graph)
|
||||
m_Container.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
private void UpdatePopup()
|
||||
{
|
||||
var value = (int)m_PropertyInfo.GetValue(m_Node, null);
|
||||
using (var changeCheckScope = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
int channelCount = SlotValueHelper.GetChannelCount(m_Node.FindSlot<MaterialSlot>(m_SlotId).concreteValueType);
|
||||
string[] enumEntryNames = Enum.GetNames(typeof(TextureChannel));
|
||||
string[] popupEntries = new string[channelCount];
|
||||
for (int i = 0; i < popupEntries.Length; i++)
|
||||
popupEntries[i] = enumEntryNames[i];
|
||||
value = EditorGUILayout.MaskField("", value, popupEntries, GUILayout.Width(80f));
|
||||
|
||||
if (changeCheckScope.changed)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c32d860c6f767f14fa889dffac527bc5
|
||||
timeCreated: 1507817885
|
|
@ -0,0 +1,214 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ChannelMixerControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
float m_Minimum;
|
||||
float m_Maximum;
|
||||
|
||||
public ChannelMixerControlAttribute(string label = null, float minimum = -2f, float maximum = 2f)
|
||||
{
|
||||
m_Label = label;
|
||||
m_Minimum = minimum;
|
||||
m_Maximum = maximum;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ChannelMixerControlView(m_Label, m_Minimum, m_Maximum, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelMixerControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
ChannelMixerNode.ChannelMixer m_ChannelMixer;
|
||||
int m_OutChannel;
|
||||
|
||||
Slider m_RedSlider;
|
||||
Slider m_GreenSlider;
|
||||
Slider m_BlueSlider;
|
||||
|
||||
FloatField m_RedInputField;
|
||||
FloatField m_GreenInputField;
|
||||
FloatField m_BlueInputField;
|
||||
|
||||
float m_Minimum;
|
||||
float m_Maximum;
|
||||
bool m_Initialized;
|
||||
|
||||
public ChannelMixerControlView(string label, float minimum, float maximum, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ChannelMixerControlView"));
|
||||
m_ChannelMixer = (ChannelMixerNode.ChannelMixer)m_PropertyInfo.GetValue(m_Node, null);
|
||||
m_OutChannel = 0;
|
||||
|
||||
m_Minimum = minimum;
|
||||
m_Maximum = maximum;
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(ChannelMixerNode.ChannelMixer))
|
||||
throw new ArgumentException("Property must be of type ChannelMixer.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var buttonPanel = new VisualElement { name = "buttonPanel" };
|
||||
|
||||
Action changedOutputRed = () => OnClickButton(0);
|
||||
var outputButtonRed = new Button(changedOutputRed);
|
||||
outputButtonRed.Add(new Label("R"));
|
||||
buttonPanel.Add(outputButtonRed);
|
||||
|
||||
Action changedOutputGreen = () => OnClickButton(1);
|
||||
var outputButtonGreen = new Button(changedOutputGreen);
|
||||
outputButtonGreen.Add(new Label("G"));
|
||||
buttonPanel.Add(outputButtonGreen);
|
||||
|
||||
Action changedOutputBlue = () => OnClickButton(2);
|
||||
var outputButtonBlue = new Button(changedOutputBlue);
|
||||
outputButtonBlue.Add(new Label("B"));
|
||||
buttonPanel.Add(outputButtonBlue);
|
||||
|
||||
Add(buttonPanel);
|
||||
|
||||
var redSliderPanel = new VisualElement { name = "sliderPanel" };
|
||||
redSliderPanel.Add(new Label("R"));
|
||||
Action<float> changedRedIn = (s) => { OnChangeSlider(s, 0); };
|
||||
m_RedSlider = new Slider(m_Minimum, m_Maximum);
|
||||
m_RedSlider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue, 0));
|
||||
|
||||
redSliderPanel.Add(m_RedSlider);
|
||||
m_RedInputField = new FloatField { value = m_ChannelMixer.outRed.x };
|
||||
m_RedInputField.RegisterCallback<ChangeEvent<double>, int>(OnChangeInputField, 0);
|
||||
redSliderPanel.Add(m_RedInputField);
|
||||
Add(redSliderPanel);
|
||||
|
||||
var greenSliderPanel = new VisualElement { name = "sliderPanel" };
|
||||
greenSliderPanel.Add(new Label("G"));
|
||||
m_GreenSlider = new Slider(m_Minimum, m_Maximum);
|
||||
m_GreenSlider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue, 1));
|
||||
greenSliderPanel.Add(m_GreenSlider);
|
||||
m_GreenInputField = new FloatField { value = m_ChannelMixer.outRed.y };
|
||||
m_GreenInputField.RegisterCallback<ChangeEvent<double>, int>(OnChangeInputField, 1);
|
||||
greenSliderPanel.Add(m_GreenInputField);
|
||||
Add(greenSliderPanel);
|
||||
|
||||
var blueSliderPanel = new VisualElement { name = "sliderPanel" };
|
||||
blueSliderPanel.Add(new Label("B"));
|
||||
m_BlueSlider = new Slider(m_Minimum, m_Maximum);
|
||||
m_BlueSlider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue, 2));
|
||||
blueSliderPanel.Add(m_BlueSlider);
|
||||
m_BlueInputField = new FloatField { value = m_ChannelMixer.outRed.z };
|
||||
m_BlueInputField.RegisterCallback<ChangeEvent<double>, int>(OnChangeInputField, 2);
|
||||
blueSliderPanel.Add(m_BlueInputField);
|
||||
Add(blueSliderPanel);
|
||||
|
||||
m_Initialized = true;
|
||||
ResetSliders();
|
||||
}
|
||||
|
||||
void ResetSliders()
|
||||
{
|
||||
Vector3 outputChannel = GetOutputChannel();
|
||||
m_RedSlider.value = outputChannel[0];
|
||||
m_GreenSlider.value = outputChannel[1];
|
||||
m_BlueSlider.value = outputChannel[2];
|
||||
}
|
||||
|
||||
void OnChangeSlider(float value, int inChannel)
|
||||
{
|
||||
if (!m_Initialized)
|
||||
return;
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Slider Change");
|
||||
switch (m_OutChannel)
|
||||
{
|
||||
case 1:
|
||||
m_ChannelMixer.outGreen[inChannel] = value;
|
||||
break;
|
||||
case 2:
|
||||
m_ChannelMixer.outBlue[inChannel] = value;
|
||||
break;
|
||||
default:
|
||||
m_ChannelMixer.outRed[inChannel] = value;
|
||||
break;
|
||||
}
|
||||
switch (inChannel)
|
||||
{
|
||||
case 1:
|
||||
m_GreenInputField.value = value;
|
||||
break;
|
||||
case 2:
|
||||
m_BlueInputField.value = value;
|
||||
break;
|
||||
default:
|
||||
m_RedInputField.value = value;
|
||||
break;
|
||||
}
|
||||
m_PropertyInfo.SetValue(m_Node, m_ChannelMixer, null);
|
||||
}
|
||||
|
||||
void OnChangeInputField(ChangeEvent<double> evt, int inChannel)
|
||||
{
|
||||
if (!m_Initialized)
|
||||
return;
|
||||
var value = Mathf.Max(Mathf.Min((float)evt.newValue, m_Maximum), m_Minimum);
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Input Field Change");
|
||||
switch (m_OutChannel)
|
||||
{
|
||||
case 1:
|
||||
m_ChannelMixer.outGreen[inChannel] = value;
|
||||
break;
|
||||
case 2:
|
||||
m_ChannelMixer.outBlue[inChannel] = value;
|
||||
break;
|
||||
default:
|
||||
m_ChannelMixer.outRed[inChannel] = value;
|
||||
break;
|
||||
}
|
||||
switch (inChannel)
|
||||
{
|
||||
case 1:
|
||||
m_GreenSlider.value = value;
|
||||
break;
|
||||
case 2:
|
||||
m_BlueSlider.value = value;
|
||||
break;
|
||||
default:
|
||||
m_RedSlider.value = value;
|
||||
break;
|
||||
}
|
||||
m_PropertyInfo.SetValue(m_Node, m_ChannelMixer, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void OnClickButton(int outChannel)
|
||||
{
|
||||
m_OutChannel = outChannel;
|
||||
ResetSliders();
|
||||
}
|
||||
|
||||
Vector3 GetOutputChannel()
|
||||
{
|
||||
switch (m_OutChannel)
|
||||
{
|
||||
case 1:
|
||||
return m_ChannelMixer.outGreen;
|
||||
case 2:
|
||||
return m_ChannelMixer.outBlue;
|
||||
default:
|
||||
return m_ChannelMixer.outRed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 74a26e3294ebad94fa5d78ee95751556
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using Color = UnityEditor.ShaderGraph.ColorNode.Color;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ColorControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
ColorMode m_ColorMode;
|
||||
|
||||
public ColorControlAttribute(string label = null, ColorMode colorMode = ColorMode.Default)
|
||||
{
|
||||
m_Label = label;
|
||||
m_ColorMode = colorMode;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ColorControlView(m_Label, m_ColorMode, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ColorControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
Color m_Color;
|
||||
ColorField m_ColorField;
|
||||
|
||||
public ColorControlView(string label, ColorMode colorMode, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ColorControlView"));
|
||||
if (propertyInfo.PropertyType != typeof(Color))
|
||||
throw new ArgumentException("Property must be of type Color.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
m_Color = (Color)m_PropertyInfo.GetValue(m_Node, null);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
m_ColorField = new ColorField { value = m_Color.color, hdr = m_Color.mode == ColorMode.HDR, showEyeDropper = false };
|
||||
m_ColorField.RegisterValueChangedCallback(OnChange);
|
||||
Add(m_ColorField);
|
||||
|
||||
VisualElement enumPanel = new VisualElement { name = "enumPanel" };
|
||||
enumPanel.Add(new Label("Mode"));
|
||||
var enumField = new EnumField(m_Color.mode);
|
||||
enumField.RegisterValueChangedCallback(OnModeChanged);
|
||||
enumPanel.Add(enumField);
|
||||
Add(enumPanel);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Color> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Color Change");
|
||||
m_Color.color = evt.newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Color, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void OnModeChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
if (!evt.newValue.Equals(m_Color.mode))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_Color.mode = (ColorMode)evt.newValue;
|
||||
m_ColorField.hdr = m_Color.mode == ColorMode.HDR;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Color, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 34e77444559b414f9fe81f9775229abf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class CubemapControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public CubemapControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new CubemapControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class CubemapControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public CubemapControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(Cubemap))
|
||||
throw new ArgumentException("Property must be of type Texture.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var cubemapField = new ObjectField { value = (Cubemap)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Cubemap) };
|
||||
cubemapField.RegisterValueChangedCallback(OnChange);
|
||||
Add(cubemapField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Object> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Cubemap Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: abdad6e5c36e0994290fcacce2fab066
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class DefaultControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (propertyInfo.PropertyType == typeof(Color))
|
||||
return new ColorControlView(null, ColorMode.Default, node, propertyInfo);
|
||||
if (typeof(Enum).IsAssignableFrom(propertyInfo.PropertyType))
|
||||
return new EnumControlView(null, node, propertyInfo);
|
||||
if (propertyInfo.PropertyType == typeof(Texture2D))
|
||||
return new TextureControlView(null, node, propertyInfo);
|
||||
if (propertyInfo.PropertyType == typeof(Texture2DArray))
|
||||
return new TextureArrayControlView(null, node, propertyInfo);
|
||||
if (propertyInfo.PropertyType == typeof(Texture3D))
|
||||
return new Texture3DControlView(null, node, propertyInfo);
|
||||
if (MultiFloatControlView.validTypes.Contains(propertyInfo.PropertyType))
|
||||
return new MultiFloatControlView(null, "X", "Y", "Z", "W", node, propertyInfo);
|
||||
if (typeof(Object).IsAssignableFrom(propertyInfo.PropertyType))
|
||||
return new ObjectControlView(null, node, propertyInfo);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 20e01cb163f347c7819ae286c33baf85
|
||||
timeCreated: 1507642280
|
|
@ -0,0 +1,189 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class DielectricSpecularControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
public DielectricSpecularControlAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new DielectricSpecularControlView(node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class DielectricSpecularControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
DielectricSpecularNode.DielectricMaterial m_DielectricMaterial;
|
||||
|
||||
VisualElement m_RangePanel;
|
||||
Slider m_RangeSlider;
|
||||
FloatField m_RangeField;
|
||||
VisualElement m_IORPanel;
|
||||
Slider m_IORSlider;
|
||||
FloatField m_IORField;
|
||||
|
||||
public DielectricSpecularControlView(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/DielectricSpecularControlView"));
|
||||
m_DielectricMaterial = (DielectricSpecularNode.DielectricMaterial)m_PropertyInfo.GetValue(m_Node, null);
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(DielectricSpecularNode.DielectricMaterial))
|
||||
throw new ArgumentException("Property must be of type DielectricMaterial.", "propertyInfo");
|
||||
|
||||
var enumPanel = new VisualElement { name = "enumPanel" };
|
||||
enumPanel.Add(new Label("Material"));
|
||||
var enumField = new EnumField(m_DielectricMaterial.type);
|
||||
enumField.RegisterValueChangedCallback(OnEnumChanged);
|
||||
enumPanel.Add(enumField);
|
||||
Add(enumPanel);
|
||||
|
||||
m_RangePanel = new VisualElement { name = "sliderPanel" };
|
||||
m_RangePanel.Add(new Label("Range"));
|
||||
m_RangeSlider = new Slider(0.01f, 1) { value = m_DielectricMaterial.range };
|
||||
m_RangeSlider.RegisterValueChangedCallback((evt) => OnChangeRangeSlider(evt.newValue));
|
||||
|
||||
m_RangePanel.Add(m_RangeSlider);
|
||||
m_RangeField = AddField(m_RangePanel, m_RangeSlider, 0, m_DielectricMaterial);
|
||||
m_RangePanel.SetEnabled(m_DielectricMaterial.type == DielectricMaterialType.Common);
|
||||
Add(m_RangePanel);
|
||||
|
||||
m_IORPanel = new VisualElement { name = "sliderPanel" };
|
||||
m_IORPanel.Add(new Label("IOR"));
|
||||
m_IORSlider = new Slider(1, 2.5f) { value = m_DielectricMaterial.indexOfRefraction };
|
||||
m_IORSlider.RegisterValueChangedCallback((evt) => OnChangeIORSlider(evt.newValue));
|
||||
|
||||
m_IORPanel.Add(m_IORSlider);
|
||||
m_IORField = AddField(m_IORPanel, m_IORSlider, 1, m_DielectricMaterial);
|
||||
m_IORPanel.SetEnabled(m_DielectricMaterial.type == DielectricMaterialType.Custom);
|
||||
Add(m_IORPanel);
|
||||
}
|
||||
|
||||
void OnEnumChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
if (!evt.newValue.Equals(m_DielectricMaterial.type))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_DielectricMaterial.type = (DielectricMaterialType)evt.newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_DielectricMaterial, null);
|
||||
|
||||
switch (m_DielectricMaterial.type)
|
||||
{
|
||||
case DielectricMaterialType.Common:
|
||||
m_RangePanel.SetEnabled(true);
|
||||
m_IORPanel.SetEnabled(false);
|
||||
break;
|
||||
case DielectricMaterialType.Custom:
|
||||
m_RangePanel.SetEnabled(false);
|
||||
m_IORPanel.SetEnabled(true);
|
||||
break;
|
||||
default:
|
||||
m_RangePanel.SetEnabled(false);
|
||||
m_IORPanel.SetEnabled(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnChangeRangeSlider(float newValue)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Slider Change");
|
||||
m_DielectricMaterial.range = newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_DielectricMaterial, null);
|
||||
if (m_RangeField != null)
|
||||
m_RangeField.value = newValue;
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void OnChangeIORSlider(float newValue)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Slider Change");
|
||||
m_DielectricMaterial.indexOfRefraction = newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_DielectricMaterial, null);
|
||||
if (m_IORField != null)
|
||||
m_IORField.value = newValue;
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
FloatField AddField(VisualElement panel, Slider slider, int index, DielectricSpecularNode.DielectricMaterial initMaterial)
|
||||
{
|
||||
float initValue;
|
||||
if (index == 1)
|
||||
initValue = initMaterial.indexOfRefraction;
|
||||
else
|
||||
initValue = initMaterial.range;
|
||||
|
||||
var field = new FloatField { userData = index, value = initValue };
|
||||
|
||||
field.RegisterCallback<MouseDownEvent>(Repaint);
|
||||
field.RegisterCallback<MouseMoveEvent>(Repaint);
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
var fieldValue = (float)evt.newValue;
|
||||
if (index == 1)
|
||||
m_DielectricMaterial.indexOfRefraction = fieldValue;
|
||||
else
|
||||
m_DielectricMaterial.range = fieldValue;
|
||||
|
||||
m_PropertyInfo.SetValue(m_Node, m_DielectricMaterial, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
field.Q("unity-text-input").RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
if (index == 1)
|
||||
RedrawIORControls(m_DielectricMaterial.indexOfRefraction);
|
||||
else
|
||||
RedrawRangeControls(m_DielectricMaterial.range);
|
||||
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
panel.Add(field);
|
||||
return field;
|
||||
}
|
||||
|
||||
void RedrawRangeControls(float value)
|
||||
{
|
||||
value = Mathf.Max(Mathf.Min(value, 1), 0.01f);
|
||||
m_RangePanel.Remove(m_RangeSlider);
|
||||
m_RangeSlider = new Slider(0.01f, 1) { value = value };
|
||||
m_RangeSlider.RegisterValueChangedCallback((evt) => OnChangeRangeSlider(evt.newValue));
|
||||
m_RangePanel.Add(m_RangeSlider);
|
||||
m_RangePanel.Remove(m_RangeField);
|
||||
m_RangeField.value = value;
|
||||
m_RangePanel.Add(m_RangeField);
|
||||
}
|
||||
|
||||
void RedrawIORControls(float value)
|
||||
{
|
||||
value = Mathf.Max(Mathf.Min(value, 5), 1);
|
||||
m_IORPanel.Remove(m_IORSlider);
|
||||
m_IORSlider = new Slider(1, 2.5f) { value = value };
|
||||
m_IORSlider.RegisterValueChangedCallback((evt) => OnChangeIORSlider(evt.newValue));
|
||||
|
||||
m_IORPanel.Add(m_IORSlider);
|
||||
m_IORPanel.Remove(m_IORField);
|
||||
m_IORField.value = value;
|
||||
m_IORPanel.Add(m_IORField);
|
||||
}
|
||||
|
||||
void Repaint<T>(MouseEventBase<T> evt) where T : MouseEventBase<T>, new()
|
||||
{
|
||||
evt.StopPropagation();
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 20b1492eed8154d8f9a454e3563d9c01
|
||||
timeCreated: 1507817885
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class EnumControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public EnumControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new EnumControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class EnumControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public EnumControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/EnumControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (!propertyInfo.PropertyType.IsEnum)
|
||||
throw new ArgumentException("Property must be an enum.", "propertyInfo");
|
||||
Add(new Label(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name)));
|
||||
var enumField = new EnumField((Enum)m_PropertyInfo.GetValue(m_Node, null));
|
||||
enumField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(enumField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
var value = (Enum)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (!evt.newValue.Equals(value))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3a1e13c5e67541d7ad7ae18ea5a834e4
|
||||
timeCreated: 1507817885
|
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
interface IEnumConversion
|
||||
{
|
||||
Enum from { get; set; }
|
||||
Enum to { get; set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class EnumConversionControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new EnumConversionControlView(node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class EnumConversionControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
IEnumConversion value
|
||||
{
|
||||
get { return (IEnumConversion)m_PropertyInfo.GetValue(m_Node, null); }
|
||||
set { m_PropertyInfo.SetValue(m_Node, value, null); }
|
||||
}
|
||||
|
||||
public EnumConversionControlView(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!propertyInfo.PropertyType.GetInterfaces().Any(t => t == typeof(IEnumConversion)))
|
||||
throw new ArgumentException("Property type must implement IEnumConversion.");
|
||||
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/EnumConversionControlView"));
|
||||
var currentValue = value;
|
||||
|
||||
var ec = (IEnumConversion)propertyInfo.GetValue(m_Node, null);
|
||||
propertyInfo.SetValue(m_Node, ec, null);
|
||||
|
||||
var fromField = new EnumField(currentValue.from);
|
||||
fromField.RegisterValueChangedCallback(OnFromChanged);
|
||||
Add(fromField);
|
||||
|
||||
var arrowLabel = new Label("➔");
|
||||
Add(arrowLabel);
|
||||
|
||||
var toField = new EnumField(currentValue.to);
|
||||
toField.RegisterValueChangedCallback(OnToChanged);
|
||||
Add(toField);
|
||||
}
|
||||
|
||||
void OnFromChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
var currentValue = value;
|
||||
if (!Equals(currentValue.from, evt.newValue))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change Colorspace From");
|
||||
currentValue.from = evt.newValue;
|
||||
value = currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
void OnToChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
var currentValue = value;
|
||||
if (!Equals(currentValue.to, evt.newValue))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change Colorspace To");
|
||||
currentValue.to = evt.newValue;
|
||||
value = currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 50c6ddf46f8445fba2689962d438c227
|
||||
timeCreated: 1512640228
|
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class GradientControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public GradientControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new GradientControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class GradientObject : ScriptableObject
|
||||
{
|
||||
public Gradient gradient = new Gradient();
|
||||
}
|
||||
|
||||
class GradientControlView : VisualElement
|
||||
{
|
||||
GUIContent m_Label;
|
||||
|
||||
AbstractMaterialNode m_Node;
|
||||
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
[SerializeField]
|
||||
GradientObject m_GradientObject;
|
||||
|
||||
[SerializeField]
|
||||
SerializedObject m_SerializedObject;
|
||||
|
||||
public GradientControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/GradientControlView"));
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(Gradient))
|
||||
throw new ArgumentException("Property must be of type Gradient.", "propertyInfo");
|
||||
new GUIContent(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name));
|
||||
|
||||
m_GradientObject = ScriptableObject.CreateInstance<GradientObject>();
|
||||
m_GradientObject.gradient = new Gradient();
|
||||
m_SerializedObject = new SerializedObject(m_GradientObject);
|
||||
|
||||
var gradient = (Gradient)m_PropertyInfo.GetValue(m_Node, null);
|
||||
m_GradientObject.gradient.SetKeys(gradient.colorKeys, gradient.alphaKeys);
|
||||
m_GradientObject.gradient.mode = gradient.mode;
|
||||
|
||||
var gradientPanel = new VisualElement { name = "gradientPanel" };
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
gradientPanel.Add(new Label(label));
|
||||
|
||||
var gradientField = new GradientField() { value = m_GradientObject.gradient, colorSpace = ColorSpace.Linear };
|
||||
gradientField.RegisterValueChangedCallback(OnValueChanged);
|
||||
gradientPanel.Add(gradientField);
|
||||
|
||||
Add(gradientPanel);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Gradient> evt)
|
||||
{
|
||||
m_SerializedObject.Update();
|
||||
var value = (Gradient)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (!evt.newValue.Equals(value))
|
||||
{
|
||||
m_GradientObject.gradient.SetKeys(evt.newValue.colorKeys, evt.newValue.alphaKeys);
|
||||
m_GradientObject.gradient.mode = evt.newValue.mode;
|
||||
m_SerializedObject.ApplyModifiedProperties();
|
||||
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, m_GradientObject.gradient, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cd2268cf57344487791694f9f136712e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
using System.Reflection;
|
||||
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
interface IControlAttribute
|
||||
{
|
||||
VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5f008211c3394775bdefde0b20e5c8ff
|
||||
timeCreated: 1507642348
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class IdentifierControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public IdentifierControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new IdentifierControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class IdentifierControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public IdentifierControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
var style = Resources.Load<StyleSheet>("Styles/Controls/IdentifierControlView");
|
||||
if (style) styleSheets.Add(style);
|
||||
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(string))
|
||||
throw new ArgumentException("Property must be of type string.", "propertyInfo");
|
||||
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var strField = new IdentifierField() { value = (string)m_PropertyInfo.GetValue(m_Node, null) };
|
||||
strField.RegisterValueChangedCallback(OnChange);
|
||||
Add(strField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<string> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Identifier Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a0e142b8abd53a243b029b58247caacc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class IntegerControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public IntegerControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new IntegerControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class IntegerControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public IntegerControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/IntegerControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(int))
|
||||
throw new ArgumentException("Property must be of type integer.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var intField = new IntegerField { value = (int)m_PropertyInfo.GetValue(m_Node, null) };
|
||||
intField.RegisterValueChangedCallback(OnChange);
|
||||
|
||||
Add(intField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<int> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Integer Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 57d18a57866821542806ab5253cf4310
|
||||
timeCreated: 1507817885
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ObjectControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public ObjectControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ObjectControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public ObjectControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!typeof(Object).IsAssignableFrom(propertyInfo.PropertyType))
|
||||
throw new ArgumentException("Property must be assignable to UnityEngine.Object.");
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
label = label ?? propertyInfo.Name;
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label { text = label });
|
||||
|
||||
var value = (Object)m_PropertyInfo.GetValue(m_Node, null);
|
||||
var objectField = new ObjectField { objectType = propertyInfo.PropertyType, value = value };
|
||||
objectField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(objectField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Object> evt)
|
||||
{
|
||||
var value = (Object)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (evt.newValue != value)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change + " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3f483132ae7b4689b20fd6b8c59633b5
|
||||
timeCreated: 1507900115
|
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class PopupControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
//string[] m_Entries;
|
||||
|
||||
public PopupControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
//m_Entries = entries;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new PopupControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
struct PopupList
|
||||
{
|
||||
public int selectedEntry;
|
||||
public string[] popupEntries;
|
||||
|
||||
public PopupList(string[] entries, int defaultEntry)
|
||||
{
|
||||
popupEntries = entries;
|
||||
selectedEntry = defaultEntry;
|
||||
}
|
||||
}
|
||||
|
||||
class PopupControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
PopupField<string> m_PopupField;
|
||||
|
||||
public PopupControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/PopupControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
|
||||
Type type = propertyInfo.PropertyType;
|
||||
if (type != typeof(PopupList))
|
||||
{
|
||||
throw new ArgumentException("Property must be a PopupList.", "propertyInfo");
|
||||
}
|
||||
|
||||
Add(new Label(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name)));
|
||||
var value = (PopupList)propertyInfo.GetValue(m_Node, null);
|
||||
m_PopupField = new PopupField<string>(new List<string>(value.popupEntries), value.selectedEntry);
|
||||
m_PopupField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(m_PopupField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<string> evt)
|
||||
{
|
||||
var value = (PopupList)m_PropertyInfo.GetValue(m_Node, null);
|
||||
value.selectedEntry = m_PopupField.index;
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 348b3c9679acca945b0df8cb701908f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,169 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
using System.Globalization;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class SliderControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
bool m_DisplayMinMax;
|
||||
|
||||
public SliderControlAttribute(string label = null, bool displayMinMax = false)
|
||||
{
|
||||
m_Label = label;
|
||||
m_DisplayMinMax = displayMinMax;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new SliderControlView(m_Label, m_DisplayMinMax, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class SliderControlView : VisualElement, AbstractMaterialNodeModificationListener
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
bool m_DisplayMinMax;
|
||||
Vector3 m_Value;
|
||||
|
||||
VisualElement m_SliderPanel;
|
||||
Slider m_Slider;
|
||||
FloatField m_SliderInput;
|
||||
FloatField m_MinField;
|
||||
FloatField m_MaxField;
|
||||
|
||||
public SliderControlView(string label, bool displayMinMax, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/SliderControlView"));
|
||||
m_DisplayMinMax = displayMinMax;
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(Vector3))
|
||||
throw new ArgumentException("Property must be of type Vector3.", "propertyInfo");
|
||||
new GUIContent(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name));
|
||||
m_Value = (Vector3)m_PropertyInfo.GetValue(m_Node, null);
|
||||
|
||||
m_Slider = new Slider(m_Value.y, m_Value.z) { value = m_Value.x };
|
||||
m_Slider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue));
|
||||
|
||||
m_SliderInput = new FloatField { value = m_Value.x };
|
||||
m_SliderInput.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
var value = (float)evt.newValue;
|
||||
m_Value.x = value;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
m_SliderInput.Q("unity-text-input").RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
float minValue = Mathf.Min(m_Value.x, m_Value.y);
|
||||
float maxValue = Mathf.Max(m_Value.x, m_Value.z);
|
||||
m_Value = new Vector3(m_Value.x, minValue, maxValue);
|
||||
m_MinField.value = minValue;
|
||||
m_MaxField.value = maxValue;
|
||||
UpdateSlider();
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
|
||||
m_SliderPanel = new VisualElement { name = "SliderPanel" };
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
m_SliderPanel.Add(new Label(label));
|
||||
|
||||
m_SliderPanel.Add(m_Slider);
|
||||
m_SliderPanel.Add(m_SliderInput);
|
||||
Add(m_SliderPanel);
|
||||
|
||||
if (m_DisplayMinMax)
|
||||
{
|
||||
var fieldsPanel = new VisualElement { name = "FieldsPanel" };
|
||||
m_MinField = AddMinMaxField(fieldsPanel, "Min", 1);
|
||||
m_MaxField = AddMinMaxField(fieldsPanel, "Max", 2);
|
||||
Add(fieldsPanel);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNodeModified(ModificationScope scope)
|
||||
{
|
||||
if (scope == ModificationScope.Graph)
|
||||
{
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
void OnChangeSlider(float newValue)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Slider Change");
|
||||
var value = (Vector3)m_PropertyInfo.GetValue(m_Node, null);
|
||||
value.x = newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
if (m_SliderInput != null)
|
||||
m_SliderInput.value = newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void UpdateSlider()
|
||||
{
|
||||
m_SliderPanel.Remove(m_Slider);
|
||||
m_Slider = new Slider(m_Value.y, m_Value.z) { value = m_Value.x };
|
||||
m_Slider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue));
|
||||
m_SliderPanel.Add(m_Slider);
|
||||
m_SliderPanel.Add(m_SliderInput);
|
||||
}
|
||||
|
||||
FloatField AddMinMaxField(VisualElement panel, string label, int index)
|
||||
{
|
||||
var floatField = new FloatField { value = m_Value[index] };
|
||||
if (label != null)
|
||||
{
|
||||
var labelField = new Label(label);
|
||||
panel.Add(labelField);
|
||||
}
|
||||
|
||||
floatField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
m_Value[index] = (float)evt.newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
floatField.Q("unity-text-input").RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
if (index == 1)
|
||||
{
|
||||
m_Value[index] = Mathf.Min(m_Value[index], m_Value.z);
|
||||
m_MinField.value = m_Value[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Value[index] = Mathf.Max(m_Value[index], m_Value.y);
|
||||
m_MaxField.value = m_Value[index];
|
||||
}
|
||||
float newValue = Mathf.Max(Mathf.Min(m_Value.x, m_Value.z), m_Value.y);
|
||||
m_Value.x = newValue;
|
||||
m_SliderInput.value = newValue;
|
||||
UpdateSlider();
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
|
||||
panel.Add(floatField);
|
||||
return floatField;
|
||||
}
|
||||
|
||||
void Repaint<T>(MouseEventBase<T> evt) where T : MouseEventBase<T>, new()
|
||||
{
|
||||
evt.StopPropagation();
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c74a50771637c2d49980f713bbb7f4ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEditor.Graphing;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class TextControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
public TextControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!TextControlView.validTypes.Contains(propertyInfo.PropertyType))
|
||||
return null;
|
||||
return new TextControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class TextControlView : VisualElement
|
||||
{
|
||||
public static Type[] validTypes = { typeof(string) };
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
string m_Value;
|
||||
int m_UndoGroup = -1;
|
||||
public TextControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/TextControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
var container = new VisualElement { name = "container" };
|
||||
var thisLabel = new Label(label);
|
||||
container.Add(thisLabel);
|
||||
m_Value = GetValue();
|
||||
string value = null;
|
||||
var field = new TextField { value = m_Value };
|
||||
field.RegisterCallback<MouseDownEvent>(Repaint);
|
||||
field.RegisterCallback<MouseMoveEvent>(Repaint);
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
value = GetValue();
|
||||
value = evt.newValue;
|
||||
if (m_Node.GetType() != typeof(SwizzleNode))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change" + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
m_UndoGroup = -1;
|
||||
}
|
||||
});
|
||||
|
||||
// Pressing escape while we are editing causes it to revert to the original value when we gained focus
|
||||
field.Q("unity-text-input").RegisterCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Escape && m_UndoGroup > -1)
|
||||
{
|
||||
Undo.RevertAllDownToGroup(m_UndoGroup);
|
||||
m_UndoGroup = -1;
|
||||
evt.StopPropagation();
|
||||
}
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
field.Q("unity-text-input").RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
if (m_Node.GetType() == typeof(SwizzleNode))
|
||||
{
|
||||
//Only set node value when mouse clicked away
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change" + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
m_UndoGroup = -1;
|
||||
//Validate graph to update downstream input slot
|
||||
m_Node.owner.ValidateGraph();
|
||||
m_Node.Dirty(ModificationScope.Topological);
|
||||
}
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
container.Add(field);
|
||||
Add(container);
|
||||
}
|
||||
|
||||
string GetValue()
|
||||
{
|
||||
var value = m_PropertyInfo.GetValue(m_Node, null);
|
||||
Assert.IsNotNull(value);
|
||||
return (string)value;
|
||||
}
|
||||
|
||||
void Repaint<T>(MouseEventBase<T> evt) where T : MouseEventBase<T>, new()
|
||||
{
|
||||
evt.StopPropagation();
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 29c513b592822a84d99669e7845b9d45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class Texture3DControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public Texture3DControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new Texture3DControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class Texture3DControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public Texture3DControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(Texture3D))
|
||||
throw new ArgumentException("Property must be of type Texture 3D.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var textureField = new ObjectField { value = (Texture3D)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Texture3D) };
|
||||
textureField.RegisterValueChangedCallback(OnChange);
|
||||
Add(textureField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Object> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Texture Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 841bd408b769d48caa3011ed9219df24
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class TextureArrayControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public TextureArrayControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new TextureArrayControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class TextureArrayControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public TextureArrayControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(Texture2DArray))
|
||||
throw new ArgumentException("Property must be of type Texture 2D Array.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var textureField = new ObjectField { value = (Texture2DArray)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Texture2DArray) };
|
||||
textureField.RegisterValueChangedCallback(OnChange);
|
||||
Add(textureField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Object> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Texture Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0e3e309a7d3ff4d2e81a55970ef9b026
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class TextureControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public TextureControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new TextureControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class TextureControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public TextureControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(Texture))
|
||||
throw new ArgumentException("Property must be of type Texture.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var textureField = new ObjectField { value = (Texture)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Texture) };
|
||||
textureField.RegisterValueChangedCallback(OnChange);
|
||||
Add(textureField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Object> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Texture Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 12334ec97337f32408251974c4077033
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[Serializable]
|
||||
struct ToggleData
|
||||
{
|
||||
public bool isOn;
|
||||
public bool isEnabled;
|
||||
|
||||
public ToggleData(bool on, bool enabled)
|
||||
{
|
||||
isOn = on;
|
||||
isEnabled = enabled;
|
||||
}
|
||||
|
||||
public ToggleData(bool on)
|
||||
{
|
||||
isOn = on;
|
||||
isEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ToggleControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public ToggleControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ToggleControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleControlView : VisualElement, AbstractMaterialNodeModificationListener
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
Label m_Label;
|
||||
Toggle m_Toggle;
|
||||
|
||||
public ToggleControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ToggleControlView"));
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(ToggleData))
|
||||
throw new ArgumentException("Property must be a Toggle.", "propertyInfo");
|
||||
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
var value = (ToggleData)m_PropertyInfo.GetValue(m_Node, null);
|
||||
var panel = new VisualElement { name = "togglePanel" };
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
{
|
||||
m_Label = new Label(label);
|
||||
m_Label.SetEnabled(value.isEnabled);
|
||||
panel.Add(m_Label);
|
||||
}
|
||||
|
||||
m_Toggle = new Toggle();
|
||||
m_Toggle.OnToggleChanged(OnChangeToggle);
|
||||
m_Toggle.SetEnabled(value.isEnabled);
|
||||
m_Toggle.value = value.isOn;
|
||||
panel.Add(m_Toggle);
|
||||
Add(panel);
|
||||
}
|
||||
|
||||
public void OnNodeModified(ModificationScope scope)
|
||||
{
|
||||
var value = (ToggleData)m_PropertyInfo.GetValue(m_Node, null);
|
||||
m_Toggle.SetEnabled(value.isEnabled);
|
||||
if (m_Label != null)
|
||||
m_Label.SetEnabled(value.isEnabled);
|
||||
|
||||
if (scope == ModificationScope.Graph)
|
||||
{
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
void OnChangeToggle(ChangeEvent<bool> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Toggle Change");
|
||||
var value = (ToggleData)m_PropertyInfo.GetValue(m_Node, null);
|
||||
value.isOn = evt.newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5e83ab8eb10c20c4fbed2700d4f4f11c
|
||||
timeCreated: 1507817885
|
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class MultiFloatControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
string m_SubLabel1;
|
||||
string m_SubLabel2;
|
||||
string m_SubLabel3;
|
||||
string m_SubLabel4;
|
||||
|
||||
public MultiFloatControlAttribute(string label = null, string subLabel1 = "X", string subLabel2 = "Y", string subLabel3 = "Z", string subLabel4 = "W")
|
||||
{
|
||||
m_SubLabel1 = subLabel1;
|
||||
m_SubLabel2 = subLabel2;
|
||||
m_SubLabel3 = subLabel3;
|
||||
m_SubLabel4 = subLabel4;
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!MultiFloatControlView.validTypes.Contains(propertyInfo.PropertyType))
|
||||
return null;
|
||||
return new MultiFloatControlView(m_Label, m_SubLabel1, m_SubLabel2, m_SubLabel3, m_SubLabel4, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiFloatControlView : VisualElement
|
||||
{
|
||||
public static Type[] validTypes = { typeof(float), typeof(Vector2), typeof(Vector3), typeof(Vector4) };
|
||||
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
Vector4 m_Value;
|
||||
int m_UndoGroup = -1;
|
||||
|
||||
public MultiFloatControlView(string label, string subLabel1, string subLabel2, string subLabel3, string subLabel4, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
var components = Array.IndexOf(validTypes, propertyInfo.PropertyType) + 1;
|
||||
if (components == -1)
|
||||
throw new ArgumentException("Property must be of type float, Vector2, Vector3 or Vector4.", "propertyInfo");
|
||||
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/MultiFloatControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
m_Value = GetValue();
|
||||
AddField(0, subLabel1);
|
||||
if (components > 1)
|
||||
AddField(1, subLabel2);
|
||||
if (components > 2)
|
||||
AddField(2, subLabel3);
|
||||
if (components > 3)
|
||||
AddField(3, subLabel4);
|
||||
}
|
||||
|
||||
void AddField(int index, string subLabel)
|
||||
{
|
||||
var dummy = new VisualElement { name = "dummy" };
|
||||
var label = new Label(subLabel);
|
||||
dummy.Add(label);
|
||||
Add(dummy);
|
||||
var field = new FloatField { userData = index, value = m_Value[index] };
|
||||
var dragger = new FieldMouseDragger<double>(field);
|
||||
dragger.SetDragZone(label);
|
||||
field.RegisterCallback<MouseDownEvent>(Repaint);
|
||||
field.RegisterCallback<MouseMoveEvent>(Repaint);
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
var value = GetValue();
|
||||
value[index] = (float)evt.newValue;
|
||||
SetValue(value);
|
||||
m_UndoGroup = -1;
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
field.Q("unity-text-input").RegisterCallback<InputEvent>(evt =>
|
||||
{
|
||||
if (m_UndoGroup == -1)
|
||||
{
|
||||
m_UndoGroup = Undo.GetCurrentGroup();
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
}
|
||||
float newValue;
|
||||
if (!float.TryParse(evt.newData, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out newValue))
|
||||
newValue = 0f;
|
||||
var value = GetValue();
|
||||
value[index] = newValue;
|
||||
SetValue(value);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
field.Q("unity-text-input").RegisterCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Escape && m_UndoGroup > -1)
|
||||
{
|
||||
Undo.RevertAllDownToGroup(m_UndoGroup);
|
||||
m_UndoGroup = -1;
|
||||
m_Value = GetValue();
|
||||
evt.StopPropagation();
|
||||
}
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
Add(field);
|
||||
}
|
||||
|
||||
object ValueToPropertyType(Vector4 value)
|
||||
{
|
||||
if (m_PropertyInfo.PropertyType == typeof(float))
|
||||
return value.x;
|
||||
if (m_PropertyInfo.PropertyType == typeof(Vector2))
|
||||
return (Vector2)value;
|
||||
if (m_PropertyInfo.PropertyType == typeof(Vector3))
|
||||
return (Vector3)value;
|
||||
return value;
|
||||
}
|
||||
|
||||
Vector4 GetValue()
|
||||
{
|
||||
var value = m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (m_PropertyInfo.PropertyType == typeof(float))
|
||||
return new Vector4((float)value, 0f, 0f, 0f);
|
||||
if (m_PropertyInfo.PropertyType == typeof(Vector2))
|
||||
return (Vector2)value;
|
||||
if (m_PropertyInfo.PropertyType == typeof(Vector3))
|
||||
return (Vector3)value;
|
||||
return (Vector4)value;
|
||||
}
|
||||
|
||||
void SetValue(Vector4 value)
|
||||
{
|
||||
m_PropertyInfo.SetValue(m_Node, ValueToPropertyType(value), null);
|
||||
}
|
||||
|
||||
void Repaint<T>(MouseEventBase<T> evt) where T : MouseEventBase<T>, new()
|
||||
{
|
||||
evt.StopPropagation();
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8a515dc1bacc453b870777d8da9a7211
|
||||
timeCreated: 1507642288
|
|
@ -0,0 +1,44 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using Edge = UnityEditor.Experimental.GraphView.Edge;
|
||||
using UnityEditor.Searcher;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class EdgeConnectorListener : IEdgeConnectorListener
|
||||
{
|
||||
readonly GraphData m_Graph;
|
||||
readonly SearchWindowProvider m_SearchWindowProvider;
|
||||
readonly EditorWindow m_editorWindow;
|
||||
|
||||
public EdgeConnectorListener(GraphData graph, SearchWindowProvider searchWindowProvider, EditorWindow editorWindow)
|
||||
{
|
||||
m_Graph = graph;
|
||||
m_SearchWindowProvider = searchWindowProvider;
|
||||
m_editorWindow = editorWindow;
|
||||
}
|
||||
|
||||
public void OnDropOutsidePort(Edge edge, Vector2 position)
|
||||
{
|
||||
var draggedPort = (edge.output != null ? edge.output.edgeConnector.edgeDragHelper.draggedPort : null) ?? (edge.input != null ? edge.input.edgeConnector.edgeDragHelper.draggedPort : null);
|
||||
m_SearchWindowProvider.target = null;
|
||||
m_SearchWindowProvider.connectedPort = (ShaderPort)draggedPort;
|
||||
m_SearchWindowProvider.regenerateEntries = true;//need to be sure the entires are relevant to the edge we are dragging
|
||||
SearcherWindow.Show(m_editorWindow, (m_SearchWindowProvider as SearcherProvider).LoadSearchWindow(),
|
||||
item => (m_SearchWindowProvider as SearcherProvider).OnSearcherSelectEntry(item, position),
|
||||
position, null);
|
||||
m_SearchWindowProvider.regenerateEntries = true;//entries no longer necessarily relevant, need to regenerate
|
||||
}
|
||||
|
||||
public void OnDrop(GraphView graphView, Edge edge)
|
||||
{
|
||||
var leftSlot = edge.output.GetSlot();
|
||||
var rightSlot = edge.input.GetSlot();
|
||||
if (leftSlot != null && rightSlot != null)
|
||||
{
|
||||
m_Graph.owner.RegisterCompleteObjectUndo("Connect Edge");
|
||||
m_Graph.Connect(leftSlot.slotReference, rightSlot.slotReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 36c61698900b42c9a6b5e28c3651249a
|
||||
timeCreated: 1512740035
|
|
@ -0,0 +1,9 @@
|
|||
using UnityEditor.Graphing;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
interface AbstractMaterialNodeModificationListener
|
||||
{
|
||||
void OnNodeModified(ModificationScope scope);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 22f268830a014848b3a8bfdd5142a2d0
|
||||
timeCreated: 1510751718
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1d1f4aaa81084a13aaeb78dfaf7d35d7
|
||||
timeCreated: 1503663057
|
|
@ -0,0 +1,308 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers;
|
||||
using UnityEditor.ShaderGraph.Drawing.Views;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector
|
||||
{
|
||||
class InspectorView : GraphSubWindow
|
||||
{
|
||||
const float k_InspectorUpdateInterval = 0.25f;
|
||||
const int k_InspectorElementLimit = 20;
|
||||
|
||||
bool m_GraphSettingsTabFocused = false;
|
||||
|
||||
int m_CurrentlyInspectedElementsCount = 0;
|
||||
|
||||
readonly List<Type> m_PropertyDrawerList = new List<Type>();
|
||||
|
||||
HashSet<IInspectable> cachedInspectables = new();
|
||||
|
||||
// There's persistent data that is stored in the graph settings property drawer that we need to hold onto between interactions
|
||||
IPropertyDrawer m_graphSettingsPropertyDrawer = new GraphDataPropertyDrawer();
|
||||
public override string windowTitle => "Graph Inspector";
|
||||
public override string elementName => "InspectorView";
|
||||
public override string styleName => "InspectorView";
|
||||
public override string UxmlName => "GraphInspector";
|
||||
public override string layoutKey => "UnityEditor.ShaderGraph.InspectorWindow";
|
||||
|
||||
TabbedView m_GraphInspectorView;
|
||||
TabbedView m_NodeSettingsTab;
|
||||
protected VisualElement m_GraphSettingsContainer;
|
||||
protected VisualElement m_NodeSettingsContainer;
|
||||
|
||||
Label m_MaxItemsMessageLabel;
|
||||
|
||||
void RegisterPropertyDrawer(Type newPropertyDrawerType)
|
||||
{
|
||||
if (typeof(IPropertyDrawer).IsAssignableFrom(newPropertyDrawerType) == false)
|
||||
{
|
||||
Debug.Log("Attempted to register a property drawer that doesn't inherit from IPropertyDrawer!");
|
||||
return;
|
||||
}
|
||||
|
||||
var newPropertyDrawerAttribute = newPropertyDrawerType.GetCustomAttribute<SGPropertyDrawerAttribute>();
|
||||
|
||||
if (newPropertyDrawerAttribute != null)
|
||||
{
|
||||
foreach (var existingPropertyDrawerType in m_PropertyDrawerList)
|
||||
{
|
||||
var existingPropertyDrawerAttribute = existingPropertyDrawerType.GetCustomAttribute<SGPropertyDrawerAttribute>();
|
||||
if (newPropertyDrawerAttribute.propertyType.IsSubclassOf(existingPropertyDrawerAttribute.propertyType))
|
||||
{
|
||||
// Derived types need to be at start of list
|
||||
m_PropertyDrawerList.Insert(0, newPropertyDrawerType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingPropertyDrawerAttribute.propertyType.IsSubclassOf(newPropertyDrawerAttribute.propertyType))
|
||||
{
|
||||
// Add new base class type to end of list
|
||||
m_PropertyDrawerList.Add(newPropertyDrawerType);
|
||||
// Shift already added existing type to the beginning of the list
|
||||
m_PropertyDrawerList.Remove(existingPropertyDrawerType);
|
||||
m_PropertyDrawerList.Insert(0, existingPropertyDrawerType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_PropertyDrawerList.Add(newPropertyDrawerType);
|
||||
}
|
||||
else
|
||||
Debug.Log("Attempted to register property drawer: " + newPropertyDrawerType + " that isn't marked up with the SGPropertyDrawer attribute!");
|
||||
}
|
||||
|
||||
public InspectorView(InspectorViewModel viewModel) : base(viewModel)
|
||||
{
|
||||
m_GraphInspectorView = m_MainContainer.Q<TabbedView>("GraphInspectorView");
|
||||
m_GraphSettingsContainer = m_GraphInspectorView.Q<VisualElement>("GraphSettingsContainer");
|
||||
m_NodeSettingsContainer = m_GraphInspectorView.Q<VisualElement>("NodeSettingsContainer");
|
||||
m_MaxItemsMessageLabel = m_GraphInspectorView.Q<Label>("maxItemsMessageLabel");
|
||||
m_ContentContainer.Add(m_GraphInspectorView);
|
||||
m_ScrollView = this.Q<ScrollView>();
|
||||
m_GraphInspectorView.Q<TabButton>("GraphSettingsButton").OnSelect += GraphSettingsTabClicked;
|
||||
m_GraphInspectorView.Q<TabButton>("NodeSettingsButton").OnSelect += NodeSettingsTabClicked;
|
||||
|
||||
isWindowScrollable = true;
|
||||
isWindowResizable = true;
|
||||
|
||||
var unregisteredPropertyDrawerTypes = TypeCache.GetTypesDerivedFrom<IPropertyDrawer>().ToList();
|
||||
|
||||
foreach (var type in unregisteredPropertyDrawerTypes)
|
||||
{
|
||||
RegisterPropertyDrawer(type);
|
||||
}
|
||||
|
||||
// By default at startup, show graph settings
|
||||
m_GraphInspectorView.Activate(m_GraphInspectorView.Q<TabButton>("GraphSettingsButton"));
|
||||
}
|
||||
|
||||
void GraphSettingsTabClicked(TabButton button)
|
||||
{
|
||||
m_GraphSettingsTabFocused = true;
|
||||
m_ScrollView.mode = ScrollViewMode.Vertical;
|
||||
}
|
||||
|
||||
void NodeSettingsTabClicked(TabButton button)
|
||||
{
|
||||
m_GraphSettingsTabFocused = false;
|
||||
m_ScrollView.mode = ScrollViewMode.VerticalAndHorizontal;
|
||||
}
|
||||
|
||||
public void InitializeGraphSettings()
|
||||
{
|
||||
ShowGraphSettings_Internal(m_GraphSettingsContainer);
|
||||
}
|
||||
|
||||
public bool doesInspectorNeedUpdate { get; set; }
|
||||
|
||||
public void TriggerInspectorUpdate(IEnumerable<ISelectable> selectionList)
|
||||
{
|
||||
// An optimization that prevents inspector updates from getting triggered every time a selection event is issued in the event of large selections
|
||||
if (selectionList?.Count() > k_InspectorElementLimit)
|
||||
return;
|
||||
doesInspectorNeedUpdate = true;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
ShowGraphSettings_Internal(m_GraphSettingsContainer);
|
||||
|
||||
m_NodeSettingsContainer.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
bool anySelectables = false;
|
||||
int currentInspectablesCount = 0;
|
||||
var currentInspectables = new HashSet<IInspectable>();
|
||||
foreach (var selectable in selection)
|
||||
{
|
||||
if (selectable is IInspectable inspectable)
|
||||
{
|
||||
DrawInspectable(m_NodeSettingsContainer, inspectable);
|
||||
currentInspectablesCount++;
|
||||
anySelectables = true;
|
||||
currentInspectables.Add(inspectable);
|
||||
}
|
||||
|
||||
if (currentInspectablesCount == k_InspectorElementLimit)
|
||||
{
|
||||
m_NodeSettingsContainer.Add(m_MaxItemsMessageLabel);
|
||||
m_MaxItemsMessageLabel.style.visibility = Visibility.Visible;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have changed our inspector selection while the graph settings tab was focused, we want to switch back to the node settings tab, so invalidate the flag
|
||||
foreach (var currentInspectable in currentInspectables)
|
||||
{
|
||||
if (cachedInspectables.Contains(currentInspectable) == false)
|
||||
m_GraphSettingsTabFocused = false;
|
||||
}
|
||||
|
||||
cachedInspectables = currentInspectables;
|
||||
m_CurrentlyInspectedElementsCount = currentInspectablesCount;
|
||||
|
||||
if (anySelectables && !m_GraphSettingsTabFocused)
|
||||
{
|
||||
// Anything selectable in the graph (GraphSettings not included) is only ever interacted with through the
|
||||
// Node Settings tab so we can make the assumption they want to see that tab
|
||||
m_GraphInspectorView.Activate(m_GraphInspectorView.Q<TabButton>("NodeSettingsButton"));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
|
||||
if (doesInspectorNeedUpdate)
|
||||
doesInspectorNeedUpdate = false;
|
||||
|
||||
m_NodeSettingsContainer.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void DrawInspectable(
|
||||
VisualElement outputVisualElement,
|
||||
IInspectable inspectable,
|
||||
IPropertyDrawer propertyDrawerToUse = null)
|
||||
{
|
||||
InspectorUtils.GatherInspectorContent(m_PropertyDrawerList, outputVisualElement, inspectable, TriggerInspectorUpdate, propertyDrawerToUse);
|
||||
}
|
||||
|
||||
internal void HandleGraphChanges()
|
||||
{
|
||||
float timePassed = (float)(EditorApplication.timeSinceStartup % k_InspectorUpdateInterval);
|
||||
|
||||
int currentInspectablesCount = 0;
|
||||
foreach (var selectable in selection)
|
||||
{
|
||||
if (selectable is IInspectable)
|
||||
currentInspectablesCount++;
|
||||
}
|
||||
|
||||
// Don't update for selections beyond a certain amount as they are no longer visible in the inspector past a certain point and only cost performance as the user performs operations
|
||||
if (timePassed < 0.01f && selection.Count < k_InspectorElementLimit && currentInspectablesCount != m_CurrentlyInspectedElementsCount)
|
||||
{
|
||||
m_GraphSettingsTabFocused = false;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerInspectorUpdate()
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
// This should be implemented by any inspector class that wants to define its own GraphSettings
|
||||
// which for SG, is a representation of the settings in GraphData
|
||||
protected virtual void ShowGraphSettings_Internal(VisualElement contentContainer)
|
||||
{
|
||||
var graphEditorView = ParentView.GetFirstAncestorOfType<GraphEditorView>();
|
||||
if (graphEditorView == null)
|
||||
return;
|
||||
|
||||
contentContainer.Clear();
|
||||
DrawInspectable(contentContainer, (IInspectable)ParentView, m_graphSettingsPropertyDrawer);
|
||||
contentContainer.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
public static class InspectorUtils
|
||||
{
|
||||
internal static void GatherInspectorContent(
|
||||
List<Type> propertyDrawerList,
|
||||
VisualElement outputVisualElement,
|
||||
IInspectable inspectable,
|
||||
Action propertyChangeCallback,
|
||||
IPropertyDrawer propertyDrawerToUse = null)
|
||||
{
|
||||
var dataObject = inspectable.GetObjectToInspect();
|
||||
if (dataObject == null)
|
||||
throw new NullReferenceException("DataObject returned by Inspectable is null!");
|
||||
|
||||
var properties = inspectable.GetType().GetProperties(BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (properties == null)
|
||||
throw new NullReferenceException("PropertyInfos returned by Inspectable is null!");
|
||||
|
||||
foreach (var propertyInfo in properties)
|
||||
{
|
||||
var attribute = propertyInfo.GetCustomAttribute<InspectableAttribute>();
|
||||
if (attribute == null)
|
||||
continue;
|
||||
|
||||
var propertyType = propertyInfo.GetGetMethod(true).Invoke(inspectable, new object[] { }).GetType();
|
||||
|
||||
if (IsPropertyTypeHandled(propertyDrawerList, propertyType, out var propertyDrawerTypeToUse))
|
||||
{
|
||||
var propertyDrawerInstance = propertyDrawerToUse ??
|
||||
(IPropertyDrawer)Activator.CreateInstance(propertyDrawerTypeToUse);
|
||||
// Assign the inspector update delegate so any property drawer can trigger an inspector update if it needs it
|
||||
propertyDrawerInstance.inspectorUpdateDelegate = propertyChangeCallback;
|
||||
// Supply any required data to this particular kind of property drawer
|
||||
inspectable.SupplyDataToPropertyDrawer(propertyDrawerInstance, propertyChangeCallback);
|
||||
var propertyGUI = propertyDrawerInstance.DrawProperty(propertyInfo, dataObject, attribute);
|
||||
outputVisualElement.Add(propertyGUI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsPropertyTypeHandled(
|
||||
List<Type> propertyDrawerList,
|
||||
Type typeOfProperty,
|
||||
out Type propertyDrawerToUse)
|
||||
{
|
||||
propertyDrawerToUse = null;
|
||||
|
||||
// Check to see if a property drawer has been registered that handles this type
|
||||
foreach (var propertyDrawerType in propertyDrawerList)
|
||||
{
|
||||
var typeHandledByPropertyDrawer = propertyDrawerType.GetCustomAttribute<SGPropertyDrawerAttribute>();
|
||||
// Numeric types and boolean wrapper types like ToggleData handled here
|
||||
if (typeHandledByPropertyDrawer.propertyType == typeOfProperty)
|
||||
{
|
||||
propertyDrawerToUse = propertyDrawerType;
|
||||
return true;
|
||||
}
|
||||
// Generics and Enumerable types are handled here
|
||||
else if (typeHandledByPropertyDrawer.propertyType.IsAssignableFrom(typeOfProperty))
|
||||
{
|
||||
// Before returning it, check for a more appropriate type further
|
||||
propertyDrawerToUse = propertyDrawerType;
|
||||
return true;
|
||||
}
|
||||
// Enums are weird and need to be handled explicitly as done below as their runtime type isn't the same as System.Enum
|
||||
else if (typeHandledByPropertyDrawer.propertyType == typeOfProperty.BaseType)
|
||||
{
|
||||
propertyDrawerToUse = propertyDrawerType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6759820ca0a974f36b1a63ec6f6e1a57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,239 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.UIElements.StyleSheets;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector
|
||||
{
|
||||
class MasterPreviewView : VisualElement
|
||||
{
|
||||
PreviewManager m_PreviewManager;
|
||||
GraphData m_Graph;
|
||||
|
||||
PreviewRenderData m_PreviewRenderHandle;
|
||||
Image m_PreviewTextureView;
|
||||
|
||||
public Image previewTextureView
|
||||
{
|
||||
get { return m_PreviewTextureView; }
|
||||
}
|
||||
|
||||
Vector2 m_PreviewScrollPosition;
|
||||
ObjectField m_PreviewMeshPicker;
|
||||
|
||||
Mesh m_PreviousMesh;
|
||||
|
||||
bool m_RecalculateLayout;
|
||||
|
||||
ResizeBorderFrame m_PreviewResizeBorderFrame;
|
||||
|
||||
public ResizeBorderFrame previewResizeBorderFrame
|
||||
{
|
||||
get { return m_PreviewResizeBorderFrame; }
|
||||
}
|
||||
|
||||
VisualElement m_Preview;
|
||||
Label m_Title;
|
||||
|
||||
public VisualElement preview
|
||||
{
|
||||
get { return m_Preview; }
|
||||
}
|
||||
|
||||
List<string> m_DoNotShowPrimitives = new List<string>(new string[] { PrimitiveType.Plane.ToString() });
|
||||
|
||||
static Type s_ContextualMenuManipulator = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypesOrNothing()).FirstOrDefault(t => t.FullName == "UnityEngine.UIElements.ContextualMenuManipulator");
|
||||
static Type s_ObjectSelector = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypesOrNothing()).FirstOrDefault(t => t.FullName == "UnityEditor.ObjectSelector");
|
||||
|
||||
public string assetName
|
||||
{
|
||||
get { return m_Title.text; }
|
||||
set { m_Title.text = value; }
|
||||
}
|
||||
|
||||
public MasterPreviewView(PreviewManager previewManager, GraphData graph)
|
||||
{
|
||||
style.overflow = Overflow.Hidden;
|
||||
m_PreviewManager = previewManager;
|
||||
m_Graph = graph;
|
||||
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/MasterPreviewView"));
|
||||
|
||||
m_PreviewRenderHandle = previewManager.masterRenderData;
|
||||
if (m_PreviewRenderHandle != null)
|
||||
{
|
||||
m_PreviewRenderHandle.onPreviewChanged += OnPreviewChanged;
|
||||
}
|
||||
|
||||
var topContainer = new VisualElement() { name = "top" };
|
||||
{
|
||||
m_Title = new Label() { name = "title" };
|
||||
m_Title.text = "Main Preview";
|
||||
|
||||
topContainer.Add(m_Title);
|
||||
}
|
||||
Add(topContainer);
|
||||
|
||||
m_Preview = new VisualElement { name = "middle" };
|
||||
{
|
||||
m_PreviewTextureView = CreatePreview(Texture2D.blackTexture);
|
||||
m_PreviewScrollPosition = new Vector2(0f, 0f);
|
||||
preview.Add(m_PreviewTextureView);
|
||||
preview.AddManipulator(new Scrollable(OnScroll));
|
||||
}
|
||||
Add(preview);
|
||||
|
||||
m_PreviewResizeBorderFrame = new ResizeBorderFrame(this, this) { name = "resizeBorderFrame" };
|
||||
m_PreviewResizeBorderFrame.maintainAspectRatio = true;
|
||||
Add(m_PreviewResizeBorderFrame);
|
||||
|
||||
m_RecalculateLayout = false;
|
||||
this.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
|
||||
}
|
||||
|
||||
Image CreatePreview(Texture texture)
|
||||
{
|
||||
if (m_PreviewRenderHandle?.texture != null)
|
||||
{
|
||||
texture = m_PreviewRenderHandle.texture;
|
||||
}
|
||||
|
||||
var image = new Image { name = "preview", image = texture };
|
||||
image.AddManipulator(new Draggable(OnMouseDragPreviewMesh, true));
|
||||
image.AddManipulator((IManipulator)Activator.CreateInstance(s_ContextualMenuManipulator, (Action<ContextualMenuPopulateEvent>)BuildContextualMenu));
|
||||
return image;
|
||||
}
|
||||
|
||||
void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
foreach (var primitiveTypeName in Enum.GetNames(typeof(PrimitiveType)))
|
||||
{
|
||||
if (m_DoNotShowPrimitives.Contains(primitiveTypeName))
|
||||
continue;
|
||||
evt.menu.AppendAction(primitiveTypeName, e => ChangePrimitiveMesh(primitiveTypeName), DropdownMenuAction.AlwaysEnabled);
|
||||
}
|
||||
|
||||
evt.menu.AppendAction("Sprite", e => ChangeMeshSprite(), DropdownMenuAction.AlwaysEnabled);
|
||||
evt.menu.AppendAction("Custom Mesh", e => ChangeMeshCustom(), DropdownMenuAction.AlwaysEnabled);
|
||||
}
|
||||
|
||||
void OnPreviewChanged()
|
||||
{
|
||||
m_PreviewTextureView.image = m_PreviewRenderHandle?.texture ?? Texture2D.blackTexture;
|
||||
if (m_PreviewRenderHandle != null && m_PreviewRenderHandle.shaderData.isOutOfDate)
|
||||
m_PreviewTextureView.tintColor = new Color(1.0f, 1.0f, 1.0f, 0.3f);
|
||||
else
|
||||
m_PreviewTextureView.tintColor = Color.white;
|
||||
m_PreviewTextureView.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void ChangePrimitiveMesh(string primitiveName)
|
||||
{
|
||||
Mesh changedPrimitiveMesh = Resources.GetBuiltinResource(typeof(Mesh), string.Format("{0}.fbx", primitiveName)) as Mesh;
|
||||
|
||||
ChangeMesh(changedPrimitiveMesh);
|
||||
}
|
||||
|
||||
void ChangeMesh(Mesh mesh)
|
||||
{
|
||||
Mesh changedMesh = mesh;
|
||||
|
||||
m_PreviewManager.UpdateMasterPreview(ModificationScope.Node);
|
||||
|
||||
if (m_Graph.previewData.serializedMesh.mesh != changedMesh)
|
||||
{
|
||||
m_Graph.previewData.rotation = Quaternion.identity;
|
||||
m_PreviewScrollPosition = Vector2.zero;
|
||||
}
|
||||
|
||||
m_Graph.previewData.preventRotation = false;
|
||||
m_Graph.previewData.serializedMesh.mesh = changedMesh;
|
||||
}
|
||||
|
||||
private static EditorWindow Get()
|
||||
{
|
||||
PropertyInfo P = s_ObjectSelector.GetProperty("get", BindingFlags.Public | BindingFlags.Static);
|
||||
return P.GetValue(null, null) as EditorWindow;
|
||||
}
|
||||
|
||||
void OnMeshChanged(Object obj)
|
||||
{
|
||||
var mesh = obj as Mesh;
|
||||
if (mesh == null)
|
||||
mesh = m_PreviousMesh;
|
||||
ChangeMesh(mesh);
|
||||
}
|
||||
|
||||
void ChangeMeshSprite()
|
||||
{
|
||||
ChangePrimitiveMesh(PrimitiveType.Quad.ToString());
|
||||
|
||||
m_Graph.previewData.rotation = Quaternion.identity;
|
||||
m_Graph.previewData.preventRotation = true;
|
||||
}
|
||||
|
||||
void ChangeMeshCustom()
|
||||
{
|
||||
MethodInfo ShowMethod = s_ObjectSelector.GetMethod("Show", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly, Type.DefaultBinder, new[] { typeof(Object), typeof(Type), typeof(Object), typeof(bool), typeof(List<int>), typeof(Action<Object>), typeof(Action<Object>) }, new ParameterModifier[7]);
|
||||
m_PreviousMesh = m_Graph.previewData.serializedMesh.mesh;
|
||||
ShowMethod.Invoke(Get(), new object[] { null, typeof(Mesh), null, false, null, (Action<Object>)OnMeshChanged, (Action<Object>)OnMeshChanged });
|
||||
}
|
||||
|
||||
void OnGeometryChanged(GeometryChangedEvent evt)
|
||||
{
|
||||
if (m_RecalculateLayout)
|
||||
{
|
||||
WindowDockingLayout dockingLayout = new WindowDockingLayout();
|
||||
dockingLayout.CalculateDockingCornerAndOffset(layout, parent.layout);
|
||||
dockingLayout.ClampToParentWindow();
|
||||
dockingLayout.ApplyPosition(this);
|
||||
m_RecalculateLayout = false;
|
||||
}
|
||||
|
||||
var currentWidth = m_PreviewRenderHandle?.texture != null ? m_PreviewRenderHandle.texture.width : -1;
|
||||
var currentHeight = m_PreviewRenderHandle?.texture != null ? m_PreviewRenderHandle.texture.height : -1;
|
||||
|
||||
var targetWidth = Mathf.Max(1f, m_PreviewTextureView.contentRect.width);
|
||||
var targetHeight = Mathf.Max(1f, m_PreviewTextureView.contentRect.height);
|
||||
|
||||
if (Mathf.Approximately(currentWidth, targetHeight) && Mathf.Approximately(currentHeight, targetWidth))
|
||||
return;
|
||||
|
||||
m_PreviewTextureView.style.width = evt.newRect.width;
|
||||
m_PreviewTextureView.style.height = evt.newRect.height - 40.0f;
|
||||
m_PreviewManager.ResizeMasterPreview(new Vector2(evt.newRect.width, evt.newRect.width));
|
||||
}
|
||||
|
||||
void OnScroll(float scrollValue)
|
||||
{
|
||||
float rescaleAmount = -scrollValue * .03f;
|
||||
m_Graph.previewData.scale = Mathf.Clamp(m_Graph.previewData.scale + rescaleAmount, 0.2f, 5f);
|
||||
|
||||
m_PreviewManager.UpdateMasterPreview(ModificationScope.Node);
|
||||
}
|
||||
|
||||
void OnMouseDragPreviewMesh(Vector2 deltaMouse)
|
||||
{
|
||||
if (m_Graph.previewData.preventRotation) return;
|
||||
|
||||
Vector2 previewSize = m_PreviewTextureView.contentRect.size;
|
||||
|
||||
m_PreviewScrollPosition -= deltaMouse * (Event.current.shift ? 3f : 1f) / Mathf.Min(previewSize.x, previewSize.y) * 140f;
|
||||
m_PreviewScrollPosition.y = Mathf.Clamp(m_PreviewScrollPosition.y, -90f, 90f);
|
||||
Quaternion previewRotation = Quaternion.Euler(m_PreviewScrollPosition.y, 0, 0) * Quaternion.Euler(0, m_PreviewScrollPosition.x, 0);
|
||||
m_Graph.previewData.rotation = previewRotation;
|
||||
|
||||
m_PreviewManager.UpdateMasterPreview(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 546947d46a85b284a971a3f056495735
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,189 @@
|
|||
using System;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector
|
||||
{
|
||||
public static class PropertyDrawerUtils
|
||||
{
|
||||
public static Label CreateLabel(string text, int indentLevel = 0, FontStyle fontStyle = FontStyle.Normal)
|
||||
{
|
||||
string label = new string(' ', indentLevel * 4);
|
||||
var labelVisualElement = new Label(label + text);
|
||||
labelVisualElement.style.unityFontStyleAndWeight = fontStyle;
|
||||
labelVisualElement.name = "header";
|
||||
return labelVisualElement;
|
||||
}
|
||||
|
||||
public static Label CreateLabel(string text, int indentLevel = 0)
|
||||
{
|
||||
string label = new string(' ', indentLevel * 4);
|
||||
var labelVisualElement = new Label(label + text);
|
||||
return labelVisualElement;
|
||||
}
|
||||
|
||||
internal enum UIPrecisionForShaderGraphs
|
||||
{
|
||||
Inherit = Precision.Inherit,
|
||||
Single = Precision.Single,
|
||||
Half = Precision.Half,
|
||||
UseGraphPrecision = Precision.Graph,
|
||||
}
|
||||
|
||||
internal static void AddDefaultNodeProperties(VisualElement parentElement, AbstractMaterialNode node, Action setNodesAsDirtyCallback, Action updateNodeViewsCallback)
|
||||
{
|
||||
EnumField precisionField = null;
|
||||
if (node.canSetPrecision)
|
||||
{
|
||||
precisionField = new EnumField((UIPrecisionForShaderGraphs)node.precision);
|
||||
var propertyRow = new PropertyRow(new Label("Precision"));
|
||||
propertyRow.Add(precisionField, (field) =>
|
||||
{
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (evt.newValue.Equals(node.precision))
|
||||
return;
|
||||
|
||||
setNodesAsDirtyCallback?.Invoke();
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change precision");
|
||||
node.precision = (Precision)evt.newValue;
|
||||
node.owner.ValidateGraph();
|
||||
updateNodeViewsCallback?.Invoke();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
});
|
||||
});
|
||||
if (node is Serialization.MultiJsonInternal.UnknownNodeType)
|
||||
precisionField.SetEnabled(false);
|
||||
parentElement.Add(propertyRow);
|
||||
}
|
||||
|
||||
EnumField previewField = null;
|
||||
if (node.hasPreview)
|
||||
{
|
||||
previewField = new EnumField(node.m_PreviewMode);
|
||||
var propertyRow = new PropertyRow(new Label("Preview"));
|
||||
propertyRow.Add(previewField, (field) =>
|
||||
{
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (evt.newValue.Equals(node.m_PreviewMode))
|
||||
return;
|
||||
|
||||
setNodesAsDirtyCallback?.Invoke();
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change preview");
|
||||
node.m_PreviewMode = (PreviewMode)evt.newValue;
|
||||
updateNodeViewsCallback?.Invoke();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
});
|
||||
});
|
||||
if (node is Serialization.MultiJsonInternal.UnknownNodeType)
|
||||
previewField.SetEnabled(false);
|
||||
parentElement.Add(propertyRow);
|
||||
}
|
||||
|
||||
if (node is BlockNode bnode)
|
||||
{
|
||||
AddCustomInterpolatorProperties(parentElement, bnode, setNodesAsDirtyCallback, updateNodeViewsCallback);
|
||||
}
|
||||
|
||||
if (node is CustomInterpolatorNode cinode)
|
||||
{
|
||||
if (cinode.e_targetBlockNode != null)
|
||||
{
|
||||
AddCustomInterpolatorProperties(parentElement, cinode.e_targetBlockNode, setNodesAsDirtyCallback, updateNodeViewsCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AddCustomInterpolatorProperties(VisualElement parentElement, BlockNode node, Action setNodesAsDirtyCallback, Action updateNodeViewsCallback)
|
||||
{
|
||||
if (!node.isCustomBlock)
|
||||
return;
|
||||
|
||||
TextField textField = null;
|
||||
{
|
||||
textField = new TextField { value = node.customName, multiline = false };
|
||||
var propertyRow = new PropertyRow(new Label("Name"));
|
||||
propertyRow.Add(textField, (field) =>
|
||||
{
|
||||
field.RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
if (field.value.Equals(node.customName))
|
||||
return;
|
||||
|
||||
HashSet<string> usedNames = new HashSet<string>();
|
||||
foreach (var other in node.contextData.blocks)
|
||||
if (other != node)
|
||||
usedNames.Add(other.value.descriptor.displayName);
|
||||
|
||||
setNodesAsDirtyCallback?.Invoke();
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Block Name");
|
||||
field.value = node.customName = GraphUtil.SanitizeName(usedNames, "{0}_{1}", NodeUtils.ConvertToValidHLSLIdentifier(field.value));
|
||||
updateNodeViewsCallback?.Invoke();
|
||||
node.Dirty(ModificationScope.Node);
|
||||
node.owner?.ValidateGraph();
|
||||
});
|
||||
});
|
||||
parentElement.Add(propertyRow);
|
||||
}
|
||||
|
||||
EnumField typeField = null;
|
||||
{
|
||||
typeField = new EnumField(node.customWidth);
|
||||
var propertyRow = new PropertyRow(new Label("Type"));
|
||||
propertyRow.Add(typeField, (field) =>
|
||||
{
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (evt.newValue.Equals(node.customWidth))
|
||||
return;
|
||||
|
||||
setNodesAsDirtyCallback?.Invoke();
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Block Type");
|
||||
node.customWidth = (BlockNode.CustomBlockType)evt.newValue;
|
||||
updateNodeViewsCallback?.Invoke();
|
||||
node.Dirty(ModificationScope.Topological);
|
||||
node.owner?.ValidateGraph();
|
||||
});
|
||||
});
|
||||
parentElement.Add(propertyRow);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AddCustomCheckboxProperty(
|
||||
VisualElement parentElement, AbstractMaterialNode node,
|
||||
Action setNodesAsDirtyCallback, Action updateNodeViewsCallback,
|
||||
String label,
|
||||
String undoLabel,
|
||||
Func<bool> GetterFn,
|
||||
Action<bool> SetterFn)
|
||||
{
|
||||
var fieldObj = new Toggle();
|
||||
fieldObj.value = GetterFn();
|
||||
var propertyRow = new PropertyRow(new Label(label));
|
||||
propertyRow.Add(fieldObj, (field) =>
|
||||
{
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (evt.newValue.Equals(GetterFn()))
|
||||
return;
|
||||
|
||||
setNodesAsDirtyCallback?.Invoke();
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Disable Global Mip Bias");
|
||||
SetterFn((bool)evt.newValue);
|
||||
node.owner.ValidateGraph();
|
||||
updateNodeViewsCallback?.Invoke();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
});
|
||||
});
|
||||
if (node is Serialization.MultiJsonInternal.UnknownNodeType)
|
||||
fieldObj.SetEnabled(false);
|
||||
parentElement.Add(propertyRow);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bee8af59ecde4f88b12e15e45df7704c
|
||||
timeCreated: 1587016996
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8488c4add4764941a2d903bf014329b9
|
||||
timeCreated: 1588090481
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
internal interface IGetNodePropertyDrawerPropertyData
|
||||
{
|
||||
void GetPropertyData(Action setNodesAsDirtyCallback, Action updateNodeViewsCallback);
|
||||
}
|
||||
|
||||
[SGPropertyDrawer(typeof(AbstractMaterialNode))]
|
||||
public class AbstractMaterialNodePropertyDrawer : IPropertyDrawer, IGetNodePropertyDrawerPropertyData
|
||||
{
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
Action m_setNodesAsDirtyCallback;
|
||||
Action m_updateNodeViewsCallback;
|
||||
|
||||
public void GetPropertyData(Action setNodesAsDirtyCallback, Action updateNodeViewsCallback)
|
||||
{
|
||||
m_setNodesAsDirtyCallback = setNodesAsDirtyCallback;
|
||||
m_updateNodeViewsCallback = updateNodeViewsCallback;
|
||||
}
|
||||
|
||||
internal virtual void AddCustomNodeProperties(VisualElement parentElement, AbstractMaterialNode node, Action setNodesAsDirtyCallback, Action updateNodeViewsCallback)
|
||||
{
|
||||
}
|
||||
|
||||
VisualElement CreateGUI(AbstractMaterialNode node, InspectableAttribute attribute, out VisualElement propertyVisualElement)
|
||||
{
|
||||
VisualElement nodeSettings = new VisualElement();
|
||||
var nameLabel = PropertyDrawerUtils.CreateLabel($"{node.name} Node", 0, FontStyle.Bold);
|
||||
nodeSettings.Add(nameLabel);
|
||||
if (node.sgVersion < node.latestVersion)
|
||||
{
|
||||
string deprecationText = null;
|
||||
string buttonText = null;
|
||||
string labelText = null;
|
||||
MessageType messageType = MessageType.Warning;
|
||||
if (node is IHasCustomDeprecationMessage nodeWithCustomDeprecationSettings)
|
||||
{
|
||||
nodeWithCustomDeprecationSettings.GetCustomDeprecationMessage(out deprecationText, out buttonText, out labelText, out messageType);
|
||||
}
|
||||
|
||||
var help = HelpBoxRow.TryGetDeprecatedHelpBoxRow($"{node.name} Node", () =>
|
||||
{
|
||||
m_setNodesAsDirtyCallback?.Invoke();
|
||||
node.owner.owner.RegisterCompleteObjectUndo($"Update {node.name} Node");
|
||||
node.ChangeVersion(node.latestVersion);
|
||||
inspectorUpdateDelegate?.Invoke();
|
||||
m_updateNodeViewsCallback?.Invoke();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
}, deprecationText, buttonText, labelText, messageType);
|
||||
|
||||
if (help != null)
|
||||
{
|
||||
nodeSettings.Insert(0, help);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyDrawerUtils.AddDefaultNodeProperties(nodeSettings, node, m_setNodesAsDirtyCallback, m_updateNodeViewsCallback);
|
||||
AddCustomNodeProperties(nodeSettings, node, m_setNodesAsDirtyCallback, m_updateNodeViewsCallback);
|
||||
|
||||
propertyVisualElement = null;
|
||||
|
||||
return nodeSettings;
|
||||
}
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
(AbstractMaterialNode)actualObject,
|
||||
attribute,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6436de6b57634b32a3c95d37c1b9b2c0
|
||||
timeCreated: 1588712275
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(bool))]
|
||||
class BoolPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(bool newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
bool fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyToggle,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var row = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
// Create and assign toggle as out variable here so that callers can also do additional work with enabling/disabling if needed
|
||||
propertyToggle = new Toggle();
|
||||
row.Add((Toggle)propertyToggle, (toggle) =>
|
||||
{
|
||||
toggle.value = fieldToDraw;
|
||||
});
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
var toggle = (Toggle)propertyToggle;
|
||||
toggle.OnToggleChanged(evt => valueChangedCallback(evt.newValue));
|
||||
}
|
||||
|
||||
row.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return row;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(
|
||||
PropertyInfo propertyInfo,
|
||||
object actualObject,
|
||||
InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newBoolValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] { newBoolValue }),
|
||||
(bool)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c6b03af40f8944e4a3c303c7875029d0
|
||||
timeCreated: 1588094617
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue