public TextureArrayControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo) { m_Node = node; m_PropertyInfo = propertyInfo; if (propertyInfo.PropertyType != typeof(Texture2DArray)) { throw new ArgumentException("Property must be of type Texture 2D Array.", "propertyInfo"); } label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name); if (!string.IsNullOrEmpty(label)) { Add(new Label(label)); } var textureField = new ObjectField { value = (Texture2DArray)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Texture2DArray) }; textureField.RegisterValueChangedCallback(OnChange); Add(textureField); }
public AbstractMaterialNode CopyNodeForGraph(AbstractMaterialNode oldNode) { var newNode = (AbstractMaterialNode)Activator.CreateInstance(oldNode.GetType()); if (newNode is SubGraphNode subgraphNode) { subgraphNode.asset = ((SubGraphNode)oldNode).asset; } else if (newNode is PropertyNode propertyNode) { propertyNode.owner = m_Graph; propertyNode.property = ((PropertyNode)oldNode).property; propertyNode.owner = null; } else if (newNode is KeywordNode keywordNode) { keywordNode.owner = m_Graph; keywordNode.keyword = ((KeywordNode)oldNode).keyword; keywordNode.owner = null; } return(newNode); }
public SliderControlView(string label, bool displayMinMax, AbstractMaterialNode node, PropertyInfo propertyInfo) { m_Node = node; m_PropertyInfo = propertyInfo; styleSheets.Add(Resources.Load <StyleSheet>("Styles/Controls/SliderControlView")); m_DisplayMinMax = displayMinMax; if (propertyInfo.PropertyType != typeof(Vector3)) { throw new ArgumentException("Property must be of type Vector3.", "propertyInfo"); } new GUIContent(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name)); m_Value = (Vector3)m_PropertyInfo.GetValue(m_Node, null); m_SliderPanel = new VisualElement { name = "SliderPanel" }; if (!string.IsNullOrEmpty(label)) { m_SliderPanel.Add(new Label(label)); } m_Slider = new Slider(m_Value.y, m_Value.z); m_Slider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue)); m_Slider.value = m_Value.x; m_SliderPanel.Add(m_Slider); m_SliderInput = AddField(m_SliderPanel, "", 0, m_Value); Add(m_SliderPanel); if (m_DisplayMinMax) { var fieldsPanel = new VisualElement { name = "FieldsPanel" }; AddField(fieldsPanel, "Min", 1, m_Value); AddField(fieldsPanel, "Max", 2, m_Value); Add(fieldsPanel); } }
public CubemapControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo) { m_Node = node; m_PropertyInfo = propertyInfo; if (propertyInfo.PropertyType != typeof(Cubemap)) { throw new ArgumentException("Property must be of type Texture.", "propertyInfo"); } label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name); if (!string.IsNullOrEmpty(label)) { Add(new Label(label)); } var cubemapField = new ObjectField { value = (Cubemap)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Cubemap) }; cubemapField.OnValueChanged(OnChange); Add(cubemapField); }
public IntegerControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo) { m_Node = node; m_PropertyInfo = propertyInfo; if (propertyInfo.PropertyType != typeof(int)) { throw new ArgumentException("Property must be of type integer.", "propertyInfo"); } label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name); if (!string.IsNullOrEmpty(label)) { Add(new Label(label)); } var intField = new IntegerField { value = (int)m_PropertyInfo.GetValue(m_Node, null) }; intField.OnValueChanged(OnChange); Add(intField); }
public SliderControlView(string label, bool displayMinMax, AbstractMaterialNode node, PropertyInfo propertyInfo) { m_Node = node; m_PropertyInfo = propertyInfo; m_DisplayMinMax = displayMinMax; if (propertyInfo.PropertyType != typeof(Vector3)) { throw new ArgumentException("Property must be of type Vector3.", "propertyInfo"); } m_Label = new GUIContent(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name)); m_Value = (Vector3)m_PropertyInfo.GetValue(m_Node, null); m_SliderPanel = new VisualElement { name = "SliderPanel" }; if (!string.IsNullOrEmpty(label)) { m_SliderPanel.Add(new Label(label)); } Action <float> changedSlider = (s) => { OnChangeSlider(s); }; m_Slider = new Slider(m_Value.y, m_Value.z, changedSlider); m_Slider.value = m_Value.x; m_SliderPanel.Add(m_Slider); m_SliderInput = AddField(m_SliderPanel, "", 0, m_Value); Add(m_SliderPanel); if (m_DisplayMinMax) { var fieldsPanel = new VisualElement { name = "FieldsPanel" }; m_MinField = AddField(fieldsPanel, "Min", 1, m_Value); m_MaxField = AddField(fieldsPanel, "Max", 2, m_Value); Add(fieldsPanel); } }
public AbstractMaterialNode CopyNodeForGraph(AbstractMaterialNode oldNode) { var newNode = (AbstractMaterialNode)Activator.CreateInstance(oldNode.GetType()); if (ShaderGraphPreferences.allowDeprecatedBehaviors && oldNode.sgVersion != newNode.sgVersion) { newNode.ChangeVersion(oldNode.sgVersion); } if (newNode is SubGraphNode subgraphNode) { subgraphNode.asset = ((SubGraphNode)oldNode).asset; } else if (newNode is PropertyNode propertyNode) { propertyNode.owner = m_Graph; propertyNode.property = ((PropertyNode)oldNode).property; propertyNode.owner = null; } else if (newNode is KeywordNode keywordNode) { keywordNode.owner = m_Graph; keywordNode.keyword = ((KeywordNode)oldNode).keyword; keywordNode.owner = null; } else if (newNode is BlockNode blockNode) { blockNode.owner = m_Graph; blockNode.Init(((BlockNode)oldNode).descriptor); blockNode.owner = null; } else if (newNode is CustomInterpolatorNode cinode) { cinode.owner = m_Graph; cinode.ConnectToCustomBlockByName(((CustomInterpolatorNode)oldNode).customBlockNodeName); cinode.owner = null; } return(newNode); }
public ColorControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo) { m_Node = node; m_PropertyInfo = propertyInfo; if (propertyInfo.PropertyType != typeof(Color)) { throw new ArgumentException("Property must be of type Color.", "propertyInfo"); } label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name); if (!string.IsNullOrEmpty(label)) { Add(new Label(label)); } var colorField = new ColorField { value = (Color)m_PropertyInfo.GetValue(m_Node, null), showEyeDropper = false }; colorField.OnValueChanged(OnChange); Add(colorField); }
void CollectShaderProperties(AbstractMaterialNode node, PreviewRenderData renderData) { m_PreviewProperties.Clear(); m_PropertyNodes.Clear(); m_PropertyNodes.Add(node); PropagateNodeList(m_PropertyNodes, PropagationDirection.Upstream); foreach (var propNode in m_PropertyNodes) { propNode.CollectPreviewMaterialProperties(m_PreviewProperties); } foreach (var prop in m_Graph.properties) { m_PreviewProperties.Add(prop.GetPreviewMaterialProperty()); } foreach (var previewProperty in m_PreviewProperties) { renderData.shaderData.mat.SetPreviewProperty(previewProperty); } }
public NoisePeriodicityEnumControlView(string label, int slotId, AbstractMaterialNode node, PropertyInfo propertyInfo) { styleSheets.Add(Resources.Load <StyleSheet>("Styles/Controls/NoisePeriodicityEnumControlView")); m_PropertyInfo = propertyInfo; m_SlotId = slotId; if (!propertyInfo.PropertyType.IsEnum) { throw new ArgumentException("Property must be an enum.", "propertyInfo"); } NoiseNode noiseNode = node as NoiseNode; if (noiseNode == null) { throw new ArgumentException("NoisePeriodicityEnumControl can only be applied on NoiseNode.", "node"); } m_Node = noiseNode; Add(new Label(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name))); m_ValueNames = NoisePeriodicityUtils.GetDisplayNames(); CreatePopup(); }
public static string GetDuplicateSafeNameForSlot(AbstractMaterialNode node, string name) { List <MaterialSlot> slots = new List <MaterialSlot>(); node.GetSlots(slots); int duplicateNameCount = 0; foreach (MaterialSlot value in slots) { string rawDisplayName = value.RawDisplayName(); if (rawDisplayName.Contains(name)) { duplicateNameCount++; } } if (duplicateNameCount > 0) { name += string.Format(" ({0})", duplicateNameCount); } return(name); }
private static ActiveFields GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass) { var activeFields = new ActiveFields(); var baseActiveFields = activeFields.baseInstance; HDUnlitMasterNode masterNode = iMasterNode as HDUnlitMasterNode; if (masterNode == null) { return(activeFields); } if (masterNode.alphaTest.isOn && pass.PixelShaderUsesSlot(HDUnlitMasterNode.AlphaThresholdSlotId)) { baseActiveFields.Add("AlphaTest"); } if (masterNode.surfaceType != SurfaceType.Opaque) { if (masterNode.transparencyFog.isOn) { baseActiveFields.Add("AlphaFog"); } } if (masterNode.addPrecomputedVelocity.isOn) { baseActiveFields.Add("AddPrecomputedVelocity"); } if (masterNode.enableShadowMatte.isOn) { baseActiveFields.Add("EnableShadowMatte"); } return(activeFields); }
void AddPreview(AbstractMaterialNode node) { var shaderData = new PreviewShaderData { node = node, shader = m_UberShader }; var renderData = new PreviewRenderData { shaderData = shaderData, renderTexture = new RenderTexture(200, 200, 16, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default) { hideFlags = HideFlags.HideAndDontSave }, }; renderData.renderTexture.Create(); Set(m_Identifiers, node.tempId, node.tempId); Set(m_RenderDatas, node.tempId, renderData); node.RegisterCallback(OnNodeModified); var isMaster = node is IMasterNode; if (masterRenderData.shaderData == null && (isMaster || node is SubGraphOutputNode)) { masterRenderData.shaderData = shaderData; // If it's actually the master, clear the shader since it will be assigned // later. SubGraphOutputNode still needs the UberShader. if (isMaster) { masterRenderData.shaderData.shader = null; } } m_NeedShaderUpdate = true; }
internal override void AddCustomNodeProperties(VisualElement parentElement, AbstractMaterialNode nodeBase, Action setNodesAsDirtyCallback, Action updateNodeViewsCallback) { var node = nodeBase as PositionNode; var previewField = new EnumField(node.m_PositionSource); var propertyRow = new PropertyRow(new Label("Source")); propertyRow.Add(previewField, (field) => { field.RegisterValueChangedCallback(evt => { if (evt.newValue.Equals(node.m_PositionSource)) { return; } setNodesAsDirtyCallback?.Invoke(); node.owner.owner.RegisterCompleteObjectUndo("Change position source"); node.m_PositionSource = (UnityEditor.ShaderGraph.Internal.PositionSource)evt.newValue; updateNodeViewsCallback?.Invoke(); node.Dirty(ModificationScope.Graph); }); }); parentElement.Add(propertyRow); }
public static IEnumerable <IEdge> GetAllEdges(AbstractMaterialNode node) { var result = new List <IEdge>(); var validSlots = ListPool <MaterialSlot> .Get(); validSlots.AddRange(node.GetInputSlots <MaterialSlot>()); for (int index = 0; index < validSlots.Count; index++) { var inputSlot = validSlots[index]; result.AddRange(node.owner.GetEdges(inputSlot.slotReference)); } validSlots.Clear(); validSlots.AddRange(node.GetOutputSlots <MaterialSlot>()); for (int index = 0; index < validSlots.Count; index++) { var outputSlot = validSlots[index]; result.AddRange(node.owner.GetEdges(outputSlot.slotReference)); } ListPool <MaterialSlot> .Release(validSlots); return(result); }
internal static void AddCustomCheckboxProperty( VisualElement parentElement, AbstractMaterialNode node, Action setNodesAsDirtyCallback, Action updateNodeViewsCallback, String label, String undoLabel, Func <bool> GetterFn, Action <bool> SetterFn) { var fieldObj = new Toggle(); fieldObj.value = GetterFn(); var propertyRow = new PropertyRow(new Label(label)); propertyRow.Add(fieldObj, (field) => { field.RegisterValueChangedCallback(evt => { if (evt.newValue.Equals(GetterFn())) { return; } setNodesAsDirtyCallback?.Invoke(); node.owner.owner.RegisterCompleteObjectUndo("Change Disable Global Mip Bias"); SetterFn((bool)evt.newValue); node.owner.ValidateGraph(); updateNodeViewsCallback?.Invoke(); node.Dirty(ModificationScope.Graph); }); }); if (node is Serialization.MultiJsonInternal.UnknownNodeType) { fieldObj.SetEnabled(false); } parentElement.Add(propertyRow); }
public UnlitSettingsView(AbstractMaterialNode node) : base(node) { m_Node = node as UnlitMasterNode; PropertySheet ps = new PropertySheet(); ps.Add(new PropertyRow(new Label("Surface")), (row) => { row.Add(new EnumField(SurfaceType.Opaque), (field) => { field.value = m_Node.surfaceType; field.RegisterValueChangedCallback(ChangeSurface); }); }); ps.Add(new PropertyRow(new Label("Blend")), (row) => { row.Add(new EnumField(AlphaMode.Additive), (field) => { field.value = m_Node.alphaMode; field.RegisterValueChangedCallback(ChangeAlphaMode); }); }); ps.Add(new PropertyRow(new Label("Two Sided")), (row) => { row.Add(new Toggle(), (toggle) => { toggle.value = m_Node.twoSided.isOn; toggle.OnToggleChanged(ChangeTwoSided); }); }); Add(ps); Add(GetShaderGUIOverridePropertySheet()); }
internal override void AddCustomNodeProperties(VisualElement parentElement, AbstractMaterialNode nodeBase, Action setNodesAsDirtyCallback, Action updateNodeViewsCallback) { var node = nodeBase as SamplerStateNode; var previewField = new EnumField(node.anisotropic); var propertyRow = new PropertyRow(new Label("Anisotropic Filtering")); propertyRow.Add(previewField, (field) => { field.RegisterValueChangedCallback(evt => { if (evt.newValue.Equals(node.anisotropic)) { return; } setNodesAsDirtyCallback?.Invoke(); node.owner.owner.RegisterCompleteObjectUndo("Change anisotropic filtering"); node.anisotropic = (TextureSamplerState.Anisotropic)evt.newValue; updateNodeViewsCallback?.Invoke(); node.Dirty(ModificationScope.Graph); }); }); parentElement.Add(propertyRow); }
private static bool GenerateShaderPassLit(AbstractMaterialNode masterNode, Pass pass, GenerationMode mode, ShaderGenerator result, List <string> sourceAssetDependencyPaths) { if (mode == GenerationMode.ForReals || pass.UseInPreview) { pass.OnGeneratePass(masterNode as PBRMasterNode); // apply master node options to active fields var activeFields = GetActiveFieldsFromMasterNode(masterNode, pass); // use standard shader pass generation bool vertexActive = false; if (masterNode.IsSlotConnected(PBRMasterNode.PositionSlotId) || masterNode.IsSlotConnected(PBRMasterNode.VertNormalSlotId) || masterNode.IsSlotConnected(PBRMasterNode.VertTangentSlotId)) { vertexActive = true; } return(HDSubShaderUtilities.GenerateShaderPass(masterNode, pass, mode, activeFields, result, sourceAssetDependencyPaths, vertexActive)); } else { return(false); } }
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo) { if (propertyInfo.PropertyType == typeof(Color)) { return(new ColorControlView(null, ColorMode.Default, node, propertyInfo)); } if (typeof(Enum).IsAssignableFrom(propertyInfo.PropertyType)) { return(new EnumControlView(null, node, propertyInfo)); } if (propertyInfo.PropertyType == typeof(Texture2D)) { return(new TextureControlView(null, node, propertyInfo)); } if (MultiFloatControlView.validTypes.Contains(propertyInfo.PropertyType)) { return(new MultiFloatControlView(null, "X", "Y", "Z", "W", node, propertyInfo)); } if (typeof(Object).IsAssignableFrom(propertyInfo.PropertyType)) { return(new ObjectControlView(null, node, propertyInfo)); } return(null); }
private static HashSet <string> GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass) { HashSet <string> activeFields = new HashSet <string>(); DecalMasterNode masterNode = iMasterNode as DecalMasterNode; if (masterNode == null) { return(activeFields); } if (masterNode.affectsAlbedo.isOn) { activeFields.Add("Material.AffectsAlbedo"); } if (masterNode.affectsNormal.isOn) { activeFields.Add("Material.AffectsNormal"); } if (masterNode.affectsEmission.isOn) { activeFields.Add("Material.AffectsEmission"); } return(activeFields); }
public void ToSubGraph() { var graphView = graphEditorView.graphView; var path = EditorUtility.SaveFilePanelInProject("Save Sub Graph", "New Shader Sub Graph", ShaderSubGraphImporter.Extension, ""); path = path.Replace(Application.dataPath, "Assets"); if (path.Length == 0) { return; } graphObject.RegisterCompleteObjectUndo("Convert To Subgraph"); var nodes = graphView.selection.OfType <IShaderNodeView>().Where(x => !(x.node is PropertyNode || x.node is SubGraphOutputNode)).Select(x => x.node).Where(x => x.allowedInSubGraph).ToArray(); var bounds = Rect.MinMaxRect(float.PositiveInfinity, float.PositiveInfinity, float.NegativeInfinity, float.NegativeInfinity); foreach (var node in nodes) { var center = node.drawState.position.center; bounds = Rect.MinMaxRect( Mathf.Min(bounds.xMin, center.x), Mathf.Min(bounds.yMin, center.y), Mathf.Max(bounds.xMax, center.x), Mathf.Max(bounds.yMax, center.y)); } var middle = bounds.center; bounds.center = Vector2.zero; // Collect graph inputs var graphInputs = graphView.selection.OfType <BlackboardField>().Select(x => x.userData as ShaderInput); // Collect the property nodes and get the corresponding properties var propertyNodeGuids = graphView.selection.OfType <IShaderNodeView>().Where(x => (x.node is PropertyNode)).Select(x => ((PropertyNode)x.node).propertyGuid); var metaProperties = graphView.graph.properties.Where(x => propertyNodeGuids.Contains(x.guid)); // Collect the keyword nodes and get the corresponding keywords var keywordNodeGuids = graphView.selection.OfType <IShaderNodeView>().Where(x => (x.node is KeywordNode)).Select(x => ((KeywordNode)x.node).keywordGuid); var metaKeywords = graphView.graph.keywords.Where(x => keywordNodeGuids.Contains(x.guid)); var copyPasteGraph = new CopyPasteGraph( graphView.graph.assetGuid, graphView.selection.OfType <ShaderGroup>().Select(x => x.userData), graphView.selection.OfType <IShaderNodeView>().Where(x => !(x.node is PropertyNode || x.node is SubGraphOutputNode)).Select(x => x.node).Where(x => x.allowedInSubGraph).ToArray(), graphView.selection.OfType <Edge>().Select(x => x.userData as IEdge), graphInputs, metaProperties, metaKeywords, graphView.selection.OfType <StickyNote>().Select(x => x.userData)); var deserialized = CopyPasteGraph.FromJson(JsonUtility.ToJson(copyPasteGraph, false)); if (deserialized == null) { return; } var subGraph = new GraphData { isSubGraph = true }; subGraph.path = "Sub Graphs"; var subGraphOutputNode = new SubGraphOutputNode(); { var drawState = subGraphOutputNode.drawState; drawState.position = new Rect(new Vector2(bounds.xMax + 200f, 0f), drawState.position.size); subGraphOutputNode.drawState = drawState; } subGraph.AddNode(subGraphOutputNode); // Always copy deserialized keyword inputs foreach (ShaderKeyword keyword in deserialized.metaKeywords) { ShaderInput copiedInput = keyword.Copy(); subGraph.SanitizeGraphInputName(copiedInput); subGraph.SanitizeGraphInputReferenceName(copiedInput, keyword.overrideReferenceName); subGraph.AddGraphInput(copiedInput); // Update the keyword nodes that depends on the copied keyword var dependentKeywordNodes = deserialized.GetNodes <KeywordNode>().Where(x => x.keywordGuid == keyword.guid); foreach (var node in dependentKeywordNodes) { node.owner = graphView.graph; node.keywordGuid = copiedInput.guid; } } var groupGuidMap = new Dictionary <Guid, Guid>(); foreach (GroupData groupData in deserialized.groups) { var oldGuid = groupData.guid; var newGuid = groupData.RewriteGuid(); groupGuidMap[oldGuid] = newGuid; subGraph.CreateGroup(groupData); } List <Guid> groupGuids = new List <Guid>(); var nodeGuidMap = new Dictionary <Guid, Guid>(); foreach (var node in deserialized.GetNodes <AbstractMaterialNode>()) { var oldGuid = node.guid; var newGuid = node.RewriteGuid(); nodeGuidMap[oldGuid] = newGuid; var drawState = node.drawState; drawState.position = new Rect(drawState.position.position - middle, drawState.position.size); node.drawState = drawState; if (!groupGuids.Contains(node.groupGuid)) { groupGuids.Add(node.groupGuid); } // Checking if the group guid is also being copied. // If not then nullify that guid if (node.groupGuid != Guid.Empty) { node.groupGuid = !groupGuidMap.ContainsKey(node.groupGuid) ? Guid.Empty : groupGuidMap[node.groupGuid]; } subGraph.AddNode(node); } foreach (var note in deserialized.stickyNotes) { if (!groupGuids.Contains(note.groupGuid)) { groupGuids.Add(note.groupGuid); } if (note.groupGuid != Guid.Empty) { note.groupGuid = !groupGuidMap.ContainsKey(note.groupGuid) ? Guid.Empty : groupGuidMap[note.groupGuid]; } note.RewriteGuid(); subGraph.AddStickyNote(note); } // figure out what needs remapping var externalOutputSlots = new List <IEdge>(); var externalInputSlots = new List <IEdge>(); foreach (var edge in deserialized.edges) { var outputSlot = edge.outputSlot; var inputSlot = edge.inputSlot; Guid remappedOutputNodeGuid; Guid remappedInputNodeGuid; var outputSlotExistsInSubgraph = nodeGuidMap.TryGetValue(outputSlot.nodeGuid, out remappedOutputNodeGuid); var inputSlotExistsInSubgraph = nodeGuidMap.TryGetValue(inputSlot.nodeGuid, out remappedInputNodeGuid); // pasting nice internal links! if (outputSlotExistsInSubgraph && inputSlotExistsInSubgraph) { var outputSlotRef = new SlotReference(remappedOutputNodeGuid, outputSlot.slotId); var inputSlotRef = new SlotReference(remappedInputNodeGuid, inputSlot.slotId); subGraph.Connect(outputSlotRef, inputSlotRef); } // one edge needs to go to outside world else if (outputSlotExistsInSubgraph) { externalInputSlots.Add(edge); } else if (inputSlotExistsInSubgraph) { externalOutputSlots.Add(edge); } } // Find the unique edges coming INTO the graph var uniqueIncomingEdges = externalOutputSlots.GroupBy( edge => edge.outputSlot, edge => edge, (key, edges) => new { slotRef = key, edges = edges.ToList() }); var externalInputNeedingConnection = new List <KeyValuePair <IEdge, AbstractShaderProperty> >(); var amountOfProps = uniqueIncomingEdges.Count(); const int height = 40; const int subtractHeight = 20; var propPos = new Vector2(0, -((amountOfProps / 2) + height) - subtractHeight); foreach (var group in uniqueIncomingEdges) { var sr = group.slotRef; var fromNode = graphObject.graph.GetNodeFromGuid(sr.nodeGuid); var fromSlot = fromNode.FindOutputSlot <MaterialSlot>(sr.slotId); AbstractShaderProperty prop; switch (fromSlot.concreteValueType) { case ConcreteSlotValueType.Texture2D: prop = new Texture2DShaderProperty(); break; case ConcreteSlotValueType.Texture2DArray: prop = new Texture2DArrayShaderProperty(); break; case ConcreteSlotValueType.Texture3D: prop = new Texture3DShaderProperty(); break; case ConcreteSlotValueType.Cubemap: prop = new CubemapShaderProperty(); break; case ConcreteSlotValueType.Vector4: prop = new Vector4ShaderProperty(); break; case ConcreteSlotValueType.Vector3: prop = new Vector3ShaderProperty(); break; case ConcreteSlotValueType.Vector2: prop = new Vector2ShaderProperty(); break; case ConcreteSlotValueType.Vector1: prop = new Vector1ShaderProperty(); break; case ConcreteSlotValueType.Boolean: prop = new BooleanShaderProperty(); break; case ConcreteSlotValueType.Matrix2: prop = new Matrix2ShaderProperty(); break; case ConcreteSlotValueType.Matrix3: prop = new Matrix3ShaderProperty(); break; case ConcreteSlotValueType.Matrix4: prop = new Matrix4ShaderProperty(); break; case ConcreteSlotValueType.SamplerState: prop = new SamplerStateShaderProperty(); break; case ConcreteSlotValueType.Gradient: prop = new GradientShaderProperty(); break; default: throw new ArgumentOutOfRangeException(); } if (prop != null) { var materialGraph = (GraphData)graphObject.graph; var fromPropertyNode = fromNode as PropertyNode; var fromProperty = fromPropertyNode != null?materialGraph.properties.FirstOrDefault(p => p.guid == fromPropertyNode.propertyGuid) : null; prop.displayName = fromProperty != null ? fromProperty.displayName : fromSlot.concreteValueType.ToString(); subGraph.AddGraphInput(prop); var propNode = new PropertyNode(); { var drawState = propNode.drawState; drawState.position = new Rect(new Vector2(bounds.xMin - 300f, 0f) + propPos, drawState.position.size); propPos += new Vector2(0, height); propNode.drawState = drawState; } subGraph.AddNode(propNode); propNode.propertyGuid = prop.guid; foreach (var edge in group.edges) { subGraph.Connect( new SlotReference(propNode.guid, PropertyNode.OutputSlotId), new SlotReference(nodeGuidMap[edge.inputSlot.nodeGuid], edge.inputSlot.slotId)); externalInputNeedingConnection.Add(new KeyValuePair <IEdge, AbstractShaderProperty>(edge, prop)); } } } var uniqueOutgoingEdges = externalInputSlots.GroupBy( edge => edge.outputSlot, edge => edge, (key, edges) => new { slot = key, edges = edges.ToList() }); var externalOutputsNeedingConnection = new List <KeyValuePair <IEdge, IEdge> >(); foreach (var group in uniqueOutgoingEdges) { var outputNode = subGraph.outputNode as SubGraphOutputNode; AbstractMaterialNode node = graphView.graph.GetNodeFromGuid(group.edges[0].outputSlot.nodeGuid); MaterialSlot slot = node.FindSlot <MaterialSlot>(group.edges[0].outputSlot.slotId); var slotId = outputNode.AddSlot(slot.concreteValueType); var inputSlotRef = new SlotReference(outputNode.guid, slotId); foreach (var edge in group.edges) { var newEdge = subGraph.Connect(new SlotReference(nodeGuidMap[edge.outputSlot.nodeGuid], edge.outputSlot.slotId), inputSlotRef); externalOutputsNeedingConnection.Add(new KeyValuePair <IEdge, IEdge>(edge, newEdge)); } } if (FileUtilities.WriteShaderGraphToDisk(path, subGraph)) { AssetDatabase.ImportAsset(path); } var loadedSubGraph = AssetDatabase.LoadAssetAtPath(path, typeof(SubGraphAsset)) as SubGraphAsset; if (loadedSubGraph == null) { return; } var subGraphNode = new SubGraphNode(); var ds = subGraphNode.drawState; ds.position = new Rect(middle - new Vector2(100f, 150f), Vector2.zero); subGraphNode.drawState = ds; // Add the subgraph into the group if the nodes was all in the same group group if (groupGuids.Count == 1) { subGraphNode.groupGuid = groupGuids[0]; } graphObject.graph.AddNode(subGraphNode); subGraphNode.asset = loadedSubGraph; foreach (var edgeMap in externalInputNeedingConnection) { graphObject.graph.Connect(edgeMap.Key.outputSlot, new SlotReference(subGraphNode.guid, edgeMap.Value.guid.GetHashCode())); } foreach (var edgeMap in externalOutputsNeedingConnection) { graphObject.graph.Connect(new SlotReference(subGraphNode.guid, edgeMap.Value.inputSlot.slotId), edgeMap.Key.inputSlot); } graphObject.graph.RemoveElements( graphView.selection.OfType <IShaderNodeView>().Select(x => x.node).Where(x => x.allowedInSubGraph).ToArray(), new IEdge[] {}, new GroupData[] {}, graphView.selection.OfType <StickyNote>().Select(x => x.userData).ToArray()); graphObject.graph.ValidateGraph(); }
public PreviewRenderData GetPreview(AbstractMaterialNode node) { return(m_RenderDatas[node.tempId.index]); }
public void Initialize(AbstractMaterialNode inNode, PreviewManager previewManager, IEdgeConnectorListener connectorListener, GraphView 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; m_ConnectorListener = connectorListener; node = inNode; viewDataKey = node.guid.ToString(); UpdateTitle(); // 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); } if (node.hasPreview) { // Add actual preview which floats on top of the node m_PreviewContainer = new VisualElement { name = "previewContainer", cacheAsBitmap = true, 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(() => { node.owner.owner.RegisterCompleteObjectUndo("Collapse Preview"); UpdatePreviewExpandedState(false); })); m_PreviewImage.Add(collapsePreviewButton); } m_PreviewContainer.Add(m_PreviewImage); // Hook up preview image to preview manager m_PreviewRenderData = previewManager.GetPreview(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(() => { node.owner.owner.RegisterCompleteObjectUndo("Expand Preview"); UpdatePreviewExpandedState(true); })); m_PreviewFiller.Add(expandPreviewButton); } contents.Add(m_PreviewFiller); UpdatePreviewExpandedState(node.previewExpanded); } // Add port input container, which acts as a pixel cache for all port inputs m_PortInputContainer = new VisualElement { name = "portInputContainer", cacheAsBitmap = true, style = { overflow = Overflow.Hidden }, pickingMode = PickingMode.Ignore }; Add(m_PortInputContainer); AddSlots(node.GetSlots <MaterialSlot>()); UpdatePortInputs(); base.expanded = node.drawState.expanded; RefreshExpandedState(); //This should not be needed. GraphView needs to improve the extension api here UpdatePortInputVisibilities(); SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0)); if (node is SubGraphNode) { RegisterCallback <MouseDownEvent>(OnSubGraphDoubleClick); } m_PortInputContainer.SendToBack(); m_TitleContainer = this.Q("title"); var masterNode = node as IMasterNode; if (masterNode != null) { AddToClassList("master"); if (!masterNode.IsPipelineCompatible(GraphicsSettings.renderPipelineAsset)) { AttachMessage("The current render pipeline is not compatible with this master node.", ShaderCompilerMessageSeverity.Error); } } m_NodeSettingsView = new NodeSettingsView(); m_NodeSettingsView.visible = false; Add(m_NodeSettingsView); m_SettingsButton = new VisualElement { name = "settings-button" }; m_SettingsButton.Add(new VisualElement { name = "icon" }); m_Settings = new VisualElement(); AddDefaultSettings(); // Add Node type specific settings var nodeTypeSettings = node as IHasSettings; if (nodeTypeSettings != null) { m_Settings.Add(nodeTypeSettings.CreateSettingsElement()); } // Add manipulators m_SettingsButton.AddManipulator(new Clickable(() => { UpdateSettingsExpandedState(); })); if (m_Settings.childCount > 0) { m_ButtonContainer = new VisualElement { name = "button-container" }; m_ButtonContainer.style.flexDirection = FlexDirection.Row; m_ButtonContainer.Add(m_SettingsButton); m_ButtonContainer.Add(m_CollapseButton); m_TitleContainer.Add(m_ButtonContainer); } }
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo) { return(new ColorControlView(m_Label, m_ColorMode, node, propertyInfo)); }
void AddEntries(AbstractMaterialNode node, string[] title, List <NodeEntry> nodeEntries) { if (m_Graph.isSubGraph && !node.allowedInSubGraph) { return; } if (!m_Graph.isSubGraph && !node.allowedInMainGraph) { return; } if (connectedPort == null) { nodeEntries.Add(new NodeEntry { node = node, title = title, compatibleSlotId = -1 }); return; } var connectedSlot = connectedPort.slot; m_Slots.Clear(); node.GetSlots(m_Slots); var hasSingleSlot = m_Slots.Count(s => s.isOutputSlot != connectedSlot.isOutputSlot) == 1; m_Slots.RemoveAll(slot => { var materialSlot = (MaterialSlot)slot; return(!materialSlot.IsCompatibleWith(connectedSlot)); }); m_Slots.RemoveAll(slot => { var materialSlot = (MaterialSlot)slot; return(!materialSlot.IsCompatibleStageWith(connectedSlot)); }); if (hasSingleSlot && m_Slots.Count == 1) { nodeEntries.Add(new NodeEntry { node = node, title = title, compatibleSlotId = m_Slots.First().id }); return; } foreach (var slot in m_Slots) { var entryTitle = new string[title.Length]; title.CopyTo(entryTitle, 0); entryTitle[entryTitle.Length - 1] += ": " + slot.displayName; nodeEntries.Add(new NodeEntry { title = entryTitle, node = node, compatibleSlotId = slot.id }); } }
public void Initialize(AbstractMaterialNode inNode, PreviewManager previewManager, IEdgeConnectorListener connectorListener) { AddStyleSheetPath("Styles/MaterialNodeView"); AddToClassList("MaterialNode"); if (inNode == null) { return; } var contents = this.Q("contents"); m_ConnectorListener = connectorListener; node = inNode; persistenceKey = node.guid.ToString(); UpdateTitle(); // 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); } if (node.hasPreview) { // Add actual preview which floats on top of the node m_PreviewContainer = new VisualElement { name = "previewContainer", clippingOptions = ClippingOptions.ClipAndCacheContents, 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(() => { node.owner.owner.RegisterCompleteObjectUndo("Collapse Preview"); UpdatePreviewExpandedState(false); })); m_PreviewImage.Add(collapsePreviewButton); } m_PreviewContainer.Add(m_PreviewImage); // Hook up preview image to preview manager m_PreviewRenderData = previewManager.GetPreview(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(() => { node.owner.owner.RegisterCompleteObjectUndo("Expand Preview"); UpdatePreviewExpandedState(true); })); m_PreviewFiller.Add(expandPreviewButton); } contents.Add(m_PreviewFiller); UpdatePreviewExpandedState(node.previewExpanded); } // Add port input container, which acts as a pixel cache for all port inputs m_PortInputContainer = new VisualElement { name = "portInputContainer", clippingOptions = ClippingOptions.ClipAndCacheContents, pickingMode = PickingMode.Ignore }; Add(m_PortInputContainer); AddSlots(node.GetSlots <MaterialSlot>()); UpdatePortInputs(); base.expanded = node.drawState.expanded; RefreshExpandedState(); //This should not be needed. GraphView needs to improve the extension api here UpdatePortInputVisibilities(); SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0)); /*if (node is PreviewNode) * { * var resizeHandle = new Label { name = "resize", text = "" }; * resizeHandle.AddManipulator(new Draggable(OnResize)); * Add(resizeHandle); * UpdateSize(); * }*/ if (node is SubGraphNode) { RegisterCallback <MouseDownEvent>(OnSubGraphDoubleClick); } m_PortInputContainer.SendToBack(); if (node.hasPreview) { m_PreviewFiller.BringToFront(); } }
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo) { return(new ChannelMixerControlView(m_Label, m_Minimum, m_Maximum, node, propertyInfo)); }
public static IEnumerable<T> GetOutputSlots<T>(this AbstractMaterialNode node) where T : ISlot { var slots = new List<T>(); node.GetOutputSlots(slots); return slots; }
public void ToSubGraph() { var path = EditorUtility.SaveFilePanelInProject("Save Sub Graph", "New Shader Sub Graph", ShaderSubGraphImporter.Extension, ""); path = path.Replace(Application.dataPath, "Assets"); if (path.Length == 0) { return; } graphObject.RegisterCompleteObjectUndo("Convert To Subgraph"); var graphView = graphEditorView.graphView; var nodes = graphView.selection.OfType <IShaderNodeView>().Where(x => !(x.node is PropertyNode)).Select(x => x.node as AbstractMaterialNode).ToArray(); var bounds = Rect.MinMaxRect(float.PositiveInfinity, float.PositiveInfinity, float.NegativeInfinity, float.NegativeInfinity); foreach (var node in nodes) { var center = node.drawState.position.center; bounds = Rect.MinMaxRect( Mathf.Min(bounds.xMin, center.x), Mathf.Min(bounds.yMin, center.y), Mathf.Max(bounds.xMax, center.x), Mathf.Max(bounds.yMax, center.y)); } var middle = bounds.center; bounds.center = Vector2.zero; // Collect the property nodes and get the corresponding properties var propertyNodeGuids = graphView.selection.OfType <IShaderNodeView>().Where(x => (x.node is PropertyNode)).Select(x => ((PropertyNode)x.node).propertyGuid); var metaProperties = graphView.graph.properties.Where(x => propertyNodeGuids.Contains(x.guid)); var copyPasteGraph = new CopyPasteGraph( graphView.graph.guid, graphView.selection.OfType <ShaderGroup>().Select(x => x.userData), graphView.selection.OfType <IShaderNodeView>().Where(x => !(x.node is PropertyNode)).Select(x => x.node as AbstractMaterialNode), graphView.selection.OfType <Edge>().Select(x => x.userData as IEdge), graphView.selection.OfType <BlackboardField>().Select(x => x.userData as AbstractShaderProperty), metaProperties); var deserialized = CopyPasteGraph.FromJson(JsonUtility.ToJson(copyPasteGraph, false)); if (deserialized == null) { return; } var subGraph = new GraphData { isSubGraph = true }; subGraph.path = "Sub Graphs"; var subGraphOutputNode = new SubGraphOutputNode(); { var drawState = subGraphOutputNode.drawState; drawState.position = new Rect(new Vector2(bounds.xMax + 200f, 0f), drawState.position.size); subGraphOutputNode.drawState = drawState; } subGraph.AddNode(subGraphOutputNode); var nodeGuidMap = new Dictionary <Guid, Guid>(); foreach (var node in deserialized.GetNodes <AbstractMaterialNode>()) { var oldGuid = node.guid; var newGuid = node.RewriteGuid(); nodeGuidMap[oldGuid] = newGuid; var drawState = node.drawState; drawState.position = new Rect(drawState.position.position - middle, drawState.position.size); node.drawState = drawState; subGraph.AddNode(node); } // figure out what needs remapping var externalOutputSlots = new List <IEdge>(); var externalInputSlots = new List <IEdge>(); foreach (var edge in deserialized.edges) { var outputSlot = edge.outputSlot; var inputSlot = edge.inputSlot; Guid remappedOutputNodeGuid; Guid remappedInputNodeGuid; var outputSlotExistsInSubgraph = nodeGuidMap.TryGetValue(outputSlot.nodeGuid, out remappedOutputNodeGuid); var inputSlotExistsInSubgraph = nodeGuidMap.TryGetValue(inputSlot.nodeGuid, out remappedInputNodeGuid); // pasting nice internal links! if (outputSlotExistsInSubgraph && inputSlotExistsInSubgraph) { var outputSlotRef = new SlotReference(remappedOutputNodeGuid, outputSlot.slotId); var inputSlotRef = new SlotReference(remappedInputNodeGuid, inputSlot.slotId); subGraph.Connect(outputSlotRef, inputSlotRef); } // one edge needs to go to outside world else if (outputSlotExistsInSubgraph) { externalInputSlots.Add(edge); } else if (inputSlotExistsInSubgraph) { externalOutputSlots.Add(edge); } } // Find the unique edges coming INTO the graph var uniqueIncomingEdges = externalOutputSlots.GroupBy( edge => edge.outputSlot, edge => edge, (key, edges) => new { slotRef = key, edges = edges.ToList() }); var externalInputNeedingConnection = new List <KeyValuePair <IEdge, AbstractShaderProperty> >(); foreach (var group in uniqueIncomingEdges) { var sr = group.slotRef; var fromNode = graphObject.graph.GetNodeFromGuid(sr.nodeGuid); var fromSlot = fromNode.FindOutputSlot <MaterialSlot>(sr.slotId); AbstractShaderProperty prop; switch (fromSlot.concreteValueType) { case ConcreteSlotValueType.Texture2D: prop = new TextureShaderProperty(); break; case ConcreteSlotValueType.Texture2DArray: prop = new Texture2DArrayShaderProperty(); break; case ConcreteSlotValueType.Texture3D: prop = new Texture3DShaderProperty(); break; case ConcreteSlotValueType.Cubemap: prop = new CubemapShaderProperty(); break; case ConcreteSlotValueType.Vector4: prop = new Vector4ShaderProperty(); break; case ConcreteSlotValueType.Vector3: prop = new Vector3ShaderProperty(); break; case ConcreteSlotValueType.Vector2: prop = new Vector2ShaderProperty(); break; case ConcreteSlotValueType.Vector1: prop = new Vector1ShaderProperty(); break; case ConcreteSlotValueType.Boolean: prop = new BooleanShaderProperty(); break; default: throw new ArgumentOutOfRangeException(); } if (prop != null) { var materialGraph = (GraphData)graphObject.graph; var fromPropertyNode = fromNode as PropertyNode; var fromProperty = fromPropertyNode != null?materialGraph.properties.FirstOrDefault(p => p.guid == fromPropertyNode.propertyGuid) : null; prop.displayName = fromProperty != null ? fromProperty.displayName : fromSlot.concreteValueType.ToString(); subGraph.AddShaderProperty(prop); var propNode = new PropertyNode(); { var drawState = propNode.drawState; drawState.position = new Rect(new Vector2(bounds.xMin - 300f, 0f), drawState.position.size); propNode.drawState = drawState; } subGraph.AddNode(propNode); propNode.propertyGuid = prop.guid; foreach (var edge in group.edges) { subGraph.Connect( new SlotReference(propNode.guid, PropertyNode.OutputSlotId), new SlotReference(nodeGuidMap[edge.inputSlot.nodeGuid], edge.inputSlot.slotId)); externalInputNeedingConnection.Add(new KeyValuePair <IEdge, AbstractShaderProperty>(edge, prop)); } } } var uniqueOutgoingEdges = externalInputSlots.GroupBy( edge => edge.inputSlot, edge => edge, (key, edges) => new { slot = key, edges = edges.ToList() }); var externalOutputsNeedingConnection = new List <KeyValuePair <IEdge, IEdge> >(); foreach (var group in uniqueOutgoingEdges) { var outputNode = subGraph.outputNode as SubGraphOutputNode; AbstractMaterialNode node = graphView.graph.GetNodeFromGuid(group.edges[0].outputSlot.nodeGuid); MaterialSlot slot = node.FindSlot <MaterialSlot>(group.edges[0].outputSlot.slotId); var slotId = outputNode.AddSlot(slot.concreteValueType); var inputSlotRef = new SlotReference(outputNode.guid, slotId); foreach (var edge in group.edges) { var newEdge = subGraph.Connect(new SlotReference(nodeGuidMap[edge.outputSlot.nodeGuid], edge.outputSlot.slotId), inputSlotRef); externalOutputsNeedingConnection.Add(new KeyValuePair <IEdge, IEdge>(edge, newEdge)); } } File.WriteAllText(path, EditorJsonUtility.ToJson(subGraph)); AssetDatabase.ImportAsset(path); var loadedSubGraph = AssetDatabase.LoadAssetAtPath(path, typeof(MaterialSubGraphAsset)) as MaterialSubGraphAsset; if (loadedSubGraph == null) { return; } var subGraphNode = new SubGraphNode(); var ds = subGraphNode.drawState; ds.position = new Rect(middle - new Vector2(100f, 150f), Vector2.zero); subGraphNode.drawState = ds; graphObject.graph.AddNode(subGraphNode); subGraphNode.subGraphAsset = loadedSubGraph; foreach (var edgeMap in externalInputNeedingConnection) { graphObject.graph.Connect(edgeMap.Key.outputSlot, new SlotReference(subGraphNode.guid, edgeMap.Value.guid.GetHashCode())); } foreach (var edgeMap in externalOutputsNeedingConnection) { graphObject.graph.Connect(new SlotReference(subGraphNode.guid, edgeMap.Value.inputSlot.slotId), edgeMap.Key.inputSlot); } graphObject.graph.RemoveElements( graphView.selection.OfType <IShaderNodeView>().Select(x => x.node as AbstractMaterialNode), Enumerable.Empty <IEdge>(), Enumerable.Empty <GroupData>()); graphObject.graph.ValidateGraph(); }