您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
477 行
21 KiB
477 行
21 KiB
using System;
|
|
using UnityEngine.MaterialGraph;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEditor.Experimental.UIElements.GraphView;
|
|
using UnityEditor.Graphing.Util;
|
|
using UnityEngine;
|
|
using UnityEngine.Assertions;
|
|
using UnityEngine.Graphing;
|
|
|
|
namespace UnityEditor.MaterialGraph.Drawing
|
|
{
|
|
[Serializable]
|
|
public class MaterialGraphPresenter : GraphViewPresenter
|
|
{
|
|
HashSet<INode> m_TimeDependentNodes = new HashSet<INode>();
|
|
|
|
protected GraphTypeMapper typeMapper { get; set; }
|
|
|
|
public IGraphAsset graphAsset { get; private set; }
|
|
|
|
[SerializeField]
|
|
EditorWindow m_Container;
|
|
|
|
[SerializeField]
|
|
TitleBarPresenter m_TitleBar;
|
|
|
|
public TitleBarPresenter titleBar
|
|
{
|
|
get { return m_TitleBar; }
|
|
}
|
|
|
|
public GraphInspectorPresenter graphInspectorPresenter
|
|
{
|
|
get { return m_GraphInspectorPresenter; }
|
|
set { m_GraphInspectorPresenter = value; }
|
|
}
|
|
|
|
[SerializeField]
|
|
GraphInspectorPresenter m_GraphInspectorPresenter;
|
|
|
|
protected MaterialGraphPresenter()
|
|
{
|
|
typeMapper = new GraphTypeMapper(typeof(MaterialNodePresenter));
|
|
typeMapper[typeof(AbstractMaterialNode)] = typeof(MaterialNodePresenter);
|
|
typeMapper[typeof(ColorNode)] = typeof(ColorNodePresenter);
|
|
typeMapper[typeof(GradientNode)] = typeof(GradientNodePresenter);
|
|
// typeMapper[typeof(ScatterNode)] = typeof(ScatterNodePresenter);
|
|
//typeMapper[typeof(TextureNode)] = typeof(TextureNodePresenter);
|
|
//typeMapper[typeof(SamplerAssetNode)] = typeof(SamplerAssetNodePresenter);
|
|
//typeMapper[typeof(TextureSamplerNode)] = typeof(TextureSamplerNodePresenter);
|
|
typeMapper[typeof(Texture2DNode)] = typeof(TextureAssetNodePresenter);
|
|
typeMapper[typeof(TextureLODNode)] = typeof(TextureLODNodePresenter);
|
|
typeMapper[typeof(SamplerStateNode)] = typeof(SamplerStateNodePresenter);
|
|
typeMapper[typeof(CubemapNode)] = typeof(CubeNodePresenter);
|
|
typeMapper[typeof(ToggleNode)] = typeof(ToggleNodePresenter);
|
|
typeMapper[typeof(UVNode)] = typeof(UVNodePresenter);
|
|
typeMapper[typeof(Vector1Node)] = typeof(Vector1NodePresenter);
|
|
typeMapper[typeof(Vector2Node)] = typeof(Vector2NodePresenter);
|
|
typeMapper[typeof(Vector3Node)] = typeof(Vector3NodePresenter);
|
|
typeMapper[typeof(Vector4Node)] = typeof(Vector4NodePresenter);
|
|
/* typeMapper[typeof(ScaleOffsetNode)] = typeof(AnyNodePresenter); // anything derived from AnyNode should use the AnyNodePresenter
|
|
typeMapper[typeof(RadialShearNode)] = typeof(AnyNodePresenter); // anything derived from AnyNode should use the AnyNodePresenter
|
|
typeMapper[typeof(SphereWarpNode)] = typeof(AnyNodePresenter); // anything derived from AnyNode should use the AnyNodePresenter
|
|
typeMapper[typeof(SphericalIndentationNode)] = typeof(AnyNodePresenter); // anything derived from AnyNode should use the AnyNodePresenter
|
|
typeMapper[typeof(AACheckerboardNode)] = typeof(AnyNodePresenter); // anything derived from AnyNode should use the AnyNodePresenter
|
|
typeMapper[typeof(AACheckerboard3dNode)] = typeof(AnyNodePresenter); // anything derived from AnyNode should use the AnyNodePresenter*/
|
|
typeMapper[typeof(SubGraphNode)] = typeof(SubgraphNodePresenter);
|
|
typeMapper[typeof(RemapMasterNode)] = typeof(RemapMasterNodePresenter);
|
|
typeMapper[typeof(MasterRemapInputNode)] = typeof(RemapInputNodePresenter);
|
|
typeMapper[typeof(AbstractSubGraphIONode)] = typeof(SubgraphIONodePresenter);
|
|
typeMapper[typeof(AbstractSurfaceMasterNode)] = typeof(SurfaceMasterNodePresenter);
|
|
typeMapper[typeof(LevelsNode)] = typeof(LevelsNodePresenter);
|
|
typeMapper[typeof(ConstantsNode)] = typeof(ConstantsNodePresenter);
|
|
//typeMapper[typeof(SwizzleNode)] = typeof(SwizzleNodePresenter);
|
|
typeMapper[typeof(BlendModeNode)] = typeof(BlendModeNodePresenter);
|
|
// typeMapper[typeof(AddManyNode)] = typeof(AddManyNodePresenter);
|
|
typeMapper[typeof(IfNode)] = typeof(IfNodePresenter);
|
|
//typeMapper[typeof(CustomCodeNode)] = typeof(CustomCodePresenter);
|
|
typeMapper[typeof(Matrix2Node)] = typeof(Matrix2NodePresenter);
|
|
typeMapper[typeof(Matrix3Node)] = typeof(Matrix3NodePresenter);
|
|
typeMapper[typeof(Matrix4Node)] = typeof(Matrix4NodePresenter);
|
|
typeMapper[typeof(MatrixCommonNode)] = typeof(MatrixCommonNodePresenter);
|
|
typeMapper[typeof(TransformNode)] = typeof(TransformNodePresenter);
|
|
// typeMapper[typeof(ConvolutionFilterNode)] = typeof(ConvolutionFilterNodePresenter);
|
|
}
|
|
|
|
public override List<NodeAnchorPresenter> GetCompatibleAnchors(NodeAnchorPresenter startAnchor, NodeAdapter nodeAdapter)
|
|
{
|
|
return allChildren.OfType<NodeAnchorPresenter>()
|
|
.Where(nap => nap.IsConnectable() &&
|
|
nap.orientation == startAnchor.orientation &&
|
|
nap.direction != startAnchor.direction &&
|
|
nodeAdapter.GetAdapter(nap.source, startAnchor.source) != null &&
|
|
(startAnchor is GraphAnchorPresenter && ((GraphAnchorPresenter)nap).slot is MaterialSlot &&
|
|
((MaterialSlot)((GraphAnchorPresenter)startAnchor).slot).IsCompatibleWithInputSlotType(((MaterialSlot)((GraphAnchorPresenter)nap).slot).valueType)))
|
|
.ToList();
|
|
}
|
|
|
|
void OnNodeChanged(INode inNode, ModificationScope scope)
|
|
{
|
|
var dependentNodes = new List<INode>();
|
|
NodeUtils.CollectNodesNodeFeedsInto(dependentNodes, inNode);
|
|
foreach (var node in dependentNodes)
|
|
{
|
|
var theElements = m_Elements.OfType<MaterialNodePresenter>().ToList();
|
|
var found = theElements.Where(x => x.node.guid == node.guid).ToList();
|
|
foreach (var drawableNodeData in found)
|
|
drawableNodeData.OnModified(scope);
|
|
}
|
|
|
|
if (scope == ModificationScope.Topological)
|
|
UpdateData();
|
|
|
|
EditorUtility.SetDirty(graphAsset.GetScriptableObject());
|
|
|
|
if (m_Container != null)
|
|
m_Container.Repaint();
|
|
}
|
|
|
|
void UpdateData()
|
|
{
|
|
// Find all nodes currently being drawn which are no longer in the graph (i.e. deleted)
|
|
var deletedElementPresenters = m_Elements
|
|
.OfType<MaterialNodePresenter>()
|
|
.Where(nd => !graphAsset.graph.GetNodes<INode>().Contains(nd.node))
|
|
.OfType<GraphElementPresenter>()
|
|
.ToList();
|
|
|
|
var deletedEdgePresenters = m_Elements.OfType<GraphEdgePresenter>()
|
|
.Where(ed => !graphAsset.graph.edges.Contains(ed.edge));
|
|
|
|
// Find all edges currently being drawn which are no longer in the graph (i.e. deleted)
|
|
foreach (var edgePresenter in deletedEdgePresenters)
|
|
{
|
|
// Make sure to disconnect the node, otherwise new connections won't be allowed for the used slots
|
|
edgePresenter.output.Disconnect(edgePresenter);
|
|
edgePresenter.input.Disconnect(edgePresenter);
|
|
|
|
var fromNodeGuid = edgePresenter.edge.outputSlot.nodeGuid;
|
|
var fromNodePresenter = m_Elements.OfType<MaterialNodePresenter>().FirstOrDefault(nd => nd.node.guid == fromNodeGuid);
|
|
|
|
var toNodeGuid = edgePresenter.edge.inputSlot.nodeGuid;
|
|
var toNodePresenter = m_Elements.OfType<MaterialNodePresenter>().FirstOrDefault(nd => nd.node.guid == toNodeGuid);
|
|
|
|
if (toNodePresenter != null)
|
|
// Make the input node (i.e. right side of the connection) re-render
|
|
OnNodeChanged(toNodePresenter.node, ModificationScope.Graph);
|
|
|
|
deletedElementPresenters.Add(edgePresenter);
|
|
}
|
|
|
|
// Remove all nodes and edges marked for deletion
|
|
foreach (var elementPresenter in deletedElementPresenters)
|
|
{
|
|
m_Elements.Remove(elementPresenter);
|
|
}
|
|
|
|
var addedNodePresenters = new List<MaterialNodePresenter>();
|
|
|
|
// Find all new nodes and mark for addition
|
|
foreach (var node in graphAsset.graph.GetNodes<INode>())
|
|
{
|
|
// Check whether node already exists
|
|
if (m_Elements.OfType<MaterialNodePresenter>().Any(e => e.node == node))
|
|
continue;
|
|
|
|
var nodePresenter = (MaterialNodePresenter)typeMapper.Create(node);
|
|
node.onModified += OnNodeChanged;
|
|
nodePresenter.Initialize(node);
|
|
addedNodePresenters.Add(nodePresenter);
|
|
}
|
|
|
|
// Create edge data for nodes marked for addition
|
|
var edgePresenters = new List<GraphEdgePresenter>();
|
|
foreach (var addedNodePresenter in addedNodePresenters)
|
|
{
|
|
var addedNode = addedNodePresenter.node;
|
|
foreach (var slot in addedNode.GetOutputSlots<ISlot>())
|
|
{
|
|
var sourceAnchors = addedNodePresenter.outputAnchors.OfType<GraphAnchorPresenter>();
|
|
var sourceAnchor = sourceAnchors.FirstOrDefault(x => x.slot == slot);
|
|
|
|
var edges = addedNode.owner.GetEdges(new SlotReference(addedNode.guid, slot.id));
|
|
foreach (var edge in edges)
|
|
{
|
|
var toNode = addedNode.owner.GetNodeFromGuid(edge.inputSlot.nodeGuid);
|
|
var toSlot = toNode.FindInputSlot<ISlot>(edge.inputSlot.slotId);
|
|
var targetNode = addedNodePresenters.FirstOrDefault(x => x.node == toNode);
|
|
var targetAnchors = targetNode.inputAnchors.OfType<GraphAnchorPresenter>();
|
|
var targetAnchor = targetAnchors.FirstOrDefault(x => x.slot == toSlot);
|
|
|
|
var edgePresenter = CreateInstance<GraphEdgePresenter>();
|
|
edgePresenter.Initialize(edge);
|
|
edgePresenter.output = sourceAnchor;
|
|
edgePresenter.output.Connect(edgePresenter);
|
|
edgePresenter.input = targetAnchor;
|
|
edgePresenter.input.Connect(edgePresenter);
|
|
edgePresenters.Add(edgePresenter);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add nodes marked for addition
|
|
m_Elements.AddRange(addedNodePresenters.OfType<GraphElementPresenter>());
|
|
|
|
// Find edges in the graph that are not being drawn and create edge data for them
|
|
foreach (var edge in graphAsset.graph.edges)
|
|
{
|
|
if (m_Elements.OfType<GraphEdgePresenter>().Any(ed => ed.edge == edge))
|
|
continue;
|
|
|
|
var sourceNode = graphAsset.graph.GetNodeFromGuid(edge.outputSlot.nodeGuid);
|
|
var sourceSlot = sourceNode.FindOutputSlot<ISlot>(edge.outputSlot.slotId);
|
|
var sourceNodePresenter = m_Elements.OfType<MaterialNodePresenter>().FirstOrDefault(x => x.node == sourceNode);
|
|
var sourceAnchorPresenters = sourceNodePresenter.outputAnchors.OfType<GraphAnchorPresenter>();
|
|
var sourceAnchorPresenter = sourceAnchorPresenters.FirstOrDefault(x => x.slot == sourceSlot);
|
|
|
|
var targetNode = graphAsset.graph.GetNodeFromGuid(edge.inputSlot.nodeGuid);
|
|
var targetSlot = targetNode.FindInputSlot<ISlot>(edge.inputSlot.slotId);
|
|
var targetNodePresenter = m_Elements.OfType<MaterialNodePresenter>().FirstOrDefault(x => x.node == targetNode);
|
|
var targetAnchors = targetNodePresenter.inputAnchors.OfType<GraphAnchorPresenter>();
|
|
var targetAnchor = targetAnchors.FirstOrDefault(x => x.slot == targetSlot);
|
|
|
|
OnNodeChanged(targetNodePresenter.node, ModificationScope.Graph);
|
|
|
|
var edgePresenter = CreateInstance<GraphEdgePresenter>();
|
|
edgePresenter.Initialize(edge);
|
|
edgePresenter.output = sourceAnchorPresenter;
|
|
edgePresenter.output.Connect(edgePresenter);
|
|
edgePresenter.input = targetAnchor;
|
|
edgePresenter.input.Connect(edgePresenter);
|
|
edgePresenters.Add(edgePresenter);
|
|
}
|
|
|
|
m_Elements.AddRange(edgePresenters.OfType<GraphElementPresenter>());
|
|
|
|
|
|
// Calculate which nodes require updates each frame (i.e. are time-dependent).
|
|
|
|
// Let the node set contain all the nodes that are directly time-dependent.
|
|
m_TimeDependentNodes.Clear();
|
|
foreach (var node in graphAsset.graph.GetNodes<INode>().Where(x => x is IRequiresTime))
|
|
m_TimeDependentNodes.Add(node);
|
|
|
|
// The wavefront contains time-dependent nodes from which we wish to propagate time-dependency into the
|
|
// nodes that it feeds into.
|
|
var wavefront = new Stack<INode>(m_TimeDependentNodes);
|
|
while (wavefront.Count > 0)
|
|
{
|
|
var node = wavefront.Pop();
|
|
// Loop through all nodes that the node feeds into.
|
|
foreach (var slot in node.GetOutputSlots<ISlot>())
|
|
{
|
|
foreach (var edge in node.owner.GetEdges(slot.slotReference))
|
|
{
|
|
var inputNode = node.owner.GetNodeFromGuid(edge.inputSlot.nodeGuid);
|
|
if (!m_TimeDependentNodes.Contains(inputNode))
|
|
{
|
|
// If the node is not in the set of time-dependent nodes, add it.
|
|
m_TimeDependentNodes.Add(inputNode);
|
|
|
|
// Also add it to the wavefront, such that we can process the nodes that it feeds into.
|
|
wavefront.Push(inputNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update presenters `requiresTime` based on the hash set values.
|
|
foreach (var nodePresenter in m_Elements.OfType<MaterialNodePresenter>())
|
|
nodePresenter.requiresTime = m_TimeDependentNodes.Contains(nodePresenter.node);
|
|
}
|
|
|
|
public virtual void Initialize(IGraphAsset graphAsset, MaterialGraphEditWindow container)
|
|
{
|
|
this.graphAsset = graphAsset;
|
|
m_Container = container;
|
|
|
|
m_TitleBar = CreateInstance<TitleBarPresenter>();
|
|
m_TitleBar.Initialize(container);
|
|
|
|
m_GraphInspectorPresenter = CreateInstance<GraphInspectorPresenter>();
|
|
m_GraphInspectorPresenter.Initialize();
|
|
|
|
if (graphAsset == null)
|
|
return;
|
|
|
|
UpdateData();
|
|
}
|
|
|
|
public void AddNode(INode node)
|
|
{
|
|
graphAsset.graph.AddNode(node);
|
|
EditorUtility.SetDirty(graphAsset.GetScriptableObject());
|
|
UpdateData();
|
|
}
|
|
|
|
public void RemoveElements(IEnumerable<MaterialNodePresenter> nodes, IEnumerable<GraphEdgePresenter> edges)
|
|
{
|
|
graphAsset.graph.RemoveElements(nodes.Select(x => x.node), edges.Select(x => x.edge));
|
|
graphAsset.graph.ValidateGraph();
|
|
EditorUtility.SetDirty(graphAsset.GetScriptableObject());
|
|
UpdateData();
|
|
}
|
|
|
|
public void Connect(GraphAnchorPresenter left, GraphAnchorPresenter right)
|
|
{
|
|
if (left != null && right != null)
|
|
{
|
|
graphAsset.graph.Connect(left.slot.slotReference, right.slot.slotReference);
|
|
EditorUtility.SetDirty(graphAsset.GetScriptableObject());
|
|
UpdateData();
|
|
}
|
|
}
|
|
|
|
CopyPasteGraph CreateCopyPasteGraph(IEnumerable<GraphElementPresenter> selection)
|
|
{
|
|
var graph = new CopyPasteGraph();
|
|
foreach (var presenter in selection)
|
|
{
|
|
var nodePresenter = presenter as MaterialNodePresenter;
|
|
if (nodePresenter != null)
|
|
{
|
|
graph.AddNode(nodePresenter.node);
|
|
foreach (var edge in NodeUtils.GetAllEdges(nodePresenter.node))
|
|
graph.AddEdge(edge);
|
|
}
|
|
|
|
var edgePresenter = presenter as GraphEdgePresenter;
|
|
if (edgePresenter != null)
|
|
graph.AddEdge(edgePresenter.edge);
|
|
}
|
|
return graph;
|
|
}
|
|
|
|
CopyPasteGraph DeserializeCopyBuffer(string copyBuffer)
|
|
{
|
|
try
|
|
{
|
|
return JsonUtility.FromJson<CopyPasteGraph>(copyBuffer);
|
|
}
|
|
catch
|
|
{
|
|
// ignored. just means copy buffer was not a graph :(
|
|
return null;
|
|
}
|
|
}
|
|
|
|
void InsertCopyPasteGraph(CopyPasteGraph graph)
|
|
{
|
|
if (graph == null || graphAsset == null || graphAsset.graph == null)
|
|
return;
|
|
|
|
var addedNodes = new List<INode>();
|
|
|
|
var nodeGuidMap = new Dictionary<Guid, Guid>();
|
|
foreach (var node in graph.GetNodes<INode>())
|
|
{
|
|
var oldGuid = node.guid;
|
|
var newGuid = node.RewriteGuid();
|
|
nodeGuidMap[oldGuid] = newGuid;
|
|
|
|
var drawState = node.drawState;
|
|
var position = drawState.position;
|
|
position.x += 30;
|
|
position.y += 30;
|
|
drawState.position = position;
|
|
node.drawState = drawState;
|
|
graphAsset.graph.AddNode(node);
|
|
addedNodes.Add(node);
|
|
}
|
|
|
|
// only connect edges within pasted elements, discard
|
|
// external edges.
|
|
var addedEdges = new List<IEdge>();
|
|
|
|
foreach (var edge in graph.edges)
|
|
{
|
|
var outputSlot = edge.outputSlot;
|
|
var inputSlot = edge.inputSlot;
|
|
|
|
Guid remappedOutputNodeGuid;
|
|
Guid remappedInputNodeGuid;
|
|
if (nodeGuidMap.TryGetValue(outputSlot.nodeGuid, out remappedOutputNodeGuid)
|
|
&& nodeGuidMap.TryGetValue(inputSlot.nodeGuid, out remappedInputNodeGuid))
|
|
{
|
|
var outputSlotRef = new SlotReference(remappedOutputNodeGuid, outputSlot.slotId);
|
|
var inputSlotRef = new SlotReference(remappedInputNodeGuid, inputSlot.slotId);
|
|
addedEdges.Add(graphAsset.graph.Connect(outputSlotRef, inputSlotRef));
|
|
}
|
|
}
|
|
|
|
graphAsset.graph.ValidateGraph();
|
|
UpdateData();
|
|
|
|
graphAsset.drawingData.selection = addedNodes.Select(n => n.guid);
|
|
}
|
|
|
|
public bool canCopy
|
|
{
|
|
get { return elements.Any(e => e.selected); }
|
|
}
|
|
|
|
public void Copy()
|
|
{
|
|
var graph = CreateCopyPasteGraph(elements.Where(e => e.selected));
|
|
EditorGUIUtility.systemCopyBuffer = JsonUtility.ToJson(graph, true);
|
|
}
|
|
|
|
public bool canCut
|
|
{
|
|
get { return canCopy; }
|
|
}
|
|
|
|
public void Cut()
|
|
{
|
|
Copy();
|
|
RemoveElements(elements.OfType<MaterialNodePresenter>().Where(e => e.selected), elements.OfType<GraphEdgePresenter>().Where(e => e.selected));
|
|
}
|
|
|
|
public bool canPaste
|
|
{
|
|
get { return DeserializeCopyBuffer(EditorGUIUtility.systemCopyBuffer) != null; }
|
|
}
|
|
|
|
public void Paste()
|
|
{
|
|
var pastedGraph = DeserializeCopyBuffer(EditorGUIUtility.systemCopyBuffer);
|
|
InsertCopyPasteGraph(pastedGraph);
|
|
}
|
|
|
|
public bool canDuplicate
|
|
{
|
|
get { return canCopy; }
|
|
}
|
|
|
|
public void Duplicate()
|
|
{
|
|
var graph = DeserializeCopyBuffer(JsonUtility.ToJson(CreateCopyPasteGraph(elements.Where(e => e.selected)), true));
|
|
InsertCopyPasteGraph(graph);
|
|
}
|
|
|
|
public bool canDelete
|
|
{
|
|
get { return canCopy; }
|
|
}
|
|
|
|
public void Delete()
|
|
{
|
|
RemoveElements(elements.OfType<MaterialNodePresenter>().Where(e => e.selected), elements.OfType<GraphEdgePresenter>().Where(e => e.selected));
|
|
}
|
|
|
|
public override void AddElement(EdgePresenter edge)
|
|
{
|
|
Connect(edge.output as GraphAnchorPresenter, edge.input as GraphAnchorPresenter);
|
|
}
|
|
|
|
public void UpdateSelection(IEnumerable<MaterialNodePresenter> presenters)
|
|
{
|
|
if (graphAsset == null)
|
|
return;
|
|
graphAsset.drawingData.selection = presenters.Select(x => x.node.guid);
|
|
m_GraphInspectorPresenter.UpdateSelection(presenters.Select(x => x.node));
|
|
}
|
|
|
|
public override void AddElement(GraphElementPresenter element)
|
|
{
|
|
throw new ArgumentException("Not supported on Serializable Graph, data comes from data store");
|
|
}
|
|
|
|
public override void RemoveElement(GraphElementPresenter element)
|
|
{
|
|
throw new ArgumentException("Not supported on Serializable Graph, data comes from data store");
|
|
}
|
|
}
|
|
}
|