528 lines
16 KiB
C#
528 lines
16 KiB
C#
// Amplify Shader Editor - Visual Shader Editing Tool
|
|
// Copyright (c) Amplify Creations, Lda <info@amplify.pt>
|
|
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace AmplifyShaderEditor
|
|
{
|
|
[Serializable]
|
|
[NodeAttributes( "Function Input", "Functions", "Function Input adds an input port to the shader function", NodeAvailabilityFlags = (int)NodeAvailability.ShaderFunction )]
|
|
public sealed class FunctionInput : ParentNode
|
|
{
|
|
private const string InputTypeStr = "Input Type";
|
|
private readonly string[] m_inputValueTypes ={ "Int",
|
|
"Float",
|
|
"Vector2",
|
|
"Vector3",
|
|
"Vector4",
|
|
"Color",
|
|
"Matrix 3x3",
|
|
"Matrix 4x4",
|
|
"Sampler 1D",
|
|
"Sampler 2D",
|
|
"Sampler 3D",
|
|
"Sampler Cube",
|
|
"Sampler 2D Array",
|
|
"Sampler State",
|
|
"Custom"};
|
|
|
|
[SerializeField]
|
|
private int m_selectedInputTypeInt = 1;
|
|
|
|
private WirePortDataType m_selectedInputType = WirePortDataType.FLOAT;
|
|
|
|
[SerializeField]
|
|
private FunctionNode m_functionNode;
|
|
|
|
[SerializeField]
|
|
private string m_inputName = "Input";
|
|
|
|
[SerializeField]
|
|
private bool m_autoCast = false;
|
|
|
|
[SerializeField]
|
|
private int m_orderIndex = -1;
|
|
|
|
private int m_typeId = -1;
|
|
|
|
public bool m_ignoreConnection = false;
|
|
|
|
public delegate string PortGeneration( ref MasterNodeDataCollector dataCollector, int index, ParentGraph graph );
|
|
public PortGeneration OnPortGeneration = null;
|
|
|
|
//Title editing
|
|
[SerializeField]
|
|
private string m_uniqueName;
|
|
|
|
private bool m_isEditing;
|
|
private bool m_stopEditing;
|
|
private bool m_startEditing;
|
|
private double m_clickTime;
|
|
private double m_doubleClickTime = 0.3;
|
|
private Rect m_titleClickArea;
|
|
private bool m_showTitleWhenNotEditing = true;
|
|
|
|
|
|
protected override void CommonInit( int uniqueId )
|
|
{
|
|
base.CommonInit( uniqueId );
|
|
AddInputPort( WirePortDataType.FLOAT, false, Constants.EmptyPortValue );
|
|
m_inputPorts[ 0 ].AutoDrawInternalData = true;
|
|
//m_inputPorts[ 0 ].Visible = false;
|
|
AddOutputPort( WirePortDataType.FLOAT, Constants.EmptyPortValue );
|
|
m_autoWrapProperties = true;
|
|
m_textLabelWidth = 100;
|
|
SetTitleText( m_inputName );
|
|
UpdatePorts();
|
|
SetAdditonalTitleText( "( " + m_inputValueTypes[ m_selectedInputTypeInt ] + " )" );
|
|
m_previewShaderGUID = "04bc8e7b317dccb4d8da601680dd8140";
|
|
}
|
|
|
|
public override void SetPreviewInputs()
|
|
{
|
|
if( Fnode == null )
|
|
{
|
|
m_ignoreConnection = false;
|
|
CheckSpherePreview();
|
|
}
|
|
else
|
|
{
|
|
var input = Fnode.GetInput( this );
|
|
if( input != null && ( !InputPorts[ 0 ].IsConnected || input.IsConnected ) )
|
|
{
|
|
m_ignoreConnection = true;
|
|
InputPorts[ 0 ].PreparePortCacheID();
|
|
Fnode.SetPreviewInput( input );
|
|
if( input.ExternalReferences.Count > 0 )
|
|
{
|
|
SpherePreview = Fnode.ContainerGraph.GetNode( input.ExternalReferences[ 0 ].NodeId ).SpherePreview;
|
|
}
|
|
else
|
|
{
|
|
SpherePreview = false;
|
|
}
|
|
PreviewMaterial.SetTexture( InputPorts[ 0 ].CachedPropertyId, input.InputPreviewTexture( Fnode.ContainerGraph ) );
|
|
}
|
|
else
|
|
{
|
|
m_ignoreConnection = false;
|
|
CheckSpherePreview();
|
|
}
|
|
}
|
|
|
|
if( !m_ignoreConnection )
|
|
base.SetPreviewInputs();
|
|
|
|
for( int i = 0; i < OutputPorts[ 0 ].ExternalReferences.Count; i++ )
|
|
{
|
|
ContainerGraph.GetNode( OutputPorts[ 0 ].ExternalReferences[ i ].NodeId ).OnNodeChange();
|
|
}
|
|
|
|
if( m_typeId == -1 )
|
|
m_typeId = Shader.PropertyToID( "_Type" );
|
|
|
|
if( m_inputPorts[ 0 ].DataType == WirePortDataType.FLOAT || m_inputPorts[ 0 ].DataType == WirePortDataType.INT )
|
|
PreviewMaterial.SetInt( m_typeId, 1 );
|
|
else if( m_inputPorts[ 0 ].DataType == WirePortDataType.FLOAT2 )
|
|
PreviewMaterial.SetInt( m_typeId, 2 );
|
|
else if( m_inputPorts[ 0 ].DataType == WirePortDataType.FLOAT3 )
|
|
PreviewMaterial.SetInt( m_typeId, 3 );
|
|
else
|
|
PreviewMaterial.SetInt( m_typeId, 0 );
|
|
|
|
}
|
|
|
|
public override bool RecursivePreviewUpdate( Dictionary<string, bool> duplicatesDict = null )
|
|
{
|
|
if( duplicatesDict == null )
|
|
{
|
|
duplicatesDict = ContainerGraph.ParentWindow.VisitedChanged;
|
|
}
|
|
|
|
for( int i = 0; i < InputPorts.Count; i++ )
|
|
{
|
|
ParentNode outNode = null;
|
|
if( Fnode != null )
|
|
{
|
|
var input = Fnode.GetInput( this );
|
|
if( input.ExternalReferences.Count > 0 )
|
|
{
|
|
outNode = Fnode.ContainerGraph.GetNode( input.ExternalReferences[ 0 ].NodeId );
|
|
}
|
|
else if( InputPorts[ i ].ExternalReferences.Count > 0 )
|
|
{
|
|
outNode = ContainerGraph.GetNode( InputPorts[ i ].ExternalReferences[ 0 ].NodeId );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( InputPorts[ i ].ExternalReferences.Count > 0 )
|
|
{
|
|
outNode = ContainerGraph.GetNode( InputPorts[ i ].ExternalReferences[ 0 ].NodeId );
|
|
}
|
|
}
|
|
if( outNode != null )
|
|
{
|
|
if( !duplicatesDict.ContainsKey( outNode.OutputId ) )
|
|
{
|
|
bool result = outNode.RecursivePreviewUpdate();
|
|
if( result )
|
|
PreviewIsDirty = true;
|
|
}
|
|
else if( duplicatesDict[ outNode.OutputId ] )
|
|
{
|
|
PreviewIsDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool needsUpdate = PreviewIsDirty;
|
|
RenderNodePreview();
|
|
if( !duplicatesDict.ContainsKey( OutputId ) )
|
|
duplicatesDict.Add( OutputId, needsUpdate );
|
|
return needsUpdate;
|
|
}
|
|
|
|
protected override void OnUniqueIDAssigned()
|
|
{
|
|
base.OnUniqueIDAssigned();
|
|
UIUtils.RegisterFunctionInputNode( this );
|
|
if( m_nodeAttribs != null )
|
|
m_uniqueName = m_nodeAttribs.Name + UniqueId;
|
|
}
|
|
|
|
public override void Destroy()
|
|
{
|
|
base.Destroy();
|
|
OnPortGeneration = null;
|
|
UIUtils.UnregisterFunctionInputNode( this );
|
|
}
|
|
|
|
public override void OnInputPortConnected( int portId, int otherNodeId, int otherPortId, bool activateNode = true )
|
|
{
|
|
base.OnInputPortConnected( portId, otherNodeId, otherPortId, activateNode );
|
|
if( AutoCast )
|
|
{
|
|
m_inputPorts[ 0 ].MatchPortToConnection();
|
|
SetIntTypeFromPort();
|
|
UpdatePorts();
|
|
SetAdditonalTitleText( "( " + m_inputValueTypes[ m_selectedInputTypeInt ] + " )" );
|
|
}
|
|
}
|
|
|
|
public override void OnConnectedOutputNodeChanges( int portId, int otherNodeId, int otherPortId, string name, WirePortDataType type )
|
|
{
|
|
base.OnConnectedOutputNodeChanges( portId, otherNodeId, otherPortId, name, type );
|
|
if( AutoCast )
|
|
{
|
|
m_inputPorts[ 0 ].MatchPortToConnection();
|
|
SetIntTypeFromPort();
|
|
UpdatePorts();
|
|
SetAdditonalTitleText( "( " + m_inputValueTypes[ m_selectedInputTypeInt ] + " )" );
|
|
}
|
|
}
|
|
|
|
public void SetIntTypeFromPort()
|
|
{
|
|
switch( m_inputPorts[ 0 ].DataType )
|
|
{
|
|
case WirePortDataType.INT: m_selectedInputTypeInt = 0; break;
|
|
default:
|
|
case WirePortDataType.FLOAT: m_selectedInputTypeInt = 1; break;
|
|
case WirePortDataType.FLOAT2: m_selectedInputTypeInt = 2; break;
|
|
case WirePortDataType.FLOAT3: m_selectedInputTypeInt = 3; break;
|
|
case WirePortDataType.FLOAT4: m_selectedInputTypeInt = 4; break;
|
|
case WirePortDataType.COLOR: m_selectedInputTypeInt = 5; break;
|
|
case WirePortDataType.FLOAT3x3: m_selectedInputTypeInt = 6; break;
|
|
case WirePortDataType.FLOAT4x4: m_selectedInputTypeInt = 7; break;
|
|
case WirePortDataType.SAMPLER1D: m_selectedInputTypeInt = 8; break;
|
|
case WirePortDataType.SAMPLER2D: m_selectedInputTypeInt = 9; break;
|
|
case WirePortDataType.SAMPLER3D: m_selectedInputTypeInt = 10; break;
|
|
case WirePortDataType.SAMPLERCUBE: m_selectedInputTypeInt = 11; break;
|
|
case WirePortDataType.SAMPLER2DARRAY: m_selectedInputTypeInt = 12; break;
|
|
case WirePortDataType.SAMPLERSTATE: m_selectedInputTypeInt = 13; break;
|
|
case WirePortDataType.OBJECT: m_selectedInputTypeInt = 14; break;
|
|
}
|
|
}
|
|
|
|
public override void Draw( DrawInfo drawInfo )
|
|
{
|
|
base.Draw( drawInfo );
|
|
// Custom Editable Title
|
|
if( ContainerGraph.LodLevel <= ParentGraph.NodeLOD.LOD3 )
|
|
{
|
|
if( !m_isEditing && ( ( !ContainerGraph.ParentWindow.MouseInteracted && drawInfo.CurrentEventType == EventType.MouseDown && m_titleClickArea.Contains( drawInfo.MousePosition ) ) ) )
|
|
{
|
|
if( ( EditorApplication.timeSinceStartup - m_clickTime ) < m_doubleClickTime )
|
|
m_startEditing = true;
|
|
else
|
|
GUI.FocusControl( null );
|
|
m_clickTime = EditorApplication.timeSinceStartup;
|
|
}
|
|
else if( m_isEditing && ( ( drawInfo.CurrentEventType == EventType.MouseDown && !m_titleClickArea.Contains( drawInfo.MousePosition ) ) || !EditorGUIUtility.editingTextField ) )
|
|
{
|
|
m_stopEditing = true;
|
|
}
|
|
|
|
if( m_isEditing || m_startEditing )
|
|
{
|
|
EditorGUI.BeginChangeCheck();
|
|
GUI.SetNextControlName( m_uniqueName );
|
|
m_inputName = EditorGUITextField( m_titleClickArea, string.Empty, m_inputName, UIUtils.GetCustomStyle( CustomStyle.NodeTitle ) );
|
|
if( EditorGUI.EndChangeCheck() )
|
|
{
|
|
SetTitleText( m_inputName );
|
|
UIUtils.UpdateFunctionInputData( UniqueId, m_inputName );
|
|
}
|
|
|
|
if( m_startEditing )
|
|
EditorGUI.FocusTextInControl( m_uniqueName );
|
|
|
|
}
|
|
|
|
if( drawInfo.CurrentEventType == EventType.Repaint )
|
|
{
|
|
if( m_startEditing )
|
|
{
|
|
m_startEditing = false;
|
|
m_isEditing = true;
|
|
}
|
|
|
|
if( m_stopEditing )
|
|
{
|
|
m_stopEditing = false;
|
|
m_isEditing = false;
|
|
GUI.FocusControl( null );
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
public override void OnNodeLayout( DrawInfo drawInfo )
|
|
{
|
|
// RUN LAYOUT CHANGES AFTER TITLES CHANGE
|
|
base.OnNodeLayout( drawInfo );
|
|
m_titleClickArea = m_titlePos;
|
|
m_titleClickArea.height = Constants.NODE_HEADER_HEIGHT;
|
|
}
|
|
|
|
public override void OnNodeRepaint( DrawInfo drawInfo )
|
|
{
|
|
base.OnNodeRepaint( drawInfo );
|
|
|
|
if( !m_isVisible )
|
|
return;
|
|
|
|
// Fixed Title ( only renders when not editing )
|
|
if( m_showTitleWhenNotEditing && !m_isEditing && !m_startEditing && ContainerGraph.LodLevel <= ParentGraph.NodeLOD.LOD3 )
|
|
{
|
|
GUI.Label( m_titleClickArea, m_content, UIUtils.GetCustomStyle( CustomStyle.NodeTitle ) );
|
|
}
|
|
}
|
|
|
|
public override void OnNodeDoubleClicked( Vector2 currentMousePos2D )
|
|
{
|
|
if( currentMousePos2D.y - m_globalPosition.y > ( Constants.NODE_HEADER_HEIGHT + Constants.NODE_HEADER_EXTRA_HEIGHT ) * ContainerGraph.ParentWindow.CameraDrawInfo.InvertedZoom )
|
|
{
|
|
ContainerGraph.ParentWindow.ParametersWindow.IsMaximized = !ContainerGraph.ParentWindow.ParametersWindow.IsMaximized;
|
|
}
|
|
}
|
|
|
|
public override void DrawProperties()
|
|
{
|
|
base.DrawProperties();
|
|
EditorGUILayout.BeginVertical();
|
|
EditorGUI.BeginChangeCheck();
|
|
m_inputName = EditorGUILayoutTextField( "Name", m_inputName );
|
|
if( EditorGUI.EndChangeCheck() )
|
|
{
|
|
SetTitleText( m_inputName );
|
|
UIUtils.UpdateFunctionInputData( UniqueId, m_inputName );
|
|
}
|
|
EditorGUI.BeginChangeCheck();
|
|
m_selectedInputTypeInt = EditorGUILayoutPopup( InputTypeStr, m_selectedInputTypeInt, m_inputValueTypes );
|
|
if( EditorGUI.EndChangeCheck() )
|
|
{
|
|
UpdatePorts();
|
|
SetAdditonalTitleText( "( " + m_inputValueTypes[ m_selectedInputTypeInt ] + " )" );
|
|
}
|
|
|
|
m_autoCast = EditorGUILayoutToggle( "Auto Cast", m_autoCast );
|
|
|
|
EditorGUILayout.Separator();
|
|
if( !m_inputPorts[ 0 ].IsConnected && m_inputPorts[ 0 ].ValidInternalData )
|
|
{
|
|
m_inputPorts[ 0 ].ShowInternalData( this, true, "Default Value" );
|
|
}
|
|
|
|
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
void UpdatePorts()
|
|
{
|
|
switch( m_selectedInputTypeInt )
|
|
{
|
|
case 0: m_selectedInputType = WirePortDataType.INT; break;
|
|
default:
|
|
case 1: m_selectedInputType = WirePortDataType.FLOAT; break;
|
|
case 2: m_selectedInputType = WirePortDataType.FLOAT2; break;
|
|
case 3: m_selectedInputType = WirePortDataType.FLOAT3; break;
|
|
case 4: m_selectedInputType = WirePortDataType.FLOAT4; break;
|
|
case 5: m_selectedInputType = WirePortDataType.COLOR; break;
|
|
case 6: m_selectedInputType = WirePortDataType.FLOAT3x3; break;
|
|
case 7: m_selectedInputType = WirePortDataType.FLOAT4x4; break;
|
|
case 8: m_selectedInputType = WirePortDataType.SAMPLER1D; break;
|
|
case 9: m_selectedInputType = WirePortDataType.SAMPLER2D; break;
|
|
case 10: m_selectedInputType = WirePortDataType.SAMPLER3D; break;
|
|
case 11: m_selectedInputType = WirePortDataType.SAMPLERCUBE; break;
|
|
case 12: m_selectedInputType = WirePortDataType.SAMPLER2DARRAY; break;
|
|
case 13: m_selectedInputType = WirePortDataType.SAMPLERSTATE; break;
|
|
case 14: m_selectedInputType = WirePortDataType.OBJECT; break;
|
|
}
|
|
|
|
ChangeInputType( m_selectedInputType, false );
|
|
|
|
//This node doesn't have any restrictions but changing types should be restricted to prevent invalid connections
|
|
m_outputPorts[ 0 ].ChangeTypeWithRestrictions( m_selectedInputType, PortCreateRestriction( m_selectedInputType ) );
|
|
m_sizeIsDirty = true;
|
|
}
|
|
|
|
public int PortCreateRestriction( WirePortDataType dataType )
|
|
{
|
|
int restrictions = 0;
|
|
WirePortDataType[] types = null;
|
|
switch( dataType )
|
|
{
|
|
case WirePortDataType.OBJECT:
|
|
break;
|
|
case WirePortDataType.FLOAT:
|
|
case WirePortDataType.FLOAT2:
|
|
case WirePortDataType.FLOAT3:
|
|
case WirePortDataType.FLOAT4:
|
|
case WirePortDataType.COLOR:
|
|
case WirePortDataType.INT:
|
|
{
|
|
types = new WirePortDataType[] { WirePortDataType.FLOAT, WirePortDataType.FLOAT2, WirePortDataType.FLOAT3, WirePortDataType.FLOAT4, WirePortDataType.COLOR, WirePortDataType.INT, WirePortDataType.OBJECT };
|
|
}
|
|
break;
|
|
case WirePortDataType.FLOAT3x3:
|
|
case WirePortDataType.FLOAT4x4:
|
|
{
|
|
types = new WirePortDataType[] { WirePortDataType.FLOAT3x3, WirePortDataType.FLOAT4x4, WirePortDataType.OBJECT };
|
|
}
|
|
break;
|
|
case WirePortDataType.SAMPLER1D:
|
|
case WirePortDataType.SAMPLER2D:
|
|
case WirePortDataType.SAMPLER3D:
|
|
case WirePortDataType.SAMPLERCUBE:
|
|
case WirePortDataType.SAMPLER2DARRAY:
|
|
{
|
|
types = new WirePortDataType[] { WirePortDataType.SAMPLER1D, WirePortDataType.SAMPLER2D, WirePortDataType.SAMPLER3D, WirePortDataType.SAMPLERCUBE, WirePortDataType.SAMPLER2DARRAY, WirePortDataType.OBJECT };
|
|
}
|
|
break;
|
|
case WirePortDataType.SAMPLERSTATE:
|
|
{
|
|
types = new WirePortDataType[] { WirePortDataType.SAMPLERSTATE };
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if( types != null )
|
|
{
|
|
for( int i = 0; i < types.Length; i++ )
|
|
{
|
|
restrictions = restrictions | (int)types[ i ];
|
|
}
|
|
}
|
|
|
|
return restrictions;
|
|
}
|
|
|
|
public override string GenerateShaderForOutput( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar )
|
|
{
|
|
if( m_outputPorts[ outputId ].IsLocalValue( dataCollector.PortCategory ) )
|
|
return m_outputPorts[ outputId ].LocalValue( dataCollector.PortCategory );
|
|
|
|
string result = string.Empty;
|
|
if( OnPortGeneration != null )
|
|
result = OnPortGeneration( ref dataCollector, m_orderIndex, ContainerGraph.ParentWindow.CustomGraph );
|
|
else
|
|
result = m_inputPorts[ 0 ].GeneratePortInstructions( ref dataCollector );
|
|
|
|
if( m_outputPorts[ outputId ].ConnectionCount > 1 )
|
|
RegisterLocalVariable( outputId, result, ref dataCollector );
|
|
else
|
|
m_outputPorts[ outputId ].SetLocalValue( result, dataCollector.PortCategory );
|
|
|
|
return m_outputPorts[ outputId ].LocalValue( dataCollector.PortCategory );
|
|
}
|
|
|
|
public override void WriteToString( ref string nodeInfo, ref string connectionsInfo )
|
|
{
|
|
base.WriteToString( ref nodeInfo, ref connectionsInfo );
|
|
IOUtils.AddFieldValueToString( ref nodeInfo, m_inputName );
|
|
IOUtils.AddFieldValueToString( ref nodeInfo, m_selectedInputTypeInt );
|
|
IOUtils.AddFieldValueToString( ref nodeInfo, m_orderIndex );
|
|
IOUtils.AddFieldValueToString( ref nodeInfo, m_autoCast );
|
|
}
|
|
|
|
public override void ReadFromString( ref string[] nodeParams )
|
|
{
|
|
base.ReadFromString( ref nodeParams );
|
|
m_inputName = GetCurrentParam( ref nodeParams );
|
|
m_selectedInputTypeInt = Convert.ToInt32( GetCurrentParam( ref nodeParams ) );
|
|
m_orderIndex = Convert.ToInt32( GetCurrentParam( ref nodeParams ) );
|
|
m_autoCast = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) );
|
|
|
|
SetTitleText( m_inputName );
|
|
UpdatePorts();
|
|
SetAdditonalTitleText( "( " + m_inputValueTypes[ m_selectedInputTypeInt ] + " )" );
|
|
}
|
|
|
|
public override void RefreshExternalReferences()
|
|
{
|
|
base.RefreshExternalReferences();
|
|
|
|
SetTitleText( m_inputName );
|
|
UpdatePorts();
|
|
SetAdditonalTitleText( "( " + m_inputValueTypes[ m_selectedInputTypeInt ] + " )" );
|
|
}
|
|
|
|
public WirePortDataType SelectedInputType
|
|
{
|
|
get { return m_selectedInputType; }
|
|
}
|
|
|
|
public string InputName
|
|
{
|
|
get { return m_inputName; }
|
|
}
|
|
|
|
public int OrderIndex
|
|
{
|
|
get { return m_orderIndex; }
|
|
set { m_orderIndex = value; }
|
|
}
|
|
|
|
public bool AutoCast
|
|
{
|
|
get { return m_autoCast; }
|
|
set { m_autoCast = value; }
|
|
}
|
|
|
|
public FunctionNode Fnode
|
|
{
|
|
get { return m_functionNode; }
|
|
set { m_functionNode = value; }
|
|
}
|
|
}
|
|
}
|