initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -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);
});
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86f8ce6f2b8e29c45a3d40f1c0cfd4d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f1ab7cad0fa94639990c801ecf80bff2
timeCreated: 1512568655

View file

@ -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;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 161180898f1d444880080d666ab7be1d
timeCreated: 1509019463

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 39a37c8d2156b41e182286e521fe51f4
timeCreated: 1481279276
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 275dd8c834214250ac83d2a4eeab3660
timeCreated: 1583780366

View file

@ -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;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2193a2e210bbc714d9db73d1ba2c6587
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6334d158427214af2861c42a0b51f9cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b03bcd5e49f90634390c43e32abae2f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 991f68f49473a9d45a1586045c1e649c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e0bbd4215e018eb4bbdb28bfaa7eabdd
timeCreated: 1476707367
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fafbde32fa2b69e4c8a5fe32bf82c7ff
timeCreated: 1476707367
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 827eeffd55f0ac94b9c13553edbe457b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9f69414f1afe45f794ec4b5d5bc2bcb5
timeCreated: 1509629683

View file

@ -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);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 37873c3b010e40d1ac1e6aace3c0fb10
timeCreated: 1510662606

View file

@ -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");
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71056b51439c71e4e9ba450dda30ff20
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 917a1d0b14b14ca0861b93792b550f83
timeCreated: 1521637416

View file

@ -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);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 145d8ce4e0814506803af49f799cdb02
timeCreated: 1521645038

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c79d992ee243342ad90f55ece679c57c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cba41fbec6264db1acb8a265a70cc038
timeCreated: 1589241013

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d07b8f263fc7450f849343b74f1807ab
timeCreated: 1605228263

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d61fb6932381e5347bb43516b9dd108b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 96f5a3d0303d4d9395a450dcce906110
timeCreated: 1512739794

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 56e544af973b458ea7e8051cd0848d24
timeCreated: 1509718923

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7fc29d436587748dcaaefd8669c3d946
timeCreated: 1510659384

View file

@ -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);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a184620cc50fd2e40a845d6ebb39529e
timeCreated: 1510659384

View file

@ -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);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 07332ea5e51a4bd3b6fb63e8e07cc30b
timeCreated: 1510659384

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b54f542a9d5f7b84e974b54d625d73ea
timeCreated: 1509718979

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e5c808b828bf44895b35be954f0906e5
timeCreated: 1510659384

View file

@ -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);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b7e0bdc2deb5a43aca74fd1e84c94cfa
timeCreated: 1509718979

View file

@ -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);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6511e850402b445298bdb3256d9a131b
timeCreated: 1509721796

View file

@ -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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b854c7256bfe15c42918d727349e387c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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)
{
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce60f7df8973ec749b679977e5fd7687
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1774f54355319894888ab5ec90b728e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 843052fb41ee94a92b93c2070c1bf577
timeCreated: 1509718979

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b65e883d3170e4edca841ea6a8fca626
timeCreated: 1509718979

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 49f18880b4854ccc8f383c55a7bc47b3
timeCreated: 1509718979

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f19b65893a414156825079dfc9a6dea3
timeCreated: 1509961226

View file

@ -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;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d292685fd85281e4c8227eede0e9f090
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: