You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
359 lines
14 KiB
359 lines
14 KiB
using System; |
|
using System.Linq; |
|
using UnityEngine; |
|
using UnityEditor.Graphing; |
|
using UnityEditor.ShaderGraph.Internal; |
|
using System.Collections.Generic; |
|
using System.Text.RegularExpressions; |
|
|
|
namespace UnityEditor.ShaderGraph |
|
{ |
|
class BlockNode : AbstractMaterialNode |
|
, IMayRequireNormal |
|
, IMayRequireTangent |
|
, IMayRequireBitangent |
|
, IMayRequireMeshUV |
|
, IMayRequireScreenPosition |
|
, IMayRequireViewDirection |
|
, IMayRequirePosition |
|
, IMayRequirePositionPredisplacement |
|
, IMayRequireVertexColor |
|
{ |
|
[SerializeField] |
|
string m_SerializedDescriptor; |
|
|
|
[NonSerialized] |
|
ContextData m_ContextData; |
|
|
|
[NonSerialized] |
|
BlockFieldDescriptor m_Descriptor; |
|
|
|
public override bool canCutNode => false; |
|
public override bool canCopyNode => false; |
|
|
|
// Because the GraphData is deserialized after its child elements |
|
// the descriptor list is not built (and owner is not set) |
|
// at the time of node deserialization |
|
// Therefore we need to deserialize this element at GraphData.OnAfterDeserialize |
|
public string serializedDescriptor => m_SerializedDescriptor; |
|
|
|
public ContextData contextData |
|
{ |
|
get => m_ContextData; |
|
set => m_ContextData = value; |
|
} |
|
|
|
public int index => contextData.blocks.IndexOf(this); |
|
|
|
public BlockFieldDescriptor descriptor |
|
{ |
|
get => m_Descriptor; |
|
set => m_Descriptor = value; |
|
} |
|
|
|
const string k_CustomBlockDefaultName = "CustomInterpolator"; |
|
|
|
internal enum CustomBlockType { Float = 1, Vector2 = 2, Vector3 = 3, Vector4 = 4 } |
|
|
|
internal bool isCustomBlock { get => m_Descriptor?.isCustom ?? false; } |
|
|
|
internal string customName |
|
{ |
|
get => m_Descriptor.name; |
|
set => OnCustomBlockFieldModified(value, customWidth); |
|
} |
|
|
|
internal CustomBlockType customWidth |
|
{ |
|
get => (CustomBlockType)ControlToWidth(m_Descriptor.control); |
|
set => OnCustomBlockFieldModified(customName, value); |
|
} |
|
|
|
public void Init(BlockFieldDescriptor fieldDescriptor) |
|
{ |
|
m_Descriptor = fieldDescriptor; |
|
|
|
// custom blocks can be "copied" via a custom Field Descriptor, we'll use the CI name instead though. |
|
name = !isCustomBlock |
|
? $"{fieldDescriptor.tag}.{fieldDescriptor.name}" |
|
: $"{BlockFields.VertexDescription.name}.{k_CustomBlockDefaultName}"; |
|
|
|
|
|
// TODO: This exposes the MaterialSlot API |
|
// TODO: This needs to be removed but is currently required by HDRP for DiffusionProfileInputMaterialSlot |
|
if (m_Descriptor is CustomSlotBlockFieldDescriptor customSlotDescriptor) |
|
{ |
|
var newSlot = customSlotDescriptor.createSlot(); |
|
AddSlot(newSlot); |
|
RemoveSlotsNameNotMatching(new int[] { 0 }); |
|
return; |
|
} |
|
|
|
AddSlotFromControlType(); |
|
} |
|
|
|
internal void InitCustomDefault() |
|
{ |
|
Init(MakeCustomBlockField(k_CustomBlockDefaultName, CustomBlockType.Vector4)); |
|
} |
|
|
|
private void AddSlotFromControlType(bool attemptToModifyExisting = true) |
|
{ |
|
// TODO: this should really just use callbacks like the CustomSlotBlockFieldDescriptor. then we wouldn't need this switch to make a copy |
|
var stageCapability = m_Descriptor.shaderStage.GetShaderStageCapability(); |
|
switch (descriptor.control) |
|
{ |
|
case PositionControl positionControl: |
|
AddSlot(new PositionMaterialSlot(0, descriptor.displayName, descriptor.name, positionControl.space, stageCapability), attemptToModifyExisting); |
|
break; |
|
case NormalControl normalControl: |
|
AddSlot(new NormalMaterialSlot(0, descriptor.displayName, descriptor.name, normalControl.space, stageCapability), attemptToModifyExisting); |
|
break; |
|
case TangentControl tangentControl: |
|
AddSlot(new TangentMaterialSlot(0, descriptor.displayName, descriptor.name, tangentControl.space, stageCapability), attemptToModifyExisting); |
|
break; |
|
case VertexColorControl vertexColorControl: |
|
AddSlot(new VertexColorMaterialSlot(0, descriptor.displayName, descriptor.name, stageCapability), attemptToModifyExisting); |
|
break; |
|
case ColorControl colorControl: |
|
var colorMode = colorControl.hdr ? ColorMode.HDR : ColorMode.Default; |
|
AddSlot(new ColorRGBMaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, colorControl.value, colorMode, stageCapability), attemptToModifyExisting); |
|
break; |
|
case ColorRGBAControl colorRGBAControl: |
|
AddSlot(new ColorRGBAMaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, colorRGBAControl.value, stageCapability), attemptToModifyExisting); |
|
break; |
|
case FloatControl floatControl: |
|
AddSlot(new Vector1MaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, floatControl.value, stageCapability), attemptToModifyExisting); |
|
break; |
|
case Vector2Control vector2Control: |
|
AddSlot(new Vector2MaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, vector2Control.value, stageCapability), attemptToModifyExisting); |
|
break; |
|
case Vector3Control vector3Control: |
|
AddSlot(new Vector3MaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, vector3Control.value, stageCapability), attemptToModifyExisting); |
|
break; |
|
case Vector4Control vector4Control: |
|
AddSlot(new Vector4MaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, vector4Control.value, stageCapability), attemptToModifyExisting); |
|
break; |
|
} |
|
RemoveSlotsNameNotMatching(new int[] { 0 }); |
|
} |
|
|
|
public override string GetVariableNameForNode() |
|
{ |
|
// Temporary block nodes have temporary guids that cannot be used to set preview data |
|
// Since each block is unique anyway we just omit the guid |
|
return NodeUtils.GetHLSLSafeName(name); |
|
} |
|
|
|
public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability) |
|
{ |
|
if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability()) |
|
return NeededCoordinateSpace.None; |
|
|
|
if (m_Descriptor.control == null) |
|
return NeededCoordinateSpace.None; |
|
|
|
var requirements = m_Descriptor.control.GetRequirements(); |
|
return requirements.requiresNormal; |
|
} |
|
|
|
public NeededCoordinateSpace RequiresViewDirection(ShaderStageCapability stageCapability) |
|
{ |
|
if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability()) |
|
return NeededCoordinateSpace.None; |
|
|
|
if (m_Descriptor.control == null) |
|
return NeededCoordinateSpace.None; |
|
|
|
var requirements = m_Descriptor.control.GetRequirements(); |
|
return requirements.requiresViewDir; |
|
} |
|
|
|
public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability) |
|
{ |
|
if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability()) |
|
return NeededCoordinateSpace.None; |
|
|
|
if (m_Descriptor.control == null) |
|
return NeededCoordinateSpace.None; |
|
|
|
var requirements = m_Descriptor.control.GetRequirements(); |
|
return requirements.requiresPosition; |
|
} |
|
|
|
public NeededCoordinateSpace RequiresPositionPredisplacement(ShaderStageCapability stageCapability) |
|
{ |
|
if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability()) |
|
return NeededCoordinateSpace.None; |
|
|
|
if (m_Descriptor.control == null) |
|
return NeededCoordinateSpace.None; |
|
|
|
var requirements = m_Descriptor.control.GetRequirements(); |
|
return requirements.requiresPositionPredisplacement; |
|
} |
|
|
|
public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability) |
|
{ |
|
if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability()) |
|
return NeededCoordinateSpace.None; |
|
|
|
if (m_Descriptor.control == null) |
|
return NeededCoordinateSpace.None; |
|
|
|
var requirements = m_Descriptor.control.GetRequirements(); |
|
return requirements.requiresTangent; |
|
} |
|
|
|
public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability) |
|
{ |
|
if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability()) |
|
return NeededCoordinateSpace.None; |
|
|
|
if (m_Descriptor.control == null) |
|
return NeededCoordinateSpace.None; |
|
|
|
var requirements = m_Descriptor.control.GetRequirements(); |
|
return requirements.requiresBitangent; |
|
} |
|
|
|
public bool RequiresMeshUV(UVChannel channel, ShaderStageCapability stageCapability) |
|
{ |
|
if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability()) |
|
return false; |
|
|
|
if (m_Descriptor.control == null) |
|
return false; |
|
|
|
var requirements = m_Descriptor.control.GetRequirements(); |
|
return requirements.requiresMeshUVs.Contains(channel); |
|
} |
|
|
|
public bool RequiresScreenPosition(ShaderStageCapability stageCapability) |
|
{ |
|
if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability()) |
|
return false; |
|
|
|
if (m_Descriptor.control == null) |
|
return false; |
|
|
|
var requirements = m_Descriptor.control.GetRequirements(); |
|
return requirements.requiresScreenPosition; |
|
} |
|
|
|
public bool RequiresVertexColor(ShaderStageCapability stageCapability) |
|
{ |
|
if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability()) |
|
return false; |
|
|
|
if (m_Descriptor.control == null) |
|
return false; |
|
|
|
var requirements = m_Descriptor.control.GetRequirements(); |
|
return requirements.requiresVertexColor; |
|
} |
|
|
|
private void OnCustomBlockFieldModified(string name, CustomBlockType width) |
|
{ |
|
if (!isCustomBlock) |
|
{ |
|
Debug.LogWarning(String.Format("{0} is not a custom interpolator.", this.name)); |
|
return; |
|
} |
|
|
|
m_Descriptor = MakeCustomBlockField(name, width); |
|
|
|
// TODO: Preserve the original slot's value and try to reapply after the slot is updated. |
|
AddSlotFromControlType(false); |
|
|
|
owner?.ValidateGraph(); |
|
} |
|
|
|
public override void OnBeforeSerialize() |
|
{ |
|
base.OnBeforeSerialize(); |
|
if (descriptor != null) |
|
{ |
|
if (isCustomBlock) |
|
{ |
|
int width = ControlToWidth(m_Descriptor.control); |
|
m_SerializedDescriptor = $"{m_Descriptor.tag}.{m_Descriptor.name}#{width}"; |
|
} |
|
else |
|
{ |
|
m_SerializedDescriptor = $"{m_Descriptor.tag}.{m_Descriptor.name}"; |
|
} |
|
} |
|
} |
|
|
|
public override void OnAfterDeserialize() |
|
{ |
|
// TODO: Go find someone to tell @esme not to do this. |
|
if (m_SerializedDescriptor.Contains("#")) |
|
{ |
|
string descName = k_CustomBlockDefaultName; |
|
CustomBlockType descWidth = CustomBlockType.Vector4; |
|
var descTag = BlockFields.VertexDescription.name; |
|
|
|
name = $"{descTag}.{descName}"; |
|
|
|
var wsplit = m_SerializedDescriptor.Split(new char[] { '#', '.' }); |
|
|
|
try |
|
{ |
|
descWidth = (CustomBlockType)int.Parse(wsplit[2]); |
|
} |
|
catch |
|
{ |
|
Debug.LogWarning(String.Format("Bad width found while deserializing custom interpolator {0}, defaulting to 4.", m_SerializedDescriptor)); |
|
descWidth = CustomBlockType.Vector4; |
|
} |
|
|
|
IControl control; |
|
try { control = (IControl)FindSlot<MaterialSlot>(0).InstantiateControl(); } |
|
catch { control = WidthToControl((int)descWidth); } |
|
|
|
descName = NodeUtils.ConvertToValidHLSLIdentifier(wsplit[1]); |
|
m_Descriptor = new BlockFieldDescriptor(descTag, descName, "", control, ShaderStage.Vertex, isCustom: true); |
|
} |
|
} |
|
|
|
#region CustomInterpolatorHelpers |
|
private static BlockFieldDescriptor MakeCustomBlockField(string name, CustomBlockType width) |
|
{ |
|
name = NodeUtils.ConvertToValidHLSLIdentifier(name); |
|
var referenceName = name; |
|
var define = ""; |
|
IControl control = WidthToControl((int)width); |
|
var tag = BlockFields.VertexDescription.name; |
|
|
|
return new BlockFieldDescriptor(tag, referenceName, define, control, ShaderStage.Vertex, isCustom: true); |
|
} |
|
|
|
private static IControl WidthToControl(int width) |
|
{ |
|
switch (width) |
|
{ |
|
case 1: return new FloatControl(default(float)); |
|
case 2: return new Vector2Control(default(Vector2)); |
|
case 3: return new Vector3Control(default(Vector3)); |
|
case 4: return new Vector4Control(default(Vector4)); |
|
default: return null; |
|
} |
|
} |
|
|
|
private static int ControlToWidth(IControl control) |
|
{ |
|
switch (control) |
|
{ |
|
case FloatControl a: return 1; |
|
case Vector2Control b: return 2; |
|
case Vector3Control c: return 3; |
|
case Vector4Control d: return 4; |
|
default: return -1; |
|
} |
|
} |
|
|
|
#endregion |
|
} |
|
}
|
|
|