diff --git a/com.unity.shadergraph/CHANGELOG.md b/com.unity.shadergraph/CHANGELOG.md index 4c001bb6cab..d3e01b472ed 100644 --- a/com.unity.shadergraph/CHANGELOG.md +++ b/com.unity.shadergraph/CHANGELOG.md @@ -31,9 +31,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Shaders using SamplerState types now compile with GLES2 (SamplerStates are ignored, falls back to Texture-associated sampler state) [1292031] - Fixed Graph Inspector scaling that was allocating too much space to the labels [1268134] -### Fixed +### Fixed +- Fixed some issues with our Convert To Subgraph contextual menu to allow passthrough and fix inputs/outputs getting lost. - Fixed issue where a NullReferenceException would be thrown on resetting reference name for a Shader Graph property -- ## [10.2.0] - 2020-10-19 @@ -64,6 +64,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed an issue where unknown type Nodes (i.e. HDRP-only nodes used without HDRP package) could be copied, resulting in an unloadable graph [1288475] - Fixed an issue where dropping HDRP-only properties from the blackboard field into the graph would soft-lock the graph [1288887] + ## [10.1.0] - 2020-10-12 ### Added diff --git a/com.unity.shadergraph/Editor/Data/Graphs/GraphData.cs b/com.unity.shadergraph/Editor/Data/Graphs/GraphData.cs index 1ab33bb9e43..511c9c51751 100644 --- a/com.unity.shadergraph/Editor/Data/Graphs/GraphData.cs +++ b/com.unity.shadergraph/Editor/Data/Graphs/GraphData.cs @@ -1063,6 +1063,7 @@ void RemoveEdgeNoValidate(IEdge e, bool reevaluateActivity = true) if (m_NodeEdges.TryGetValue(output.objectId, out outputNodeEdges)) outputNodeEdges.Remove(e); + m_AddedEdges.Remove(e); m_RemovedEdges.Add(e); if (b != null) { @@ -1135,6 +1136,21 @@ public IEnumerable GetEdges(SlotReference s) return edges; } + public void GetEdges(AbstractMaterialNode node, List foundEdges) + { + if (m_NodeEdges.TryGetValue(node.objectId, out var edges)) + { + foundEdges.AddRange(edges); + } + } + + public IEnumerable GetEdges(AbstractMaterialNode node) + { + List edges = new List(); + GetEdges(node, edges); + return edges; + } + public void ForeachHLSLProperty(Action action) { foreach (var prop in properties) diff --git a/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs b/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs index 125a6ba24c7..0caa250fc2e 100644 --- a/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs +++ b/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs @@ -688,13 +688,14 @@ public void ToSubGraph() var metaKeywords = graphView.graph.keywords.Where(x => keywordNodes.Contains(x)); var copyPasteGraph = new CopyPasteGraph(graphView.selection.OfType().Select(x => x.userData), - graphView.selection.OfType().Where(x => !(x.node is PropertyNode || x.node is SubGraphOutputNode)).Select(x => x.node).Where(x => x.allowedInSubGraph).ToArray(), + nodes, graphView.selection.OfType().Select(x => x.userData as Graphing.Edge), graphInputs, metaProperties, metaKeywords, graphView.selection.OfType().Select(x => x.userData), - true); + true, + false); // 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? @@ -763,6 +764,7 @@ public void ToSubGraph() // figure out what needs remapping var externalOutputSlots = new List(); var externalInputSlots = new List(); + var passthroughSlots = new List(); foreach (var edge in deserialized.edges) { var outputSlot = edge.outputSlot; @@ -785,6 +787,12 @@ public void ToSubGraph() { externalOutputSlots.Add(edge); } + else + { + externalInputSlots.Add(edge); + externalOutputSlots.Add(edge); + passthroughSlots.Add(edge); + } } // Find the unique edges coming INTO the graph @@ -800,6 +808,9 @@ public void ToSubGraph() const int subtractHeight = 20; var propPos = new Vector2(0, -((amountOfProps / 2) + height) - subtractHeight); + var passthroughSlotRefLookup = new Dictionary(); + + var passedInProperties = new Dictionary(); foreach (var group in uniqueIncomingEdges) { var sr = group.slotRef; @@ -812,68 +823,78 @@ public void ToSubGraph() : null; AbstractShaderProperty prop; - switch (fromSlot.concreteValueType) + if (fromProperty != null && passedInProperties.TryGetValue(fromProperty, out prop)) { - 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(); } + else + { + 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); + 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); + if (fromProperty != null) + { + passedInProperties.Add(fromProperty, prop); + } + } - subGraph.AddGraphInput(prop); var propNode = new PropertyNode(); { var drawState = propNode.drawState; @@ -885,13 +906,45 @@ public void ToSubGraph() subGraph.AddNode(propNode); propNode.property = prop; + + Vector2 avg = Vector2.zero; foreach (var edge in group.edges) { - subGraph.Connect( - new SlotReference(propNode, PropertyNode.OutputSlotId), - edge.inputSlot); - externalInputNeedingConnection.Add(new KeyValuePair(edge, prop)); + if (passthroughSlots.Contains(edge) && !passthroughSlotRefLookup.ContainsKey(sr)) + { + passthroughSlotRefLookup.Add(sr, new SlotReference(propNode, PropertyNode.OutputSlotId)); + } + else + { + subGraph.Connect( + new SlotReference(propNode, PropertyNode.OutputSlotId), + edge.inputSlot); + + int i; + var inputs = edge.inputSlot.node.GetInputSlots().ToList(); + + for (i = 0; i < inputs.Count; ++i) + { + if (inputs[i].slotReference.slotId == edge.inputSlot.slotId) + { + break; + } + } + avg += new Vector2(edge.inputSlot.node.drawState.position.xMin, edge.inputSlot.node.drawState.position.center.y + 30f * i); + } + //we collapse input properties so dont add edges that are already being added + if (!externalInputNeedingConnection.Any(x => x.Key.outputSlot.slot == edge.outputSlot.slot && x.Value == prop)) + { + externalInputNeedingConnection.Add(new KeyValuePair(edge, prop)); + } } + avg /= group.edges.Count; + var pos = avg - new Vector2(150f, 0f); + propNode.drawState = new DrawState() + { + position = new Rect(pos, propNode.drawState.position.size), + expanded = propNode.drawState.expanded + }; } var uniqueOutgoingEdges = externalInputSlots.GroupBy( @@ -912,7 +965,7 @@ public void ToSubGraph() foreach (var edge in group.edges) { - var newEdge = subGraph.Connect(edge.outputSlot, inputSlotRef); + var newEdge = subGraph.Connect(passthroughSlotRefLookup.TryGetValue(edge.outputSlot, out SlotReference remap) ? remap : edge.outputSlot, inputSlotRef); externalOutputsNeedingConnection.Add(new KeyValuePair(edge, newEdge)); } } @@ -961,10 +1014,46 @@ public void ToSubGraph() } graphObject.graph.RemoveElements( - graphView.selection.OfType().Select(x => x.node).Where(x => x.allowedInSubGraph).ToArray(), + graphView.selection.OfType().Select(x => x.node).Where(x => !(x is PropertyNode || x is SubGraphOutputNode) && x.allowedInSubGraph).ToArray(), new IEdge[] {}, new GroupData[] {}, graphView.selection.OfType().Select(x => x.userData).ToArray()); + + List moved = new List(); + foreach (var nodeView in graphView.selection.OfType()) + { + var node = nodeView.node; + if (graphView.graph.removedNodes.Contains(node) || node is SubGraphOutputNode) + { + continue; + } + + var edges = graphView.graph.GetEdges(node); + int numEdges = edges.Count(); + if (numEdges == 0) + { + graphView.graph.RemoveNode(node); + } + else if (numEdges == 1 && edges.First().inputSlot.node != node) //its an output edge + { + var edge = edges.First(); + int i; + var inputs = edge.inputSlot.node.GetInputSlots().ToList(); + for (i = 0; i < inputs.Count; ++i) + { + if (inputs[i].slotReference.slotId == edge.inputSlot.slotId) + { + break; + } + } + node.drawState = new DrawState() + { + position = new Rect(new Vector2(edge.inputSlot.node.drawState.position.xMin, edge.inputSlot.node.drawState.position.center.y) - new Vector2(150f, -30f * i), node.drawState.position.size), + expanded = node.drawState.expanded + }; + (nodeView as GraphElement).SetPosition(node.drawState.position); + } + } graphObject.graph.ValidateGraph(); } diff --git a/com.unity.shadergraph/Editor/Util/CopyPasteGraph.cs b/com.unity.shadergraph/Editor/Util/CopyPasteGraph.cs index 073b57308d5..51145a2f04e 100644 --- a/com.unity.shadergraph/Editor/Util/CopyPasteGraph.cs +++ b/com.unity.shadergraph/Editor/Util/CopyPasteGraph.cs @@ -47,7 +47,8 @@ sealed class CopyPasteGraph : JsonObject public CopyPasteGraph() {} public CopyPasteGraph(IEnumerable groups, IEnumerable nodes, IEnumerable edges, - IEnumerable inputs, IEnumerable metaProperties, IEnumerable metaKeywords, IEnumerable notes, bool keepOutputEdges = false) + IEnumerable inputs, IEnumerable metaProperties, IEnumerable metaKeywords, IEnumerable notes, + bool keepOutputEdges = false, bool removeOrphanEdges = true) { if (groups != null) { @@ -103,10 +104,12 @@ public CopyPasteGraph(IEnumerable groups, IEnumerable nodeSet.Contains(edge.inputSlot.node) || (keepOutputEdges && nodeSet.Contains(edge.outputSlot.node))) - .ToList(); + var distinct = m_Edges.Distinct(); + if (removeOrphanEdges) + { + distinct = distinct.Where(edge => nodeSet.Contains(edge.inputSlot.node) || (keepOutputEdges && nodeSet.Contains(edge.outputSlot.node))); + } + m_Edges = distinct.ToList(); } void AddGroup(GroupData group)