您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
385 行
16 KiB
385 行
16 KiB
//using System.Reflection;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEditor.Graphing;
|
|
using UnityEngine;
|
|
using System.Linq;
|
|
|
|
namespace UnityEditor.ShaderGraph
|
|
{
|
|
[Title("Math", "Basic", "Multiply")]
|
|
public class MultiplyNode : AbstractMaterialNode, IGeneratesBodyCode, IGeneratesFunction
|
|
{
|
|
public MultiplyNode()
|
|
{
|
|
name = "Multiply";
|
|
UpdateNodeAfterDeserialization();
|
|
}
|
|
|
|
public override string documentationURL
|
|
{
|
|
get { return "https://github.com/Unity-Technologies/ShaderGraph/wiki/Multiply-Node"; }
|
|
}
|
|
|
|
const int Input1SlotId = 0;
|
|
const int Input2SlotId = 1;
|
|
const int OutputSlotId = 2;
|
|
const string kInput1SlotName = "A";
|
|
const string kInput2SlotName = "B";
|
|
const string kOutputSlotName = "Out";
|
|
|
|
public enum MultiplyType
|
|
{
|
|
Vector,
|
|
Matrix,
|
|
Mixed
|
|
}
|
|
|
|
MultiplyType m_MultiplyType;
|
|
|
|
public override bool hasPreview
|
|
{
|
|
get { return m_MultiplyType != MultiplyType.Matrix; }
|
|
}
|
|
|
|
string GetFunctionHeader()
|
|
{
|
|
return string.Format("Unity_Multiply_{0}", precision);
|
|
}
|
|
|
|
public sealed override void UpdateNodeAfterDeserialization()
|
|
{
|
|
AddSlot(new DynamicValueMaterialSlot(Input1SlotId, kInput1SlotName, kInput1SlotName, SlotType.Input, Matrix4x4.zero));
|
|
AddSlot(new DynamicValueMaterialSlot(Input2SlotId, kInput2SlotName, kInput2SlotName, SlotType.Input, new Matrix4x4(new Vector4(2,2,2,2), new Vector4(2,2,2,2), new Vector4(2,2,2,2), new Vector4(2,2,2,2))));
|
|
AddSlot(new DynamicValueMaterialSlot(OutputSlotId, kOutputSlotName, kOutputSlotName, SlotType.Output, Matrix4x4.zero));
|
|
RemoveSlotsNameNotMatching(new[] { Input1SlotId, Input2SlotId, OutputSlotId });
|
|
}
|
|
|
|
public void GenerateNodeCode(ShaderGenerator visitor, GenerationMode generationMode)
|
|
{
|
|
var sb = new ShaderStringBuilder();
|
|
var input1Value = GetSlotValue(Input1SlotId, generationMode);
|
|
var input2Value = GetSlotValue(Input2SlotId, generationMode);
|
|
var outputValue = GetSlotValue(OutputSlotId, generationMode);
|
|
|
|
sb.AppendLine("{0} {1};", NodeUtils.ConvertConcreteSlotValueTypeToString(precision, FindOutputSlot<MaterialSlot>(OutputSlotId).concreteValueType), GetVariableNameForSlot(OutputSlotId));
|
|
sb.AppendLine("{0}({1}, {2}, {3});", GetFunctionHeader(), input1Value, input2Value, outputValue);
|
|
|
|
visitor.AddShaderChunk(sb.ToString(), false);
|
|
}
|
|
|
|
string GetFunctionName()
|
|
{
|
|
return string.Format("{0}_{1}_{2}",
|
|
GetFunctionHeader(),
|
|
FindInputSlot<MaterialSlot>(Input1SlotId).concreteValueType.ToString(precision),
|
|
FindInputSlot<MaterialSlot>(Input2SlotId).concreteValueType.ToString(precision));
|
|
}
|
|
|
|
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
|
|
{
|
|
registry.ProvideFunction(GetFunctionName(), s =>
|
|
{
|
|
s.AppendLine("void {0} ({1} A, {2} B, out {3} Out)",
|
|
GetFunctionHeader(),
|
|
FindInputSlot<MaterialSlot>(Input1SlotId).concreteValueType.ToString(precision),
|
|
FindInputSlot<MaterialSlot>(Input2SlotId).concreteValueType.ToString(precision),
|
|
FindOutputSlot<MaterialSlot>(OutputSlotId).concreteValueType.ToString(precision));
|
|
using (s.BlockScope())
|
|
{
|
|
switch(m_MultiplyType)
|
|
{
|
|
case MultiplyType.Vector:
|
|
s.AppendLine("Out = A * B;");
|
|
break;
|
|
default:
|
|
s.AppendLine("Out = mul(A, B);");
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Internal validation
|
|
// -------------------------------------------------
|
|
|
|
public override void ValidateNode()
|
|
{
|
|
var isInError = false;
|
|
|
|
// all children nodes needs to be updated first
|
|
// so do that here
|
|
var slots = ListPool<MaterialSlot>.Get();
|
|
GetInputSlots(slots);
|
|
foreach (var inputSlot in slots)
|
|
{
|
|
inputSlot.hasError = false;
|
|
|
|
var edges = owner.GetEdges(inputSlot.slotReference);
|
|
foreach (var edge in edges)
|
|
{
|
|
var fromSocketRef = edge.outputSlot;
|
|
var outputNode = owner.GetNodeFromGuid(fromSocketRef.nodeGuid);
|
|
if (outputNode == null)
|
|
continue;
|
|
|
|
outputNode.ValidateNode();
|
|
if (outputNode.hasError)
|
|
isInError = true;
|
|
}
|
|
}
|
|
ListPool<MaterialSlot>.Release(slots);
|
|
|
|
var dynamicInputSlotsToCompare = DictionaryPool<DynamicValueMaterialSlot, ConcreteSlotValueType>.Get();
|
|
var skippedDynamicSlots = ListPool<DynamicValueMaterialSlot>.Get();
|
|
|
|
// iterate the input slots
|
|
s_TempSlots.Clear();
|
|
GetInputSlots(s_TempSlots);
|
|
foreach (var inputSlot in s_TempSlots)
|
|
{
|
|
// if there is a connection
|
|
var edges = owner.GetEdges(inputSlot.slotReference).ToList();
|
|
if (!edges.Any())
|
|
{
|
|
if (inputSlot is DynamicValueMaterialSlot)
|
|
skippedDynamicSlots.Add(inputSlot as DynamicValueMaterialSlot);
|
|
continue;
|
|
}
|
|
|
|
// get the output details
|
|
var outputSlotRef = edges[0].outputSlot;
|
|
var outputNode = owner.GetNodeFromGuid(outputSlotRef.nodeGuid);
|
|
if (outputNode == null)
|
|
continue;
|
|
|
|
var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(outputSlotRef.slotId);
|
|
if (outputSlot == null)
|
|
continue;
|
|
|
|
if (outputSlot.hasError)
|
|
{
|
|
inputSlot.hasError = true;
|
|
continue;
|
|
}
|
|
|
|
var outputConcreteType = outputSlot.concreteValueType;
|
|
// dynamic input... depends on output from other node.
|
|
// we need to compare ALL dynamic inputs to make sure they
|
|
// are compatable.
|
|
if (inputSlot is DynamicValueMaterialSlot)
|
|
{
|
|
dynamicInputSlotsToCompare.Add((DynamicValueMaterialSlot)inputSlot, outputConcreteType);
|
|
continue;
|
|
}
|
|
|
|
// if we have a standard connection... just check the types work!
|
|
if (!ImplicitConversionExists(outputConcreteType, inputSlot.concreteValueType))
|
|
inputSlot.hasError = true;
|
|
}
|
|
|
|
m_MultiplyType = GetMultiplyType(dynamicInputSlotsToCompare.Values);
|
|
|
|
// Resolve dynamics depending on matrix/vector configuration
|
|
switch(m_MultiplyType)
|
|
{
|
|
// If all matrix resolve as per dynamic matrix
|
|
case MultiplyType.Matrix:
|
|
var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
|
|
foreach (var dynamicKvP in dynamicInputSlotsToCompare)
|
|
dynamicKvP.Key.SetConcreteType(dynamicMatrixType);
|
|
foreach (var skippedSlot in skippedDynamicSlots)
|
|
skippedSlot.SetConcreteType(dynamicMatrixType);
|
|
break;
|
|
// If mixed handle differently:
|
|
// Iterate all slots and set their concretes based on their edges
|
|
// Find matrix slot and convert its type to a vector type
|
|
// Reiterate all slots and set non matrix slots to the vector type
|
|
case MultiplyType.Mixed:
|
|
foreach (var dynamicKvP in dynamicInputSlotsToCompare)
|
|
{
|
|
SetConcreteValueTypeFromEdge(dynamicKvP.Key);
|
|
}
|
|
MaterialSlot matrixSlot = GetMatrixSlot();
|
|
ConcreteSlotValueType vectorType = SlotValueHelper.ConvertMatrixToVectorType(matrixSlot.concreteValueType);
|
|
foreach (var dynamicKvP in dynamicInputSlotsToCompare)
|
|
{
|
|
if(dynamicKvP.Key != matrixSlot)
|
|
dynamicKvP.Key.SetConcreteType(vectorType);
|
|
}
|
|
foreach (var skippedSlot in skippedDynamicSlots)
|
|
{
|
|
skippedSlot.SetConcreteType(vectorType);
|
|
}
|
|
break;
|
|
// If all vector resolve as per dynamic vector
|
|
default:
|
|
var dynamicVectorType = ConvertDynamicInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
|
|
foreach (var dynamicKvP in dynamicInputSlotsToCompare)
|
|
dynamicKvP.Key.SetConcreteType(dynamicVectorType);
|
|
foreach (var skippedSlot in skippedDynamicSlots)
|
|
skippedSlot.SetConcreteType(dynamicVectorType);
|
|
break;
|
|
}
|
|
|
|
s_TempSlots.Clear();
|
|
GetInputSlots(s_TempSlots);
|
|
var inputError = s_TempSlots.Any(x => x.hasError);
|
|
|
|
// configure the output slots now
|
|
// their slotType will either be the default output slotType
|
|
// or the above dynanic slotType for dynamic nodes
|
|
// or error if there is an input error
|
|
s_TempSlots.Clear();
|
|
GetOutputSlots(s_TempSlots);
|
|
foreach (var outputSlot in s_TempSlots)
|
|
{
|
|
outputSlot.hasError = false;
|
|
|
|
if (inputError)
|
|
{
|
|
outputSlot.hasError = true;
|
|
continue;
|
|
}
|
|
|
|
if (outputSlot is DynamicValueMaterialSlot)
|
|
{
|
|
// Apply similar logic to output slot
|
|
switch(m_MultiplyType)
|
|
{
|
|
// As per dynamic matrix
|
|
case MultiplyType.Matrix:
|
|
var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
|
|
(outputSlot as DynamicValueMaterialSlot).SetConcreteType(dynamicMatrixType);
|
|
break;
|
|
// Mixed configuration
|
|
// Find matrix slot and convert type to vector
|
|
// Set output concrete to vector
|
|
case MultiplyType.Mixed:
|
|
MaterialSlot matrixSlot = GetMatrixSlot();
|
|
ConcreteSlotValueType vectorType = SlotValueHelper.ConvertMatrixToVectorType(matrixSlot.concreteValueType);
|
|
(outputSlot as DynamicValueMaterialSlot).SetConcreteType(vectorType);
|
|
break;
|
|
// As per dynamic vector
|
|
default:
|
|
var dynamicVectorType = ConvertDynamicInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
|
|
(outputSlot as DynamicValueMaterialSlot).SetConcreteType(dynamicVectorType);
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
isInError |= inputError;
|
|
s_TempSlots.Clear();
|
|
GetOutputSlots(s_TempSlots);
|
|
isInError |= s_TempSlots.Any(x => x.hasError);
|
|
isInError |= CalculateNodeHasError();
|
|
hasError = isInError;
|
|
|
|
if (!hasError)
|
|
{
|
|
++version;
|
|
}
|
|
|
|
ListPool<DynamicValueMaterialSlot>.Release(skippedDynamicSlots);
|
|
DictionaryPool<DynamicValueMaterialSlot, ConcreteSlotValueType>.Release(dynamicInputSlotsToCompare);
|
|
}
|
|
|
|
protected override bool CalculateNodeHasError()
|
|
{
|
|
if(m_MultiplyType == MultiplyType.Matrix)
|
|
{
|
|
foreach (var slot in this.GetOutputSlots<ISlot>())
|
|
{
|
|
foreach (var edge in owner.GetEdges(slot.slotReference))
|
|
{
|
|
var inputNode = owner.GetNodeFromGuid(edge.inputSlot.nodeGuid);
|
|
List<MaterialSlot> slots = new List<MaterialSlot>();
|
|
inputNode.GetInputSlots(slots);
|
|
foreach(var s in slots)
|
|
{
|
|
foreach(var inputEdge in inputNode.owner.GetEdges(s.slotReference))
|
|
{
|
|
if(inputEdge == edge)
|
|
{
|
|
if(s as DynamicValueMaterialSlot == null)
|
|
{
|
|
if(s.concreteValueType != ConcreteSlotValueType.Matrix4
|
|
&& s.concreteValueType != ConcreteSlotValueType.Matrix3
|
|
&& s.concreteValueType != ConcreteSlotValueType.Matrix2)
|
|
{
|
|
Debug.Log("ERROR: slot "+s.displayName+" cannot accept a Matrix type input");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private MultiplyType GetMultiplyType(IEnumerable<ConcreteSlotValueType> inputTypes)
|
|
{
|
|
var concreteSlotValueTypes = inputTypes as List<ConcreteSlotValueType> ?? inputTypes.ToList();
|
|
int matrixCount = 0;
|
|
int vectorCount = 0;
|
|
for (int i = 0; i < concreteSlotValueTypes.Count; i++)
|
|
{
|
|
if(concreteSlotValueTypes[i] == ConcreteSlotValueType.Vector4
|
|
|| concreteSlotValueTypes[i] == ConcreteSlotValueType.Vector3
|
|
|| concreteSlotValueTypes[i] == ConcreteSlotValueType.Vector2
|
|
|| concreteSlotValueTypes[i] == ConcreteSlotValueType.Vector1)
|
|
{
|
|
vectorCount++;
|
|
}
|
|
else if(concreteSlotValueTypes[i] == ConcreteSlotValueType.Matrix4
|
|
|| concreteSlotValueTypes[i] == ConcreteSlotValueType.Matrix3
|
|
|| concreteSlotValueTypes[i] == ConcreteSlotValueType.Matrix2)
|
|
{
|
|
matrixCount++;
|
|
}
|
|
}
|
|
if(matrixCount == 2)
|
|
return MultiplyType.Matrix;
|
|
else if(vectorCount == 2)
|
|
return MultiplyType.Vector;
|
|
else if(matrixCount == 1)
|
|
return MultiplyType.Mixed;
|
|
else
|
|
return MultiplyType.Vector;
|
|
}
|
|
|
|
private MaterialSlot GetMatrixSlot()
|
|
{
|
|
List<MaterialSlot> slots = new List<MaterialSlot>();
|
|
GetInputSlots(slots);
|
|
for (int i = 0; i < slots.Count; i++)
|
|
{
|
|
var edges = owner.GetEdges(slots[i].slotReference).ToList();
|
|
if(!edges.Any())
|
|
continue;
|
|
var outputNode = owner.GetNodeFromGuid(edges[0].outputSlot.nodeGuid);
|
|
var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(edges[0].outputSlot.slotId);
|
|
if(outputSlot.concreteValueType == ConcreteSlotValueType.Matrix4
|
|
|| outputSlot.concreteValueType == ConcreteSlotValueType.Matrix3
|
|
|| outputSlot.concreteValueType == ConcreteSlotValueType.Matrix2)
|
|
return slots[i];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void SetConcreteValueTypeFromEdge(DynamicValueMaterialSlot slot)
|
|
{
|
|
var edges = owner.GetEdges(slot.slotReference).ToList();
|
|
if(!edges.Any())
|
|
return;
|
|
var outputNode = owner.GetNodeFromGuid(edges[0].outputSlot.nodeGuid);
|
|
var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(edges[0].outputSlot.slotId);
|
|
slot.SetConcreteType(outputSlot.concreteValueType);
|
|
}
|
|
}
|
|
}
|