您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
434 行
17 KiB
434 行
17 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using JetBrains.Annotations;
|
|
using UnityEngine;
|
|
using UnityEditor.Graphing;
|
|
|
|
namespace UnityEditor.ShaderGraph
|
|
{
|
|
public abstract class CodeFunctionNode : AbstractMaterialNode
|
|
, IGeneratesBodyCode
|
|
, IGeneratesFunction
|
|
, IMayRequireNormal
|
|
, IMayRequireTangent
|
|
, IMayRequireBitangent
|
|
, IMayRequireMeshUV
|
|
, IMayRequireScreenPosition
|
|
, IMayRequireViewDirection
|
|
, IMayRequirePosition
|
|
, IMayRequireVertexColor
|
|
{
|
|
[NonSerialized]
|
|
private List<SlotAttribute> m_Slots = new List<SlotAttribute>();
|
|
|
|
public override bool hasPreview
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
protected CodeFunctionNode()
|
|
{
|
|
UpdateNodeAfterDeserialization();
|
|
}
|
|
|
|
protected struct Vector1
|
|
{}
|
|
|
|
protected struct Texture2D
|
|
{}
|
|
|
|
protected struct SamplerState
|
|
{}
|
|
|
|
protected struct DynamicDimensionVector
|
|
{}
|
|
|
|
protected enum Binding
|
|
{
|
|
None,
|
|
ObjectSpaceNormal,
|
|
ObjectSpaceTangent,
|
|
ObjectSpaceBitangent,
|
|
ObjectSpacePosition,
|
|
ViewSpaceNormal,
|
|
ViewSpaceTangent,
|
|
ViewSpaceBitangent,
|
|
ViewSpacePosition,
|
|
WorldSpaceNormal,
|
|
WorldSpaceTangent,
|
|
WorldSpaceBitangent,
|
|
WorldSpacePosition,
|
|
TangentSpaceNormal,
|
|
TangentSpaceTangent,
|
|
TangentSpaceBitangent,
|
|
TangentSpacePosition,
|
|
MeshUV0,
|
|
MeshUV1,
|
|
MeshUV2,
|
|
MeshUV3,
|
|
ScreenPosition,
|
|
ObjectSpaceViewDirection,
|
|
ViewSpaceViewDirection,
|
|
WorldSpaceViewDirection,
|
|
TangentSpaceViewDirection,
|
|
VertexColor,
|
|
}
|
|
|
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
|
|
protected class SlotAttribute : Attribute
|
|
{
|
|
public int slotId { get; private set; }
|
|
public Binding binding { get; private set; }
|
|
public bool hidden { get; private set; }
|
|
public Vector4? defaultValue { get; private set; }
|
|
|
|
public SlotAttribute(int mSlotId, Binding mImplicitBinding)
|
|
{
|
|
slotId = mSlotId;
|
|
binding = mImplicitBinding;
|
|
defaultValue = null;
|
|
}
|
|
|
|
public SlotAttribute(int mSlotId, Binding mImplicitBinding, bool mHidden)
|
|
{
|
|
slotId = mSlotId;
|
|
binding = mImplicitBinding;
|
|
hidden = mHidden;
|
|
defaultValue = null;
|
|
}
|
|
|
|
public SlotAttribute(int mSlotId, Binding mImplicitBinding, float defaultX, float defaultY, float defaultZ, float defaultW)
|
|
{
|
|
slotId = mSlotId;
|
|
binding = mImplicitBinding;
|
|
defaultValue = new Vector4(defaultX, defaultY, defaultZ, defaultW);
|
|
}
|
|
}
|
|
|
|
protected abstract MethodInfo GetFunctionToConvert();
|
|
|
|
private static SlotValueType ConvertTypeToSlotValueType(ParameterInfo p)
|
|
{
|
|
Type t = p.ParameterType;
|
|
if (p.ParameterType.IsByRef)
|
|
t = p.ParameterType.GetElementType();
|
|
|
|
if (t == typeof(Vector1))
|
|
{
|
|
return SlotValueType.Vector1;
|
|
}
|
|
if (t == typeof(Vector2))
|
|
{
|
|
return SlotValueType.Vector2;
|
|
}
|
|
if (t == typeof(Vector3))
|
|
{
|
|
return SlotValueType.Vector3;
|
|
}
|
|
if (t == typeof(Vector4))
|
|
{
|
|
return SlotValueType.Vector4;
|
|
}
|
|
if (t == typeof(Color))
|
|
{
|
|
return SlotValueType.Vector4;
|
|
}
|
|
if (t == typeof(Texture2D))
|
|
{
|
|
return SlotValueType.Texture2D;
|
|
}
|
|
if (t == typeof(SamplerState))
|
|
{
|
|
return SlotValueType.SamplerState;
|
|
}
|
|
if (t == typeof(DynamicDimensionVector))
|
|
{
|
|
return SlotValueType.Dynamic;
|
|
}
|
|
if (t == typeof(Matrix4x4))
|
|
{
|
|
return SlotValueType.Matrix4;
|
|
}
|
|
throw new ArgumentException("Unsupported type " + t);
|
|
}
|
|
|
|
public sealed override void UpdateNodeAfterDeserialization()
|
|
{
|
|
var method = GetFunctionToConvert();
|
|
|
|
if (method == null)
|
|
throw new ArgumentException("Mapped method is null on node" + this);
|
|
|
|
if (method.ReturnType != typeof(string))
|
|
throw new ArgumentException("Mapped function should return string");
|
|
|
|
// validate no duplicates
|
|
var slotAtributes = method.GetParameters().Select(GetSlotAttribute).ToList();
|
|
if (slotAtributes.Any(x => x == null))
|
|
throw new ArgumentException("Missing SlotAttribute on " + method.Name);
|
|
|
|
if (slotAtributes.GroupBy(x => x.slotId).Any(x => x.Count() > 1))
|
|
throw new ArgumentException("Duplicate SlotAttribute on " + method.Name);
|
|
|
|
List<MaterialSlot> slots = new List<MaterialSlot>();
|
|
foreach (var par in method.GetParameters())
|
|
{
|
|
var attribute = GetSlotAttribute(par);
|
|
|
|
MaterialSlot s;
|
|
if (attribute.binding == Binding.None && !par.IsOut && par.ParameterType == typeof(Color))
|
|
s = new ColorMaterialSlot(attribute.slotId, par.Name, par.Name, SlotType.Input, attribute.defaultValue ?? Vector4.zero, hidden: attribute.hidden);
|
|
else if (attribute.binding == Binding.None || par.IsOut)
|
|
s = MaterialSlot.CreateMaterialSlot(
|
|
ConvertTypeToSlotValueType(par),
|
|
attribute.slotId,
|
|
par.Name,
|
|
par.Name,
|
|
par.IsOut ? SlotType.Output : SlotType.Input,
|
|
attribute.defaultValue ?? Vector4.zero,
|
|
hidden: attribute.hidden);
|
|
else
|
|
s = CreateBoundSlot(attribute.binding, attribute.slotId, par.Name, par.Name, attribute.hidden);
|
|
slots.Add(s);
|
|
|
|
m_Slots.Add(attribute);
|
|
}
|
|
foreach (var slot in slots)
|
|
{
|
|
AddSlot(slot);
|
|
}
|
|
RemoveSlotsNameNotMatching(slots.Select(x => x.id));
|
|
}
|
|
|
|
private static MaterialSlot CreateBoundSlot(Binding attributeBinding, int slotId, string displayName, string shaderOutputName, bool hidden)
|
|
{
|
|
switch (attributeBinding)
|
|
{
|
|
case Binding.ObjectSpaceNormal:
|
|
return new NormalMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object);
|
|
case Binding.ObjectSpaceTangent:
|
|
return new TangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object);
|
|
case Binding.ObjectSpaceBitangent:
|
|
return new BitangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object);
|
|
case Binding.ObjectSpacePosition:
|
|
return new PositionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object);
|
|
case Binding.ViewSpaceNormal:
|
|
return new NormalMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View);
|
|
case Binding.ViewSpaceTangent:
|
|
return new TangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View);
|
|
case Binding.ViewSpaceBitangent:
|
|
return new BitangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View);
|
|
case Binding.ViewSpacePosition:
|
|
return new PositionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View);
|
|
case Binding.WorldSpaceNormal:
|
|
return new NormalMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World);
|
|
case Binding.WorldSpaceTangent:
|
|
return new TangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World);
|
|
case Binding.WorldSpaceBitangent:
|
|
return new BitangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World);
|
|
case Binding.WorldSpacePosition:
|
|
return new PositionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World);
|
|
case Binding.TangentSpaceNormal:
|
|
return new NormalMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent);
|
|
case Binding.TangentSpaceTangent:
|
|
return new TangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent);
|
|
case Binding.TangentSpaceBitangent:
|
|
return new BitangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent);
|
|
case Binding.TangentSpacePosition:
|
|
return new PositionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent);
|
|
case Binding.MeshUV0:
|
|
return new UVMaterialSlot(slotId, displayName, shaderOutputName, UVChannel.uv0);
|
|
case Binding.MeshUV1:
|
|
return new UVMaterialSlot(slotId, displayName, shaderOutputName, UVChannel.uv1);
|
|
case Binding.MeshUV2:
|
|
return new UVMaterialSlot(slotId, displayName, shaderOutputName, UVChannel.uv2);
|
|
case Binding.MeshUV3:
|
|
return new UVMaterialSlot(slotId, displayName, shaderOutputName, UVChannel.uv3);
|
|
case Binding.ScreenPosition:
|
|
return new ScreenPositionMaterialSlot(slotId, displayName, shaderOutputName);
|
|
case Binding.ObjectSpaceViewDirection:
|
|
return new ViewDirectionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object);
|
|
case Binding.ViewSpaceViewDirection:
|
|
return new ViewDirectionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View);
|
|
case Binding.WorldSpaceViewDirection:
|
|
return new ViewDirectionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World);
|
|
case Binding.TangentSpaceViewDirection:
|
|
return new ViewDirectionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent);
|
|
case Binding.VertexColor:
|
|
return new VertexColorMaterialSlot(slotId, displayName, shaderOutputName);
|
|
default:
|
|
throw new ArgumentOutOfRangeException("attributeBinding", attributeBinding, null);
|
|
}
|
|
}
|
|
|
|
public void GenerateNodeCode(ShaderGenerator visitor, GenerationMode generationMode)
|
|
{
|
|
foreach (var outSlot in GetOutputSlots<MaterialSlot>())
|
|
{
|
|
visitor.AddShaderChunk(GetParamTypeName(outSlot) + " " + GetVariableNameForSlot(outSlot.id) + ";", true);
|
|
}
|
|
|
|
string call = GetFunctionName() + "(";
|
|
bool first = true;
|
|
foreach (var slot in GetSlots<MaterialSlot>().OrderBy(x => x.id))
|
|
{
|
|
if (!first)
|
|
{
|
|
call += ", ";
|
|
}
|
|
first = false;
|
|
|
|
if (slot.isInputSlot)
|
|
call += GetSlotValue(slot.id, generationMode);
|
|
else
|
|
call += GetVariableNameForSlot(slot.id);
|
|
}
|
|
call += ");";
|
|
|
|
visitor.AddShaderChunk(call, true);
|
|
}
|
|
|
|
private string GetParamTypeName(MaterialSlot slot)
|
|
{
|
|
return ConvertConcreteSlotValueTypeToString(precision, slot.concreteValueType);
|
|
}
|
|
|
|
private string GetFunctionName()
|
|
{
|
|
var function = GetFunctionToConvert();
|
|
return function.Name + "_" + (function.IsStatic ? string.Empty : GuidEncoder.Encode(guid) + "_") + precision;
|
|
}
|
|
|
|
private string GetFunctionHeader()
|
|
{
|
|
string header = "void " + GetFunctionName() + "(";
|
|
|
|
var first = true;
|
|
foreach (var slot in GetSlots<MaterialSlot>().OrderBy(x => x.id))
|
|
{
|
|
if (!first)
|
|
header += ", ";
|
|
|
|
first = false;
|
|
|
|
if (slot.isOutputSlot)
|
|
header += "out ";
|
|
|
|
header += GetParamTypeName(slot) + " " + slot.shaderOutputName;
|
|
}
|
|
|
|
header += ")";
|
|
return header;
|
|
}
|
|
|
|
private static object GetDefault(Type type)
|
|
{
|
|
return type.IsValueType ? Activator.CreateInstance(type) : null;
|
|
}
|
|
|
|
private string GetFunctionBody(MethodInfo info)
|
|
{
|
|
var args = new List<object>();
|
|
foreach (var param in info.GetParameters())
|
|
args.Add(GetDefault(param.ParameterType));
|
|
|
|
var result = info.Invoke(this, args.ToArray()) as string;
|
|
|
|
if (string.IsNullOrEmpty(result))
|
|
return string.Empty;
|
|
|
|
result = result.Replace("{precision}", precision.ToString());
|
|
foreach (var slot in GetSlots<MaterialSlot>())
|
|
{
|
|
var toReplace = string.Format("{{slot{0}dimension}}", slot.id);
|
|
var replacement = GetSlotDimension(slot.concreteValueType);
|
|
result = result.Replace(toReplace, replacement);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public virtual void GenerateNodeFunction(ShaderGenerator visitor, GenerationMode generationMode)
|
|
{
|
|
string function = GetFunctionHeader() + GetFunctionBody(GetFunctionToConvert());
|
|
visitor.AddShaderChunk(function, true);
|
|
}
|
|
|
|
private static SlotAttribute GetSlotAttribute([NotNull] ParameterInfo info)
|
|
{
|
|
var attrs = info.GetCustomAttributes(typeof(SlotAttribute), false).OfType<SlotAttribute>().ToList();
|
|
return attrs.FirstOrDefault();
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresNormal()
|
|
{
|
|
var binding = NeededCoordinateSpace.None;
|
|
foreach (var slot in GetInputSlots<MaterialSlot>().OfType<IMayRequireNormal>())
|
|
binding |= slot.RequiresNormal();
|
|
return binding;
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresViewDirection()
|
|
{
|
|
var binding = NeededCoordinateSpace.None;
|
|
foreach (var slot in GetInputSlots<MaterialSlot>().OfType<IMayRequireViewDirection>())
|
|
binding |= slot.RequiresViewDirection();
|
|
return binding;
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresPosition()
|
|
{
|
|
var binding = NeededCoordinateSpace.None;
|
|
foreach (var slot in GetInputSlots<MaterialSlot>().OfType<IMayRequirePosition>())
|
|
binding |= slot.RequiresPosition();
|
|
return binding;
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresTangent()
|
|
{
|
|
var binding = NeededCoordinateSpace.None;
|
|
foreach (var slot in GetInputSlots<MaterialSlot>().OfType<IMayRequireTangent>())
|
|
binding |= slot.RequiresTangent();
|
|
return binding;
|
|
}
|
|
|
|
public NeededCoordinateSpace RequiresBitangent()
|
|
{
|
|
var binding = NeededCoordinateSpace.None;
|
|
foreach (var slot in GetInputSlots<MaterialSlot>().OfType<IMayRequireBitangent>())
|
|
binding |= slot.RequiresBitangent();
|
|
return binding;
|
|
}
|
|
|
|
public bool RequiresMeshUV(UVChannel channel)
|
|
{
|
|
foreach (var slot in GetInputSlots<MaterialSlot>().OfType<IMayRequireMeshUV>())
|
|
{
|
|
if (slot.RequiresMeshUV(channel))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool RequiresScreenPosition()
|
|
{
|
|
foreach (var slot in GetInputSlots<MaterialSlot>().OfType<IMayRequireScreenPosition>())
|
|
{
|
|
if (slot.RequiresScreenPosition())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool RequiresVertexColor()
|
|
{
|
|
foreach (var slot in GetInputSlots<MaterialSlot>().OfType<IMayRequireVertexColor>())
|
|
{
|
|
if (slot.RequiresVertexColor())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|