예제 #1
0
        void HandleVirtualTextureProperty(PropertySheet propertySheet, VirtualTextureShaderProperty virtualTextureProperty)
        {
            var container = new IMGUIContainer(() => OnVTGUIHandler(virtualTextureProperty))
            {
                name = "ListContainer"
            };

            AddPropertyRowToSheet(propertySheet, container, "Entries");
        }
예제 #2
0
        private void OnVTGUIHandler(VirtualTextureShaderProperty property)
        {
            if (m_VTReorderableList == null)
            {
                VTRecreateList(property);
                VTAddCallbacks(property);
            }

            m_VTReorderableList.index = m_VTSelectedIndex;
            m_VTReorderableList.DoLayoutList();
        }
예제 #3
0
        private void VTRemoveEntry(ReorderableList list, VirtualTextureShaderProperty property)
        {
            this._preChangeValueCallback("Remove Virtual Texture Entry");

            // Remove entry
            m_VTSelectedIndex = list.index;
            var selectedEntry = (SerializableVirtualTextureLayer)m_VTReorderableList.list[list.index];

            property.value.layers.Remove(selectedEntry);

            // Update Blackboard & Nodes
            //DirtyNodes();
            this._postChangeValueCallback(true);
            m_VTSelectedIndex = m_VTSelectedIndex >= list.list.Count - 1 ? list.list.Count - 1 : m_VTSelectedIndex;
        }
예제 #4
0
        // Allowed indicies are 1-MAX_ENUM_ENTRIES
        private int VTGetFirstUnusedID(VirtualTextureShaderProperty property)
        {
            List <int> ususedIDs = new List <int>();

            foreach (SerializableVirtualTextureLayer virtualTextureEntry in property.value.layers)
            {
                ususedIDs.Add(property.value.layers.IndexOf(virtualTextureEntry));
            }

            for (int x = 1; x <= 4; x++)
            {
                if (!ususedIDs.Contains(x))
                {
                    return(x);
                }
            }

            Debug.LogError("GetFirstUnusedID: Attempting to get unused ID when all IDs are used.");
            return(-1);
        }
예제 #5
0
        private void VTAddEntry(ReorderableList list, VirtualTextureShaderProperty property)
        {
            this._preChangeValueCallback("Add Virtual Texture Entry");

            int index = VTGetFirstUnusedID(property);

            if (index <= 0)
            {
                return; // Error has already occured, don't attempt to add this entry.
            }
            var layerName = "Layer" + index.ToString();

            // Add new entry
            property.value.layers.Add(new SerializableVirtualTextureLayer(layerName, layerName, new SerializableTexture()));

            // Update Blackboard & Nodes
            //DirtyNodes();
            this._postChangeValueCallback(true);
            m_VTSelectedIndex = list.list.Count - 1;
        }
예제 #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();
        }
예제 #7
0
        private void VTAddCallbacks(VirtualTextureShaderProperty property)
        {
            // Draw Header
            m_VTReorderableList.drawHeaderCallback = (Rect rect) =>
            {
                int indent      = 14;
                var displayRect = new Rect(rect.x + indent, rect.y, (rect.width - indent) / 3, rect.height);
                EditorGUI.LabelField(displayRect, "Display Name");
                var referenceRect = new Rect((rect.x) + (rect.width - indent) / 3, rect.y, (rect.width - indent) / 3, rect.height);
                EditorGUI.LabelField(referenceRect, "Reference Name");
                var textureRect = new Rect((rect.x) + (rect.width - indent) / 3 * 2, rect.y, (rect.width - indent) / 3, rect.height);
                EditorGUI.LabelField(textureRect, "Texture Asset");
            };

            // Draw Element
            m_VTReorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
            {
                SerializableVirtualTextureLayer entry = ((SerializableVirtualTextureLayer)m_VTReorderableList.list[index]);
                EditorGUI.BeginChangeCheck();

                var layerName      = EditorGUI.DelayedTextField(new Rect(rect.x, rect.y, rect.width / 3, EditorGUIUtility.singleLineHeight), entry.layerName, EditorStyles.label);
                var layerRefName   = EditorGUI.DelayedTextField(new Rect((rect.x + rect.width) / 3, rect.y, rect.width / 3, EditorGUIUtility.singleLineHeight), entry.layerRefName, EditorStyles.label);
                var selectedObject = EditorGUI.ObjectField(new Rect((rect.x + rect.width) / 3 * 2, rect.y, rect.width / 3, EditorGUIUtility.singleLineHeight), entry.layerTexture.texture, typeof(Texture), false);

                SerializableTexture layerTexture = new SerializableTexture();
                layerTexture.texture = (Texture)selectedObject;

                if (EditorGUI.EndChangeCheck())
                {
                    //need to sanitize each layer with all existing properties
                    string oldLayerName = property.value.layers[index].layerName;
                    if (layerName != oldLayerName)
                    {
                        var otherPropertyNames = graphData.BuildPropertyDisplayNameList(property, oldLayerName);
                        layerName = GraphUtil.SanitizeName(otherPropertyNames, "{0} ({1})", layerName);
                    }

                    string oldLayerRefName = property.value.layers[index].layerRefName;
                    if (layerRefName != oldLayerRefName)
                    {
                        if (!string.IsNullOrEmpty(layerRefName))
                        {
                            string name = layerRefName.Trim();
                            if (!string.IsNullOrEmpty(layerRefName))
                            {
                                if (Regex.IsMatch(name, @"^\d+"))
                                {
                                    name = "_" + name;
                                }
                                name = Regex.Replace(name, @"(?:[^A-Za-z_0-9])|(?:\s)", "_");
                                var otherPropertyRefNames = graphData.BuildPropertyReferenceNameList(property, oldLayerRefName);
                                layerRefName = GraphUtil.SanitizeName(otherPropertyRefNames, "{0}_{1}", name);
                            }
                        }
                    }

                    property.value.layers[index] = new SerializableVirtualTextureLayer(layerName, layerRefName, layerTexture);

                    //DirtyNodes();
                    this._postChangeValueCallback(true);
                }
            };

            // Element height
            m_VTReorderableList.elementHeightCallback = (int indexer) =>
            {
                return(m_VTReorderableList.elementHeight);
            };

            // Can add
            m_VTReorderableList.onCanAddCallback = (ReorderableList list) =>
            {
                return(list.count < 4);
            };

            // Can remove
            m_VTReorderableList.onCanRemoveCallback = (ReorderableList list) =>
            {
                return(list.count > 1);
            };

            void AddEntryLamda(ReorderableList list) => VTAddEntry(list, property);
            void RemoveEntryLamda(ReorderableList list) => VTRemoveEntry(list, property);

            // Add callback delegates
            m_VTReorderableList.onSelectCallback  += VTSelectEntry;
            m_VTReorderableList.onAddCallback     += AddEntryLamda;
            m_VTReorderableList.onRemoveCallback  += RemoveEntryLamda;
            m_VTReorderableList.onReorderCallback += VTReorderEntries;
        }
예제 #8
0
 internal void VTRecreateList(VirtualTextureShaderProperty property)
 {
     // Create reorderable list from entries
     m_VTReorderableList = new ReorderableList(property.value.layers, typeof(SerializableVirtualTextureLayer), true, true, true, true);
 }