示例#1
0
        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);
        }
示例#2
0
        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);
        }
示例#5
0
        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);
        }
示例#6
0
        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);
            }
        }
示例#7
0
        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);
        }
示例#8
0
        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);
        }
示例#9
0
        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();
        }
示例#11
0
        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);
        }
示例#12
0
        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);
        }
示例#13
0
        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);
        }
示例#15
0
        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);
        }
示例#16
0
        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());
        }
示例#18
0
        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);
        }
示例#19
0
        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();
        }
示例#23
0
 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);
            }
        }
示例#25
0
 public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
 {
     return(new ColorControlView(m_Label, m_ColorMode, node, propertyInfo));
 }
示例#26
0
        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
                });
            }
        }
示例#27
0
        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));
 }
示例#29
0
 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();
        }