Example #1
0
        public void DefaultNamePropertyTest()
        {
            var A = new Vector4ShaderProperty()
            {
                displayName = "A"
            };

            m_Graph.AddGraphInput(A);

            // check that default reference name gets set to match display name
            Assert.IsTrue(A.referenceName == "A");
        }
Example #2
0
        public void DefaultNamePropertyTest()
        {
            // Validate that all property types have the correct default display and reference name
            var properties = new List <(ShaderInput shaderInput, string displayName, string referenceName)>
            {
                (new Vector1ShaderProperty(), "Float", "_Float"),
                (new Vector2ShaderProperty(), "Vector2", "_Vector2"),
                (new Vector3ShaderProperty(), "Vector3", "_Vector3"),
                (new Vector4ShaderProperty(), "Vector4", "_Vector4"),
                (new ColorShaderProperty(), "Color", "_Color"),
                (new BooleanShaderProperty(), "Boolean", "_Boolean"),
                (new GradientShaderProperty(), "Gradient", "_Gradient"),
                (new Texture2DShaderProperty(), "Texture2D", "_Texture2D"),
                (new Texture2DArrayShaderProperty(), "Texture2D Array", "_Texture2D_Array"),
                (new Texture3DShaderProperty(), "Texture3D", "_Texture3D"),
                (new CubemapShaderProperty(), "Cubemap", "_Cubemap"),
                (new VirtualTextureShaderProperty(), "VirtualTexture", "_VirtualTexture"),
                (new Matrix2ShaderProperty(), "Matrix2x2", "_Matrix2x2"),
                (new Matrix3ShaderProperty(), "Matrix3x3", "_Matrix3x3"),
                (new Matrix4ShaderProperty(), "Matrix4x4", "_Matrix4x4"),
                (new SamplerStateShaderProperty(), "SamplerState", "_SamplerState"),
                (new ShaderKeyword(KeywordType.Boolean), "Boolean", "_BOOLEAN"),
                (new ShaderKeyword(KeywordType.Enum), "Enum", "_ENUM"),
                (new ShaderKeyword(KeywordType.Enum)
                {
                    displayName = "Material Quality", isBuiltIn = true
                }, "Material Quality", "MATERIAL_QUALITY"),
                // A second vector1 property should properly change the display and reference name
                (new Vector1ShaderProperty(), "Float (1)", "_Float_1"),
                // Test manually setting the display name. This should just pre-pend an underscore
                (new Vector4ShaderProperty()
                {
                    displayName = "A"
                }, "A", "_A"),
                // Validate duplicate display names are correctly handled too
                (new Vector4ShaderProperty()
                {
                    displayName = "A"
                }, "A (1)", "_A_1"),
            };

            foreach (var property in properties)
            {
                m_Graph.AddGraphInput(property.shaderInput);
                // Check that the default display and reference names match what's expected
                Assert.IsTrue(property.shaderInput.displayName == property.displayName, "Expected display name '{0}' but was '{1}'", property.displayName, property.shaderInput.displayName);
                Assert.IsTrue(property.shaderInput.referenceName == property.referenceName, "Expected reference name '{0}' but was '{1}'", property.referenceName, property.shaderInput.referenceName);
            }
        }
Example #3
0
        void AddShaderInput(GraphData graphData)
        {
            AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out AddShaderInputAction");

            // If type property is valid, create instance of that type
            if (blackboardItemType != null && blackboardItemType.IsSubclassOf(typeof(BlackboardItem)))
            {
                shaderInputReference = (BlackboardItem)Activator.CreateInstance(blackboardItemType, true);
            }
            else if (shaderInputReferenceGetter != null)
            {
                shaderInputReference = shaderInputReferenceGetter();
            }
            // If type is null a direct override object must have been provided or else we are in an error-state
            else if (shaderInputReference == null)
            {
                AssertHelpers.Fail("BlackboardController: Unable to complete Add Shader Input action.");
                return;
            }

            shaderInputReference.generatePropertyBlock = shaderInputReference.isExposable;

            graphData.owner.RegisterCompleteObjectUndo("Add Shader Input");
            graphData.AddGraphInput(shaderInputReference);
        }
Example #4
0
        void AddShaderInput(GraphData graphData)
        {
            AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out AddShaderInputAction");

            // If type property is valid, create instance of that type
            if (blackboardItemType != null && blackboardItemType.IsSubclassOf(typeof(BlackboardItem)))
            {
                shaderInputReference = (BlackboardItem)Activator.CreateInstance(blackboardItemType, true);
            }
            else if (m_ShaderInputReferenceGetter != null)
            {
                shaderInputReference = m_ShaderInputReferenceGetter();
            }
            // If type is null a direct override object must have been provided or else we are in an error-state
            else if (shaderInputReference == null)
            {
                AssertHelpers.Fail("BlackboardController: Unable to complete Add Shader Input action.");
                return;
            }

            shaderInputReference.generatePropertyBlock = shaderInputReference.isExposable;

            if (graphData.owner != null)
            {
                graphData.owner.RegisterCompleteObjectUndo("Add Shader Input");
            }
            else
            {
                AssertHelpers.Fail("GraphObject is null while carrying out AddShaderInputAction");
            }

            graphData.AddGraphInput(shaderInputReference);

            // If no categoryToAddItemToGuid is provided, add the input to the default category
            if (categoryToAddItemToGuid == String.Empty)
            {
                var defaultCategory = graphData.categories.FirstOrDefault();
                AssertHelpers.IsNotNull(defaultCategory, "Default category reference is null.");
                if (defaultCategory != null)
                {
                    var addItemToCategoryAction = new AddItemToCategoryAction();
                    addItemToCategoryAction.categoryGuid = defaultCategory.categoryGuid;
                    addItemToCategoryAction.itemToAdd    = shaderInputReference;
                    graphData.owner.graphDataStore.Dispatch(addItemToCategoryAction);
                }
            }
            else
            {
                var addItemToCategoryAction = new AddItemToCategoryAction();
                addItemToCategoryAction.categoryGuid = categoryToAddItemToGuid;
                addItemToCategoryAction.itemToAdd    = shaderInputReference;
                graphData.owner.graphDataStore.Dispatch(addItemToCategoryAction);
            }
        }
        public 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();
        }
Example #6
0
        public void ToSubGraph()
        {
            var graphView = graphEditorView.graphView;

            string path;
            string sessionStateResult = SessionState.GetString(k_PrevSubGraphPathKey, k_PrevSubGraphPathDefaultValue);
            string pathToOriginSG     = Path.GetDirectoryName(AssetDatabase.GUIDToAssetPath(selectedGuid));

            if (!sessionStateResult.Equals(k_PrevSubGraphPathDefaultValue))
            {
                path = sessionStateResult;
            }
            else
            {
                path = pathToOriginSG;
            }

            path = EditorUtility.SaveFilePanelInProject("Save Sub Graph", "New Shader Sub Graph", ShaderSubGraphImporter.Extension, "", path);
            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 propertyNodes  = graphView.selection.OfType <IShaderNodeView>().Where(x => (x.node is PropertyNode)).Select(x => ((PropertyNode)x.node).property);
            var metaProperties = graphView.graph.properties.Where(x => propertyNodes.Contains(x));

            // Collect the keyword nodes and get the corresponding keywords
            var keywordNodes = graphView.selection.OfType <IShaderNodeView>().Where(x => (x.node is KeywordNode)).Select(x => ((KeywordNode)x.node).keyword);
            var metaKeywords = graphView.graph.keywords.Where(x => keywordNodes.Contains(x));

            var copyPasteGraph = new CopyPasteGraph(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 Graphing.Edge),
                                                    graphInputs,
                                                    metaProperties,
                                                    metaKeywords,
                                                    graphView.selection.OfType <StickyNote>().Select(x => x.userData),
                                                    true);

            // why do we serialize and deserialize only to make copies of everything in the steps below?
            // is this just to clear out all non-serialized data?
            var deserialized = CopyPasteGraph.FromJson(MultiJson.Serialize(copyPasteGraph), graphView.graph);

            if (deserialized == null)
            {
                return;
            }

            var subGraph = new GraphData {
                isSubGraph = true, 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);
            subGraph.outputNode = subGraphOutputNode;

            // Always copy deserialized keyword inputs
            foreach (ShaderKeyword keyword in deserialized.metaKeywords)
            {
                var copiedInput = (ShaderKeyword)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.keyword == keyword);
                foreach (var node in dependentKeywordNodes)
                {
                    node.owner   = graphView.graph;
                    node.keyword = copiedInput;
                }
            }

            foreach (GroupData groupData in deserialized.groups)
            {
                subGraph.CreateGroup(groupData);
            }

            foreach (var node in deserialized.GetNodes <AbstractMaterialNode>())
            {
                var drawState = node.drawState;
                drawState.position = new Rect(drawState.position.position - middle, drawState.position.size);
                node.drawState     = drawState;

                // Checking if the group guid is also being copied.
                // If not then nullify that guid
                if (node.group != null && !subGraph.groups.Contains(node.group))
                {
                    node.group = null;
                }

                subGraph.AddNode(node);
            }

            foreach (var note in deserialized.stickyNotes)
            {
                if (note.group != null && !subGraph.groups.Contains(note.group))
                {
                    note.group = null;
                }

                subGraph.AddStickyNote(note);
            }

            // figure out what needs remapping
            var externalOutputSlots = new List <Graphing.Edge>();
            var externalInputSlots  = new List <Graphing.Edge>();

            foreach (var edge in deserialized.edges)
            {
                var outputSlot = edge.outputSlot;
                var inputSlot  = edge.inputSlot;

                var outputSlotExistsInSubgraph = subGraph.ContainsNode(outputSlot.node);
                var inputSlotExistsInSubgraph  = subGraph.ContainsNode(inputSlot.node);

                // pasting nice internal links!
                if (outputSlotExistsInSubgraph && inputSlotExistsInSubgraph)
                {
                    subGraph.Connect(outputSlot, inputSlot);
                }
                // 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 = sr.node;
                var fromSlot = sr.slot;

                var materialGraph = graphObject.graph;
                var fromProperty  = fromNode is PropertyNode fromPropertyNode
                    ? materialGraph.properties.FirstOrDefault(p => p == fromPropertyNode.property)
                    : null;

                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;

                case ConcreteSlotValueType.VirtualTexture:
                    prop = new VirtualTextureShaderProperty()
                    {
                        // also copy the VT settings over from the original property (if there is one)
                        value = (fromProperty as VirtualTextureShaderProperty)?.value ?? new SerializableVirtualTexture()
                    };
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }

                prop.displayName = fromProperty != null
                    ? fromProperty.displayName
                    : fromSlot.concreteValueType.ToString();
                prop.displayName = GraphUtil.SanitizeName(subGraph.addedInputs.Select(p => p.displayName), "{0} ({1})",
                                                          prop.displayName);

                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.property = prop;

                foreach (var edge in group.edges)
                {
                    subGraph.Connect(
                        new SlotReference(propNode, PropertyNode.OutputSlotId),
                        edge.inputSlot);
                    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 = group.edges[0].outputSlot.node;
                MaterialSlot         slot = node.FindSlot <MaterialSlot>(group.edges[0].outputSlot.slotId);
                var slotId = outputNode.AddSlot(slot.concreteValueType);

                var inputSlotRef = new SlotReference(outputNode, slotId);

                foreach (var edge in group.edges)
                {
                    var newEdge = subGraph.Connect(edge.outputSlot, inputSlotRef);
                    externalOutputsNeedingConnection.Add(new KeyValuePair <IEdge, IEdge>(edge, newEdge));
                }
            }

            if (FileUtilities.WriteShaderGraphToDisk(path, subGraph))
            {
                AssetDatabase.ImportAsset(path);
            }

            // Store path for next time
            if (!pathToOriginSG.Equals(Path.GetDirectoryName(path)))
            {
                SessionState.SetString(k_PrevSubGraphPathKey, Path.GetDirectoryName(path));
            }
            else
            {
                // Or continue to make it so that next time it will open up in the converted-from SG's directory
                SessionState.EraseString(k_PrevSubGraphPathKey);
            }

            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
            var firstNode = copyPasteGraph.GetNodes <AbstractMaterialNode>().FirstOrDefault();

            if (firstNode != null && copyPasteGraph.GetNodes <AbstractMaterialNode>().All(x => x.group == firstNode.group))
            {
                subGraphNode.group = firstNode.group;
            }

            subGraphNode.asset = loadedSubGraph;
            graphObject.graph.AddNode(subGraphNode);

            foreach (var edgeMap in externalInputNeedingConnection)
            {
                graphObject.graph.Connect(edgeMap.Key.outputSlot, new SlotReference(subGraphNode, edgeMap.Value.guid.GetHashCode()));
            }

            foreach (var edgeMap in externalOutputsNeedingConnection)
            {
                graphObject.graph.Connect(new SlotReference(subGraphNode, 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();
        }
Example #7
0
        void AddInputRow(ShaderInput input, bool create = false, int index = -1)
        {
            if (m_InputRows.ContainsKey(input.guid))
            {
                return;
            }

            if (create)
            {
                m_Graph.SanitizeGraphInputName(input);
            }

            if (index < 0)
            {
                index = m_InputRows.Count;
            }

            BlackboardField field = null;
            BlackboardRow   row   = null;

            switch (input)
            {
            case AbstractShaderProperty property:
            {
                var icon = (m_Graph.isSubGraph || (property.isExposable && property.generatePropertyBlock)) ? exposedIcon : null;
                field = new BlackboardField(icon, property.displayName, property.propertyType.ToString())
                {
                    userData = property
                };
                var propertyView = new BlackboardFieldPropertyView(field, m_Graph, property);
                row = new BlackboardRow(field, propertyView)
                {
                    userData = input
                };
                if (index == m_PropertySection.childCount)
                {
                    m_PropertySection.Add(row);
                }
                else
                {
                    m_PropertySection.Insert(index, row);
                }
                break;
            }

            default:
                throw new ArgumentOutOfRangeException();
            }

            if (field == null || row == null)
            {
                return;
            }

            var pill = row.Q <Pill>();

            pill.RegisterCallback <MouseEnterEvent>(evt => OnMouseHover(evt, input));
            pill.RegisterCallback <MouseLeaveEvent>(evt => OnMouseHover(evt, input));
            pill.RegisterCallback <DragUpdatedEvent>(OnDragUpdatedEvent);

            var expandButton = row.Q <Button>("expandButton");

            expandButton.RegisterCallback <MouseDownEvent>(evt => OnExpanded(evt, input), TrickleDown.TrickleDown);

            m_InputRows[input.guid]          = row;
            m_InputRows[input.guid].expanded = SessionState.GetBool(input.guid.ToString(), true);

            if (create)
            {
                row.expanded = true;
                m_Graph.owner.RegisterCompleteObjectUndo("Create Graph Input");
                m_Graph.AddGraphInput(input);
                field.OpenTextEditor();
            }
        }
Example #8
0
        void AddInputRow(ShaderInput input, bool addToGraph = false, int index = -1)
        {
            if (m_InputRows.ContainsKey(input))
            {
                return;
            }

            if (addToGraph)
            {
                m_Graph.owner.RegisterCompleteObjectUndo("Create Graph Input");

                // this pathway is mostly used for adding newly inputs to the graph
                // so this is setting up the default state for those inputs
                // here we flag it exposed, if the input type is exposable
                input.generatePropertyBlock = input.isExposable;

                m_Graph.AddGraphInput(input);       // TODO: index after currently selected property
            }

            BlackboardFieldView field = null;
            BlackboardRow       row   = null;

            switch (input)
            {
            case AbstractShaderProperty property:
            {
                var icon = (m_Graph.isSubGraph || property.isExposed) ? exposedIcon : null;
                field = new BlackboardFieldView(m_Graph, property, icon, property.displayName, property.GetPropertyTypeString())
                {
                    userData = property
                };
                field.RegisterCallback <AttachToPanelEvent>(UpdateSelectionAfterUndoRedo);
                property.onBeforeVersionChange += (_) => m_Graph.owner.RegisterCompleteObjectUndo($"Change {property.displayName} Version");
                void UpdateField()
                {
                    field.typeText = property.GetPropertyTypeString();
                    field.InspectorUpdateTrigger();
                }

                property.onAfterVersionChange += UpdateField;
                row = new BlackboardRow(field, null);

                if (index < 0 || index > m_InputRows.Count)
                {
                    index = m_InputRows.Count;
                }

                if (index == m_InputRows.Count)
                {
                    m_PropertySection.Add(row);
                }
                else
                {
                    m_PropertySection.Insert(index, row);
                }

                break;
            }

            case ShaderKeyword keyword:
            {
                var icon = (m_Graph.isSubGraph || keyword.isExposed) ? exposedIcon : null;

                string typeText = keyword.keywordType.ToString() + " Keyword";
                typeText = keyword.isBuiltIn ? "Built-in " + typeText : typeText;

                field = new BlackboardFieldView(m_Graph, keyword, icon, keyword.displayName, typeText)
                {
                    userData = keyword
                };
                field.RegisterCallback <AttachToPanelEvent>(UpdateSelectionAfterUndoRedo);
                row = new BlackboardRow(field, null);

                if (index < 0 || index > m_InputRows.Count)
                {
                    index = m_InputRows.Count;
                }

                if (index == m_InputRows.Count)
                {
                    m_KeywordSection.Add(row);
                }
                else
                {
                    m_KeywordSection.Insert(index, row);
                }

                break;
            }

            default:

                throw new ArgumentOutOfRangeException();
            }

            field.RegisterCallback <MouseEnterEvent>(evt => OnMouseHover(evt, input));
            field.RegisterCallback <MouseLeaveEvent>(evt => OnMouseHover(evt, input));
            field.RegisterCallback <DragUpdatedEvent>(OnDragUpdatedEvent);
            // These callbacks are used for the property dragging scroll behavior
            field.RegisterCallback <DragEnterEvent>(evt => blackboard.ShowScrollBoundaryRegions());
            field.RegisterCallback <DragExitedEvent>(evt => blackboard.HideScrollBoundaryRegions());

            // Removing the expand button from the blackboard, its added by default
            var expandButton = row.Q <Button>("expandButton");

            expandButton.RemoveFromHierarchy();

            m_InputRows[input] = row;

            if (!addToGraph)
            {
                m_InputRows[input].expanded = SessionState.GetBool($"Unity.ShaderGraph.Input.{input.objectId}.isExpanded", false);
            }
            else
            {
                row.expanded = true;
                field.OpenTextEditor();
                if (input as ShaderKeyword != null)
                {
                    m_Graph.OnKeywordChangedNoValidate();
                }
            }
        }
Example #9
0
        void AddInputRow(ShaderInput input, bool create = false, int index = -1)
        {
            if (m_InputRows.ContainsKey(input))
            {
                return;
            }

            if (create)
            {
                m_Graph.SanitizeGraphInputName(input);
                input.generatePropertyBlock = input.isExposable;
            }

            BlackboardFieldView field = null;
            BlackboardRow       row   = null;

            switch (input)
            {
            case AbstractShaderProperty property:
            {
                var icon = (m_Graph.isSubGraph || (property.isExposable && property.generatePropertyBlock)) ? exposedIcon : null;
                field = new BlackboardFieldView(m_Graph, property, UpdateBlackboardView, icon, property.displayName, property.GetPropertyTypeString())
                {
                    userData = property
                };
                field.RegisterCallback <AttachToPanelEvent>(UpdateSelectionAfterUndoRedo);
                property.onBeforeVersionChange += (_) => m_Graph.owner.RegisterCompleteObjectUndo($"Change {property.displayName} Version");
                void UpdateField()
                {
                    field.typeText = property.GetPropertyTypeString();
                    field.InspectorUpdateTrigger();
                }

                property.onAfterVersionChange += UpdateField;
                row = new BlackboardRow(field, null);

                if (index < 0 || index > m_InputRows.Count)
                {
                    index = m_InputRows.Count;
                }

                if (index == m_InputRows.Count)
                {
                    m_PropertySection.Add(row);
                }
                else
                {
                    m_PropertySection.Insert(index, row);
                }

                break;
            }

            case ShaderKeyword keyword:
            {
                var icon = (m_Graph.isSubGraph || (keyword.isExposable && keyword.generatePropertyBlock)) ? exposedIcon : null;

                string typeText = keyword.keywordType.ToString() + " Keyword";
                typeText = keyword.isBuiltIn ? "Built-in " + typeText : typeText;

                field = new BlackboardFieldView(m_Graph, keyword, UpdateBlackboardView, icon, keyword.displayName, typeText)
                {
                    userData = keyword
                };
                field.RegisterCallback <AttachToPanelEvent>(UpdateSelectionAfterUndoRedo);
                row = new BlackboardRow(field, null);

                if (index < 0 || index > m_InputRows.Count)
                {
                    index = m_InputRows.Count;
                }

                if (index == m_InputRows.Count)
                {
                    m_KeywordSection.Add(row);
                }
                else
                {
                    m_KeywordSection.Insert(index, row);
                }

                break;
            }

            default:
                throw new ArgumentOutOfRangeException();
            }

            field.RegisterCallback <MouseEnterEvent>(evt => OnMouseHover(evt, input));
            field.RegisterCallback <MouseLeaveEvent>(evt => OnMouseHover(evt, input));
            field.RegisterCallback <DragUpdatedEvent>(OnDragUpdatedEvent);

            // Removing the expand button from the blackboard, its added by default
            var expandButton = row.Q <Button>("expandButton");

            expandButton.RemoveFromHierarchy();

            m_InputRows[input] = row;

            if (!create)
            {
                m_InputRows[input].expanded = SessionState.GetBool($"Unity.ShaderGraph.Input.{input.objectId}.isExpanded", false);
            }
            else
            {
                row.expanded = true;
                m_Graph.owner.RegisterCompleteObjectUndo("Create Graph Input");
                m_Graph.AddGraphInput(input);
                field.OpenTextEditor();

                if (input as ShaderKeyword != null)
                {
                    m_Graph.OnKeywordChangedNoValidate();
                }
            }
        }
Example #10
0
 public void AddInputToGraph(ShaderInput input)
 {
     _graph.AddGraphInput(input);
 }
Example #11
0
        void AddInputRow(ShaderInput input, bool create = false, int index = -1)
        {
            if (m_InputRows.ContainsKey(input.guid))
            {
                return;
            }

            if (create)
            {
                m_Graph.SanitizeGraphInputName(input);
                input.generatePropertyBlock = input.isExposable;
            }

            BlackboardField field = null;
            BlackboardRow   row   = null;

            switch (input)
            {
            case AbstractShaderProperty property:
            {
                var icon = (m_Graph.isSubGraph || (property.isExposable && property.generatePropertyBlock)) ? exposedIcon : null;
                field = new BlackboardField(icon, property.displayName, property.propertyType.ToString())
                {
                    userData = property
                };
                var propertyView = new BlackboardFieldPropertyView(field, m_Graph, property);
                row = new BlackboardRow(field, propertyView)
                {
                    userData = input
                };

                if (index < 0 || index > m_InputRows.Count)
                {
                    index = m_InputRows.Count;
                }

                if (index == m_InputRows.Count)
                {
                    m_PropertySection.Add(row);
                }
                else
                {
                    m_PropertySection.Insert(index, row);
                }

                break;
            }

            case ShaderKeyword keyword:
            {
                var icon = (m_Graph.isSubGraph || (keyword.isExposable && keyword.generatePropertyBlock)) ? exposedIcon : null;

                string typeText = keyword.keywordType.ToString() + " Keyword";
                typeText = keyword.isBuiltIn ? "Built-in " + typeText : typeText;

                field = new BlackboardField(icon, keyword.displayName, typeText)
                {
                    userData = keyword
                };
                var keywordView = new BlackboardFieldKeywordView(field, m_Graph, keyword);
                row = new BlackboardRow(field, keywordView);

                if (index < 0 || index > m_InputRows.Count)
                {
                    index = m_InputRows.Count;
                }

                if (index == m_InputRows.Count)
                {
                    m_KeywordSection.Add(row);
                }
                else
                {
                    m_KeywordSection.Insert(index, row);
                }

                break;
            }

            default:
                throw new ArgumentOutOfRangeException();
            }

            if (field == null || row == null)
            {
                return;
            }

            var pill = row.Q <Pill>();

            pill.RegisterCallback <MouseEnterEvent>(evt => OnMouseHover(evt, input));
            pill.RegisterCallback <MouseLeaveEvent>(evt => OnMouseHover(evt, input));
            pill.RegisterCallback <DragUpdatedEvent>(OnDragUpdatedEvent);

            var expandButton = row.Q <Button>("expandButton");

            expandButton.RegisterCallback <MouseDownEvent>(evt => OnExpanded(evt, input), TrickleDown.TrickleDown);

            m_InputRows[input.guid] = row;

            if (!create)
            {
                m_InputRows[input.guid].expanded = SessionState.GetBool($"Unity.ShaderGraph.Input.{input.guid.ToString()}.isExpanded", false);
            }
            else
            {
                row.expanded = true;
                m_ExpandedInputs[input.guid] = true;
                m_Graph.owner.RegisterCompleteObjectUndo("Create Graph Input");
                m_Graph.AddGraphInput(input);
                field.OpenTextEditor();

                if (input as ShaderKeyword != null)
                {
                    m_Graph.OnKeywordChangedNoValidate();
                }
            }
        }