initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,144 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Serialization;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
sealed class ContextView : StackNode
|
||||
{
|
||||
ContextData m_ContextData;
|
||||
|
||||
// Currently we only need one Port per context
|
||||
// As the Contexts are hardcoded we know their directions
|
||||
Port m_Port;
|
||||
|
||||
//need this from graph view specifically for nodecreation
|
||||
EditorWindow m_EditorWindow;
|
||||
|
||||
// When dealing with more Contexts, `name` should be serialized in the ContextData
|
||||
// Right now we dont do this so we dont overcommit to serializing unknowns
|
||||
public ContextView(string name, ContextData contextData, EditorWindow editorWindow)
|
||||
{
|
||||
// Set data
|
||||
m_ContextData = contextData;
|
||||
m_EditorWindow = editorWindow;
|
||||
|
||||
// Header
|
||||
var headerLabel = new Label() { name = "headerLabel" };
|
||||
headerLabel.text = name;
|
||||
headerContainer.Add(headerLabel);
|
||||
}
|
||||
|
||||
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
// Disable the context menu for block nodes. This prevents a duplicate "disconnect all"
|
||||
// option from getting registered which grays out stack block node's option.
|
||||
if (evt.target is MaterialNodeView) return;
|
||||
|
||||
// If the user didn't click on a block node (i.e. the stack frame), include the "Add Block Node" item.
|
||||
InsertCreateNodeAction(evt, childCount, 0);
|
||||
evt.menu.InsertSeparator(null, 1);
|
||||
}
|
||||
|
||||
public ContextData contextData => m_ContextData;
|
||||
public Port port => m_Port;
|
||||
|
||||
// We need to use graphViewChange.movedElements to check whether a BlockNode has moved onto the GraphView
|
||||
// but Nodes return in movedElements when they are mid-drag because they are removed from the stack (placeholder)
|
||||
// StackNode has `dragEntered` but its protected so we need `isDragging`
|
||||
public bool isDragging => dragEntered;
|
||||
|
||||
public void AddPort(Direction direction)
|
||||
{
|
||||
var capacity = direction == Direction.Input ? Port.Capacity.Single : Port.Capacity.Multi;
|
||||
var container = direction == Direction.Input ? inputContainer : outputContainer;
|
||||
m_Port = Port.Create<Edge>(Orientation.Vertical, direction, capacity, null);
|
||||
m_Port.portName = "";
|
||||
|
||||
// Vertical ports have no representation in Model
|
||||
// Therefore we need to disable interaction
|
||||
m_Port.pickingMode = PickingMode.Ignore;
|
||||
|
||||
container.Add(m_Port);
|
||||
}
|
||||
|
||||
public void InsertBlock(MaterialNodeView nodeView)
|
||||
{
|
||||
if (!(nodeView.userData is BlockNode blockNode))
|
||||
return;
|
||||
|
||||
// If index is -1 the node is being added to the end of the Stack
|
||||
if (blockNode.index == -1)
|
||||
{
|
||||
AddElement(nodeView);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add or Insert based on index
|
||||
if (blockNode.index >= contentContainer.childCount)
|
||||
{
|
||||
AddElement(nodeView);
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertElement(blockNode.index, nodeView);
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertElements(int insertIndex, IEnumerable<GraphElement> elements)
|
||||
{
|
||||
var blockDatas = elements.Select(x => x.userData as BlockNode).ToArray();
|
||||
for (int i = 0; i < blockDatas.Length; i++)
|
||||
{
|
||||
contextData.blocks.Remove(blockDatas[i]);
|
||||
}
|
||||
|
||||
int count = elements.Count();
|
||||
var refs = new JsonRef<BlockNode>[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
refs[i] = blockDatas[i];
|
||||
}
|
||||
|
||||
contextData.blocks.InsertRange(insertIndex, refs);
|
||||
|
||||
var window = m_EditorWindow as MaterialGraphEditWindow;
|
||||
window?.graphEditorView?.graphView?.graph?.ValidateCustomBlockLimit();
|
||||
}
|
||||
|
||||
protected override bool AcceptsElement(GraphElement element, ref int proposedIndex, int maxIndex)
|
||||
{
|
||||
return element.userData is BlockNode blockNode && blockNode.descriptor != null &&
|
||||
blockNode.descriptor.shaderStage == contextData.shaderStage;
|
||||
}
|
||||
|
||||
protected override void OnSeparatorContextualMenuEvent(ContextualMenuPopulateEvent evt, int separatorIndex)
|
||||
{
|
||||
base.OnSeparatorContextualMenuEvent(evt, separatorIndex);
|
||||
InsertCreateNodeAction(evt, separatorIndex, 0);
|
||||
}
|
||||
|
||||
void InsertCreateNodeAction(ContextualMenuPopulateEvent evt, int separatorIndex, int itemIndex)
|
||||
{
|
||||
//we need to arbitrarily add the editor position values because node creation context
|
||||
//exptects a non local coordinate
|
||||
var mousePosition = evt.mousePosition + m_EditorWindow.position.position;
|
||||
var graphView = GetFirstAncestorOfType<MaterialGraphView>();
|
||||
|
||||
evt.menu.InsertAction(itemIndex, "Add Block Node", (e) =>
|
||||
{
|
||||
var context = new NodeCreationContext
|
||||
{
|
||||
screenMousePosition = mousePosition,
|
||||
target = this,
|
||||
index = separatorIndex,
|
||||
};
|
||||
graphView.nodeCreationRequest(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 86f8ce6f2b8e29c45a3d40f1c0cfd4d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using UnityEditor.UIElements;
|
||||
using System.Globalization;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class FloatField : DoubleField
|
||||
{
|
||||
protected override string ValueToString(double v)
|
||||
{
|
||||
return ((float)v).ToString(CultureInfo.InvariantCulture.NumberFormat);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f1ab7cad0fa94639990c801ecf80bff2
|
||||
timeCreated: 1512568655
|
|
@ -0,0 +1,54 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class GradientEdge : Edge
|
||||
{
|
||||
readonly CustomStyleProperty<Color> k_InputColorProperty = new CustomStyleProperty<Color>("--edge-input-color");
|
||||
readonly CustomStyleProperty<Color> k_OutputColorProperty = new CustomStyleProperty<Color>("--edge-output-color");
|
||||
|
||||
Color m_InputColor;
|
||||
Color m_OutputColor;
|
||||
|
||||
public Color inputColor
|
||||
{
|
||||
get { return m_InputColor; }
|
||||
}
|
||||
|
||||
public Color outputColor
|
||||
{
|
||||
get { return m_OutputColor; }
|
||||
}
|
||||
|
||||
public GradientEdge()
|
||||
{
|
||||
m_InputColor = defaultColor;
|
||||
m_OutputColor = defaultColor;
|
||||
|
||||
RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
|
||||
}
|
||||
|
||||
public void UpdateClasses(ConcreteSlotValueType outputType, ConcreteSlotValueType inputType)
|
||||
{
|
||||
ClearClassList();
|
||||
AddToClassList("edge");
|
||||
AddToClassList("from" + outputType);
|
||||
AddToClassList("to" + inputType);
|
||||
}
|
||||
|
||||
private void OnCustomStyleResolved(CustomStyleResolvedEvent e)
|
||||
{
|
||||
Color inputValue;
|
||||
Color outputValue;
|
||||
|
||||
ICustomStyle styles = e.customStyle;
|
||||
if (styles.TryGetValue(k_InputColorProperty, out inputValue))
|
||||
m_InputColor = inputValue;
|
||||
|
||||
if (styles.TryGetValue(k_OutputColorProperty, out outputValue))
|
||||
m_OutputColor = outputValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 161180898f1d444880080d666ab7be1d
|
||||
timeCreated: 1509019463
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 39a37c8d2156b41e182286e521fe51f4
|
||||
timeCreated: 1481279276
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,373 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Drawing.Interfaces;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Views
|
||||
{
|
||||
interface ISelectionProvider
|
||||
{
|
||||
List<ISelectable> GetSelection { get; }
|
||||
}
|
||||
|
||||
class GraphSubWindow : GraphElement, ISGResizable
|
||||
{
|
||||
ISGViewModel m_ViewModel;
|
||||
|
||||
ISGViewModel ViewModel
|
||||
{
|
||||
get => m_ViewModel;
|
||||
set => m_ViewModel = value;
|
||||
}
|
||||
|
||||
Dragger m_Dragger;
|
||||
|
||||
// This needs to be something that each subclass defines for itself at creation time
|
||||
// if they all use the same they'll be stacked on top of each other at SG window creation
|
||||
protected WindowDockingLayout windowDockingLayout { get; private set; } = new WindowDockingLayout
|
||||
{
|
||||
dockingTop = true,
|
||||
dockingLeft = false,
|
||||
verticalOffset = 8,
|
||||
horizontalOffset = 8,
|
||||
};
|
||||
|
||||
// Used to cache the window docking layout between resizing operations as it interferes with window resizing operations
|
||||
private IStyle cachedWindowDockingStyle;
|
||||
|
||||
protected VisualElement m_MainContainer;
|
||||
protected VisualElement m_Root;
|
||||
protected Label m_TitleLabel;
|
||||
protected Label m_SubTitleLabel;
|
||||
protected ScrollView m_ScrollView;
|
||||
protected VisualElement m_ContentContainer;
|
||||
protected VisualElement m_HeaderItem;
|
||||
protected VisualElement m_ParentView;
|
||||
|
||||
// These are used as default values for styling and layout purposes
|
||||
// They can be overriden if a child class wants to roll its own style and layout behavior
|
||||
public virtual string layoutKey => "UnityEditor.ShaderGraph.SubWindow";
|
||||
public virtual string styleName => "GraphSubWindow";
|
||||
public virtual string UxmlName => "GraphSubWindow";
|
||||
|
||||
// Each sub-window will override these if they need to
|
||||
public virtual string elementName => "";
|
||||
public virtual string windowTitle => "";
|
||||
|
||||
public VisualElement ParentView
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!isWindowed && m_ParentView == null)
|
||||
m_ParentView = GetFirstAncestorOfType<GraphView>();
|
||||
return m_ParentView;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!isWindowed)
|
||||
return;
|
||||
m_ParentView = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ISelectable> selection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ParentView is ISelectionProvider selectionProvider)
|
||||
return selectionProvider.GetSelection;
|
||||
|
||||
AssertHelpers.Fail("GraphSubWindow was unable to find a selection provider. Please check if parent view of: " + name + " implements ISelectionProvider::GetSelection");
|
||||
return new List<ISelectable>();
|
||||
}
|
||||
}
|
||||
|
||||
public override string title
|
||||
{
|
||||
get { return m_TitleLabel.text; }
|
||||
set { m_TitleLabel.text = value; }
|
||||
}
|
||||
|
||||
public string subTitle
|
||||
{
|
||||
get { return m_SubTitleLabel.text; }
|
||||
set { m_SubTitleLabel.text = value; }
|
||||
}
|
||||
|
||||
// Intended for future handling of docking to sides of the shader graph window
|
||||
bool m_IsWindowed;
|
||||
public bool isWindowed
|
||||
{
|
||||
get { return m_IsWindowed; }
|
||||
set
|
||||
{
|
||||
if (m_IsWindowed == value) return;
|
||||
|
||||
if (value)
|
||||
{
|
||||
capabilities &= ~Capabilities.Movable;
|
||||
AddToClassList("windowed");
|
||||
this.RemoveManipulator(m_Dragger);
|
||||
}
|
||||
else
|
||||
{
|
||||
capabilities |= Capabilities.Movable;
|
||||
RemoveFromClassList("windowed");
|
||||
this.AddManipulator(m_Dragger);
|
||||
}
|
||||
m_IsWindowed = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override VisualElement contentContainer => m_ContentContainer;
|
||||
|
||||
private bool m_IsResizable = false;
|
||||
|
||||
// Can be set by child classes as needed
|
||||
protected bool isWindowResizable
|
||||
{
|
||||
get => m_IsResizable;
|
||||
set
|
||||
{
|
||||
if (m_IsResizable != value)
|
||||
{
|
||||
m_IsResizable = value;
|
||||
HandleResizingBehavior(m_IsResizable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleResizingBehavior(bool isResizable)
|
||||
{
|
||||
if (isResizable)
|
||||
{
|
||||
var resizeElement = this.Q<ResizableElement>();
|
||||
resizeElement.BindOnResizeCallback(OnWindowResize);
|
||||
hierarchy.Add(resizeElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
var resizeElement = this.Q<ResizableElement>();
|
||||
resizeElement.SetResizeRules(ResizableElement.Resizer.None);
|
||||
hierarchy.Remove(resizeElement);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetResizingRules(ResizableElement.Resizer resizeDirections)
|
||||
{
|
||||
var resizeElement = this.Q<ResizableElement>();
|
||||
resizeElement.SetResizeRules(resizeDirections);
|
||||
}
|
||||
|
||||
private bool m_IsScrollable = false;
|
||||
|
||||
// Can be set by child classes as needed
|
||||
protected bool isWindowScrollable
|
||||
{
|
||||
get => m_IsScrollable;
|
||||
set
|
||||
{
|
||||
if (m_IsScrollable != value)
|
||||
{
|
||||
m_IsScrollable = value;
|
||||
HandleScrollingBehavior(m_IsScrollable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected float scrollableWidth
|
||||
{
|
||||
get { return m_ScrollView.contentContainer.layout.width - m_ScrollView.contentViewport.layout.width; }
|
||||
}
|
||||
|
||||
protected float scrollableHeight
|
||||
{
|
||||
get { return contentContainer.layout.height - m_ScrollView.contentViewport.layout.height; }
|
||||
}
|
||||
|
||||
void HandleScrollingBehavior(bool scrollable)
|
||||
{
|
||||
if (scrollable)
|
||||
{
|
||||
// Remove the categories container from the content item and add it to the scrollview
|
||||
m_ContentContainer.RemoveFromHierarchy();
|
||||
m_ScrollView.Add(m_ContentContainer);
|
||||
AddToClassList("scrollable");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove the categories container from the scrollview and add it to the content item
|
||||
m_ContentContainer.RemoveFromHierarchy();
|
||||
m_Root.Add(m_ContentContainer);
|
||||
|
||||
RemoveFromClassList("scrollable");
|
||||
}
|
||||
}
|
||||
|
||||
protected GraphSubWindow(ISGViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
m_ParentView = ViewModel.parentView;
|
||||
ParentView.Add(this);
|
||||
|
||||
var styleSheet = Resources.Load<StyleSheet>($"Styles/{styleName}");
|
||||
// Setup VisualElement from Stylesheet and UXML file
|
||||
styleSheets.Add(styleSheet);
|
||||
var uxml = Resources.Load<VisualTreeAsset>($"UXML/{UxmlName}");
|
||||
m_MainContainer = uxml.Instantiate();
|
||||
m_MainContainer.AddToClassList("mainContainer");
|
||||
|
||||
m_Root = m_MainContainer.Q("content");
|
||||
m_HeaderItem = m_MainContainer.Q("header");
|
||||
m_HeaderItem.AddToClassList("subWindowHeader");
|
||||
m_ScrollView = m_MainContainer.Q<ScrollView>("scrollView");
|
||||
m_TitleLabel = m_MainContainer.Q<Label>(name: "titleLabel");
|
||||
m_SubTitleLabel = m_MainContainer.Q<Label>(name: "subTitleLabel");
|
||||
m_ContentContainer = m_MainContainer.Q(name: "contentContainer");
|
||||
|
||||
hierarchy.Add(m_MainContainer);
|
||||
|
||||
capabilities |= Capabilities.Movable | Capabilities.Resizable;
|
||||
style.overflow = Overflow.Hidden;
|
||||
focusable = false;
|
||||
|
||||
name = elementName;
|
||||
title = windowTitle;
|
||||
|
||||
ClearClassList();
|
||||
AddToClassList(name);
|
||||
|
||||
BuildManipulators();
|
||||
|
||||
/* Event interception to prevent GraphView manipulators from being triggered */
|
||||
//RegisterCallback<DragUpdatedEvent>(e =>
|
||||
//{
|
||||
// e.StopPropagation();
|
||||
//});
|
||||
|
||||
// prevent Zoomer manipulator
|
||||
RegisterCallback<WheelEvent>(e =>
|
||||
{
|
||||
e.StopPropagation();
|
||||
});
|
||||
|
||||
//RegisterCallback<MouseDownEvent>(e =>
|
||||
//{
|
||||
// // prevent ContentDragger manipulator
|
||||
// e.StopPropagation();
|
||||
//});
|
||||
}
|
||||
|
||||
public void ShowWindow()
|
||||
{
|
||||
this.style.visibility = Visibility.Visible;
|
||||
this.m_ScrollView.style.display = DisplayStyle.Flex;
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
public void HideWindow()
|
||||
{
|
||||
this.style.visibility = Visibility.Hidden;
|
||||
this.m_ScrollView.style.display = DisplayStyle.None;
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void BuildManipulators()
|
||||
{
|
||||
m_Dragger = new Dragger { clampToParentEdges = true };
|
||||
RegisterCallback<MouseUpEvent>(OnMoveEnd);
|
||||
this.AddManipulator(m_Dragger);
|
||||
}
|
||||
|
||||
#region Layout
|
||||
public void ClampToParentLayout(Rect parentLayout)
|
||||
{
|
||||
windowDockingLayout.CalculateDockingCornerAndOffset(layout, parentLayout);
|
||||
windowDockingLayout.ClampToParentWindow();
|
||||
|
||||
// If the parent shader graph window is being resized smaller than this window on either axis
|
||||
if (parentLayout.width < this.layout.width || parentLayout.height < this.layout.height)
|
||||
{
|
||||
// Don't adjust the sub window in this case as it causes flickering errors and looks broken
|
||||
}
|
||||
else
|
||||
{
|
||||
windowDockingLayout.ApplyPosition(this);
|
||||
}
|
||||
|
||||
SerializeLayout();
|
||||
}
|
||||
|
||||
public void OnStartResize()
|
||||
{
|
||||
cachedWindowDockingStyle = this.style;
|
||||
}
|
||||
|
||||
public void OnResized()
|
||||
{
|
||||
if (cachedWindowDockingStyle != null)
|
||||
{
|
||||
this.style.left = cachedWindowDockingStyle.left;
|
||||
this.style.right = cachedWindowDockingStyle.right;
|
||||
this.style.bottom = cachedWindowDockingStyle.bottom;
|
||||
this.style.top = cachedWindowDockingStyle.top;
|
||||
}
|
||||
windowDockingLayout.size = layout.size;
|
||||
SerializeLayout();
|
||||
}
|
||||
|
||||
public void DeserializeLayout()
|
||||
{
|
||||
var serializedLayout = EditorUserSettings.GetConfigValue(layoutKey);
|
||||
if (!string.IsNullOrEmpty(serializedLayout))
|
||||
windowDockingLayout = JsonUtility.FromJson<WindowDockingLayout>(serializedLayout);
|
||||
else
|
||||
{
|
||||
// The window size needs to come from the stylesheet or UXML as opposed to being defined in code
|
||||
windowDockingLayout.size = layout.size;
|
||||
}
|
||||
|
||||
windowDockingLayout.ApplySize(this);
|
||||
windowDockingLayout.ApplyPosition(this);
|
||||
}
|
||||
|
||||
protected void AddStyleSheetFromPath(string styleSheetPath)
|
||||
{
|
||||
StyleSheet sheetAsset = Resources.Load<StyleSheet>(styleSheetPath); ;
|
||||
|
||||
if (sheetAsset == null)
|
||||
{
|
||||
Debug.LogWarning(string.Format("Style sheet not found for path \"{0}\"", styleSheetPath));
|
||||
return;
|
||||
}
|
||||
styleSheets.Add(sheetAsset);
|
||||
}
|
||||
|
||||
void SerializeLayout()
|
||||
{
|
||||
windowDockingLayout.size = layout.size;
|
||||
var serializedLayout = JsonUtility.ToJson(windowDockingLayout);
|
||||
EditorUserSettings.SetConfigValue(layoutKey, serializedLayout);
|
||||
}
|
||||
|
||||
void OnMoveEnd(MouseUpEvent upEvent)
|
||||
{
|
||||
windowDockingLayout.CalculateDockingCornerAndOffset(layout, ParentView.layout);
|
||||
windowDockingLayout.ClampToParentWindow();
|
||||
|
||||
SerializeLayout();
|
||||
}
|
||||
|
||||
public bool CanResizePastParentBounds()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnWindowResize(MouseUpEvent upEvent)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 275dd8c834214250ac83d2a4eeab3660
|
||||
timeCreated: 1583780366
|
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
// Similar in function to the old EditorGUILayout.HelpBox
|
||||
class HelpBoxRow : VisualElement
|
||||
{
|
||||
VisualElement m_ContentContainer;
|
||||
VisualElement m_LabelContainer;
|
||||
|
||||
public override VisualElement contentContainer
|
||||
{
|
||||
get { return m_ContentContainer; }
|
||||
}
|
||||
|
||||
public HelpBoxRow(MessageType type)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/HelpBoxRow"));
|
||||
VisualElement container = new VisualElement { name = "container" };
|
||||
m_ContentContainer = new VisualElement { name = "content" };
|
||||
m_LabelContainer = new VisualElement { name = "label" };
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case MessageType.None:
|
||||
container.AddToClassList("help-box-row-style-info");
|
||||
break;
|
||||
case MessageType.Info:
|
||||
container.AddToClassList("help-box-row-style-info");
|
||||
break;
|
||||
case MessageType.Warning:
|
||||
container.AddToClassList("help-box-row-style-warning");
|
||||
break;
|
||||
case MessageType.Error:
|
||||
container.AddToClassList("help-box-row-style-error");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
container.Add(m_LabelContainer);
|
||||
container.Add(m_ContentContainer);
|
||||
|
||||
hierarchy.Add(container);
|
||||
}
|
||||
|
||||
public static VisualElement CreateVariantLimitHelpBox(int currentVariantCount, int maxVariantCount)
|
||||
{
|
||||
var messageType = MessageType.Error;
|
||||
HelpBoxRow help = new HelpBoxRow(messageType);
|
||||
var label = new Label("Variant limit exceeded: Hover for more info")
|
||||
{
|
||||
tooltip = ShaderKeyword.kVariantLimitWarning,
|
||||
name = "message-" + (messageType == MessageType.Warning ? "warn" : "info")
|
||||
};
|
||||
help.Add(label);
|
||||
return help;
|
||||
}
|
||||
|
||||
public static VisualElement TryGetDeprecatedHelpBoxRow(string deprecatedTypeName, Action upgradeAction, string deprecationText = null, string buttonText = null, string labelText = null, MessageType messageType = MessageType.Warning)
|
||||
{
|
||||
if (deprecationText == null)
|
||||
{
|
||||
deprecationText = $"The {deprecatedTypeName} has new updates. This version maintains the old behavior. " +
|
||||
$"If you update a {deprecatedTypeName}, you can use Undo to change it back. See the {deprecatedTypeName} " +
|
||||
$"documentation for more information.";
|
||||
}
|
||||
if (buttonText == null)
|
||||
{
|
||||
buttonText = "Update";
|
||||
}
|
||||
if (labelText == null)
|
||||
{
|
||||
labelText = "DEPRECATED: Hover for info";
|
||||
}
|
||||
|
||||
Button upgradeButton = new Button(upgradeAction) { text = buttonText, tooltip = deprecationText };
|
||||
if (!ShaderGraphPreferences.allowDeprecatedBehaviors || messageType == MessageType.Info)
|
||||
{
|
||||
HelpBoxRow help = new HelpBoxRow(messageType);
|
||||
var label = new Label(labelText)
|
||||
{
|
||||
tooltip = deprecationText,
|
||||
name = "message-" + (messageType == MessageType.Warning ? "warn" : "info")
|
||||
};
|
||||
help.Add(label);
|
||||
help.contentContainer.Add(upgradeButton);
|
||||
return help;
|
||||
}
|
||||
else
|
||||
{
|
||||
return upgradeButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2193a2e210bbc714d9db73d1ba2c6587
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEditor.Graphing;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
[Serializable]
|
||||
public enum HlslSourceType { File, String };
|
||||
|
||||
internal class HlslFunctionView : VisualElement
|
||||
{
|
||||
private EnumField m_Type;
|
||||
private TextField m_FunctionName;
|
||||
private ObjectField m_FunctionSource;
|
||||
private TextField m_FunctionBody;
|
||||
|
||||
internal HlslFunctionView(CustomFunctionNode node)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/HlslFunctionView"));
|
||||
Draw(node);
|
||||
}
|
||||
|
||||
private void Draw(CustomFunctionNode node)
|
||||
{
|
||||
var currentControls = this.Children().ToArray();
|
||||
for (int i = 0; i < currentControls.Length; i++)
|
||||
currentControls[i].RemoveFromHierarchy();
|
||||
|
||||
m_Type = new EnumField(node.sourceType);
|
||||
m_Type.RegisterValueChangedCallback(s =>
|
||||
{
|
||||
if ((HlslSourceType)s.newValue != node.sourceType)
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Function Type");
|
||||
node.sourceType = (HlslSourceType)s.newValue;
|
||||
Draw(node);
|
||||
node.ValidateNode();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
});
|
||||
|
||||
m_FunctionName = new TextField { value = node.functionName, multiline = false };
|
||||
m_FunctionName.RegisterCallback<FocusInEvent>(s =>
|
||||
{
|
||||
if (m_FunctionName.value == CustomFunctionNode.defaultFunctionName)
|
||||
m_FunctionName.value = "";
|
||||
});
|
||||
m_FunctionName.RegisterCallback<FocusOutEvent>(s =>
|
||||
{
|
||||
if (m_FunctionName.value == "")
|
||||
m_FunctionName.value = CustomFunctionNode.defaultFunctionName;
|
||||
else
|
||||
m_FunctionName.value = NodeUtils.ConvertToValidHLSLIdentifier(m_FunctionName.value);
|
||||
|
||||
if (m_FunctionName.value != node.functionName)
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Function Name");
|
||||
node.functionName = m_FunctionName.value;
|
||||
node.ValidateNode();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
});
|
||||
|
||||
string path = AssetDatabase.GUIDToAssetPath(node.functionSource);
|
||||
m_FunctionSource = new ObjectField() { value = AssetDatabase.LoadAssetAtPath<ShaderInclude>(path), objectType = typeof(ShaderInclude) };
|
||||
m_FunctionSource.RegisterValueChangedCallback(s =>
|
||||
{
|
||||
long localId;
|
||||
string guidString = string.Empty;
|
||||
if (s.newValue != null)
|
||||
{
|
||||
AssetDatabase.TryGetGUIDAndLocalFileIdentifier((ShaderInclude)s.newValue, out guidString, out localId);
|
||||
}
|
||||
|
||||
if (guidString != node.functionSource)
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Function Source");
|
||||
node.functionSource = guidString;
|
||||
node.ValidateNode();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
});
|
||||
|
||||
m_FunctionBody = new TextField { value = node.functionBody, multiline = true };
|
||||
m_FunctionBody.RegisterCallback<FocusInEvent>(s =>
|
||||
{
|
||||
if (m_FunctionBody.value == CustomFunctionNode.defaultFunctionBody)
|
||||
m_FunctionBody.value = "";
|
||||
});
|
||||
m_FunctionBody.RegisterCallback<FocusOutEvent>(s =>
|
||||
{
|
||||
if (m_FunctionBody.value == "")
|
||||
m_FunctionBody.value = CustomFunctionNode.defaultFunctionBody;
|
||||
|
||||
if (m_FunctionBody.value != node.functionBody)
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Function Body");
|
||||
node.functionBody = m_FunctionBody.value;
|
||||
node.ValidateNode();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
});
|
||||
|
||||
VisualElement typeRow = new VisualElement() { name = "Row" };
|
||||
{
|
||||
typeRow.Add(new Label("Type"));
|
||||
typeRow.Add(m_Type);
|
||||
}
|
||||
Add(typeRow);
|
||||
VisualElement nameRow = new VisualElement() { name = "Row" };
|
||||
{
|
||||
nameRow.Add(new Label("Name"));
|
||||
nameRow.Add(m_FunctionName);
|
||||
}
|
||||
Add(nameRow);
|
||||
switch (node.sourceType)
|
||||
{
|
||||
case HlslSourceType.File:
|
||||
VisualElement sourceRow = new VisualElement() { name = "Row" };
|
||||
{
|
||||
sourceRow.Add(new Label("Source"));
|
||||
sourceRow.Add(m_FunctionSource);
|
||||
}
|
||||
Add(sourceRow);
|
||||
break;
|
||||
case HlslSourceType.String:
|
||||
VisualElement bodyRow = new VisualElement() { name = "Row" };
|
||||
{
|
||||
bodyRow.Add(new Label("Body"));
|
||||
bodyRow.style.height = 200;
|
||||
bodyRow.Add(m_FunctionBody);
|
||||
}
|
||||
Add(bodyRow);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6334d158427214af2861c42a0b51f9cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
interface IShaderNodeView : IDisposable
|
||||
{
|
||||
Node gvNode { get; }
|
||||
AbstractMaterialNode node { get; }
|
||||
VisualElement colorElement { get; }
|
||||
void SetColor(Color newColor);
|
||||
void ResetColor();
|
||||
void UpdatePortInputTypes();
|
||||
void UpdateDropdownEntries();
|
||||
void OnModified(ModificationScope scope);
|
||||
void AttachMessage(string errString, ShaderCompilerMessageSeverity severity);
|
||||
void ClearMessage();
|
||||
// Searches the ports on this node for one that matches the given slot.
|
||||
// Returns true if found, false if not.
|
||||
bool FindPort(SlotReference slot, out ShaderPort port);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b03bcd5e49f90634390c43e32abae2f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
/*
|
||||
Field that allows entering a valid HLSL identifier.
|
||||
(variable name, function name, ...) this means
|
||||
no spaces, no funny characters, never starts with a number, ...
|
||||
*/
|
||||
public class IdentifierField : UIElements.TextValueField<string>
|
||||
{
|
||||
IdentifierInput tsInput => (IdentifierInput)textInputBase;
|
||||
|
||||
public new class UxmlFactory : UxmlFactory<IdentifierField, UxmlTraits> { }
|
||||
public new class UxmlTraits : UIElements.TextValueFieldTraits<string, UxmlStringAttributeDescription> { }
|
||||
|
||||
protected override string ValueToString(string v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
protected override string StringToValue(string str)
|
||||
{
|
||||
// Make sure this is a valid hlsl identifier. Allowed characters already ensures the characters are valid
|
||||
// but identifiers can't start with a number so fix this here.
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
return "_0";
|
||||
}
|
||||
else if (Char.IsDigit(str[0]))
|
||||
{
|
||||
return "_" + str;
|
||||
}
|
||||
else
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public new static readonly string ussClassName = "unity-identifierfield-field";
|
||||
public new static readonly string labelUssClassName = ussClassName + "__label";
|
||||
public new static readonly string inputUssClassName = ussClassName + "__input";
|
||||
|
||||
public IdentifierField() : this((string)null) { }
|
||||
|
||||
public IdentifierField(string label) : base(label, -1, new IdentifierInput())
|
||||
{
|
||||
AddToClassList(ussClassName);
|
||||
labelElement.AddToClassList(labelUssClassName);
|
||||
tsInput.AddToClassList(inputUssClassName);
|
||||
}
|
||||
|
||||
public override void ApplyInputDeviceDelta(Vector3 delta, UIElements.DeltaSpeed speed, string startValue)
|
||||
{
|
||||
tsInput.ApplyInputDeviceDelta(delta, speed, startValue);
|
||||
}
|
||||
|
||||
class IdentifierInput : TextValueInput
|
||||
{
|
||||
IdentifierField parentField => (IdentifierField)parent;
|
||||
|
||||
internal IdentifierInput()
|
||||
{
|
||||
formatString = null;
|
||||
}
|
||||
|
||||
protected override string allowedCharacters
|
||||
{
|
||||
get { return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; }
|
||||
}
|
||||
|
||||
public override void ApplyInputDeviceDelta(Vector3 delta, UIElements.DeltaSpeed speed, string startValue)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string ValueToString(string v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
protected override string StringToValue(string str)
|
||||
{
|
||||
// Make sure this is a valid hlsl identifier. Allowed characters already ensures the characters are valid
|
||||
// but identifiers can't start with a number so fix this here.
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
return "_0";
|
||||
}
|
||||
else if (Char.IsDigit(str[0]))
|
||||
{
|
||||
return "_" + str;
|
||||
}
|
||||
else
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 991f68f49473a9d45a1586045c1e649c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e0bbd4215e018eb4bbdb28bfaa7eabdd
|
||||
timeCreated: 1476707367
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,807 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph.Drawing.Controls;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
using Node = UnityEditor.Experimental.GraphView.Node;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
sealed class MaterialNodeView : Node, IShaderNodeView, IInspectable
|
||||
{
|
||||
PreviewRenderData m_PreviewRenderData;
|
||||
Image m_PreviewImage;
|
||||
// Remove this after updated to the correct API call has landed in trunk. ------------
|
||||
VisualElement m_TitleContainer;
|
||||
new VisualElement m_ButtonContainer;
|
||||
|
||||
VisualElement m_PreviewContainer;
|
||||
VisualElement m_PreviewFiller;
|
||||
VisualElement m_ControlItems;
|
||||
VisualElement m_ControlsDivider;
|
||||
VisualElement m_DropdownItems;
|
||||
VisualElement m_DropdownsDivider;
|
||||
IEdgeConnectorListener m_ConnectorListener;
|
||||
|
||||
MaterialGraphView m_GraphView;
|
||||
|
||||
public string inspectorTitle => $"{node.name} (Node)";
|
||||
public void Initialize(AbstractMaterialNode inNode, PreviewManager previewManager, IEdgeConnectorListener connectorListener, MaterialGraphView graphView)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/MaterialNodeView"));
|
||||
styleSheets.Add(Resources.Load<StyleSheet>($"Styles/ColorMode"));
|
||||
AddToClassList("MaterialNode");
|
||||
|
||||
if (inNode == null)
|
||||
return;
|
||||
|
||||
var contents = this.Q("contents");
|
||||
|
||||
m_GraphView = graphView;
|
||||
mainContainer.style.overflow = StyleKeyword.None; // Override explicit style set in base class
|
||||
m_ConnectorListener = connectorListener;
|
||||
node = inNode;
|
||||
viewDataKey = node.objectId;
|
||||
UpdateTitle();
|
||||
|
||||
// Add disabled overlay
|
||||
Add(new VisualElement() { name = "disabledOverlay", pickingMode = PickingMode.Ignore });
|
||||
|
||||
// Add controls container
|
||||
var controlsContainer = new VisualElement { name = "controls" };
|
||||
{
|
||||
m_ControlsDivider = new VisualElement { name = "divider" };
|
||||
m_ControlsDivider.AddToClassList("horizontal");
|
||||
controlsContainer.Add(m_ControlsDivider);
|
||||
m_ControlItems = new VisualElement { name = "items" };
|
||||
controlsContainer.Add(m_ControlItems);
|
||||
|
||||
// Instantiate control views from node
|
||||
foreach (var propertyInfo in node.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
foreach (IControlAttribute attribute in propertyInfo.GetCustomAttributes(typeof(IControlAttribute), false))
|
||||
m_ControlItems.Add(attribute.InstantiateControl(node, propertyInfo));
|
||||
}
|
||||
if (m_ControlItems.childCount > 0)
|
||||
contents.Add(controlsContainer);
|
||||
|
||||
// Add dropdowns container
|
||||
var dropdownContainer = new VisualElement { name = "dropdowns" };
|
||||
{
|
||||
m_DropdownsDivider = new VisualElement { name = "divider" };
|
||||
m_DropdownsDivider.AddToClassList("horizontal");
|
||||
dropdownContainer.Add(m_DropdownsDivider);
|
||||
m_DropdownItems = new VisualElement { name = "items" };
|
||||
dropdownContainer.Add(m_DropdownItems);
|
||||
UpdateDropdownEntries();
|
||||
}
|
||||
if (m_DropdownItems.childCount > 0)
|
||||
contents.Add(dropdownContainer);
|
||||
|
||||
if (node.hasPreview)
|
||||
{
|
||||
// Add actual preview which floats on top of the node
|
||||
m_PreviewContainer = new VisualElement
|
||||
{
|
||||
name = "previewContainer",
|
||||
style = { overflow = Overflow.Hidden },
|
||||
pickingMode = PickingMode.Ignore
|
||||
};
|
||||
m_PreviewImage = new Image
|
||||
{
|
||||
name = "preview",
|
||||
pickingMode = PickingMode.Ignore,
|
||||
image = Texture2D.whiteTexture,
|
||||
};
|
||||
{
|
||||
// Add preview collapse button on top of preview
|
||||
var collapsePreviewButton = new VisualElement { name = "collapse" };
|
||||
collapsePreviewButton.Add(new VisualElement { name = "icon" });
|
||||
collapsePreviewButton.AddManipulator(new Clickable(() =>
|
||||
{
|
||||
SetPreviewExpandedStateOnSelection(false);
|
||||
}));
|
||||
m_PreviewImage.Add(collapsePreviewButton);
|
||||
}
|
||||
m_PreviewContainer.Add(m_PreviewImage);
|
||||
|
||||
// Hook up preview image to preview manager
|
||||
m_PreviewRenderData = previewManager.GetPreviewRenderData(inNode);
|
||||
m_PreviewRenderData.onPreviewChanged += UpdatePreviewTexture;
|
||||
UpdatePreviewTexture();
|
||||
|
||||
// Add fake preview which pads out the node to provide space for the floating preview
|
||||
m_PreviewFiller = new VisualElement { name = "previewFiller" };
|
||||
m_PreviewFiller.AddToClassList("expanded");
|
||||
{
|
||||
var previewDivider = new VisualElement { name = "divider" };
|
||||
previewDivider.AddToClassList("horizontal");
|
||||
m_PreviewFiller.Add(previewDivider);
|
||||
|
||||
var expandPreviewButton = new VisualElement { name = "expand" };
|
||||
expandPreviewButton.Add(new VisualElement { name = "icon" });
|
||||
expandPreviewButton.AddManipulator(new Clickable(() =>
|
||||
{
|
||||
SetPreviewExpandedStateOnSelection(true);
|
||||
}));
|
||||
m_PreviewFiller.Add(expandPreviewButton);
|
||||
}
|
||||
contents.Add(m_PreviewFiller);
|
||||
|
||||
UpdatePreviewExpandedState(node.previewExpanded);
|
||||
}
|
||||
|
||||
base.expanded = node.drawState.expanded;
|
||||
AddSlots(node.GetSlots<MaterialSlot>());
|
||||
|
||||
if (node is SubGraphNode)
|
||||
{
|
||||
RegisterCallback<MouseDownEvent>(OnSubGraphDoubleClick);
|
||||
}
|
||||
|
||||
m_TitleContainer = this.Q("title");
|
||||
|
||||
if (node is BlockNode blockData)
|
||||
{
|
||||
AddToClassList("blockData");
|
||||
m_TitleContainer.RemoveFromHierarchy();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0));
|
||||
}
|
||||
|
||||
// Update active state
|
||||
SetActive(node.isActive);
|
||||
|
||||
// Register OnMouseHover callbacks for node highlighting
|
||||
RegisterCallback<MouseEnterEvent>(OnMouseHover);
|
||||
RegisterCallback<MouseLeaveEvent>(OnMouseHover);
|
||||
|
||||
ShaderGraphPreferences.onAllowDeprecatedChanged += UpdateTitle;
|
||||
}
|
||||
|
||||
public bool FindPort(SlotReference slotRef, out ShaderPort port)
|
||||
{
|
||||
port = inputContainer.Query<ShaderPort>().ToList()
|
||||
.Concat(outputContainer.Query<ShaderPort>().ToList())
|
||||
.First(p => p.slot.slotReference.Equals(slotRef));
|
||||
|
||||
return port != null;
|
||||
}
|
||||
|
||||
public void AttachMessage(string errString, ShaderCompilerMessageSeverity severity)
|
||||
{
|
||||
ClearMessage();
|
||||
IconBadge badge;
|
||||
if (severity == ShaderCompilerMessageSeverity.Error)
|
||||
{
|
||||
badge = IconBadge.CreateError(errString);
|
||||
}
|
||||
else
|
||||
{
|
||||
badge = IconBadge.CreateComment(errString);
|
||||
}
|
||||
|
||||
Add(badge);
|
||||
|
||||
if (node is BlockNode)
|
||||
{
|
||||
FindPort(node.GetSlotReference(0), out var port);
|
||||
badge.AttachTo(port.parent, SpriteAlignment.RightCenter);
|
||||
}
|
||||
else
|
||||
{
|
||||
badge.AttachTo(m_TitleContainer, SpriteAlignment.RightCenter);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActive(bool state)
|
||||
{
|
||||
// Setup
|
||||
var disabledString = "disabled";
|
||||
var portDisabledString = "inactive";
|
||||
|
||||
|
||||
if (!state)
|
||||
{
|
||||
// Add elements to disabled class list
|
||||
AddToClassList(disabledString);
|
||||
|
||||
var inputPorts = inputContainer.Query<ShaderPort>().ToList();
|
||||
foreach (var port in inputPorts)
|
||||
{
|
||||
port.AddToClassList(portDisabledString);
|
||||
}
|
||||
var outputPorts = outputContainer.Query<ShaderPort>().ToList();
|
||||
foreach (var port in outputPorts)
|
||||
{
|
||||
port.AddToClassList(portDisabledString);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove elements from disabled class list
|
||||
RemoveFromClassList(disabledString);
|
||||
|
||||
var inputPorts = inputContainer.Query<ShaderPort>().ToList();
|
||||
foreach (var port in inputPorts)
|
||||
{
|
||||
port.RemoveFromClassList(portDisabledString);
|
||||
}
|
||||
var outputPorts = outputContainer.Query<ShaderPort>().ToList();
|
||||
foreach (var port in outputPorts)
|
||||
{
|
||||
port.RemoveFromClassList(portDisabledString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearMessage()
|
||||
{
|
||||
var badge = this.Q<IconBadge>();
|
||||
badge?.Detach();
|
||||
badge?.RemoveFromHierarchy();
|
||||
}
|
||||
|
||||
public void UpdateDropdownEntries()
|
||||
{
|
||||
if (node is SubGraphNode subGraphNode && subGraphNode.asset != null)
|
||||
{
|
||||
m_DropdownItems.Clear();
|
||||
var dropdowns = subGraphNode.asset.dropdowns;
|
||||
foreach (var dropdown in dropdowns)
|
||||
{
|
||||
if (dropdown.isExposed)
|
||||
{
|
||||
var name = subGraphNode.GetDropdownEntryName(dropdown.referenceName);
|
||||
if (!dropdown.ContainsEntry(name))
|
||||
{
|
||||
name = dropdown.entryName;
|
||||
subGraphNode.SetDropdownEntryName(dropdown.referenceName, name);
|
||||
}
|
||||
|
||||
var field = new PopupField<string>(dropdown.entries.Select(x => x.displayName).ToList(), name);
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
subGraphNode.owner.owner.RegisterCompleteObjectUndo("Change Dropdown Value");
|
||||
subGraphNode.SetDropdownEntryName(dropdown.referenceName, field.value);
|
||||
subGraphNode.Dirty(ModificationScope.Topological);
|
||||
});
|
||||
|
||||
m_DropdownItems.Add(new PropertyRow(new Label(dropdown.displayName)), (row) =>
|
||||
{
|
||||
row.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
row.Add(field);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public VisualElement colorElement
|
||||
{
|
||||
get { return this; }
|
||||
}
|
||||
|
||||
static readonly StyleColor noColor = new StyleColor(StyleKeyword.Null);
|
||||
public void SetColor(Color color)
|
||||
{
|
||||
m_TitleContainer.style.borderBottomColor = color;
|
||||
}
|
||||
|
||||
public void ResetColor()
|
||||
{
|
||||
m_TitleContainer.style.borderBottomColor = noColor;
|
||||
}
|
||||
|
||||
public Color GetColor()
|
||||
{
|
||||
return m_TitleContainer.resolvedStyle.borderBottomColor;
|
||||
}
|
||||
|
||||
void OnSubGraphDoubleClick(MouseDownEvent evt)
|
||||
{
|
||||
if (evt.clickCount == 2 && evt.button == 0)
|
||||
{
|
||||
SubGraphNode subgraphNode = node as SubGraphNode;
|
||||
|
||||
var path = AssetDatabase.GUIDToAssetPath(subgraphNode.subGraphGuid);
|
||||
ShaderGraphImporterEditor.ShowGraphEditWindow(path);
|
||||
}
|
||||
}
|
||||
|
||||
public Node gvNode => this;
|
||||
|
||||
[Inspectable("Node", null)]
|
||||
public AbstractMaterialNode node { get; private set; }
|
||||
|
||||
public override bool expanded
|
||||
{
|
||||
get => base.expanded;
|
||||
set
|
||||
{
|
||||
if (base.expanded == value)
|
||||
return;
|
||||
|
||||
base.expanded = value;
|
||||
|
||||
if (node.drawState.expanded != value)
|
||||
{
|
||||
var ds = node.drawState;
|
||||
ds.expanded = value;
|
||||
node.drawState = ds;
|
||||
}
|
||||
|
||||
foreach (var inputPort in inputContainer.Query<ShaderPort>().ToList())
|
||||
{
|
||||
inputPort.parent.style.visibility = inputPort.style.visibility;
|
||||
}
|
||||
|
||||
RefreshExpandedState(); // Necessary b/c we can't override enough Node.cs functions to update only what's needed
|
||||
}
|
||||
}
|
||||
|
||||
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
if (evt.target is Node)
|
||||
{
|
||||
var canViewShader = node.hasPreview || node is SubGraphOutputNode;
|
||||
evt.menu.AppendAction("Copy Shader", CopyToClipboard,
|
||||
_ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
|
||||
GenerationMode.ForReals);
|
||||
evt.menu.AppendAction("Show Generated Code", ShowGeneratedCode,
|
||||
_ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
|
||||
GenerationMode.ForReals);
|
||||
|
||||
if (Unsupported.IsDeveloperMode())
|
||||
{
|
||||
evt.menu.AppendAction("Show Preview Code", ShowGeneratedCode,
|
||||
_ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
|
||||
GenerationMode.Preview);
|
||||
}
|
||||
}
|
||||
|
||||
base.BuildContextualMenu(evt);
|
||||
}
|
||||
|
||||
void CopyToClipboard(DropdownMenuAction action)
|
||||
{
|
||||
GUIUtility.systemCopyBuffer = ConvertToShader((GenerationMode)action.userData);
|
||||
}
|
||||
|
||||
public string SanitizeName(string name)
|
||||
{
|
||||
return new string(name.Where(c => !Char.IsWhiteSpace(c)).ToArray());
|
||||
}
|
||||
|
||||
public void ShowGeneratedCode(DropdownMenuAction action)
|
||||
{
|
||||
string name = GetFirstAncestorOfType<GraphEditorView>().assetName;
|
||||
var mode = (GenerationMode)action.userData;
|
||||
|
||||
string path = String.Format("Temp/GeneratedFromGraph-{0}-{1}-{2}{3}.shader", SanitizeName(name),
|
||||
SanitizeName(node.name), node.objectId, mode == GenerationMode.Preview ? "-Preview" : "");
|
||||
if (GraphUtil.WriteToFile(path, ConvertToShader(mode)))
|
||||
GraphUtil.OpenFile(path);
|
||||
}
|
||||
|
||||
string ConvertToShader(GenerationMode mode)
|
||||
{
|
||||
var generator = new Generator(node.owner, node, mode, node.name, null);
|
||||
return generator.generatedShader;
|
||||
}
|
||||
|
||||
void SetNodesAsDirty()
|
||||
{
|
||||
var editorView = GetFirstAncestorOfType<GraphEditorView>();
|
||||
var nodeList = m_GraphView.Query<MaterialNodeView>().ToList();
|
||||
editorView.colorManager.SetNodesDirty(nodeList);
|
||||
}
|
||||
|
||||
void UpdateNodeViews()
|
||||
{
|
||||
var editorView = GetFirstAncestorOfType<GraphEditorView>();
|
||||
var nodeList = m_GraphView.Query<MaterialNodeView>().ToList();
|
||||
editorView.colorManager.UpdateNodeViews(nodeList);
|
||||
}
|
||||
|
||||
public object GetObjectToInspect()
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
public void SupplyDataToPropertyDrawer(IPropertyDrawer propertyDrawer, Action inspectorUpdateDelegate)
|
||||
{
|
||||
if (propertyDrawer is IGetNodePropertyDrawerPropertyData nodePropertyDrawer)
|
||||
{
|
||||
nodePropertyDrawer.GetPropertyData(SetNodesAsDirty, UpdateNodeViews);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSelfSelected()
|
||||
{
|
||||
m_GraphView.ClearSelection();
|
||||
m_GraphView.AddToSelection(this);
|
||||
}
|
||||
|
||||
protected override void ToggleCollapse()
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo(!expanded ? "Expand Nodes" : "Collapse Nodes");
|
||||
expanded = !expanded;
|
||||
|
||||
// If selected, expand/collapse the other applicable nodes that are also selected
|
||||
if (selected)
|
||||
{
|
||||
m_GraphView.SetNodeExpandedForSelectedNodes(expanded, false);
|
||||
}
|
||||
}
|
||||
|
||||
void SetPreviewExpandedStateOnSelection(bool state)
|
||||
{
|
||||
// If selected, expand/collapse the other applicable nodes that are also selected
|
||||
if (selected)
|
||||
{
|
||||
m_GraphView.SetPreviewExpandedForSelectedNodes(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo(state ? "Expand Previews" : "Collapse Previews");
|
||||
node.previewExpanded = state;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanToggleNodeExpanded()
|
||||
{
|
||||
return !(node is BlockNode) && m_CollapseButton.enabledInHierarchy;
|
||||
}
|
||||
|
||||
void UpdatePreviewExpandedState(bool expanded)
|
||||
{
|
||||
node.previewExpanded = expanded;
|
||||
if (m_PreviewFiller == null)
|
||||
return;
|
||||
if (expanded)
|
||||
{
|
||||
if (m_PreviewContainer.parent != this)
|
||||
{
|
||||
Add(m_PreviewContainer);
|
||||
m_PreviewContainer.PlaceBehind(this.Q("selection-border"));
|
||||
}
|
||||
m_PreviewFiller.AddToClassList("expanded");
|
||||
m_PreviewFiller.RemoveFromClassList("collapsed");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_PreviewContainer.parent == m_PreviewFiller)
|
||||
{
|
||||
m_PreviewContainer.RemoveFromHierarchy();
|
||||
}
|
||||
m_PreviewFiller.RemoveFromClassList("expanded");
|
||||
m_PreviewFiller.AddToClassList("collapsed");
|
||||
}
|
||||
UpdatePreviewTexture();
|
||||
}
|
||||
|
||||
void UpdateTitle()
|
||||
{
|
||||
if (node is SubGraphNode subGraphNode && subGraphNode.asset != null)
|
||||
title = subGraphNode.asset.name;
|
||||
else
|
||||
{
|
||||
if (node.sgVersion < node.latestVersion)
|
||||
{
|
||||
if (node is IHasCustomDeprecationMessage customDeprecationMessage)
|
||||
{
|
||||
title = customDeprecationMessage.GetCustomDeprecationLabel();
|
||||
}
|
||||
else if (ShaderGraphPreferences.allowDeprecatedBehaviors)
|
||||
{
|
||||
title = node.name + $" (Deprecated V{node.sgVersion})";
|
||||
}
|
||||
else
|
||||
{
|
||||
title = node.name + $" (Deprecated)";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
title = node.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateShaderPortsForSlots(bool inputSlots, List<MaterialSlot> allSlots, ShaderPort[] slotShaderPorts)
|
||||
{
|
||||
VisualElement portContainer = inputSlots ? inputContainer : outputContainer;
|
||||
var existingPorts = portContainer.Query<ShaderPort>().ToList();
|
||||
foreach (ShaderPort shaderPort in existingPorts)
|
||||
{
|
||||
var currentSlotId = shaderPort.slot.id;
|
||||
int newSlotIndex = allSlots.FindIndex(s => s.id == currentSlotId);
|
||||
if (newSlotIndex < 0)
|
||||
{
|
||||
// slot doesn't exist anymore, remove it
|
||||
if (inputSlots)
|
||||
portContainer.Remove(shaderPort.parent); // remove parent (includes the InputView)
|
||||
else
|
||||
portContainer.Remove(shaderPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newSlot = allSlots[newSlotIndex];
|
||||
slotShaderPorts[newSlotIndex] = shaderPort;
|
||||
|
||||
// these should probably be in an UpdateShaderPort(shaderPort, newSlot) function
|
||||
shaderPort.slot = newSlot;
|
||||
shaderPort.portName = newSlot.displayName;
|
||||
|
||||
if (inputSlots) // input slots also have to update the InputView
|
||||
UpdatePortInputView(shaderPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnModified(ModificationScope scope)
|
||||
{
|
||||
UpdateTitle();
|
||||
SetActive(node.isActive);
|
||||
if (node.hasPreview)
|
||||
UpdatePreviewExpandedState(node.previewExpanded);
|
||||
|
||||
base.expanded = node.drawState.expanded;
|
||||
|
||||
switch (scope)
|
||||
{
|
||||
// Update slots to match node modification
|
||||
case ModificationScope.Topological:
|
||||
{
|
||||
var slots = node.GetSlots<MaterialSlot>().ToList();
|
||||
// going to record the corresponding ShaderPort to each slot, so we can order them later
|
||||
ShaderPort[] slotShaderPorts = new ShaderPort[slots.Count];
|
||||
|
||||
// update existing input and output ports
|
||||
UpdateShaderPortsForSlots(true, slots, slotShaderPorts);
|
||||
UpdateShaderPortsForSlots(false, slots, slotShaderPorts);
|
||||
|
||||
// check if there are any new slots that must create new ports
|
||||
for (int i = 0; i < slots.Count; i++)
|
||||
{
|
||||
if (slotShaderPorts[i] == null)
|
||||
slotShaderPorts[i] = AddShaderPortForSlot(slots[i]);
|
||||
}
|
||||
|
||||
// make sure they are in the right order
|
||||
// by bringing each port to front in declaration order
|
||||
// note that this sorts input and output containers at the same time
|
||||
foreach (var shaderPort in slotShaderPorts)
|
||||
{
|
||||
if (shaderPort != null)
|
||||
{
|
||||
if (shaderPort.slot.isInputSlot)
|
||||
shaderPort.parent.BringToFront();
|
||||
else
|
||||
shaderPort.BringToFront();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RefreshExpandedState(); // Necessary b/c we can't override enough Node.cs functions to update only what's needed
|
||||
|
||||
foreach (var listener in m_ControlItems.Children().OfType<AbstractMaterialNodeModificationListener>())
|
||||
{
|
||||
if (listener != null)
|
||||
listener.OnNodeModified(scope);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderPort AddShaderPortForSlot(MaterialSlot slot)
|
||||
{
|
||||
if (slot.hidden)
|
||||
return null;
|
||||
|
||||
ShaderPort port = ShaderPort.Create(slot, m_ConnectorListener);
|
||||
if (slot.isOutputSlot)
|
||||
{
|
||||
outputContainer.Add(port);
|
||||
}
|
||||
else
|
||||
{
|
||||
var portContainer = new VisualElement();
|
||||
portContainer.style.flexDirection = FlexDirection.Row;
|
||||
var portInputView = new PortInputView(slot) { style = { position = Position.Absolute } };
|
||||
portContainer.Add(portInputView);
|
||||
portContainer.Add(port);
|
||||
inputContainer.Add(portContainer);
|
||||
|
||||
// Update active state
|
||||
if (node.isActive)
|
||||
{
|
||||
portInputView.RemoveFromClassList("disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
portInputView.AddToClassList("disabled");
|
||||
}
|
||||
}
|
||||
port.OnDisconnect = OnEdgeDisconnected;
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
void AddSlots(IEnumerable<MaterialSlot> slots)
|
||||
{
|
||||
foreach (var slot in slots)
|
||||
AddShaderPortForSlot(slot);
|
||||
// Make sure the visuals are properly updated to reflect port list
|
||||
RefreshPorts();
|
||||
}
|
||||
|
||||
void OnEdgeDisconnected(Port obj)
|
||||
{
|
||||
RefreshExpandedState();
|
||||
}
|
||||
|
||||
static bool GetPortInputView(ShaderPort port, out PortInputView view)
|
||||
{
|
||||
view = port.parent.Q<PortInputView>();
|
||||
return view != null;
|
||||
}
|
||||
|
||||
public void UpdatePortInputTypes()
|
||||
{
|
||||
var portList = inputContainer.Query<ShaderPort>().ToList();
|
||||
portList.AddRange(outputContainer.Query<ShaderPort>().ToList());
|
||||
foreach (var anchor in portList)
|
||||
{
|
||||
var slot = anchor.slot;
|
||||
anchor.portName = slot.displayName;
|
||||
anchor.visualClass = slot.concreteValueType.ToClassName();
|
||||
|
||||
if (GetPortInputView(anchor, out var portInputView))
|
||||
{
|
||||
portInputView.UpdateSlotType();
|
||||
UpdatePortInputVisibility(portInputView, anchor);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var control in m_ControlItems.Children())
|
||||
{
|
||||
if (control is AbstractMaterialNodeModificationListener listener)
|
||||
listener.OnNodeModified(ModificationScope.Graph);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePortInputView(ShaderPort port)
|
||||
{
|
||||
if (GetPortInputView(port, out var portInputView))
|
||||
{
|
||||
portInputView.UpdateSlot(port.slot);
|
||||
UpdatePortInputVisibility(portInputView, port);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePortInputVisibility(PortInputView portInputView, ShaderPort port)
|
||||
{
|
||||
SetElementVisible(portInputView, !port.slot.isConnected);
|
||||
port.parent.style.visibility = port.style.visibility;
|
||||
portInputView.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void SetElementVisible(VisualElement element, bool isVisible)
|
||||
{
|
||||
const string k_HiddenClassList = "hidden";
|
||||
|
||||
if (isVisible)
|
||||
{
|
||||
// Restore default value for visibility by setting it to StyleKeyword.Null.
|
||||
// Setting it to Visibility.Visible would make it visible even if parent is hidden.
|
||||
element.style.visibility = StyleKeyword.Null;
|
||||
element.RemoveFromClassList(k_HiddenClassList);
|
||||
}
|
||||
else
|
||||
{
|
||||
element.style.visibility = Visibility.Hidden;
|
||||
element.AddToClassList(k_HiddenClassList);
|
||||
}
|
||||
}
|
||||
|
||||
SGBlackboardRow GetAssociatedBlackboardRow()
|
||||
{
|
||||
var graphEditorView = GetFirstAncestorOfType<GraphEditorView>();
|
||||
if (graphEditorView == null)
|
||||
return null;
|
||||
|
||||
var blackboardController = graphEditorView.blackboardController;
|
||||
if (blackboardController == null)
|
||||
return null;
|
||||
|
||||
if (node is KeywordNode keywordNode)
|
||||
{
|
||||
return blackboardController.GetBlackboardRow(keywordNode.keyword);
|
||||
}
|
||||
|
||||
if (node is DropdownNode dropdownNode)
|
||||
{
|
||||
return blackboardController.GetBlackboardRow(dropdownNode.dropdown);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void OnMouseHover(EventBase evt)
|
||||
{
|
||||
// Keyword/Dropdown nodes should be highlighted when Blackboard entry is hovered
|
||||
// TODO: Move to new NodeView type when keyword node has unique style
|
||||
var blackboardRow = GetAssociatedBlackboardRow();
|
||||
if (blackboardRow != null)
|
||||
{
|
||||
if (evt.eventTypeId == MouseEnterEvent.TypeId())
|
||||
{
|
||||
blackboardRow.AddToClassList("hovered");
|
||||
}
|
||||
else
|
||||
{
|
||||
blackboardRow.RemoveFromClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePreviewTexture()
|
||||
{
|
||||
if (m_PreviewRenderData.texture == null || !node.previewExpanded)
|
||||
{
|
||||
m_PreviewImage.visible = false;
|
||||
m_PreviewImage.image = Texture2D.blackTexture;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PreviewImage.visible = true;
|
||||
m_PreviewImage.AddToClassList("visible");
|
||||
m_PreviewImage.RemoveFromClassList("hidden");
|
||||
if (m_PreviewImage.image != m_PreviewRenderData.texture)
|
||||
m_PreviewImage.image = m_PreviewRenderData.texture;
|
||||
else
|
||||
m_PreviewImage.MarkDirtyRepaint();
|
||||
|
||||
if (m_PreviewRenderData.shaderData.isOutOfDate)
|
||||
m_PreviewImage.tintColor = new Color(1.0f, 1.0f, 1.0f, 0.3f);
|
||||
else
|
||||
m_PreviewImage.tintColor = Color.white;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var portInputView in inputContainer.Query<PortInputView>().ToList())
|
||||
portInputView.Dispose();
|
||||
|
||||
var propRow = GetAssociatedBlackboardRow();
|
||||
// If this node view is deleted, remove highlighting from associated blackboard row
|
||||
if (propRow != null)
|
||||
{
|
||||
propRow.RemoveFromClassList("hovered");
|
||||
}
|
||||
|
||||
node = null;
|
||||
userData = null;
|
||||
if (m_PreviewRenderData != null)
|
||||
{
|
||||
m_PreviewRenderData.onPreviewChanged -= UpdatePreviewTexture;
|
||||
m_PreviewRenderData = null;
|
||||
}
|
||||
ShaderGraphPreferences.onAllowDeprecatedChanged -= UpdateTitle;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fafbde32fa2b69e4c8a5fe32bf82c7ff
|
||||
timeCreated: 1476707367
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class NodeSettingsView : VisualElement
|
||||
{
|
||||
VisualElement m_ContentContainer;
|
||||
|
||||
public NodeSettingsView()
|
||||
{
|
||||
pickingMode = PickingMode.Ignore;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/NodeSettings"));
|
||||
var uxml = Resources.Load<VisualTreeAsset>("UXML/NodeSettings");
|
||||
uxml.CloneTree(this);
|
||||
// Get the element we want to use as content container
|
||||
m_ContentContainer = this.Q("contentContainer");
|
||||
RegisterCallback<MouseDownEvent>(OnMouseDown);
|
||||
RegisterCallback<MouseUpEvent>(OnMouseUp);
|
||||
}
|
||||
|
||||
void OnMouseUp(MouseUpEvent evt)
|
||||
{
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
void OnMouseDown(MouseDownEvent evt)
|
||||
{
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
public override VisualElement contentContainer
|
||||
{
|
||||
get { return m_ContentContainer; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 827eeffd55f0ac94b9c13553edbe457b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,124 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class PortInputView : GraphElement, IDisposable
|
||||
{
|
||||
readonly CustomStyleProperty<Color> k_EdgeColorProperty = new CustomStyleProperty<Color>("--edge-color");
|
||||
|
||||
Color m_EdgeColor = Color.red;
|
||||
|
||||
public Color edgeColor
|
||||
{
|
||||
get { return m_EdgeColor; }
|
||||
}
|
||||
|
||||
public MaterialSlot slot
|
||||
{
|
||||
get { return m_Slot; }
|
||||
}
|
||||
|
||||
MaterialSlot m_Slot;
|
||||
ConcreteSlotValueType m_SlotType;
|
||||
VisualElement m_Control;
|
||||
VisualElement m_Container;
|
||||
EdgeControl m_EdgeControl;
|
||||
|
||||
public PortInputView(MaterialSlot slot)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/PortInputView"));
|
||||
pickingMode = PickingMode.Ignore;
|
||||
ClearClassList();
|
||||
m_Slot = slot;
|
||||
m_SlotType = slot.concreteValueType;
|
||||
AddToClassList("type" + m_SlotType);
|
||||
|
||||
m_EdgeControl = new EdgeControl
|
||||
{
|
||||
@from = new Vector2(232f - 21f, 11.5f),
|
||||
to = new Vector2(232f, 11.5f),
|
||||
edgeWidth = 2,
|
||||
pickingMode = PickingMode.Ignore
|
||||
};
|
||||
Add(m_EdgeControl);
|
||||
|
||||
m_Container = new VisualElement { name = "container" };
|
||||
{
|
||||
CreateControl();
|
||||
|
||||
var slotElement = new VisualElement { name = "slot" };
|
||||
{
|
||||
slotElement.Add(new VisualElement { name = "dot" });
|
||||
}
|
||||
m_Container.Add(slotElement);
|
||||
}
|
||||
Add(m_Container);
|
||||
|
||||
m_Container.Add(new VisualElement() { name = "disabledOverlay", pickingMode = PickingMode.Ignore });
|
||||
RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
|
||||
}
|
||||
|
||||
void OnCustomStyleResolved(CustomStyleResolvedEvent e)
|
||||
{
|
||||
Color colorValue;
|
||||
|
||||
if (e.customStyle.TryGetValue(k_EdgeColorProperty, out colorValue))
|
||||
m_EdgeColor = colorValue;
|
||||
|
||||
m_EdgeControl.UpdateLayout();
|
||||
m_EdgeControl.inputColor = edgeColor;
|
||||
m_EdgeControl.outputColor = edgeColor;
|
||||
}
|
||||
|
||||
public void UpdateSlot(MaterialSlot newSlot)
|
||||
{
|
||||
m_Slot = newSlot;
|
||||
Recreate();
|
||||
}
|
||||
|
||||
public void UpdateSlotType()
|
||||
{
|
||||
if (slot.concreteValueType != m_SlotType)
|
||||
Recreate();
|
||||
}
|
||||
|
||||
void Recreate()
|
||||
{
|
||||
RemoveFromClassList("type" + m_SlotType);
|
||||
m_SlotType = slot.concreteValueType;
|
||||
AddToClassList("type" + m_SlotType);
|
||||
if (m_Control != null)
|
||||
{
|
||||
if (m_Control is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
m_Container.Remove(m_Control);
|
||||
}
|
||||
CreateControl();
|
||||
}
|
||||
|
||||
void CreateControl()
|
||||
{
|
||||
// Specially designated properties (Use Custom Binding) are shown as a label on the slot when the slot is disconnected, with no ability to set an explicit default.
|
||||
// If the port for this property is connected to, it will use the regular slot control.
|
||||
m_Control = (!slot.isConnected && slot.IsConnectionTestable()) ? slot.InstantiateCustomControl() : slot.InstantiateControl();
|
||||
if (m_Control != null)
|
||||
{
|
||||
m_Container.Insert(0, m_Control);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some slot types don't support an input control, so hide this
|
||||
m_Container.visible = m_EdgeControl.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Control is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9f69414f1afe45f794ec4b5d5bc2bcb5
|
||||
timeCreated: 1509629683
|
|
@ -0,0 +1,183 @@
|
|||
using System;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class PreviewSceneResources : IDisposable
|
||||
{
|
||||
readonly Scene m_Scene;
|
||||
Camera m_Camera;
|
||||
public Light light0 { get; private set; }
|
||||
public Light light1 { get; private set; }
|
||||
|
||||
Material m_CheckerboardMaterial;
|
||||
Material m_BlitNoAlphaMaterial;
|
||||
|
||||
static readonly Mesh[] s_Meshes = { null, null, null, null, null };
|
||||
static readonly GUIContent[] s_MeshIcons = { null, null, null, null, null };
|
||||
static readonly GUIContent[] s_LightIcons = { null, null };
|
||||
static readonly GUIContent[] s_TimeIcons = { null, null };
|
||||
|
||||
static GameObject CreateLight()
|
||||
{
|
||||
GameObject lightGO = EditorUtility.CreateGameObjectWithHideFlags("PreRenderLight", HideFlags.HideAndDontSave, typeof(Light));
|
||||
var light = lightGO.GetComponent<Light>();
|
||||
light.type = LightType.Directional;
|
||||
light.intensity = 1.0f;
|
||||
light.enabled = false;
|
||||
return lightGO;
|
||||
}
|
||||
|
||||
public PreviewSceneResources()
|
||||
{
|
||||
m_Scene = EditorSceneManager.NewPreviewScene();
|
||||
var camGO = EditorUtility.CreateGameObjectWithHideFlags("Preview Scene Camera", HideFlags.HideAndDontSave, typeof(Camera));
|
||||
SceneManager.MoveGameObjectToScene(camGO, m_Scene);
|
||||
|
||||
m_Camera = camGO.GetComponent<Camera>();
|
||||
EditorUtility.SetCameraAnimateMaterials(camera, true);
|
||||
|
||||
camera.cameraType = CameraType.Preview;
|
||||
camera.enabled = false;
|
||||
camera.clearFlags = CameraClearFlags.Depth;
|
||||
camera.fieldOfView = 15;
|
||||
camera.farClipPlane = 10.0f;
|
||||
camera.nearClipPlane = 2.0f;
|
||||
camera.backgroundColor = new Color(49.0f / 255.0f, 49.0f / 255.0f, 49.0f / 255.0f, 1.0f);
|
||||
|
||||
// Explicitly use forward rendering for all previews
|
||||
// (deferred fails when generating some static previews at editor launch; and we never want
|
||||
// vertex lit previews if that is chosen in the player settings)
|
||||
camera.renderingPath = RenderingPath.Forward;
|
||||
camera.useOcclusionCulling = false;
|
||||
camera.scene = m_Scene;
|
||||
|
||||
var l0 = CreateLight();
|
||||
SceneManager.MoveGameObjectToScene(l0, m_Scene);
|
||||
|
||||
//previewScene.AddGameObject(l0);
|
||||
light0 = l0.GetComponent<Light>();
|
||||
|
||||
var l1 = CreateLight();
|
||||
SceneManager.MoveGameObjectToScene(l1, m_Scene);
|
||||
|
||||
//previewScene.AddGameObject(l1);
|
||||
light1 = l1.GetComponent<Light>();
|
||||
|
||||
light0.color = new Color(0.769f, 0.769f, 0.769f, 1); // SceneView.kSceneViewFrontLight
|
||||
light1.transform.rotation = Quaternion.Euler(340, 218, 177);
|
||||
light1.color = new Color(.4f, .4f, .45f, 0f) * .7f;
|
||||
|
||||
m_CheckerboardMaterial = new Material(Shader.Find("Hidden/Checkerboard"));
|
||||
m_BlitNoAlphaMaterial = new Material(Shader.Find("Hidden/BlitNoAlpha"));
|
||||
checkerboardMaterial.hideFlags = HideFlags.HideAndDontSave;
|
||||
blitNoAlphaMaterial.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
if (s_Meshes[0] == null)
|
||||
{
|
||||
var handleGo = (GameObject)EditorGUIUtility.LoadRequired("Previews/PreviewMaterials.fbx");
|
||||
|
||||
// @TODO: temp workaround to make it not render in the scene
|
||||
handleGo.SetActive(false);
|
||||
foreach (Transform t in handleGo.transform)
|
||||
{
|
||||
var meshFilter = t.GetComponent<MeshFilter>();
|
||||
switch (t.name)
|
||||
{
|
||||
case "sphere":
|
||||
s_Meshes[0] = meshFilter.sharedMesh;
|
||||
break;
|
||||
case "cube":
|
||||
s_Meshes[1] = meshFilter.sharedMesh;
|
||||
break;
|
||||
case "cylinder":
|
||||
s_Meshes[2] = meshFilter.sharedMesh;
|
||||
break;
|
||||
case "torus":
|
||||
s_Meshes[3] = meshFilter.sharedMesh;
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning("Something is wrong, weird object found: " + t.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s_MeshIcons[0] = EditorGUIUtility.IconContent("PreMatSphere");
|
||||
s_MeshIcons[1] = EditorGUIUtility.IconContent("PreMatCube");
|
||||
s_MeshIcons[2] = EditorGUIUtility.IconContent("PreMatCylinder");
|
||||
s_MeshIcons[3] = EditorGUIUtility.IconContent("PreMatTorus");
|
||||
s_MeshIcons[4] = EditorGUIUtility.IconContent("PreMatQuad");
|
||||
|
||||
s_LightIcons[0] = EditorGUIUtility.IconContent("PreMatLight0");
|
||||
s_LightIcons[1] = EditorGUIUtility.IconContent("PreMatLight1");
|
||||
|
||||
s_TimeIcons[0] = EditorGUIUtility.IconContent("PlayButton");
|
||||
s_TimeIcons[1] = EditorGUIUtility.IconContent("PauseButton");
|
||||
|
||||
Mesh quadMesh = Resources.GetBuiltinResource(typeof(Mesh), "Quad.fbx") as Mesh;
|
||||
s_Meshes[4] = quadMesh;
|
||||
}
|
||||
}
|
||||
|
||||
public Mesh sphere
|
||||
{
|
||||
get { return s_Meshes[0]; }
|
||||
}
|
||||
|
||||
public Mesh quad
|
||||
{
|
||||
get { return s_Meshes[4]; }
|
||||
}
|
||||
|
||||
public Material checkerboardMaterial
|
||||
{
|
||||
get { return m_CheckerboardMaterial; }
|
||||
}
|
||||
|
||||
public Material blitNoAlphaMaterial
|
||||
{
|
||||
get { return m_BlitNoAlphaMaterial; }
|
||||
}
|
||||
|
||||
public Camera camera
|
||||
{
|
||||
get { return m_Camera; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (light0 != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(light0.gameObject);
|
||||
light0 = null;
|
||||
}
|
||||
|
||||
if (light1 != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(light1.gameObject);
|
||||
light1 = null;
|
||||
}
|
||||
|
||||
if (camera != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(camera.gameObject);
|
||||
m_Camera = null;
|
||||
}
|
||||
|
||||
if (checkerboardMaterial != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(checkerboardMaterial, true);
|
||||
m_CheckerboardMaterial = null;
|
||||
}
|
||||
if (blitNoAlphaMaterial != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(blitNoAlphaMaterial, true);
|
||||
m_BlitNoAlphaMaterial = null;
|
||||
}
|
||||
|
||||
EditorSceneManager.ClosePreviewScene(m_Scene);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37873c3b010e40d1ac1e6aace3c0fb10
|
||||
timeCreated: 1510662606
|
|
@ -0,0 +1,449 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Drawing.Controls;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers;
|
||||
using ContextualMenuManipulator = UnityEngine.UIElements.ContextualMenuManipulator;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
sealed class PropertyNodeView : TokenNode, IShaderNodeView, IInspectable
|
||||
{
|
||||
static readonly Texture2D exposedIcon = Resources.Load<Texture2D>("GraphView/Nodes/BlackboardFieldExposed");
|
||||
|
||||
// When the properties are changed, this delegate is used to trigger an update in the view that represents those properties
|
||||
Action m_propertyViewUpdateTrigger;
|
||||
|
||||
Action m_ResetReferenceNameAction;
|
||||
|
||||
public PropertyNodeView(PropertyNode node, EdgeConnectorListener edgeConnectorListener)
|
||||
: base(null, ShaderPort.Create(node.GetOutputSlots<MaterialSlot>().First(), edgeConnectorListener))
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyNodeView"));
|
||||
this.node = node;
|
||||
viewDataKey = node.objectId.ToString();
|
||||
userData = node;
|
||||
|
||||
// Getting the generatePropertyBlock property to see if it is exposed or not
|
||||
UpdateIcon();
|
||||
|
||||
// Setting the position of the node, otherwise it ends up in the center of the canvas
|
||||
SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0));
|
||||
|
||||
// Removing the title label since it is not used and taking up space
|
||||
this.Q("title-label").RemoveFromHierarchy();
|
||||
|
||||
// Add disabled overlay
|
||||
Add(new VisualElement() { name = "disabledOverlay", pickingMode = PickingMode.Ignore });
|
||||
|
||||
// Update active state
|
||||
SetActive(node.isActive);
|
||||
|
||||
// Registering the hovering callbacks for highlighting
|
||||
RegisterCallback<MouseEnterEvent>(OnMouseHover);
|
||||
RegisterCallback<MouseLeaveEvent>(OnMouseHover);
|
||||
|
||||
// add the right click context menu
|
||||
IManipulator contextMenuManipulator = new ContextualMenuManipulator(AddContextMenuOptions);
|
||||
this.AddManipulator(contextMenuManipulator);
|
||||
|
||||
// Set callback association for display name updates
|
||||
property.displayNameUpdateTrigger += node.UpdateNodeDisplayName;
|
||||
}
|
||||
|
||||
public Node gvNode => this;
|
||||
public AbstractMaterialNode node { get; }
|
||||
public VisualElement colorElement => null;
|
||||
public string inspectorTitle => $"{property.displayName} (Node)";
|
||||
|
||||
[Inspectable("ShaderInput", null)]
|
||||
AbstractShaderProperty property => (node as PropertyNode)?.property;
|
||||
|
||||
public object GetObjectToInspect()
|
||||
{
|
||||
return property;
|
||||
}
|
||||
|
||||
public void SupplyDataToPropertyDrawer(IPropertyDrawer propertyDrawer, Action inspectorUpdateDelegate)
|
||||
{
|
||||
if (propertyDrawer is ShaderInputPropertyDrawer shaderInputPropertyDrawer)
|
||||
{
|
||||
var propNode = node as PropertyNode;
|
||||
var graph = node.owner as GraphData;
|
||||
|
||||
var shaderInputViewModel = new ShaderInputViewModel()
|
||||
{
|
||||
model = property,
|
||||
parentView = null,
|
||||
isSubGraph = graph.isSubGraph,
|
||||
isInputExposed = property.isExposed,
|
||||
inputName = property.displayName,
|
||||
inputTypeName = property.GetPropertyTypeString(),
|
||||
requestModelChangeAction = this.RequestModelChange
|
||||
};
|
||||
shaderInputPropertyDrawer.GetViewModel(shaderInputViewModel, node.owner, this.MarkNodesAsDirty);
|
||||
|
||||
this.m_propertyViewUpdateTrigger = inspectorUpdateDelegate;
|
||||
this.m_ResetReferenceNameAction = shaderInputPropertyDrawer.ResetReferenceName;
|
||||
}
|
||||
}
|
||||
|
||||
void RequestModelChange(IGraphDataAction changeAction)
|
||||
{
|
||||
node.owner?.owner.graphDataStore.Dispatch(changeAction);
|
||||
}
|
||||
|
||||
void ChangeExposedField(bool newValue)
|
||||
{
|
||||
property.generatePropertyBlock = newValue;
|
||||
UpdateIcon();
|
||||
}
|
||||
|
||||
void ChangeDisplayName(string newValue)
|
||||
{
|
||||
property.displayName = newValue;
|
||||
}
|
||||
|
||||
internal static void AddMainColorMenuOptions(ContextualMenuPopulateEvent evt, ColorShaderProperty colorProp, GraphData graphData, Action inspectorUpdateAction)
|
||||
{
|
||||
if (!graphData.isSubGraph)
|
||||
{
|
||||
if (!colorProp.isMainColor)
|
||||
{
|
||||
evt.menu.AppendAction(
|
||||
"Set as Main Color",
|
||||
e =>
|
||||
{
|
||||
ColorShaderProperty col = graphData.GetMainColor();
|
||||
if (col != null)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Change Main Color Action", $"Are you sure you want to change the Main Color from {col.displayName} to {colorProp.displayName}?", "Yes", "Cancel"))
|
||||
{
|
||||
graphData.owner.RegisterCompleteObjectUndo("Change Main Color");
|
||||
col.isMainColor = false;
|
||||
colorProp.isMainColor = true;
|
||||
inspectorUpdateAction();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
graphData.owner.RegisterCompleteObjectUndo("Set Main Color");
|
||||
colorProp.isMainColor = true;
|
||||
inspectorUpdateAction();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
evt.menu.AppendAction(
|
||||
"Clear Main Color",
|
||||
e =>
|
||||
{
|
||||
graphData.owner.RegisterCompleteObjectUndo("Clear Main Color");
|
||||
colorProp.isMainColor = false;
|
||||
inspectorUpdateAction();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AddMainTextureMenuOptions(ContextualMenuPopulateEvent evt, Texture2DShaderProperty texProp, GraphData graphData, Action inspectorUpdateAction)
|
||||
{
|
||||
if (!graphData.isSubGraph)
|
||||
{
|
||||
if (!texProp.isMainTexture)
|
||||
{
|
||||
evt.menu.AppendAction(
|
||||
"Set as Main Texture",
|
||||
e =>
|
||||
{
|
||||
Texture2DShaderProperty tex = graphData.GetMainTexture();
|
||||
// There's already a main texture, ask the user if they want to change and toggle the old one to not be main
|
||||
if (tex != null)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Change Main Texture Action", $"Are you sure you want to change the Main Texture from {tex.displayName} to {texProp.displayName}?", "Yes", "Cancel"))
|
||||
{
|
||||
graphData.owner.RegisterCompleteObjectUndo("Change Main Texture");
|
||||
tex.isMainTexture = false;
|
||||
texProp.isMainTexture = true;
|
||||
inspectorUpdateAction();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
graphData.owner.RegisterCompleteObjectUndo("Set Main Texture");
|
||||
texProp.isMainTexture = true;
|
||||
inspectorUpdateAction();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
evt.menu.AppendAction(
|
||||
"Clear Main Texture",
|
||||
e =>
|
||||
{
|
||||
graphData.owner.RegisterCompleteObjectUndo("Clear Main Texture");
|
||||
texProp.isMainTexture = false;
|
||||
inspectorUpdateAction();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddContextMenuOptions(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
// Checks if the reference name has been overridden and appends menu action to reset it, if so
|
||||
if (property.isRenamable &&
|
||||
!string.IsNullOrEmpty(property.overrideReferenceName))
|
||||
{
|
||||
evt.menu.AppendAction(
|
||||
"Reset Reference",
|
||||
e =>
|
||||
{
|
||||
m_ResetReferenceNameAction();
|
||||
DirtyNodes(ModificationScope.Graph);
|
||||
},
|
||||
DropdownMenuAction.AlwaysEnabled);
|
||||
}
|
||||
|
||||
if (property is ColorShaderProperty colorProp)
|
||||
{
|
||||
AddMainColorMenuOptions(evt, colorProp, node.owner, m_propertyViewUpdateTrigger);
|
||||
}
|
||||
|
||||
if (property is Texture2DShaderProperty texProp)
|
||||
{
|
||||
AddMainTextureMenuOptions(evt, texProp, node.owner, m_propertyViewUpdateTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterPropertyChangeUndo(string actionName)
|
||||
{
|
||||
var graph = node.owner as GraphData;
|
||||
graph.owner.RegisterCompleteObjectUndo(actionName);
|
||||
}
|
||||
|
||||
void MarkNodesAsDirty(bool triggerPropertyViewUpdate = false, ModificationScope modificationScope = ModificationScope.Node)
|
||||
{
|
||||
DirtyNodes(modificationScope);
|
||||
if (triggerPropertyViewUpdate)
|
||||
this.m_propertyViewUpdateTrigger();
|
||||
}
|
||||
|
||||
void ChangePropertyValue(object newValue)
|
||||
{
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
switch (property)
|
||||
{
|
||||
case BooleanShaderProperty booleanProperty:
|
||||
booleanProperty.value = ((ToggleData)newValue).isOn;
|
||||
break;
|
||||
case Vector1ShaderProperty vector1Property:
|
||||
vector1Property.value = (float)newValue;
|
||||
break;
|
||||
case Vector2ShaderProperty vector2Property:
|
||||
vector2Property.value = (Vector2)newValue;
|
||||
break;
|
||||
case Vector3ShaderProperty vector3Property:
|
||||
vector3Property.value = (Vector3)newValue;
|
||||
break;
|
||||
case Vector4ShaderProperty vector4Property:
|
||||
vector4Property.value = (Vector4)newValue;
|
||||
break;
|
||||
case ColorShaderProperty colorProperty:
|
||||
colorProperty.value = (Color)newValue;
|
||||
break;
|
||||
case Texture2DShaderProperty texture2DProperty:
|
||||
texture2DProperty.value.texture = (Texture)newValue;
|
||||
break;
|
||||
case Texture2DArrayShaderProperty texture2DArrayProperty:
|
||||
texture2DArrayProperty.value.textureArray = (Texture2DArray)newValue;
|
||||
break;
|
||||
case Texture3DShaderProperty texture3DProperty:
|
||||
texture3DProperty.value.texture = (Texture3D)newValue;
|
||||
break;
|
||||
case CubemapShaderProperty cubemapProperty:
|
||||
cubemapProperty.value.cubemap = (Cubemap)newValue;
|
||||
break;
|
||||
case Matrix2ShaderProperty matrix2Property:
|
||||
matrix2Property.value = (Matrix4x4)newValue;
|
||||
break;
|
||||
case Matrix3ShaderProperty matrix3Property:
|
||||
matrix3Property.value = (Matrix4x4)newValue;
|
||||
break;
|
||||
case Matrix4ShaderProperty matrix4Property:
|
||||
matrix4Property.value = (Matrix4x4)newValue;
|
||||
break;
|
||||
case SamplerStateShaderProperty samplerStateProperty:
|
||||
samplerStateProperty.value = (TextureSamplerState)newValue;
|
||||
break;
|
||||
case GradientShaderProperty gradientProperty:
|
||||
gradientProperty.value = (Gradient)newValue;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void DirtyNodes(ModificationScope modificationScope = ModificationScope.Node)
|
||||
{
|
||||
var graph = node.owner as GraphData;
|
||||
|
||||
var colorManager = GetFirstAncestorOfType<GraphEditorView>().colorManager;
|
||||
var nodes = GetFirstAncestorOfType<GraphEditorView>().graphView.Query<MaterialNodeView>().ToList();
|
||||
|
||||
colorManager.SetNodesDirty(nodes);
|
||||
colorManager.UpdateNodeViews(nodes);
|
||||
|
||||
foreach (var node in graph.GetNodes<PropertyNode>())
|
||||
{
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetColor(Color newColor)
|
||||
{
|
||||
// Nothing to do here yet
|
||||
}
|
||||
|
||||
public void ResetColor()
|
||||
{
|
||||
// Nothing to do here yet
|
||||
}
|
||||
|
||||
public void UpdatePortInputTypes()
|
||||
{
|
||||
}
|
||||
|
||||
public void UpdateDropdownEntries()
|
||||
{
|
||||
}
|
||||
|
||||
public bool FindPort(SlotReference slot, out ShaderPort port)
|
||||
{
|
||||
port = output as ShaderPort;
|
||||
return port != null && port.slot.slotReference.Equals(slot);
|
||||
}
|
||||
|
||||
void UpdateIcon()
|
||||
{
|
||||
var graph = node?.owner as GraphData;
|
||||
if ((graph != null) && (property != null))
|
||||
icon = (graph.isSubGraph || property.isExposed) ? exposedIcon : null;
|
||||
else
|
||||
icon = null;
|
||||
}
|
||||
|
||||
public void OnModified(ModificationScope scope)
|
||||
{
|
||||
//disconnected property nodes are always active
|
||||
if (!node.IsSlotConnected(PropertyNode.OutputSlotId))
|
||||
node.SetActive(true);
|
||||
|
||||
SetActive(node.isActive);
|
||||
|
||||
if (scope == ModificationScope.Graph)
|
||||
{
|
||||
UpdateIcon();
|
||||
}
|
||||
|
||||
if (scope == ModificationScope.Topological || scope == ModificationScope.Node)
|
||||
{
|
||||
// Updating the text label of the output slot
|
||||
var slot = node.GetSlots<MaterialSlot>().ToList().First();
|
||||
this.Q<Label>("type").text = slot.displayName;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActive(bool state)
|
||||
{
|
||||
// Setup
|
||||
var disabledString = "disabled";
|
||||
|
||||
if (!state)
|
||||
{
|
||||
// Add elements to disabled class list
|
||||
AddToClassList(disabledString);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove elements from disabled class list
|
||||
RemoveFromClassList(disabledString);
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachMessage(string errString, ShaderCompilerMessageSeverity severity)
|
||||
{
|
||||
ClearMessage();
|
||||
IconBadge badge;
|
||||
if (severity == ShaderCompilerMessageSeverity.Error)
|
||||
{
|
||||
badge = IconBadge.CreateError(errString);
|
||||
}
|
||||
else
|
||||
{
|
||||
badge = IconBadge.CreateComment(errString);
|
||||
}
|
||||
|
||||
Add(badge);
|
||||
badge.AttachTo(this, SpriteAlignment.RightCenter);
|
||||
}
|
||||
|
||||
public void ClearMessage()
|
||||
{
|
||||
var badge = this.Q<IconBadge>();
|
||||
if (badge != null)
|
||||
{
|
||||
badge.Detach();
|
||||
badge.RemoveFromHierarchy();
|
||||
}
|
||||
}
|
||||
|
||||
SGBlackboardRow GetAssociatedBlackboardRow()
|
||||
{
|
||||
var graphView = GetFirstAncestorOfType<GraphEditorView>();
|
||||
|
||||
var blackboardController = graphView?.blackboardController;
|
||||
if (blackboardController == null)
|
||||
return null;
|
||||
|
||||
var propNode = (PropertyNode)node;
|
||||
return blackboardController.GetBlackboardRow(propNode.property);
|
||||
}
|
||||
|
||||
void OnMouseHover(EventBase evt)
|
||||
{
|
||||
var propRow = GetAssociatedBlackboardRow();
|
||||
if (propRow != null)
|
||||
{
|
||||
if (evt.eventTypeId == MouseEnterEvent.TypeId())
|
||||
{
|
||||
propRow.AddToClassList("hovered");
|
||||
}
|
||||
else
|
||||
{
|
||||
propRow.RemoveFromClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var propRow = GetAssociatedBlackboardRow();
|
||||
// If this node view is deleted, remove highlighting from associated blackboard row
|
||||
if (propRow != null)
|
||||
{
|
||||
propRow.RemoveFromClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 71056b51439c71e4e9ba450dda30ff20
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,44 @@
|
|||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class PropertyRow : VisualElement
|
||||
{
|
||||
VisualElement m_ContentContainer;
|
||||
VisualElement m_LabelContainer;
|
||||
|
||||
public override VisualElement contentContainer
|
||||
{
|
||||
get { return m_ContentContainer; }
|
||||
}
|
||||
|
||||
public VisualElement label
|
||||
{
|
||||
get { return (m_LabelContainer.childCount > 0) ? m_LabelContainer[0] : null; }
|
||||
set
|
||||
{
|
||||
if (m_LabelContainer.childCount > 0)
|
||||
{
|
||||
m_LabelContainer.Clear();
|
||||
}
|
||||
m_LabelContainer.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyRow(VisualElement label = null)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
VisualElement container = new VisualElement { name = "container" };
|
||||
m_ContentContainer = new VisualElement { name = "content" };
|
||||
m_LabelContainer = new VisualElement { name = "label" };
|
||||
m_LabelContainer.Add(label);
|
||||
|
||||
container.Add(m_LabelContainer);
|
||||
container.Add(m_ContentContainer);
|
||||
|
||||
hierarchy.Add(container);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 917a1d0b14b14ca0861b93792b550f83
|
||||
timeCreated: 1521637416
|
|
@ -0,0 +1,49 @@
|
|||
using System.Linq;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class PropertySheet : VisualElement
|
||||
{
|
||||
VisualElement m_ContentContainer;
|
||||
VisualElement m_HeaderContainer;
|
||||
VisualElement m_WarningContainer;
|
||||
Label m_Header;
|
||||
public override VisualElement contentContainer
|
||||
{
|
||||
get { return m_ContentContainer; }
|
||||
}
|
||||
|
||||
public VisualElement warningContainer => m_WarningContainer;
|
||||
|
||||
public VisualElement headerContainer
|
||||
{
|
||||
get => m_HeaderContainer;
|
||||
set
|
||||
{
|
||||
var first = m_HeaderContainer.Children().FirstOrDefault();
|
||||
if (first != null)
|
||||
first.RemoveFromHierarchy();
|
||||
|
||||
m_HeaderContainer.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public PropertySheet(Label header = null)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertySheet"));
|
||||
m_ContentContainer = new VisualElement { name = "content" };
|
||||
m_HeaderContainer = new VisualElement { name = "header" };
|
||||
m_WarningContainer = new VisualElement { name = "error" };
|
||||
m_WarningContainer.Add(new Label(""));
|
||||
if (header != null)
|
||||
m_HeaderContainer.Add(header);
|
||||
|
||||
m_ContentContainer.Add(m_HeaderContainer);
|
||||
m_ContentContainer.Add(m_WarningContainer);
|
||||
hierarchy.Add(m_ContentContainer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 145d8ce4e0814506803af49f799cdb02
|
||||
timeCreated: 1521645038
|
|
@ -0,0 +1,274 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
internal class ReorderableSlotListView : VisualElement
|
||||
{
|
||||
int m_SelectedIndex = -1;
|
||||
SlotType m_SlotType;
|
||||
GUIStyle m_LabelStyle;
|
||||
IMGUIContainer m_Container;
|
||||
AbstractMaterialNode m_Node;
|
||||
ReorderableList m_ReorderableList;
|
||||
bool m_AllowBareResources;
|
||||
|
||||
internal delegate void ListRecreatedDelegate();
|
||||
ListRecreatedDelegate m_OnListRecreatedCallback = new ListRecreatedDelegate(() => { });
|
||||
|
||||
string label => string.Format("{0}s", m_SlotType.ToString());
|
||||
|
||||
public ReorderableList.AddCallbackDelegate OnAddCallback
|
||||
{
|
||||
get => m_ReorderableList?.onAddCallback;
|
||||
set => m_ReorderableList.onAddCallback = value;
|
||||
}
|
||||
|
||||
public ReorderableList.RemoveCallbackDelegate OnRemoveCallback
|
||||
{
|
||||
get => m_ReorderableList.onRemoveCallback;
|
||||
set => m_ReorderableList.onRemoveCallback = value;
|
||||
}
|
||||
|
||||
public ListRecreatedDelegate OnListRecreatedCallback
|
||||
{
|
||||
get => m_OnListRecreatedCallback;
|
||||
set => m_OnListRecreatedCallback = value;
|
||||
}
|
||||
|
||||
public Func<ConcreteSlotValueTypePopupName, bool> AllowedTypeCallback;
|
||||
|
||||
internal ReorderableSlotListView(AbstractMaterialNode node, SlotType slotType, bool allowBareResources)
|
||||
{
|
||||
m_AllowBareResources = allowBareResources;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/ReorderableSlotListView"));
|
||||
m_Node = node;
|
||||
m_SlotType = slotType;
|
||||
m_Container = new IMGUIContainer(() => OnGUIHandler()) { name = "ListContainer" };
|
||||
Add(m_Container);
|
||||
RecreateList();
|
||||
AddCallbacks();
|
||||
}
|
||||
|
||||
internal void RecreateList()
|
||||
{
|
||||
// Get slots based on type
|
||||
List<MaterialSlot> slots = new List<MaterialSlot>();
|
||||
if (m_SlotType == SlotType.Input)
|
||||
m_Node.GetInputSlots(slots);
|
||||
else
|
||||
m_Node.GetOutputSlots(slots);
|
||||
|
||||
// Create reorderable list from IDs
|
||||
List<int> slotIDs = slots.Select(s => s.id).ToList();
|
||||
m_ReorderableList = new ReorderableList(slotIDs, typeof(int), true, true, true, true);
|
||||
m_OnListRecreatedCallback();
|
||||
}
|
||||
|
||||
private void OnGUIHandler()
|
||||
{
|
||||
if (m_ReorderableList == null)
|
||||
{
|
||||
RecreateList();
|
||||
AddCallbacks();
|
||||
}
|
||||
|
||||
using (var changeCheckScope = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
m_ReorderableList.index = m_SelectedIndex;
|
||||
m_ReorderableList.DoLayoutList();
|
||||
|
||||
if (changeCheckScope.changed)
|
||||
m_Node.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddCallbacks()
|
||||
{
|
||||
m_ReorderableList.drawHeaderCallback = (Rect rect) =>
|
||||
{
|
||||
var labelRect = new Rect(rect.x, rect.y, rect.width - 10, rect.height);
|
||||
EditorGUI.LabelField(labelRect, label);
|
||||
};
|
||||
|
||||
// Draw Element
|
||||
m_ReorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
// Slot is guaranteed to exist in this UI state
|
||||
MaterialSlot oldSlot = m_Node.FindSlot<MaterialSlot>((int)m_ReorderableList.list[index]);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var displayName = EditorGUI.DelayedTextField(new Rect(rect.x, rect.y, rect.width / 2, EditorGUIUtility.singleLineHeight), oldSlot.RawDisplayName(), EditorStyles.label);
|
||||
|
||||
ConcreteSlotValueTypePopupName concreteValueTypePopupOrig =
|
||||
oldSlot.concreteValueType.ToConcreteSlotValueTypePopupName(oldSlot.bareResource);
|
||||
|
||||
ConcreteSlotValueTypePopupName concreteValueTypePopupNew = (ConcreteSlotValueTypePopupName)EditorGUI.EnumPopup(
|
||||
new Rect(rect.x + rect.width / 2, rect.y, rect.width - rect.width / 2, EditorGUIUtility.singleLineHeight),
|
||||
GUIContent.none,
|
||||
concreteValueTypePopupOrig,
|
||||
e =>
|
||||
{
|
||||
ConcreteSlotValueTypePopupName csvtpn = (ConcreteSlotValueTypePopupName)e;
|
||||
csvtpn.ToConcreteSlotValueType(out bool isBareResource);
|
||||
if (isBareResource && !m_AllowBareResources)
|
||||
return false;
|
||||
return AllowedTypeCallback?.Invoke(csvtpn) ?? true;
|
||||
}
|
||||
);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Modify Port");
|
||||
|
||||
displayName = NodeUtils.ConvertToValidHLSLIdentifier(displayName);
|
||||
|
||||
if (displayName != oldSlot.RawDisplayName())
|
||||
{
|
||||
using (var tempSlots = PooledList<MaterialSlot>.Get())
|
||||
{
|
||||
m_Node.GetSlots(tempSlots);
|
||||
|
||||
// deduplicate against other slot shaderOutputNames
|
||||
displayName = GraphUtil.DeduplicateName(tempSlots.Where(p => p.id != oldSlot.id).Select(p => p.shaderOutputName), "{0}_{1}", displayName);
|
||||
}
|
||||
}
|
||||
|
||||
ConcreteSlotValueType concreteValueType = concreteValueTypePopupNew.ToConcreteSlotValueType(out bool isBareResource);
|
||||
|
||||
// Because the type may have changed, we can't (always) just modify the existing slot. So create a new one and replace it.
|
||||
var newSlot = MaterialSlot.CreateMaterialSlot(concreteValueType.ToSlotValueType(), oldSlot.id, displayName, displayName, m_SlotType, Vector4.zero);
|
||||
newSlot.CopyValuesFrom(oldSlot);
|
||||
m_Node.AddSlot(newSlot, false);
|
||||
newSlot.bareResource = isBareResource;
|
||||
|
||||
List<int> orderedSlotIds = new List<int>();
|
||||
if (m_SlotType == SlotType.Input)
|
||||
{
|
||||
orderedSlotIds.AddRange(m_ReorderableList.list.OfType<int>());
|
||||
|
||||
List<MaterialSlot> slots = new List<MaterialSlot>();
|
||||
m_Node.GetOutputSlots(slots);
|
||||
orderedSlotIds.AddRange(slots.Select(s => s.id));
|
||||
}
|
||||
else
|
||||
{
|
||||
List<MaterialSlot> slots = new List<MaterialSlot>();
|
||||
m_Node.GetInputSlots(slots);
|
||||
orderedSlotIds.AddRange(slots.Select(s => s.id));
|
||||
|
||||
orderedSlotIds.AddRange(m_ReorderableList.list.OfType<int>());
|
||||
}
|
||||
|
||||
m_Node.SetSlotOrder(orderedSlotIds);
|
||||
|
||||
RecreateList();
|
||||
m_Node.ValidateNode();
|
||||
}
|
||||
};
|
||||
|
||||
// Element height
|
||||
m_ReorderableList.elementHeightCallback = (int indexer) =>
|
||||
{
|
||||
return m_ReorderableList.elementHeight;
|
||||
};
|
||||
|
||||
// Add callback delegates
|
||||
m_ReorderableList.onSelectCallback += SelectEntry;
|
||||
m_ReorderableList.onAddCallback += AddEntry;
|
||||
m_ReorderableList.onRemoveCallback += RemoveEntry;
|
||||
m_ReorderableList.onReorderCallback += ReorderEntries;
|
||||
}
|
||||
|
||||
private void SelectEntry(ReorderableList list)
|
||||
{
|
||||
m_SelectedIndex = list.index;
|
||||
}
|
||||
|
||||
private void AddEntry(ReorderableList list)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Add Port");
|
||||
// Need to get all current slots to get the next valid ID
|
||||
List<MaterialSlot> slots = new List<MaterialSlot>();
|
||||
m_Node.GetSlots(slots);
|
||||
int[] slotIDs = slots.Select(s => s.id).OrderByDescending(s => s).ToArray();
|
||||
int newSlotID = slotIDs.Length > 0 ? slotIDs[0] + 1 : 0;
|
||||
|
||||
string name = NodeUtils.GetDuplicateSafeNameForSlot(m_Node, newSlotID, "New");
|
||||
|
||||
// Create a new slot and add it
|
||||
var newSlot = MaterialSlot.CreateMaterialSlot(SlotValueType.Vector1, newSlotID, name, NodeUtils.GetHLSLSafeName(name), m_SlotType, Vector4.zero);
|
||||
m_Node.AddSlot(newSlot);
|
||||
|
||||
// Select the new slot, then validate the node
|
||||
m_SelectedIndex = list.list.Count - 1;
|
||||
m_Node.ValidateNode();
|
||||
}
|
||||
|
||||
private void RemoveEntry(ReorderableList list)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Remove Port");
|
||||
|
||||
// Remove the slot from the node
|
||||
m_SelectedIndex = list.index;
|
||||
m_Node.RemoveSlot((int)m_ReorderableList.list[m_SelectedIndex]);
|
||||
|
||||
// Then remove it from the list
|
||||
// Need to do this order to preserve the list indicies for previous step
|
||||
ReorderableList.defaultBehaviours.DoRemoveButton(list);
|
||||
|
||||
// Validate
|
||||
m_Node.ValidateNode();
|
||||
}
|
||||
|
||||
private void ReorderEntries(ReorderableList list)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Reorder Ports");
|
||||
|
||||
// Get all the current slots
|
||||
List<MaterialSlot> slots = new List<MaterialSlot>();
|
||||
if (m_SlotType == SlotType.Input)
|
||||
m_Node.GetInputSlots<MaterialSlot>(slots);
|
||||
else
|
||||
m_Node.GetOutputSlots<MaterialSlot>(slots);
|
||||
|
||||
// Store the edges
|
||||
Dictionary<MaterialSlot, List<IEdge>> edgeDict = new Dictionary<MaterialSlot, List<IEdge>>();
|
||||
foreach (MaterialSlot slot in slots)
|
||||
edgeDict.Add(slot, (List<IEdge>)slot.owner.owner.GetEdges(slot.slotReference));
|
||||
|
||||
// Get reorder slots so need to remove them all then re-add
|
||||
foreach (MaterialSlot slot in slots)
|
||||
m_Node.RemoveSlot(slot.id);
|
||||
|
||||
// Order them by their slot ID
|
||||
slots = slots.OrderBy(s => s.id).ToList();
|
||||
|
||||
// Now add the slots back based on the list order
|
||||
// For each list entry get the slot with that ID
|
||||
for (int i = 0; i < list.list.Count; i++)
|
||||
{
|
||||
var currentSlot = slots.Where(s => s.id == (int)list.list[i]).FirstOrDefault();
|
||||
m_Node.AddSlot(currentSlot);
|
||||
}
|
||||
|
||||
// Reconnect the edges
|
||||
foreach (KeyValuePair<MaterialSlot, List<IEdge>> entry in edgeDict)
|
||||
{
|
||||
foreach (IEdge edge in entry.Value)
|
||||
{
|
||||
m_Node.owner.Connect(edge.outputSlot, edge.inputSlot);
|
||||
}
|
||||
}
|
||||
|
||||
RecreateList();
|
||||
m_Node.ValidateNode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c79d992ee243342ad90f55ece679c57c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,223 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
internal class ReorderableListView<T> : VisualElement
|
||||
{
|
||||
// generic control to display and allow the user to directly reorder/add/remove from a list of T
|
||||
|
||||
// the List we are editing
|
||||
List<T> m_DataList;
|
||||
|
||||
// this is how we get the string to display for each item
|
||||
Func<T, string> m_GetDisplayTextFunc;
|
||||
|
||||
readonly bool m_AllowReorder;
|
||||
ReorderableList m_ReorderableList;
|
||||
List<string> m_TextList = new List<string>();
|
||||
|
||||
IMGUIContainer m_Container;
|
||||
GUIStyle m_LabelStyle;
|
||||
int m_SelectedIndex = -1;
|
||||
string m_HeaderLabel;
|
||||
|
||||
// list of options for the drop down menu when the user clicks the add button
|
||||
public delegate List<string> GetAddMenuOptionsDelegate();
|
||||
public GetAddMenuOptionsDelegate GetAddMenuOptions;
|
||||
|
||||
// callback when the user clicks on an item from the AddMenu
|
||||
public delegate void OnAddMenuItemDelegate(List<T> targetList, int addMenuOptionIndex, string addMenuOption);
|
||||
public OnAddMenuItemDelegate OnAddMenuItemCallback;
|
||||
|
||||
// callback to override how an item is removed from the list
|
||||
internal delegate void RemoveItemDelegate(List<T> list, int itemIndex);
|
||||
public RemoveItemDelegate RemoveItemCallback;
|
||||
|
||||
// callback when the list is reordered
|
||||
internal delegate void ListReorderedDelegate(List<T> reorderedList);
|
||||
public ListReorderedDelegate OnListReorderedCallback;
|
||||
|
||||
internal ReorderableListView(
|
||||
List<T> dataList,
|
||||
string header = "Reorder data:",
|
||||
bool allowReorder = true,
|
||||
Func<T, string> getDisplayText = null)
|
||||
{
|
||||
m_DataList = dataList;
|
||||
m_HeaderLabel = header;
|
||||
m_AllowReorder = allowReorder;
|
||||
|
||||
// setup GetDisplayTextFunc
|
||||
if (getDisplayText == null)
|
||||
m_GetDisplayTextFunc = data => data.ToString();
|
||||
else
|
||||
m_GetDisplayTextFunc = getDisplayText;
|
||||
|
||||
RebuildTextList();
|
||||
|
||||
// should we set up a new style sheet? allow user overrides?
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/ReorderableSlotListView"));
|
||||
|
||||
m_Container = new IMGUIContainer(() => OnGUIHandler()) { name = "ListContainer" };
|
||||
Add(m_Container);
|
||||
}
|
||||
|
||||
void RebuildTextList()
|
||||
{
|
||||
m_TextList.Clear();
|
||||
try
|
||||
{
|
||||
foreach (var data in m_DataList)
|
||||
{
|
||||
m_TextList.Add(m_GetDisplayTextFunc(data));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log("Exception: " + e.ToString() + " while handling ReorderableListView of type: " + typeof(T).ToString());
|
||||
}
|
||||
}
|
||||
|
||||
internal void RecreateList(List<T> dataList)
|
||||
{
|
||||
m_DataList = dataList;
|
||||
|
||||
// Create reorderable list from data list
|
||||
m_ReorderableList = new ReorderableList(
|
||||
dataList,
|
||||
typeof(T), // the type of the elements in dataList
|
||||
m_AllowReorder, // draggable (to reorder)
|
||||
true, // displayHeader
|
||||
true, // displayAddButton
|
||||
true); // displayRemoveButton
|
||||
}
|
||||
|
||||
private void OnGUIHandler()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_ReorderableList == null)
|
||||
{
|
||||
RecreateList(m_DataList);
|
||||
AddCallbacks();
|
||||
}
|
||||
|
||||
using (var changeCheckScope = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
m_ReorderableList.index = m_SelectedIndex;
|
||||
m_ReorderableList.DoLayoutList();
|
||||
|
||||
if (changeCheckScope.changed)
|
||||
{
|
||||
// Do things when changed
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log("Exception: " + e.ToString() + " while handling ReorderableListView of type: " + typeof(T).ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void AddCallbacks()
|
||||
{
|
||||
m_ReorderableList.drawHeaderCallback = (Rect rect) =>
|
||||
{
|
||||
var labelRect = new Rect(rect.x, rect.y, rect.width - 10, rect.height);
|
||||
EditorGUI.LabelField(labelRect, m_HeaderLabel);
|
||||
};
|
||||
|
||||
// Draw Element
|
||||
m_ReorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
EditorGUI.LabelField(
|
||||
new Rect(rect.x, rect.y, rect.width / 2, EditorGUIUtility.singleLineHeight),
|
||||
m_TextList[index], EditorStyles.label);
|
||||
};
|
||||
|
||||
// Element height
|
||||
m_ReorderableList.elementHeightCallback = (int indexer) => { return m_ReorderableList.elementHeight; };
|
||||
|
||||
// Add callback delegates
|
||||
m_ReorderableList.onSelectCallback += SelectEntry; // should we propagate this up if user wants to do something with selection?
|
||||
m_ReorderableList.onReorderCallback += ReorderEntries;
|
||||
m_ReorderableList.onAddDropdownCallback += OnAddDropdownMenu;
|
||||
m_ReorderableList.onRemoveCallback += OnRemove;
|
||||
}
|
||||
|
||||
private void SelectEntry(ReorderableList list)
|
||||
{
|
||||
m_SelectedIndex = list.index;
|
||||
}
|
||||
|
||||
private void ReorderEntries(ReorderableList list)
|
||||
{
|
||||
RebuildTextList();
|
||||
OnListReorderedCallback(m_DataList);
|
||||
}
|
||||
|
||||
private void OnAddDropdownMenu(Rect buttonRect, ReorderableList list)
|
||||
{
|
||||
//created the drop down menu on add item from the listview
|
||||
List<string> addMenuOptions;
|
||||
if (GetAddMenuOptions != null)
|
||||
addMenuOptions = GetAddMenuOptions();
|
||||
else
|
||||
addMenuOptions = new List<string>();
|
||||
|
||||
var menu = new GenericMenu();
|
||||
for (int optionIndex = 0; optionIndex < addMenuOptions.Count; optionIndex++)
|
||||
{
|
||||
string optionText = addMenuOptions[optionIndex];
|
||||
|
||||
// disable any option that is already in the text list
|
||||
bool optionEnabled = !m_TextList.Contains(optionText);
|
||||
|
||||
// need to make a copy so the lambda will capture the value, not the variable
|
||||
int localOptionIndex = optionIndex;
|
||||
|
||||
if (optionEnabled)
|
||||
menu.AddItem(new GUIContent(optionText), false, () => OnAddMenuClicked(localOptionIndex, optionText));
|
||||
else
|
||||
menu.AddDisabledItem(new GUIContent(optionText));
|
||||
}
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void OnAddMenuClicked(int optionIndex, string optionText)
|
||||
{
|
||||
OnAddMenuItemCallback(m_DataList, optionIndex, optionText);
|
||||
|
||||
// if anything was changed about the list in the callback, we need to rebuild our text list
|
||||
RebuildTextList();
|
||||
}
|
||||
|
||||
void OnRemove(ReorderableList list)
|
||||
{
|
||||
int indexToRemove = list.index;
|
||||
if (indexToRemove < 0)
|
||||
indexToRemove = m_DataList.Count - 1;
|
||||
|
||||
if (indexToRemove >= 0)
|
||||
{
|
||||
if (RemoveItemCallback != null)
|
||||
{
|
||||
RemoveItemCallback(m_DataList, indexToRemove);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no callback provided, do it ourselves
|
||||
m_DataList.RemoveAt(indexToRemove);
|
||||
}
|
||||
|
||||
// if anything was changed about the list in the callback, we need to rebuild our text list
|
||||
RebuildTextList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cba41fbec6264db1acb8a265a70cc038
|
||||
timeCreated: 1589241013
|
|
@ -0,0 +1,94 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class ResizableElementFactory : UxmlFactory<ResizableElement>
|
||||
{ }
|
||||
|
||||
class ResizableElement : VisualElement
|
||||
{
|
||||
Dictionary<Resizer, VisualElement> m_Resizers = new Dictionary<Resizer, VisualElement>();
|
||||
|
||||
List<Manipulator> m_Manipulators = new List<Manipulator>();
|
||||
public ResizableElement() : this("uxml/Resizable")
|
||||
{
|
||||
pickingMode = PickingMode.Ignore;
|
||||
}
|
||||
|
||||
public ResizableElement(string uiFile)
|
||||
{
|
||||
var tpl = Resources.Load<VisualTreeAsset>(uiFile);
|
||||
var sheet = Resources.Load<StyleSheet>("Resizable");
|
||||
styleSheets.Add(sheet);
|
||||
|
||||
tpl.CloneTree(this);
|
||||
|
||||
foreach (Resizer direction in new[] { Resizer.Top, Resizer.Bottom, Resizer.Left, Resizer.Right })
|
||||
{
|
||||
VisualElement resizer = this.Q(direction.ToString().ToLower() + "-resize");
|
||||
if (resizer != null)
|
||||
{
|
||||
var manipulator = new ElementResizer(this, direction);
|
||||
resizer.AddManipulator(manipulator);
|
||||
m_Manipulators.Add(manipulator);
|
||||
}
|
||||
m_Resizers[direction] = resizer;
|
||||
}
|
||||
|
||||
foreach (Resizer vertical in new[] { Resizer.Top, Resizer.Bottom })
|
||||
foreach (Resizer horizontal in new[] { Resizer.Left, Resizer.Right })
|
||||
{
|
||||
VisualElement resizer = this.Q(vertical.ToString().ToLower() + "-" + horizontal.ToString().ToLower() + "-resize");
|
||||
if (resizer != null)
|
||||
{
|
||||
var manipulator = new ElementResizer(this, vertical | horizontal);
|
||||
resizer.AddManipulator(manipulator);
|
||||
m_Manipulators.Add(manipulator);
|
||||
}
|
||||
m_Resizers[vertical | horizontal] = resizer;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetResizeRules(Resizer allowedResizeDirections)
|
||||
{
|
||||
foreach (var manipulator in m_Manipulators)
|
||||
{
|
||||
if (manipulator == null)
|
||||
return;
|
||||
var resizeElement = manipulator as ElementResizer;
|
||||
// If resizer direction is not in list of allowed directions, disable the callbacks on it
|
||||
if ((resizeElement.direction & allowedResizeDirections) == 0)
|
||||
{
|
||||
resizeElement.isEnabled = false;
|
||||
}
|
||||
else if ((resizeElement.direction & allowedResizeDirections) != 0)
|
||||
{
|
||||
resizeElement.isEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum Resizer
|
||||
{
|
||||
None = 0,
|
||||
Top = 1 << 0,
|
||||
Bottom = 1 << 1,
|
||||
Left = 1 << 2,
|
||||
Right = 1 << 3,
|
||||
}
|
||||
|
||||
// Lets visual element owners bind a callback to when any resize operation is completed
|
||||
public void BindOnResizeCallback(EventCallback<MouseUpEvent> mouseUpEvent)
|
||||
{
|
||||
foreach (var manipulator in m_Manipulators)
|
||||
{
|
||||
if (manipulator == null)
|
||||
return;
|
||||
manipulator.target.RegisterCallback(mouseUpEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d07b8f263fc7450f849343b74f1807ab
|
||||
timeCreated: 1605228263
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using ContextualMenuManipulator = UnityEngine.UIElements.ContextualMenuManipulator;
|
||||
using ContextualMenuPopulateEvent = UnityEngine.UIElements.ContextualMenuPopulateEvent;
|
||||
using VisualElementExtensions = UnityEngine.UIElements.VisualElementExtensions;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
sealed class ShaderGroup : Group
|
||||
{
|
||||
GraphData m_Graph;
|
||||
public new GroupData userData
|
||||
{
|
||||
get => (GroupData)base.userData;
|
||||
set => base.userData = value;
|
||||
}
|
||||
|
||||
public ShaderGroup()
|
||||
{
|
||||
VisualElementExtensions.AddManipulator(this, new ContextualMenuManipulator(BuildContextualMenu));
|
||||
style.backgroundColor = new StyleColor(new Color(25 / 255f, 25 / 255f, 25 / 255f, 25 / 255f));
|
||||
capabilities |= Capabilities.Ascendable;
|
||||
}
|
||||
|
||||
public void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool AcceptsElement(GraphElement element, ref string reasonWhyNotAccepted)
|
||||
{
|
||||
if (element is StackNode stackNode)
|
||||
{
|
||||
reasonWhyNotAccepted = "Vertex and Pixel Stacks cannot be grouped";
|
||||
return false;
|
||||
}
|
||||
|
||||
var nodeView = element as IShaderNodeView;
|
||||
if (nodeView == null)
|
||||
{
|
||||
// sticky notes are not nodes, but still groupable
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nodeView.node is BlockNode)
|
||||
{
|
||||
reasonWhyNotAccepted = "Block Nodes cannot be grouped";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d61fb6932381e5347bb43516b9dd108b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
sealed class ShaderPort : Port
|
||||
{
|
||||
ShaderPort(Orientation portOrientation, Direction portDirection, Capacity portCapacity, Type type)
|
||||
: base(portOrientation, portDirection, portCapacity, type)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/ShaderPort"));
|
||||
}
|
||||
|
||||
MaterialSlot m_Slot;
|
||||
|
||||
public static ShaderPort Create(MaterialSlot slot, IEdgeConnectorListener connectorListener)
|
||||
{
|
||||
var port = new ShaderPort(Orientation.Horizontal, slot.isInputSlot ? Direction.Input : Direction.Output,
|
||||
slot.isInputSlot ? Capacity.Single : Capacity.Multi, null)
|
||||
{
|
||||
m_EdgeConnector = new EdgeConnector<Edge>(connectorListener),
|
||||
};
|
||||
port.AddManipulator(port.m_EdgeConnector);
|
||||
port.slot = slot;
|
||||
port.portName = slot.displayName;
|
||||
port.visualClass = slot.concreteValueType.ToClassName();
|
||||
return port;
|
||||
}
|
||||
|
||||
public MaterialSlot slot
|
||||
{
|
||||
get { return m_Slot; }
|
||||
set
|
||||
{
|
||||
if (ReferenceEquals(value, m_Slot))
|
||||
return;
|
||||
if (value == null)
|
||||
throw new NullReferenceException();
|
||||
if (m_Slot != null && value.isInputSlot != m_Slot.isInputSlot)
|
||||
throw new ArgumentException("Cannot change direction of already created port");
|
||||
m_Slot = value;
|
||||
portName = slot.displayName;
|
||||
visualClass = slot.concreteValueType.ToClassName();
|
||||
}
|
||||
}
|
||||
|
||||
public Action<Port> OnDisconnect;
|
||||
|
||||
public override void Disconnect(Edge edge)
|
||||
{
|
||||
base.Disconnect(edge);
|
||||
OnDisconnect?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
static class ShaderPortExtensions
|
||||
{
|
||||
public static MaterialSlot GetSlot(this Port port)
|
||||
{
|
||||
var shaderPort = port as ShaderPort;
|
||||
return shaderPort != null ? shaderPort.slot : null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 96f5a3d0303d4d9395a450dcce906110
|
||||
timeCreated: 1512739794
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 56e544af973b458ea7e8051cd0848d24
|
||||
timeCreated: 1509718923
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class BooleanSlotControlView : VisualElement
|
||||
{
|
||||
BooleanMaterialSlot m_Slot;
|
||||
|
||||
public BooleanSlotControlView(BooleanMaterialSlot slot)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/BooleanSlotControlView"));
|
||||
m_Slot = slot;
|
||||
var toggleField = new Toggle() { value = m_Slot.value };
|
||||
toggleField.OnToggleChanged(OnChangeToggle);
|
||||
Add(toggleField);
|
||||
}
|
||||
|
||||
void OnChangeToggle(ChangeEvent<bool> evt)
|
||||
{
|
||||
if (evt.newValue != m_Slot.value)
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Toggle Change");
|
||||
m_Slot.value = evt.newValue;
|
||||
m_Slot.owner.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7fc29d436587748dcaaefd8669c3d946
|
||||
timeCreated: 1510659384
|
|
@ -0,0 +1,36 @@
|
|||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class ColorRGBSlotControlView : VisualElement
|
||||
{
|
||||
ColorRGBMaterialSlot m_Slot;
|
||||
|
||||
public ColorRGBSlotControlView(ColorRGBMaterialSlot slot)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ColorRGBSlotControlView"));
|
||||
m_Slot = slot;
|
||||
var colorField = new ColorField
|
||||
{
|
||||
value = new Color(slot.value.x, slot.value.y, slot.value.z, 0),
|
||||
showEyeDropper = false,
|
||||
showAlpha = false,
|
||||
hdr = (slot.colorMode == ColorMode.HDR)
|
||||
};
|
||||
colorField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(colorField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Color> evt)
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Color Change");
|
||||
m_Slot.value = new Vector3(evt.newValue.r, evt.newValue.g, evt.newValue.b);
|
||||
m_Slot.owner.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a184620cc50fd2e40a845d6ebb39529e
|
||||
timeCreated: 1510659384
|
|
@ -0,0 +1,29 @@
|
|||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class ColorRGBASlotControlView : VisualElement
|
||||
{
|
||||
ColorRGBAMaterialSlot m_Slot;
|
||||
|
||||
public ColorRGBASlotControlView(ColorRGBAMaterialSlot slot)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ColorRGBASlotControlView"));
|
||||
m_Slot = slot;
|
||||
var colorField = new ColorField { value = slot.value, showEyeDropper = false };
|
||||
colorField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(colorField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Color> evt)
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Color Change");
|
||||
m_Slot.value = evt.newValue;
|
||||
m_Slot.owner.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 07332ea5e51a4bd3b6fb63e8e07cc30b
|
||||
timeCreated: 1510659384
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class CubemapSlotControlView : VisualElement
|
||||
{
|
||||
CubemapInputMaterialSlot m_Slot;
|
||||
|
||||
public CubemapSlotControlView(CubemapInputMaterialSlot slot)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/CubemapSlotControlView"));
|
||||
m_Slot = slot;
|
||||
var objectField = new ObjectField { objectType = typeof(Cubemap), value = m_Slot.cubemap };
|
||||
objectField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(objectField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Object> evt)
|
||||
{
|
||||
var cubemap = evt.newValue as Cubemap;
|
||||
if (cubemap != m_Slot.cubemap)
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Change Cubemap");
|
||||
m_Slot.cubemap = cubemap;
|
||||
m_Slot.owner.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b54f542a9d5f7b84e974b54d625d73ea
|
||||
timeCreated: 1509718979
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEditor.ShaderGraph;
|
||||
using UnityEditor.ShaderGraph.Drawing.Controls;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.UIElements.StyleSheets;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class GradientSlotControlView : VisualElement
|
||||
{
|
||||
GradientInputMaterialSlot m_Slot;
|
||||
|
||||
[SerializeField]
|
||||
GradientObject m_GradientObject;
|
||||
|
||||
[SerializeField]
|
||||
SerializedObject m_SerializedObject;
|
||||
|
||||
public GradientSlotControlView(GradientInputMaterialSlot slot)
|
||||
{
|
||||
m_Slot = slot;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/GradientSlotControlView"));
|
||||
|
||||
m_GradientObject = ScriptableObject.CreateInstance<GradientObject>();
|
||||
m_GradientObject.gradient = new Gradient();
|
||||
m_SerializedObject = new SerializedObject(m_GradientObject);
|
||||
|
||||
m_GradientObject.gradient.SetKeys(m_Slot.value.colorKeys, m_Slot.value.alphaKeys);
|
||||
m_GradientObject.gradient.mode = m_Slot.value.mode;
|
||||
|
||||
var gradientField = new GradientField() { value = m_GradientObject.gradient, colorSpace = ColorSpace.Linear };
|
||||
gradientField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(gradientField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Gradient> evt)
|
||||
{
|
||||
m_SerializedObject.Update();
|
||||
if (!evt.newValue.Equals(m_Slot.value))
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Change Gradient");
|
||||
|
||||
m_GradientObject.gradient.SetKeys(evt.newValue.colorKeys, evt.newValue.alphaKeys);
|
||||
m_GradientObject.gradient.mode = evt.newValue.mode;
|
||||
m_SerializedObject.ApplyModifiedProperties();
|
||||
|
||||
m_Slot.value = m_GradientObject.gradient;
|
||||
m_Slot.owner.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e5c808b828bf44895b35be954f0906e5
|
||||
timeCreated: 1510659384
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class LabelSlotControlView : VisualElement
|
||||
{
|
||||
public LabelSlotControlView(string label)
|
||||
{
|
||||
var labelField = new Label(label);
|
||||
Add(labelField);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b7e0bdc2deb5a43aca74fd1e84c94cfa
|
||||
timeCreated: 1509718979
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class MultiFloatSlotControlView : VisualElement
|
||||
{
|
||||
readonly AbstractMaterialNode m_Node;
|
||||
readonly Func<Vector4> m_Get;
|
||||
readonly Action<Vector4> m_Set;
|
||||
int m_UndoGroup = -1;
|
||||
|
||||
public MultiFloatSlotControlView(AbstractMaterialNode node, string[] labels, Func<Vector4> get, Action<Vector4> set)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/MultiFloatSlotControlView"));
|
||||
m_Node = node;
|
||||
m_Get = get;
|
||||
m_Set = set;
|
||||
var initialValue = get();
|
||||
for (var i = 0; i < labels.Length; i++)
|
||||
AddField(initialValue, i, labels[i]);
|
||||
}
|
||||
|
||||
void AddField(Vector4 initialValue, 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 = initialValue[index] };
|
||||
var dragger = new FieldMouseDragger<double>(field);
|
||||
dragger.SetDragZone(label);
|
||||
field.Q("unity-text-input").RegisterCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
// Record Undo for input field edit
|
||||
if (m_UndoGroup == -1)
|
||||
{
|
||||
m_UndoGroup = Undo.GetCurrentGroup();
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
}
|
||||
// Handle scaping input field edit
|
||||
if (evt.keyCode == KeyCode.Escape && m_UndoGroup > -1)
|
||||
{
|
||||
Undo.RevertAllDownToGroup(m_UndoGroup);
|
||||
m_UndoGroup = -1;
|
||||
evt.StopPropagation();
|
||||
}
|
||||
// Dont record Undo again until input field is unfocused
|
||||
m_UndoGroup++;
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
// Called after KeyDownEvent
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
// Only true when setting value via FieldMouseDragger
|
||||
// Undo recorded once per dragger release
|
||||
if (m_UndoGroup == -1)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
}
|
||||
var value = m_Get();
|
||||
if (value[index] != (float)evt.newValue)
|
||||
{
|
||||
value[index] = (float)evt.newValue;
|
||||
m_Set(value);
|
||||
m_Node.Dirty(ModificationScope.Node);
|
||||
}
|
||||
});
|
||||
// Reset UndoGroup when done editing input field & update title
|
||||
field.Q("unity-text-input").RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
m_Node.owner.owner.isDirty = true;
|
||||
m_UndoGroup = -1;
|
||||
});
|
||||
Add(field);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6511e850402b445298bdb3256d9a131b
|
||||
timeCreated: 1509721796
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class MultiIntegerSlotControlView : VisualElement
|
||||
{
|
||||
readonly AbstractMaterialNode m_Node;
|
||||
readonly Func<Vector4> m_Get;
|
||||
readonly Action<Vector4> m_Set;
|
||||
int m_UndoGroup = -1;
|
||||
|
||||
public MultiIntegerSlotControlView(AbstractMaterialNode node, string[] labels, Func<Vector4> get, Action<Vector4> set)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/MultiIntegerSlotControlView"));
|
||||
m_Node = node;
|
||||
m_Get = get;
|
||||
m_Set = set;
|
||||
var initialValue = get();
|
||||
for (var i = 0; i < labels.Length; i++)
|
||||
AddField(initialValue, i, labels[i]);
|
||||
}
|
||||
|
||||
void AddField(Vector4 initialValue, int index, string subLabel)
|
||||
{
|
||||
var dummy = new VisualElement { name = "dummy" };
|
||||
var label = new Label(subLabel);
|
||||
dummy.Add(label);
|
||||
Add(dummy);
|
||||
var field = new IntegerField { userData = index, value = (int)initialValue[index] };
|
||||
var dragger = new FieldMouseDragger<int>(field);
|
||||
dragger.SetDragZone(label);
|
||||
field.Q("unity-text-input").RegisterCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
// Record Undo for input field edit
|
||||
if (m_UndoGroup == -1)
|
||||
{
|
||||
m_UndoGroup = Undo.GetCurrentGroup();
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
}
|
||||
// Handle scaping input field edit
|
||||
if (evt.keyCode == KeyCode.Escape && m_UndoGroup > -1)
|
||||
{
|
||||
Undo.RevertAllDownToGroup(m_UndoGroup);
|
||||
m_UndoGroup = -1;
|
||||
evt.StopPropagation();
|
||||
}
|
||||
// Dont record Undo again until input field is unfocused
|
||||
m_UndoGroup++;
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
// Called after KeyDownEvent
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
// Only true when setting value via FieldMouseDragger
|
||||
// Undo recorded once per dragger release
|
||||
if (m_UndoGroup == -1)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
}
|
||||
var value = m_Get();
|
||||
if (value[index] != (float)evt.newValue)
|
||||
{
|
||||
value[index] = (float)evt.newValue;
|
||||
m_Set(value);
|
||||
m_Node.Dirty(ModificationScope.Node);
|
||||
}
|
||||
});
|
||||
// Reset UndoGroup when done editing input field & update title
|
||||
field.Q("unity-text-input").RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
m_Node.owner.owner.isDirty = true;
|
||||
m_UndoGroup = -1;
|
||||
});
|
||||
Add(field);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b854c7256bfe15c42918d727349e387c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class PropertyConnectionStateSlotControlView : VisualElement
|
||||
{
|
||||
public PropertyConnectionStateSlotControlView(PropertyConnectionStateMaterialSlot slot)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ce60f7df8973ec749b679977e5fd7687
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class ScreenPositionSlotControlView : VisualElement
|
||||
{
|
||||
ScreenPositionMaterialSlot m_Slot;
|
||||
|
||||
public ScreenPositionSlotControlView(ScreenPositionMaterialSlot slot)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ScreenPositionSlotControlView"));
|
||||
m_Slot = slot;
|
||||
var enumField = new EnumField(slot.screenSpaceType);
|
||||
enumField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(enumField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
var screenSpaceType = (ScreenSpaceType)evt.newValue;
|
||||
if (screenSpaceType != m_Slot.screenSpaceType)
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Change Screen Space Type");
|
||||
m_Slot.screenSpaceType = screenSpaceType;
|
||||
m_Slot.owner.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1774f54355319894888ab5ec90b728e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class Texture3DSlotControlView : VisualElement
|
||||
{
|
||||
Texture3DInputMaterialSlot m_Slot;
|
||||
|
||||
public Texture3DSlotControlView(Texture3DInputMaterialSlot slot)
|
||||
{
|
||||
m_Slot = slot;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/Texture3DSlotControlView"));
|
||||
var objectField = new ObjectField { objectType = typeof(Texture3D), value = m_Slot.texture };
|
||||
objectField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(objectField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Object> evt)
|
||||
{
|
||||
var texture = evt.newValue as Texture3D;
|
||||
if (texture != m_Slot.texture)
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Change Texture");
|
||||
m_Slot.texture = texture;
|
||||
m_Slot.owner.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 843052fb41ee94a92b93c2070c1bf577
|
||||
timeCreated: 1509718979
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class TextureArraySlotControlView : VisualElement
|
||||
{
|
||||
Texture2DArrayInputMaterialSlot m_Slot;
|
||||
|
||||
public TextureArraySlotControlView(Texture2DArrayInputMaterialSlot slot)
|
||||
{
|
||||
m_Slot = slot;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/TextureArraySlotControlView"));
|
||||
var objectField = new ObjectField { objectType = typeof(Texture2DArray), value = m_Slot.textureArray };
|
||||
objectField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(objectField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Object> evt)
|
||||
{
|
||||
var textureArray = evt.newValue as Texture2DArray;
|
||||
if (textureArray != m_Slot.textureArray)
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Change Texture Array");
|
||||
m_Slot.textureArray = textureArray;
|
||||
m_Slot.owner.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b65e883d3170e4edca841ea6a8fca626
|
||||
timeCreated: 1509718979
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class TextureSlotControlView : VisualElement
|
||||
{
|
||||
Texture2DInputMaterialSlot m_Slot;
|
||||
|
||||
public TextureSlotControlView(Texture2DInputMaterialSlot slot)
|
||||
{
|
||||
m_Slot = slot;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/TextureSlotControlView"));
|
||||
var objectField = new ObjectField { objectType = typeof(Texture), value = m_Slot.texture };
|
||||
objectField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(objectField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Object> evt)
|
||||
{
|
||||
var texture = evt.newValue as Texture;
|
||||
if (texture != m_Slot.texture)
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Change Texture");
|
||||
m_Slot.texture = texture;
|
||||
m_Slot.owner.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 49f18880b4854ccc8f383c55a7bc47b3
|
||||
timeCreated: 1509718979
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Slots
|
||||
{
|
||||
class UVSlotControlView : VisualElement
|
||||
{
|
||||
UVMaterialSlot m_Slot;
|
||||
|
||||
public UVSlotControlView(UVMaterialSlot slot)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/UVSlotControlView"));
|
||||
m_Slot = slot;
|
||||
var enumField = new EnumField(slot.channel);
|
||||
enumField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(enumField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
var channel = (UVChannel)evt.newValue;
|
||||
if (channel != m_Slot.channel)
|
||||
{
|
||||
m_Slot.owner.owner.owner.RegisterCompleteObjectUndo("Change UV Channel");
|
||||
m_Slot.channel = channel;
|
||||
m_Slot.owner.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f19b65893a414156825079dfc9a6dea3
|
||||
timeCreated: 1509961226
|
|
@ -0,0 +1,469 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.ShaderGraph.Drawing.Interfaces;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class StickyNodeChangeEvent : EventBase<StickyNodeChangeEvent>
|
||||
{
|
||||
public static StickyNodeChangeEvent GetPooled(StickyNote target, Change change)
|
||||
{
|
||||
var evt = GetPooled();
|
||||
evt.target = target;
|
||||
evt.change = change;
|
||||
return evt;
|
||||
}
|
||||
|
||||
public enum Change
|
||||
{
|
||||
title,
|
||||
contents,
|
||||
theme,
|
||||
textSize,
|
||||
}
|
||||
|
||||
public Change change { get; protected set; }
|
||||
}
|
||||
|
||||
class StickyNote : GraphElement, ISGResizable
|
||||
{
|
||||
GraphData m_Graph;
|
||||
public new StickyNoteData userData
|
||||
{
|
||||
get => (StickyNoteData)base.userData;
|
||||
set => base.userData = value;
|
||||
}
|
||||
|
||||
public enum Theme
|
||||
{
|
||||
Classic,
|
||||
Black,
|
||||
Orange,
|
||||
Green,
|
||||
Blue,
|
||||
Red,
|
||||
Purple,
|
||||
Teal
|
||||
}
|
||||
|
||||
Theme m_Theme = Theme.Classic;
|
||||
public Theme theme
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Theme;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (m_Theme != value)
|
||||
{
|
||||
m_Theme = value;
|
||||
UpdateThemeClasses();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum TextSize
|
||||
{
|
||||
Small,
|
||||
Medium,
|
||||
Large,
|
||||
Huge
|
||||
}
|
||||
|
||||
TextSize m_TextSize = TextSize.Medium;
|
||||
|
||||
public TextSize textSize
|
||||
{
|
||||
get { return m_TextSize; }
|
||||
set
|
||||
{
|
||||
if (m_TextSize != value)
|
||||
{
|
||||
m_TextSize = value;
|
||||
UpdateSizeClasses();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnStartResize()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnResized()
|
||||
{
|
||||
userData.position = new Rect(resolvedStyle.left, resolvedStyle.top, style.width.value.value, style.height.value.value);
|
||||
}
|
||||
|
||||
public bool CanResizePastParentBounds()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector2 AllExtraSpace(VisualElement element)
|
||||
{
|
||||
return new Vector2(
|
||||
element.resolvedStyle.marginLeft + element.resolvedStyle.marginRight + element.resolvedStyle.paddingLeft + element.resolvedStyle.paddingRight + element.resolvedStyle.borderRightWidth + element.resolvedStyle.borderLeftWidth,
|
||||
element.resolvedStyle.marginTop + element.resolvedStyle.marginBottom + element.resolvedStyle.paddingTop + element.resolvedStyle.paddingBottom + element.resolvedStyle.borderBottomWidth + element.resolvedStyle.borderTopWidth
|
||||
);
|
||||
}
|
||||
|
||||
void OnFitToText(DropdownMenuAction a)
|
||||
{
|
||||
FitText(false);
|
||||
}
|
||||
|
||||
public void FitText(bool onlyIfSmaller)
|
||||
{
|
||||
Vector2 preferredTitleSize = Vector2.zero;
|
||||
if (!string.IsNullOrEmpty(m_Title.text))
|
||||
preferredTitleSize = m_Title.MeasureTextSize(m_Title.text, 0, MeasureMode.Undefined, 0, MeasureMode.Undefined); // This is the size of the string with the current title font and such
|
||||
|
||||
preferredTitleSize += AllExtraSpace(m_Title);
|
||||
preferredTitleSize.x += m_Title.ChangeCoordinatesTo(this, Vector2.zero).x + resolvedStyle.width - m_Title.ChangeCoordinatesTo(this, new Vector2(m_Title.layout.width, 0)).x;
|
||||
|
||||
Vector2 preferredContentsSizeOneLine = m_Contents.MeasureTextSize(m_Contents.text, 0, MeasureMode.Undefined, 0, MeasureMode.Undefined);
|
||||
|
||||
Vector2 contentExtraSpace = AllExtraSpace(m_Contents);
|
||||
preferredContentsSizeOneLine += contentExtraSpace;
|
||||
|
||||
Vector2 extraSpace = new Vector2(resolvedStyle.width, resolvedStyle.height) - m_Contents.ChangeCoordinatesTo(this, new Vector2(m_Contents.layout.width, m_Contents.layout.height));
|
||||
extraSpace += m_Title.ChangeCoordinatesTo(this, Vector2.zero);
|
||||
preferredContentsSizeOneLine += extraSpace;
|
||||
|
||||
float width = 0;
|
||||
float height = 0;
|
||||
// The content in one line is smaller than the current width.
|
||||
// Set the width to fit both title and content.
|
||||
// Set the height to have only one line in the content
|
||||
if (preferredContentsSizeOneLine.x < Mathf.Max(preferredTitleSize.x, resolvedStyle.width))
|
||||
{
|
||||
width = Mathf.Max(preferredContentsSizeOneLine.x, preferredTitleSize.x);
|
||||
height = preferredContentsSizeOneLine.y + preferredTitleSize.y;
|
||||
}
|
||||
else // The width is not enough for the content: keep the width or use the title width if bigger.
|
||||
{
|
||||
width = Mathf.Max(preferredTitleSize.x + extraSpace.x, resolvedStyle.width);
|
||||
float contextWidth = width - extraSpace.x - contentExtraSpace.x;
|
||||
Vector2 preferredContentsSize = m_Contents.MeasureTextSize(m_Contents.text, contextWidth, MeasureMode.Exactly, 0, MeasureMode.Undefined);
|
||||
|
||||
preferredContentsSize += contentExtraSpace;
|
||||
|
||||
height = preferredTitleSize.y + preferredContentsSize.y + extraSpace.y;
|
||||
}
|
||||
if (!onlyIfSmaller || resolvedStyle.width < width)
|
||||
style.width = width;
|
||||
if (!onlyIfSmaller || resolvedStyle.height < height)
|
||||
style.height = height;
|
||||
OnResized();
|
||||
}
|
||||
|
||||
void UpdateThemeClasses()
|
||||
{
|
||||
foreach (Theme value in System.Enum.GetValues(typeof(Theme)))
|
||||
{
|
||||
if (m_Theme != value)
|
||||
{
|
||||
RemoveFromClassList("theme-" + value.ToString().ToLower());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddToClassList("theme-" + value.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateSizeClasses()
|
||||
{
|
||||
foreach (TextSize value in System.Enum.GetValues(typeof(TextSize)))
|
||||
{
|
||||
if (m_TextSize != value)
|
||||
{
|
||||
RemoveFromClassList("size-" + value.ToString().ToLower());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddToClassList("size-" + value.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly Vector2 defaultSize = new Vector2(200, 160);
|
||||
|
||||
public StickyNote(Rect position, GraphData graph) : this("UXML/StickyNote", position, graph)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Selectable"));
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("StickyNote"));
|
||||
RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
|
||||
}
|
||||
|
||||
public string displayName => $"{m_Title.text} (Sticky Note)";
|
||||
|
||||
public StickyNote(string uiFile, Rect position, GraphData graph)
|
||||
{
|
||||
m_Graph = graph;
|
||||
var tpl = Resources.Load<VisualTreeAsset>(uiFile);
|
||||
|
||||
tpl.CloneTree(this);
|
||||
|
||||
capabilities = Capabilities.Movable | Capabilities.Deletable | Capabilities.Ascendable | Capabilities.Selectable | Capabilities.Copiable | Capabilities.Groupable;
|
||||
|
||||
m_Title = this.Q<Label>(name: "title");
|
||||
if (m_Title != null)
|
||||
{
|
||||
m_Title.RegisterCallback<MouseDownEvent>(OnTitleMouseDown);
|
||||
}
|
||||
|
||||
m_TitleField = this.Q<TextField>(name: "title-field");
|
||||
if (m_TitleField != null)
|
||||
{
|
||||
m_TitleField.style.display = DisplayStyle.None;
|
||||
m_TitleField.Q("unity-text-input").RegisterCallback<BlurEvent>(OnTitleBlur);
|
||||
m_TitleField.RegisterCallback<ChangeEvent<string>>(OnTitleChange);
|
||||
}
|
||||
|
||||
m_Contents = this.Q<Label>(name: "contents");
|
||||
if (m_Contents != null)
|
||||
{
|
||||
m_ContentsField = m_Contents.Q<TextField>(name: "contents-field");
|
||||
if (m_ContentsField != null)
|
||||
{
|
||||
m_ContentsField.style.display = DisplayStyle.None;
|
||||
m_ContentsField.multiline = true;
|
||||
m_ContentsField.Q("unity-text-input").RegisterCallback<BlurEvent>(OnContentsBlur);
|
||||
}
|
||||
m_Contents.RegisterCallback<MouseDownEvent>(OnContentsMouseDown);
|
||||
}
|
||||
|
||||
SetPosition(new Rect(position.x, position.y, defaultSize.x, defaultSize.y));
|
||||
|
||||
AddToClassList("sticky-note");
|
||||
AddToClassList("selectable");
|
||||
UpdateThemeClasses();
|
||||
UpdateSizeClasses();
|
||||
// Manually set the layer of the sticky note so it's always on top. This used to be in the uss
|
||||
// but that causes issues with re-laying out at times that can do weird things to selection.
|
||||
this.layer = -100;
|
||||
|
||||
this.AddManipulator(new ContextualMenuManipulator(BuildContextualMenu));
|
||||
}
|
||||
|
||||
public void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
if (evt.target is StickyNote)
|
||||
{
|
||||
/*foreach (Theme value in System.Enum.GetValues(typeof(Theme)))
|
||||
{
|
||||
evt.menu.AppendAction("Theme/" + value.ToString(), OnChangeTheme, e => DropdownMenu.MenuAction.StatusFlags.Normal, value);
|
||||
}*/
|
||||
if (theme == Theme.Black)
|
||||
evt.menu.AppendAction("Light Theme", OnChangeTheme, e => DropdownMenuAction.Status.Normal, Theme.Classic);
|
||||
else
|
||||
evt.menu.AppendAction("Dark Theme", OnChangeTheme, e => DropdownMenuAction.Status.Normal, Theme.Black);
|
||||
evt.menu.AppendSeparator();
|
||||
foreach (TextSize value in System.Enum.GetValues(typeof(TextSize)))
|
||||
{
|
||||
evt.menu.AppendAction(value.ToString() + " Text Size", OnChangeSize, e => DropdownMenuAction.Status.Normal, value);
|
||||
}
|
||||
evt.menu.AppendSeparator();
|
||||
|
||||
evt.menu.AppendAction("Fit To Text", OnFitToText, e => DropdownMenuAction.Status.Normal);
|
||||
evt.menu.AppendSeparator();
|
||||
|
||||
evt.menu.AppendAction("Delete", OnDelete, e => DropdownMenuAction.Status.Normal);
|
||||
evt.menu.AppendSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
void OnDelete(DropdownMenuAction menuAction)
|
||||
{
|
||||
m_Graph.owner.RegisterCompleteObjectUndo("Delete Sticky Note");
|
||||
m_Graph.RemoveStickyNote(userData);
|
||||
}
|
||||
|
||||
void OnTitleChange(EventBase e)
|
||||
{
|
||||
//m_Graph.owner.RegisterCompleteObjectUndo("Title Changed");
|
||||
//title = m_TitleField.value;
|
||||
//userData.title = title;
|
||||
}
|
||||
|
||||
public PropertySheet GetInspectorContent()
|
||||
{
|
||||
var sheet = new PropertySheet();
|
||||
return sheet;
|
||||
}
|
||||
|
||||
const string fitTextClass = "fit-text";
|
||||
|
||||
public override void SetPosition(Rect rect)
|
||||
{
|
||||
style.left = rect.x;
|
||||
style.top = rect.y;
|
||||
style.width = rect.width;
|
||||
style.height = rect.height;
|
||||
}
|
||||
|
||||
public override Rect GetPosition()
|
||||
{
|
||||
return new Rect(resolvedStyle.left, resolvedStyle.top, resolvedStyle.width, resolvedStyle.height);
|
||||
}
|
||||
|
||||
public string contents
|
||||
{
|
||||
get { return m_Contents.text; }
|
||||
set
|
||||
{
|
||||
if (m_Contents != null)
|
||||
{
|
||||
m_Contents.text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
public new string title
|
||||
{
|
||||
get { return m_Title.text; }
|
||||
set
|
||||
{
|
||||
if (m_Title != null)
|
||||
{
|
||||
m_Title.text = value;
|
||||
|
||||
if (!string.IsNullOrEmpty(m_Title.text))
|
||||
{
|
||||
m_Title.RemoveFromClassList("empty");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Title.AddToClassList("empty");
|
||||
}
|
||||
//UpdateTitleHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnChangeTheme(DropdownMenuAction action)
|
||||
{
|
||||
theme = (Theme)action.userData;
|
||||
NotifyChange(StickyNodeChangeEvent.Change.theme);
|
||||
}
|
||||
|
||||
void OnChangeSize(DropdownMenuAction action)
|
||||
{
|
||||
textSize = (TextSize)action.userData;
|
||||
NotifyChange(StickyNodeChangeEvent.Change.textSize);
|
||||
//panel.InternalValidateLayout();
|
||||
|
||||
FitText(true);
|
||||
}
|
||||
|
||||
void OnAttachToPanel(AttachToPanelEvent e)
|
||||
{
|
||||
//UpdateTitleHeight();
|
||||
}
|
||||
|
||||
void OnTitleBlur(BlurEvent e)
|
||||
{
|
||||
//bool changed = m_Title.text != m_TitleField.value;
|
||||
title = m_TitleField.value;
|
||||
m_TitleField.style.display = DisplayStyle.None;
|
||||
|
||||
m_Title.UnregisterCallback<GeometryChangedEvent>(OnTitleRelayout);
|
||||
|
||||
//Notify change
|
||||
//if( changed)
|
||||
{
|
||||
NotifyChange(StickyNodeChangeEvent.Change.title);
|
||||
}
|
||||
}
|
||||
|
||||
void OnContentsBlur(BlurEvent e)
|
||||
{
|
||||
bool changed = m_Contents.text != m_ContentsField.value;
|
||||
m_Contents.text = m_ContentsField.value;
|
||||
m_ContentsField.style.display = DisplayStyle.None;
|
||||
|
||||
//Notify change
|
||||
if (changed)
|
||||
{
|
||||
NotifyChange(StickyNodeChangeEvent.Change.contents);
|
||||
}
|
||||
}
|
||||
|
||||
void OnTitleRelayout(GeometryChangedEvent e)
|
||||
{
|
||||
UpdateTitleFieldRect();
|
||||
}
|
||||
|
||||
void UpdateTitleFieldRect()
|
||||
{
|
||||
Rect rect = m_Title.layout;
|
||||
m_Title.parent.ChangeCoordinatesTo(m_TitleField.parent, rect);
|
||||
|
||||
m_TitleField.style.left = rect.xMin - 1;
|
||||
m_TitleField.style.right = rect.yMin + m_Title.resolvedStyle.marginTop;
|
||||
m_TitleField.style.width = rect.width - m_Title.resolvedStyle.marginLeft - m_Title.resolvedStyle.marginRight;
|
||||
m_TitleField.style.height = rect.height - m_Title.resolvedStyle.marginTop - m_Title.resolvedStyle.marginBottom;
|
||||
}
|
||||
|
||||
void OnTitleMouseDown(MouseDownEvent e)
|
||||
{
|
||||
if (e.clickCount == 2)
|
||||
{
|
||||
m_TitleField.RemoveFromClassList("empty");
|
||||
m_TitleField.value = m_Title.text;
|
||||
m_TitleField.style.display = DisplayStyle.Flex;
|
||||
UpdateTitleFieldRect();
|
||||
m_Title.RegisterCallback<GeometryChangedEvent>(OnTitleRelayout);
|
||||
|
||||
m_TitleField.Q("unity-text-input").Focus();
|
||||
m_TitleField.SelectAll();
|
||||
|
||||
e.StopPropagation();
|
||||
e.PreventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyChange(StickyNodeChangeEvent.Change change)
|
||||
{
|
||||
m_Graph.owner.RegisterCompleteObjectUndo($"Change Sticky Note {change.ToString()}");
|
||||
if (change == StickyNodeChangeEvent.Change.title)
|
||||
{
|
||||
userData.title = title;
|
||||
}
|
||||
else if (change == StickyNodeChangeEvent.Change.contents)
|
||||
{
|
||||
userData.content = contents;
|
||||
}
|
||||
else if (change == StickyNodeChangeEvent.Change.textSize)
|
||||
{
|
||||
userData.textSize = (int)textSize;
|
||||
}
|
||||
else if (change == StickyNodeChangeEvent.Change.theme)
|
||||
{
|
||||
userData.theme = (int)theme;
|
||||
}
|
||||
}
|
||||
|
||||
public System.Action<StickyNodeChangeEvent.Change> OnChange;
|
||||
|
||||
void OnContentsMouseDown(MouseDownEvent e)
|
||||
{
|
||||
if (e.clickCount == 2)
|
||||
{
|
||||
m_ContentsField.value = m_Contents.text;
|
||||
m_ContentsField.style.display = DisplayStyle.Flex;
|
||||
m_ContentsField.Q("unity-text-input").Focus();
|
||||
e.StopPropagation();
|
||||
e.PreventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
Label m_Title;
|
||||
protected TextField m_TitleField;
|
||||
Label m_Contents;
|
||||
protected TextField m_ContentsField;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d292685fd85281e4c8227eede0e9f090
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue