您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
430 行
16 KiB
430 行
16 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEditor.ShaderGraph.Drawing.Controls;
|
|
using UnityEngine;
|
|
using UnityEditor.Graphing;
|
|
|
|
namespace UnityEditor.ShaderGraph
|
|
{
|
|
[Title("Utility", "Sub-graph")]
|
|
public class SubGraphNode : AbstractMaterialNode
|
|
, IGeneratesBodyCode
|
|
, IOnAssetEnabled
|
|
, IGeneratesFunction
|
|
, IMayRequireNormal
|
|
, IMayRequireTangent
|
|
, IMayRequireBitangent
|
|
, IMayRequireMeshUV
|
|
, IMayRequireScreenPosition
|
|
, IMayRequireViewDirection
|
|
, IMayRequirePosition
|
|
, IMayRequireVertexColor
|
|
, IMayRequireTime
|
|
{
|
|
[SerializeField]
|
|
private string m_SerializedSubGraph = string.Empty;
|
|
|
|
[NonSerialized]
|
|
MaterialSubGraphAsset m_SubGraph;
|
|
|
|
[Serializable]
|
|
private class SubGraphHelper
|
|
{
|
|
public MaterialSubGraphAsset subGraph;
|
|
}
|
|
|
|
protected SubGraph referencedGraph
|
|
{
|
|
get
|
|
{
|
|
if (subGraphAsset == null)
|
|
return null;
|
|
|
|
return subGraphAsset.subGraph;
|
|
}
|
|
}
|
|
|
|
public MaterialSubGraphAsset subGraphAsset
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrEmpty(m_SerializedSubGraph))
|
|
return null;
|
|
|
|
if (m_SubGraph == null)
|
|
{
|
|
var helper = new SubGraphHelper();
|
|
EditorJsonUtility.FromJsonOverwrite(m_SerializedSubGraph, helper);
|
|
m_SubGraph = helper.subGraph;
|
|
}
|
|
|
|
return m_SubGraph;
|
|
}
|
|
set
|
|
{
|
|
if (subGraphAsset == value)
|
|
return;
|
|
|
|
var helper = new SubGraphHelper();
|
|
helper.subGraph = value;
|
|
m_SerializedSubGraph = EditorJsonUtility.ToJson(helper, true);
|
|
m_SubGraph = null;
|
|
UpdateSlots();
|
|
|
|
Dirty(ModificationScope.Topological);
|
|
}
|
|
}
|
|
|
|
public INode outputNode
|
|
{
|
|
get
|
|
{
|
|
if (subGraphAsset != null && subGraphAsset.subGraph != null)
|
|
return subGraphAsset.subGraph.outputNode;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public override bool hasPreview
|
|
{
|
|
get { return referencedGraph != null; }
|
|
}
|
|
|
|
public override PreviewMode previewMode
|
|
{
|
|
get
|
|
{
|
|
if (referencedGraph == null)
|
|
return PreviewMode.Preview2D;
|
|
|
|
return PreviewMode.Preview3D;
|
|
}
|
|
}
|
|
|
|
public SubGraphNode()
|
|
{
|
|
name = "Sub-graph";
|
|
}
|
|
|
|
public override bool allowedInSubGraph
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override string documentationURL
|
|
{
|
|
get { return "https://github.com/Unity-Technologies/ShaderGraph/wiki/Sub-graph-Node"; }
|
|
}
|
|
|
|
public void GenerateNodeCode(ShaderGenerator visitor, GenerationMode generationMode)
|
|
{
|
|
if (referencedGraph == null)
|
|
return;
|
|
|
|
foreach (var outSlot in referencedGraph.graphOutputs)
|
|
visitor.AddShaderChunk(string.Format("{0} {1};", NodeUtils.ConvertConcreteSlotValueTypeToString(precision, outSlot.concreteValueType), GetVariableNameForSlot(outSlot.id)), true);
|
|
|
|
var arguments = new List<string>();
|
|
foreach (var prop in referencedGraph.graphInputs)
|
|
{
|
|
var inSlotId = prop.guid.GetHashCode();
|
|
|
|
if (prop is TextureShaderProperty)
|
|
arguments.Add(string.Format("TEXTURE2D_PARAM({0}, sampler{0})", GetSlotValue(inSlotId, generationMode)));
|
|
else if (prop is Texture2DArrayShaderProperty)
|
|
arguments.Add(string.Format("TEXTURE2D_ARRAY_PARAM({0}, sampler{0})", GetSlotValue(inSlotId, generationMode)));
|
|
else if (prop is Texture3DShaderProperty)
|
|
arguments.Add(string.Format("TEXTURE3D_PARAM({0}, sampler{0})", GetSlotValue(inSlotId, generationMode)));
|
|
else if (prop is CubemapShaderProperty)
|
|
arguments.Add(string.Format("TEXTURECUBE_PARAM({0}, sampler{0})", GetSlotValue(inSlotId, generationMode)));
|
|
else
|
|
arguments.Add(GetSlotValue(inSlotId, generationMode));
|
|
}
|
|
|
|
// pass surface inputs through
|
|
arguments.Add("IN");
|
|
|
|
foreach (var outSlot in referencedGraph.graphOutputs)
|
|
arguments.Add(GetVariableNameForSlot(outSlot.id));
|
|
|
|
visitor.AddShaderChunk(
|
|
string.Format("{0}({1});"
|
|
, SubGraphFunctionName()
|
|
, arguments.Aggregate((current, next) => string.Format("{0}, {1}", current, next)))
|
|
, false);
|
|
}
|
|
|
|
public void OnEnable()
|
|
{
|
|
UpdateSlots();
|
|
}
|
|
|
|
public virtual void UpdateSlots()
|
|
{
|
|
var validNames = new List<int>();
|
|
if (referencedGraph == null)
|
|
{
|
|
RemoveSlotsNameNotMatching(validNames, true);
|
|
return;
|
|
}
|
|
|
|
var props = referencedGraph.properties;
|
|
foreach (var prop in props)
|
|
{
|
|
var propType = prop.propertyType;
|
|
SlotValueType slotType;
|
|
|
|
switch (propType)
|
|
{
|
|
case PropertyType.Color:
|
|
slotType = SlotValueType.Vector4;
|
|
break;
|
|
case PropertyType.Texture2D:
|
|
slotType = SlotValueType.Texture2D;
|
|
break;
|
|
case PropertyType.Texture2DArray:
|
|
slotType = SlotValueType.Texture2DArray;
|
|
break;
|
|
case PropertyType.Texture3D:
|
|
slotType = SlotValueType.Texture3D;
|
|
break;
|
|
case PropertyType.Cubemap:
|
|
slotType = SlotValueType.Cubemap;
|
|
break;
|
|
case PropertyType.Gradient:
|
|
slotType = SlotValueType.Gradient;
|
|
break;
|
|
case PropertyType.Vector1:
|
|
slotType = SlotValueType.Vector1;
|
|
break;
|
|
case PropertyType.Vector2:
|
|
slotType = SlotValueType.Vector2;
|
|
break;
|
|
case PropertyType.Vector3:
|
|
slotType = SlotValueType.Vector3;
|
|
break;
|
|
case PropertyType.Vector4:
|
|
slotType = SlotValueType.Vector4;
|
|
break;
|
|
case PropertyType.Boolean:
|
|
slotType = SlotValueType.Boolean;
|
|
break;
|
|
case PropertyType.Matrix2:
|
|
slotType = SlotValueType.Matrix2;
|
|
break;
|
|
case PropertyType.Matrix3:
|
|
slotType = SlotValueType.Matrix3;
|
|
break;
|
|
case PropertyType.Matrix4:
|
|
slotType = SlotValueType.Matrix4;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
var id = prop.guid.GetHashCode();
|
|
MaterialSlot slot = MaterialSlot.CreateMaterialSlot(slotType, id, prop.displayName, prop.referenceName, SlotType.Input, prop.defaultValue, ShaderStageCapability.Fragment);
|
|
// copy default for texture for niceness
|
|
if (slotType == SlotValueType.Texture2D && propType == PropertyType.Texture2D)
|
|
{
|
|
var tSlot = slot as Texture2DInputMaterialSlot;
|
|
var tProp = prop as TextureShaderProperty;
|
|
if (tSlot != null && tProp != null)
|
|
tSlot.texture = tProp.value.texture;
|
|
}
|
|
// copy default for texture array for niceness
|
|
else if (slotType == SlotValueType.Texture2DArray && propType == PropertyType.Texture2DArray)
|
|
{
|
|
var tSlot = slot as Texture2DArrayInputMaterialSlot;
|
|
var tProp = prop as Texture2DArrayShaderProperty;
|
|
if (tSlot != null && tProp != null)
|
|
tSlot.textureArray = tProp.value.textureArray;
|
|
}
|
|
// copy default for texture 3d for niceness
|
|
else if (slotType == SlotValueType.Texture3D && propType == PropertyType.Texture3D)
|
|
{
|
|
var tSlot = slot as Texture3DInputMaterialSlot;
|
|
var tProp = prop as Texture3DShaderProperty;
|
|
if (tSlot != null && tProp != null)
|
|
tSlot.texture = tProp.value.texture;
|
|
}
|
|
// copy default for cubemap for niceness
|
|
else if (slotType == SlotValueType.Cubemap && propType == PropertyType.Cubemap)
|
|
{
|
|
var tSlot = slot as CubemapInputMaterialSlot;
|
|
var tProp = prop as CubemapShaderProperty;
|
|
if (tSlot != null && tProp != null)
|
|
tSlot.cubemap = tProp.value.cubemap;
|
|
}
|
|
AddSlot(slot);
|
|
validNames.Add(id);
|
|
}
|
|
|
|
var subGraphOutputNode = outputNode;
|
|
if (outputNode != null)
|
|
{
|
|
foreach (var slot in NodeExtensions.GetInputSlots<MaterialSlot>(subGraphOutputNode))
|
|
{
|
|
AddSlot(MaterialSlot.CreateMaterialSlot(slot.valueType, slot.id, slot.RawDisplayName(), slot.shaderOutputName, SlotType.Output, Vector4.zero, ShaderStageCapability.Fragment));
|
|
validNames.Add(slot.id);
|
|
}
|
|
}
|
|
|
|
RemoveSlotsNameNotMatching(validNames);
|
|
}
|
|
|
|
public override void ValidateNode()
|
|
{
|
|
if (referencedGraph != null)
|
|
{
|
|
referencedGraph.OnEnable();
|
|
referencedGraph.ValidateGraph();
|
|
|
|
if (referencedGraph.GetNodes<INode>().Any(x => x.hasError))
|
|
hasError = true;
|
|
}
|
|
|
|
base.ValidateNode();
|
|
}
|
|
|
|
public override void CollectShaderProperties(PropertyCollector visitor, GenerationMode generationMode)
|
|
{
|
|
base.CollectShaderProperties(visitor, generationMode);
|
|
|
|
if (referencedGraph == null)
|
|
return;
|
|
|
|
referencedGraph.CollectShaderProperties(visitor, GenerationMode.ForReals);
|
|
}
|
|
|
|
public override void CollectPreviewMaterialProperties(List<PreviewProperty> properties)
|
|
{
|
|
base.CollectPreviewMaterialProperties(properties);
|
|
|
|
if (referencedGraph == null)
|
|
return;
|
|
|
|
properties.AddRange(referencedGraph.GetPreviewProperties());
|
|
}
|
|
|
|
private string SubGraphFunctionName()
|
|
{
|
|
var functionName = subGraphAsset != null ? NodeUtils.GetHLSLSafeName(subGraphAsset.name) : "ERROR";
|
|
return string.Format("sg_{0}_{1}", functionName, GuidEncoder.Encode(referencedGraph.guid));
|
|
}
|
|
|
|
public virtual void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode)
|
|
{
|
|
if (subGraphAsset == null || referencedGraph == null)
|
|
return;
|
|
|
|
referencedGraph.GenerateNodeFunction(registry, graphContext, GenerationMode.ForReals);
|
|
referencedGraph.GenerateSubGraphFunction(SubGraphFunctionName(), registry, graphContext, ShaderGraphRequirements.FromNodes(new List<INode> {this}), GenerationMode.ForReals);
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability)
|
|
{
|
|
if (referencedGraph == null)
|
|
return NeededCoordinateSpace.None;
|
|
|
|
return referencedGraph.activeNodes.OfType<IMayRequireNormal>().Aggregate(NeededCoordinateSpace.None, (mask, node) =>
|
|
{
|
|
mask |= node.RequiresNormal(stageCapability);
|
|
return mask;
|
|
});
|
|
}
|
|
|
|
public bool RequiresMeshUV(UVChannel channel, ShaderStageCapability stageCapability)
|
|
{
|
|
if (referencedGraph == null)
|
|
return false;
|
|
|
|
return referencedGraph.activeNodes.OfType<IMayRequireMeshUV>().Any(x => x.RequiresMeshUV(channel, stageCapability));
|
|
}
|
|
|
|
public bool RequiresScreenPosition(ShaderStageCapability stageCapability)
|
|
{
|
|
if (referencedGraph == null)
|
|
return false;
|
|
|
|
return referencedGraph.activeNodes.OfType<IMayRequireScreenPosition>().Any(x => x.RequiresScreenPosition(stageCapability));
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresViewDirection(ShaderStageCapability stageCapability)
|
|
{
|
|
if (referencedGraph == null)
|
|
return NeededCoordinateSpace.None;
|
|
|
|
return referencedGraph.activeNodes.OfType<IMayRequireViewDirection>().Aggregate(NeededCoordinateSpace.None, (mask, node) =>
|
|
{
|
|
mask |= node.RequiresViewDirection(stageCapability);
|
|
return mask;
|
|
});
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability)
|
|
{
|
|
if (referencedGraph == null)
|
|
return NeededCoordinateSpace.None;
|
|
|
|
return referencedGraph.activeNodes.OfType<IMayRequirePosition>().Aggregate(NeededCoordinateSpace.None, (mask, node) =>
|
|
{
|
|
mask |= node.RequiresPosition(stageCapability);
|
|
return mask;
|
|
});
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability)
|
|
{
|
|
if (referencedGraph == null)
|
|
return NeededCoordinateSpace.None;
|
|
|
|
return referencedGraph.activeNodes.OfType<IMayRequireTangent>().Aggregate(NeededCoordinateSpace.None, (mask, node) =>
|
|
{
|
|
mask |= node.RequiresTangent(stageCapability);
|
|
return mask;
|
|
});
|
|
}
|
|
|
|
public bool RequiresTime()
|
|
{
|
|
if (referencedGraph == null)
|
|
return false;
|
|
|
|
return referencedGraph.activeNodes.OfType<IMayRequireTime>().Any(x => x.RequiresTime());
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability)
|
|
{
|
|
if (referencedGraph == null)
|
|
return NeededCoordinateSpace.None;
|
|
|
|
return referencedGraph.activeNodes.OfType<IMayRequireBitangent>().Aggregate(NeededCoordinateSpace.None, (mask, node) =>
|
|
{
|
|
mask |= node.RequiresBitangent(stageCapability);
|
|
return mask;
|
|
});
|
|
}
|
|
|
|
public bool RequiresVertexColor(ShaderStageCapability stageCapability)
|
|
{
|
|
if (referencedGraph == null)
|
|
return false;
|
|
|
|
return referencedGraph.activeNodes.OfType<IMayRequireVertexColor>().Any(x => x.RequiresVertexColor(stageCapability));
|
|
}
|
|
|
|
public override void GetSourceAssetDependencies(List<string> paths)
|
|
{
|
|
base.GetSourceAssetDependencies(paths);
|
|
if (subGraphAsset != null)
|
|
{
|
|
var assetPath = AssetDatabase.GetAssetPath(subGraphAsset);
|
|
paths.Add(assetPath);
|
|
foreach (var dependencyPath in AssetDatabase.GetDependencies(assetPath))
|
|
paths.Add(dependencyPath);
|
|
}
|
|
}
|
|
}
|
|
}
|