您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
211 行
6.9 KiB
211 行
6.9 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
namespace UnityEditor.Graphs.Material
|
|
{
|
|
[Title("Sub-graph/Sub-graph Node")]
|
|
public class SubGraphNode : BaseMaterialNode, IGeneratesBodyCode, IGeneratesVertexToFragmentBlock, IGeneratesFunction, IGeneratesVertexShaderBlock
|
|
{
|
|
[SerializeField]
|
|
private MaterialSubGraph m_SubGraphAsset;
|
|
|
|
public MaterialSubGraph subGraphAsset { get { return m_SubGraphAsset; } }
|
|
|
|
public override PreviewMode previewMode
|
|
{
|
|
get
|
|
{
|
|
if (subGraphAsset == null)
|
|
return PreviewMode.Preview2D;
|
|
|
|
var preview3D = subGraphAsset.outputsNode.CollectChildNodesByExecutionOrder ().Any (x => x.previewMode == PreviewMode.Preview3D);
|
|
return preview3D ? PreviewMode.Preview3D : PreviewMode.Preview2D;
|
|
}
|
|
}
|
|
|
|
public const int kMaxSlots = 16;
|
|
|
|
public override void Init()
|
|
{
|
|
base.Init();
|
|
name = "SubGraph";
|
|
position = new Rect(position.x, position.y, Mathf.Max(300, position.width), position.height);
|
|
|
|
}
|
|
|
|
static string GetInputSlotName(int n)
|
|
{
|
|
return string.Format("I{0:00}", n);
|
|
}
|
|
|
|
static string GetOutputSlotName(int n)
|
|
{
|
|
return string.Format("O{0:00}", n);
|
|
}
|
|
|
|
public override IEnumerable<Slot> GetValidInputSlots()
|
|
{
|
|
// We only want to return the input slots that are internally wired to an output slot
|
|
return base.GetValidInputSlots ().Where (slot => m_SubGraphAsset.InputInternallyWired (slot.name, this)).ToList ();
|
|
}
|
|
|
|
public override void NodeUI(GraphGUI host)
|
|
{
|
|
EditorGUI.BeginChangeCheck ();
|
|
m_SubGraphAsset = (MaterialSubGraph)EditorGUILayout.ObjectField(GUIContent.none, m_SubGraphAsset, typeof(MaterialSubGraph), false);
|
|
if (EditorGUI.EndChangeCheck () && m_SubGraphAsset != null)
|
|
{
|
|
SubGraphChanged (m_SubGraphAsset);
|
|
}
|
|
}
|
|
|
|
private void SubGraphChanged (MaterialSubGraph sender)
|
|
{
|
|
RefreshSlots (SlotType.InputSlot, inputSlots.ToList(), sender.inputsNode.slots, (s) => true);
|
|
RefreshSlots (SlotType.OutputSlot, outputSlots.ToList(), sender.outputsNode.slots, sender.OutputInternallyWired);
|
|
RegeneratePreviewShaders ();
|
|
}
|
|
|
|
private void RefreshSlots (SlotType type, IEnumerable<Slot> current, IEnumerable<Slot> updated, Func<string, bool> slotFilter)
|
|
{
|
|
var innerOutputSlots = updated.Where (n => slotFilter (n.name)).ToArray();
|
|
foreach (var slot in innerOutputSlots)
|
|
{
|
|
var s = current.FirstOrDefault(n => n.name == slot.name);
|
|
if (s == null)
|
|
AddSlot (new Slot (type, slot.name, slot.title));
|
|
else
|
|
s.title = slot.title;
|
|
}
|
|
|
|
var danglingSlots = current.Except (innerOutputSlots, (ls, rs) => ls.name == rs.name).ToArray();
|
|
foreach (var slot in danglingSlots)
|
|
RemoveSlot (slot);
|
|
}
|
|
|
|
public void GenerateNodeCode(ShaderGenerator shaderBodyVisitor, GenerationMode generationMode)
|
|
{
|
|
if (m_SubGraphAsset == null)
|
|
return;
|
|
|
|
var outputString = new ShaderGenerator ();
|
|
outputString.AddShaderChunk("// Subgraph for node " + GetOutputVariableNameForNode(), false);
|
|
|
|
// Step 1...
|
|
// find out which output slots are actually used
|
|
//TODO: Be smarter about this and only output ones that are actually USED, not just connected
|
|
//var validOutputSlots = NodeUtils.GetSlotsThatOutputToNodeRecurse(this, (graph as BaseMaterialGraph).masterNode);
|
|
var validOutputSlots = outputSlots.Where (x => x.edges.Count > 0);
|
|
foreach (var slot in validOutputSlots)
|
|
{
|
|
outputString.AddShaderChunk(
|
|
"float4 "
|
|
+ GetOutputVariableNameForSlot(slot, generationMode)
|
|
+ " = float4(0);", false);
|
|
}
|
|
|
|
// Step 2...
|
|
// Go into the subgraph
|
|
outputString.AddShaderChunk("{", false);
|
|
outputString.Indent();
|
|
|
|
// Step 3...
|
|
// For each input that is used and connects through we want to generate code.
|
|
// First we assign the input variables to the subgraph
|
|
foreach (var slot in slots)
|
|
{
|
|
if (!slot.isInputSlot)
|
|
continue;
|
|
|
|
// see if the input connects all the way though to the output
|
|
// if it does allow generation
|
|
var inputWired = m_SubGraphAsset.InputInternallyWired (slot.name, this);
|
|
if (!inputWired)
|
|
continue;
|
|
|
|
var varName = m_SubGraphAsset.GetInputVariableNameForSlotByName (slot.name, this, generationMode);
|
|
var varValue = "float4(0, 0, 0, 0);";
|
|
var slotDefaultValue = GetSlotDefaultValue (slot.name);
|
|
if (slotDefaultValue != null)
|
|
{
|
|
varValue = slotDefaultValue.GetDefaultValue (generationMode);
|
|
}
|
|
bool externallyWired = slot.edges.Count > 0;
|
|
if (externallyWired)
|
|
{
|
|
var fromSlot = slot.edges[0].fromSlot;
|
|
var fromNode = slot.edges[0].fromSlot.node as BaseMaterialNode;
|
|
varValue = fromNode.GetOutputVariableNameForSlot(fromSlot, generationMode);
|
|
}
|
|
|
|
outputString.AddShaderChunk ("float4 " + varName + " = " + varValue + ";", false);
|
|
}
|
|
|
|
// Step 4...
|
|
// Using the inputs we can now generate the shader body :)
|
|
var bodyGenerator = new ShaderGenerator ();
|
|
m_SubGraphAsset.GenerateNodeCode (bodyGenerator, this);
|
|
outputString.AddShaderChunk(bodyGenerator.GetShaderString (0), false);
|
|
|
|
// Step 5...
|
|
// Copy the outputs to the parent context name);
|
|
foreach (var slot in validOutputSlots)
|
|
{
|
|
bool internallyWired = m_SubGraphAsset.OutputInternallyWired(slot.name);
|
|
if (internallyWired)
|
|
{
|
|
outputString.AddShaderChunk(
|
|
GetOutputVariableNameForSlot(slot, generationMode)
|
|
+ " = "
|
|
+ m_SubGraphAsset.GetOutputVariableNameForSlotByName(slot.name, this, generationMode)
|
|
+ ";", false);
|
|
}
|
|
}
|
|
|
|
outputString.Deindent();
|
|
outputString.AddShaderChunk("}", false);
|
|
outputString.AddShaderChunk("// Subgraph ends", false);
|
|
|
|
shaderBodyVisitor.AddShaderChunk (outputString.GetShaderString (0), true);
|
|
}
|
|
|
|
public void GenerateVertexToFragmentBlock (ShaderGenerator visitor, GenerationMode generationMode)
|
|
{
|
|
m_SubGraphAsset.GenerateVertexToFragmentBlock (visitor, GenerationMode.SurfaceShader);
|
|
}
|
|
|
|
public void GenerateNodeFunction(ShaderGenerator visitor, GenerationMode generationMode)
|
|
{
|
|
m_SubGraphAsset.GenerateNodeFunction(visitor, GenerationMode.SurfaceShader);
|
|
}
|
|
|
|
public void GenerateVertexShaderBlock(ShaderGenerator visitor, GenerationMode generationMode)
|
|
{
|
|
m_SubGraphAsset.GenerateVertexShaderBlock(visitor, GenerationMode.SurfaceShader);
|
|
}
|
|
|
|
public override void GeneratePropertyBlock(PropertyGenerator visitor, GenerationMode generationMode)
|
|
{
|
|
base.GeneratePropertyBlock (visitor, generationMode);
|
|
m_SubGraphAsset.GeneratePropertyBlock(visitor, GenerationMode.SurfaceShader);
|
|
}
|
|
|
|
public override void GeneratePropertyUsages(ShaderGenerator visitor, GenerationMode generationMode)
|
|
{
|
|
base.GeneratePropertyUsages (visitor, generationMode);
|
|
m_SubGraphAsset.GeneratePropertyUsages(visitor, GenerationMode.SurfaceShader);
|
|
}
|
|
|
|
public override void UpdatePreviewProperties()
|
|
{
|
|
base.UpdatePreviewProperties();
|
|
|
|
var previewProperties = m_SubGraphAsset.GetPreviewProperties ();
|
|
|
|
foreach (var prop in previewProperties)
|
|
SetDependentPreviewMaterialProperty (prop);
|
|
}
|
|
}
|
|
}
|