您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

421 行
14 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using UnityEngine.Graphing;
namespace UnityEngine.MaterialGraph
{
public abstract class CodeFunctionNode : AbstractMaterialNode
, IGeneratesBodyCode
, IGeneratesFunction
, IMayRequireNormal
, IMayRequireTangent
, IMayRequireBitangent
, IMayRequireMeshUV
, IMayRequireScreenPosition
, IMayRequireViewDirection
, IMayRequireWorldPosition
, IMayRequireVertexColor
, IMayRequireViewDirectionTangentSpace
{
[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,
Normal,
Tangent,
Bitangent,
MeshUV0,
MeshUV1,
MeshUV2,
MeshUV3,
ScreenPosition,
ViewDirection,
WorldPosition,
VertexColor,
ViewDirectionTangentSpace
}
private static string BindChannelToShaderName(Binding channel)
{
switch (channel)
{
case Binding.None:
return "ERROR!";
case Binding.Normal:
return ShaderGeneratorNames.WorldSpaceNormal;
case Binding.Tangent:
return ShaderGeneratorNames.WorldSpaceTangent;
case Binding.Bitangent:
return ShaderGeneratorNames.WorldSpaceBitangent;
case Binding.MeshUV0:
return ShaderGeneratorNames.GetUVName(UVChannel.uv0);
case Binding.MeshUV1:
return ShaderGeneratorNames.GetUVName(UVChannel.uv1);
case Binding.MeshUV2:
return ShaderGeneratorNames.GetUVName(UVChannel.uv2);
case Binding.MeshUV3:
return ShaderGeneratorNames.GetUVName(UVChannel.uv3);
case Binding.ScreenPosition:
return ShaderGeneratorNames.ScreenPosition;
case Binding.ViewDirection:
return ShaderGeneratorNames.WorldSpaceViewDirection;
case Binding.WorldPosition:
return ShaderGeneratorNames.WorldSpacePosition;
case Binding.VertexColor:
return ShaderGeneratorNames.VertexColor;
case Binding.ViewDirectionTangentSpace:
return ShaderGeneratorNames.TangentSpaceViewDirection;
default:
throw new ArgumentOutOfRangeException("channel", channel, null);
}
}
[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 static MethodInfo GetMethodInfo(LambdaExpression expression)
{
MethodCallExpression outermostExpression = expression.Body as MethodCallExpression;
 
if (outermostExpression == null)
{
throw new ArgumentException("Invalid Expression. Expression should consist of a Method call only.");
}
 
return outermostExpression.Method;
}
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(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);
slots.Add(new MaterialSlot(attribute.slotId, par.Name, par.Name, par.IsOut ? SlotType.Output : SlotType.Input,
ConvertTypeToSlotValueType(par), attribute.defaultValue ?? Vector4.zero, attribute.hidden));
m_Slots.Add(attribute);
}
foreach (var slot in slots)
{
AddSlot(slot);
}
RemoveSlotsNameNotMatching(slots.Select(x => x.id));
}
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 arg in GetSlots<MaterialSlot>().OrderBy(x => x.id))
{
if (!first)
{
call += ", ";
}
first = false;
if (arg.isInputSlot)
{
var inEdges = owner.GetEdges(arg.slotReference);
if (!inEdges.Any())
{
var info = m_Slots.FirstOrDefault(x => x.slotId == arg.id);
if (info != null)
{
var bindingInfo = info.binding;
if (bindingInfo != Binding.None)
{
call += BindChannelToShaderName(bindingInfo);
continue;
}
}
}
call += GetSlotValue(arg.id, generationMode);
}
else
call += GetVariableNameForSlot(arg.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 = ConvertConcreteSlotValueTypeToString(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 bool NodeRequiresBinding(Binding channel)
{
foreach (var slot in GetSlots<MaterialSlot>())
{
if (SlotRequiresBinding(channel, slot))
return true;
}
return false;
}
private bool SlotRequiresBinding(Binding channel, [NotNull] MaterialSlot slot)
{
if (slot.isOutputSlot)
return false;
var inEdges = owner.GetEdges(slot.slotReference);
if (inEdges.Any())
return false;
var slotAttr = m_Slots.FirstOrDefault(x => x.slotId == slot.id);
if (slotAttr != null && slotAttr.binding == channel)
return true;
return false;
}
private static SlotAttribute GetSlotAttribute([NotNull] ParameterInfo info)
{
var attrs = info.GetCustomAttributes(typeof(SlotAttribute), false).OfType<SlotAttribute>().ToList();
return attrs.FirstOrDefault();
}
public bool RequiresNormal()
{
return NodeRequiresBinding(Binding.Normal);
}
public bool RequiresMeshUV(UVChannel channel)
{
switch (channel)
{
case UVChannel.uv0:
return NodeRequiresBinding(Binding.MeshUV0);
case UVChannel.uv1:
return NodeRequiresBinding(Binding.MeshUV1);
case UVChannel.uv2:
return NodeRequiresBinding(Binding.MeshUV2);
case UVChannel.uv3:
return NodeRequiresBinding(Binding.MeshUV3);
default:
throw new ArgumentOutOfRangeException("channel", channel, null);
}
}
public bool RequiresScreenPosition()
{
return NodeRequiresBinding(Binding.ScreenPosition);
}
public bool RequiresViewDirection()
{
return NodeRequiresBinding(Binding.ViewDirection);
}
public bool RequiresViewDirectionTangentSpace()
{
return NodeRequiresBinding(Binding.ViewDirectionTangentSpace);
}
public bool RequiresWorldPosition()
{
return NodeRequiresBinding(Binding.WorldPosition);
}
public bool RequiresTangent()
{
return NodeRequiresBinding(Binding.Tangent);
}
public bool RequiresBitangent()
{
return NodeRequiresBinding(Binding.Bitangent);
}
public bool RequiresVertexColor()
{
return NodeRequiresBinding(Binding.VertexColor);
}
}
}