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.

1570 lines
50 KiB

  1. // Amplify Shader Editor - Visual Shader Editing Tool
  2. // Copyright (c) Amplify Creations, Lda <info@amplify.pt>
  3. using System;
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6. using UnityEditor;
  7. using UnityEditorInternal;
  8. namespace AmplifyShaderEditor
  9. {
  10. public enum CustomExpressionMode
  11. {
  12. Create,
  13. Call
  14. }
  15. [Serializable]
  16. public class CustomExpressionInputItem
  17. {
  18. public PrecisionType Precision;
  19. public VariableQualifiers Qualifier;
  20. public WirePortDataType Type;
  21. public string CustomType;
  22. public bool FoldoutFlag;
  23. public string FoldoutLabel;
  24. public CustomExpressionInputItem( PrecisionType precision, VariableQualifiers qualifier, string customType, bool foldoutFlag, string foldoutLabel )
  25. {
  26. Precision = precision;
  27. Qualifier = qualifier;
  28. CustomType = customType;
  29. FoldoutFlag = foldoutFlag;
  30. FoldoutLabel = foldoutLabel;
  31. }
  32. }
  33. [Serializable]
  34. public class CustomExpressionDependency
  35. {
  36. public int DependencyArrayIdx;
  37. public int DependencyNodeId;
  38. public CustomExpressionDependency() { DependencyArrayIdx = DependencyNodeId = -1; }
  39. public CustomExpressionDependency( string id ) { DependencyNodeId = Convert.ToInt32( id ); DependencyArrayIdx = -1; }
  40. public void Reset()
  41. {
  42. DependencyArrayIdx = -1;
  43. DependencyNodeId = -1;
  44. }
  45. }
  46. [Serializable]
  47. [NodeAttributes( "Custom Expression", "Miscellaneous", "Creates a custom expression or function if <b>return</b> is detected in the written code." )]
  48. public sealed class CustomExpressionNode : ParentNode
  49. {
  50. private const float AddRemoveButtonLayoutWidth = 15;
  51. private const float LineAdjust = 1.15f;
  52. private const float IdentationAdjust = 5f;
  53. private const string CustomExpressionInfo = "Creates a custom expression or function according to how code is written on text area.\n\n" +
  54. " - If a return function is detected on Code text area then a function will be created.\n" +
  55. "Also in function mode a ; is expected on the end of each instruction line.\n\n" +
  56. "- If no return function is detected then an expression will be generated and used directly on the vertex/frag body.\n" +
  57. "On Expression mode a ; is not required on the end of an instruction line.";
  58. private const char LineFeedSeparator = '$';
  59. private const string ReturnHelper = "return";
  60. private const double MaxTimestamp = 1;
  61. private const string DefaultExpressionNameStr = "My Custom Expression";
  62. private const string DefaultInputNameStr = "In";
  63. private const string CodeTitleStr = "Code";
  64. private const string OutputTypeStr = "Output Type";
  65. private const string CustomTypeStr = " ";
  66. private const string InputsStr = "Inputs";
  67. private const string InputNameStr = "Name";
  68. private const string InputTypeStr = "Type";
  69. private const string InputValueStr = "Value";
  70. private const string InputQualifierStr = "Qualifier";
  71. private const string ExpressionNameLabelStr = "Name";
  72. private const string FunctionCallModeStr = "Mode";
  73. private const string GenerateUniqueNameStr = "Set Unique";
  74. private const string AutoRegisterStr = "Auto-Register";
  75. private const string DependenciesStr = "Dependencies";
  76. private readonly string[] AvailableWireTypesStr =
  77. {
  78. "int",
  79. "float",
  80. "float2",
  81. "float3",
  82. "float4",
  83. "float3x3",
  84. "float4x4",
  85. "sampler1D",
  86. "sampler2D",
  87. "sampler3D",
  88. "samplerCUBE",
  89. "custom"};
  90. private readonly string[] AvailableOutputWireTypesStr =
  91. {
  92. "int",
  93. "float",
  94. "float2",
  95. "float3",
  96. "float4",
  97. "float3x3",
  98. "float4x4",
  99. "void",
  100. };
  101. private readonly string[] QualifiersStr =
  102. {
  103. "In",
  104. "Out",
  105. "InOut"
  106. };
  107. private readonly WirePortDataType[] AvailableWireTypes =
  108. {
  109. WirePortDataType.INT,
  110. WirePortDataType.FLOAT,
  111. WirePortDataType.FLOAT2,
  112. WirePortDataType.FLOAT3,
  113. WirePortDataType.FLOAT4,
  114. WirePortDataType.FLOAT3x3,
  115. WirePortDataType.FLOAT4x4,
  116. WirePortDataType.SAMPLER1D,
  117. WirePortDataType.SAMPLER2D,
  118. WirePortDataType.SAMPLER3D,
  119. WirePortDataType.SAMPLERCUBE,
  120. WirePortDataType.OBJECT
  121. };
  122. private readonly WirePortDataType[] AvailableOutputWireTypes =
  123. {
  124. WirePortDataType.INT,
  125. WirePortDataType.FLOAT,
  126. WirePortDataType.FLOAT2,
  127. WirePortDataType.FLOAT3,
  128. WirePortDataType.FLOAT4,
  129. WirePortDataType.FLOAT3x3,
  130. WirePortDataType.FLOAT4x4,
  131. WirePortDataType.OBJECT,
  132. };
  133. private readonly Dictionary<WirePortDataType, int> WireToIdx = new Dictionary<WirePortDataType, int>
  134. {
  135. { WirePortDataType.INT, 0},
  136. { WirePortDataType.FLOAT, 1},
  137. { WirePortDataType.FLOAT2, 2},
  138. { WirePortDataType.FLOAT3, 3},
  139. { WirePortDataType.FLOAT4, 4},
  140. { WirePortDataType.FLOAT3x3, 5},
  141. { WirePortDataType.FLOAT4x4, 6},
  142. { WirePortDataType.SAMPLER1D, 7},
  143. { WirePortDataType.SAMPLER2D, 8},
  144. { WirePortDataType.SAMPLER3D, 9},
  145. { WirePortDataType.SAMPLERCUBE, 10},
  146. { WirePortDataType.OBJECT, 11}
  147. };
  148. [SerializeField]
  149. private string m_customExpressionName = DefaultExpressionNameStr;
  150. [SerializeField]
  151. private List<CustomExpressionInputItem> m_items = new List<CustomExpressionInputItem>();
  152. [SerializeField]
  153. private string m_code = " ";
  154. [SerializeField]
  155. private int m_outputTypeIdx = 1;
  156. [SerializeField]
  157. private bool m_visibleInputsFoldout = true;
  158. [SerializeField]
  159. private CustomExpressionMode m_mode = CustomExpressionMode.Create;
  160. [SerializeField]
  161. private bool m_voidMode = false;
  162. [SerializeField]
  163. private bool m_autoRegisterMode = false;
  164. [SerializeField]
  165. private bool m_functionMode = false;
  166. [SerializeField]
  167. private int m_firstAvailablePort = 0;
  168. [SerializeField]
  169. private string m_uniqueName;
  170. [SerializeField]
  171. private bool m_generateUniqueName = true;
  172. [SerializeField]
  173. private bool m_dependenciesFoldout = false;
  174. [SerializeField]
  175. private List<CustomExpressionDependency> m_dependencies = new List<CustomExpressionDependency>();
  176. private int m_markedToDelete = -1;
  177. private const float ButtonLayoutWidth = 15;
  178. private bool m_repopulateNameDictionary = true;
  179. private Dictionary<string, int> m_usedNames = new Dictionary<string, int>();
  180. private double m_lastTimeNameModified = 0;
  181. private bool m_nameModified = false;
  182. private double m_lastTimeCodeModified = 0;
  183. private bool m_codeModified = false;
  184. //Title editing
  185. private bool m_isEditing;
  186. private bool m_stopEditing;
  187. private bool m_startEditing;
  188. private double m_clickTime;
  189. private double m_doubleClickTime = 0.3;
  190. private Rect m_titleClickArea;
  191. //Item Reordable List
  192. private ReordableAction m_actionType = ReordableAction.None;
  193. private int m_actionIndex = 0;
  194. private int m_lastIndex = 0;
  195. private ReorderableList m_itemReordableList = null;
  196. private ReorderableList m_dependenciesReordableList = null;
  197. protected override void CommonInit( int uniqueId )
  198. {
  199. base.CommonInit( uniqueId );
  200. AddInputPort( WirePortDataType.FLOAT, false, "In0" );
  201. m_items.Add( new CustomExpressionInputItem( PrecisionType.Float, VariableQualifiers.In, string.Empty, true, string.Empty/*"[0]"*/ ) );
  202. AddOutputPort( WirePortDataType.FLOAT, "Out" );
  203. m_textLabelWidth = 97;
  204. }
  205. protected override void OnUniqueIDAssigned()
  206. {
  207. base.OnUniqueIDAssigned();
  208. if( m_mode == CustomExpressionMode.Create )
  209. m_containerGraph.CustomExpressionOnFunctionMode.AddNode( this );
  210. SetTitleText( m_customExpressionName );
  211. if( m_nodeAttribs != null )
  212. m_uniqueName = m_nodeAttribs.Name + OutputId;
  213. else
  214. m_uniqueName = "CustomExpression" + OutputId;
  215. }
  216. public override void OnInputPortConnected( int portId, int otherNodeId, int otherPortId, bool activateNode = true )
  217. {
  218. base.OnInputPortConnected( portId, otherNodeId, otherPortId, activateNode );
  219. CheckPortConnection( portId );
  220. }
  221. public override void OnConnectedOutputNodeChanges( int portId, int otherNodeId, int otherPortId, string name, WirePortDataType type )
  222. {
  223. base.OnConnectedOutputNodeChanges( portId, otherNodeId, otherPortId, name, type );
  224. CheckPortConnection( portId );
  225. }
  226. void CheckPortConnection( int portId )
  227. {
  228. if( portId == 0 && ( m_mode == CustomExpressionMode.Call || m_voidMode ) )
  229. {
  230. m_inputPorts[ 0 ].MatchPortToConnection();
  231. m_outputPorts[ 0 ].ChangeType( m_inputPorts[ 0 ].DataType, false );
  232. }
  233. }
  234. public override void OnNodeLogicUpdate( DrawInfo drawInfo )
  235. {
  236. base.OnNodeLogicUpdate( drawInfo );
  237. if( m_nameModified )
  238. {
  239. if( ( EditorApplication.timeSinceStartup - m_lastTimeNameModified ) > MaxTimestamp )
  240. {
  241. m_nameModified = false;
  242. m_repopulateNameDictionary = true;
  243. }
  244. }
  245. if( m_repopulateNameDictionary )
  246. {
  247. m_repopulateNameDictionary = false;
  248. m_usedNames.Clear();
  249. for( int i = 0; i < m_inputPorts.Count; i++ )
  250. {
  251. m_usedNames.Add( m_inputPorts[ i ].Name, i );
  252. }
  253. }
  254. if( m_codeModified )
  255. {
  256. if( ( EditorApplication.timeSinceStartup - m_lastTimeCodeModified ) > MaxTimestamp )
  257. {
  258. m_codeModified = false;
  259. bool functionMode = m_code.Contains( ReturnHelper );
  260. if( functionMode != m_functionMode )
  261. {
  262. m_functionMode = functionMode;
  263. CheckCallMode();
  264. }
  265. }
  266. }
  267. }
  268. bool CheckCallMode()
  269. {
  270. if( m_functionMode && m_mode == CustomExpressionMode.Call )
  271. {
  272. Mode = CustomExpressionMode.Create;
  273. m_outputTypeIdx = ( AvailableOutputWireTypesStr.Length - 1 );
  274. m_outputPorts[ 0 ].ChangeType( AvailableOutputWireTypes[ m_outputTypeIdx ], false );
  275. m_voidMode = true;
  276. return true;
  277. }
  278. return false;
  279. }
  280. public override void Draw( DrawInfo drawInfo )
  281. {
  282. base.Draw( drawInfo );
  283. if( ContainerGraph.LodLevel <= ParentGraph.NodeLOD.LOD3 )
  284. {
  285. if( !m_isEditing && ( ( !ContainerGraph.ParentWindow.MouseInteracted && drawInfo.CurrentEventType == EventType.MouseDown && m_titleClickArea.Contains( drawInfo.MousePosition ) ) ) )
  286. {
  287. if( ( EditorApplication.timeSinceStartup - m_clickTime ) < m_doubleClickTime )
  288. m_startEditing = true;
  289. else
  290. GUI.FocusControl( null );
  291. m_clickTime = EditorApplication.timeSinceStartup;
  292. }
  293. else if( m_isEditing && ( ( drawInfo.CurrentEventType == EventType.MouseDown && !m_titleClickArea.Contains( drawInfo.MousePosition ) ) || !EditorGUIUtility.editingTextField ) )
  294. {
  295. m_stopEditing = true;
  296. }
  297. if( m_isEditing || m_startEditing )
  298. {
  299. EditorGUI.BeginChangeCheck();
  300. GUI.SetNextControlName( m_uniqueName );
  301. m_customExpressionName = EditorGUITextField( m_titleClickArea, string.Empty, m_customExpressionName, UIUtils.GetCustomStyle( CustomStyle.NodeTitle ) );
  302. if( EditorGUI.EndChangeCheck() )
  303. {
  304. SetTimedUpdate( 2 );
  305. SetTitleText( m_customExpressionName );
  306. m_sizeIsDirty = true;
  307. m_isDirty = true;
  308. }
  309. if( m_startEditing )
  310. EditorGUI.FocusTextInControl( m_uniqueName );
  311. }
  312. if( drawInfo.CurrentEventType == EventType.Repaint )
  313. {
  314. if( m_startEditing )
  315. {
  316. m_startEditing = false;
  317. m_isEditing = true;
  318. }
  319. if( m_stopEditing )
  320. {
  321. m_stopEditing = false;
  322. m_isEditing = false;
  323. GUI.FocusControl( null );
  324. }
  325. }
  326. }
  327. }
  328. public override void OnNodeLayout( DrawInfo drawInfo )
  329. {
  330. base.OnNodeLayout( drawInfo );
  331. m_titleClickArea = m_titlePos;
  332. m_titleClickArea.height = Constants.NODE_HEADER_HEIGHT;
  333. }
  334. public override void OnNodeRepaint( DrawInfo drawInfo )
  335. {
  336. base.OnNodeRepaint( drawInfo );
  337. if( !m_isVisible )
  338. return;
  339. // Fixed Title ( only renders when not editing )
  340. if( !m_isEditing && !m_startEditing && ContainerGraph.LodLevel <= ParentGraph.NodeLOD.LOD3 )
  341. {
  342. GUI.Label( m_titleClickArea, m_content, UIUtils.GetCustomStyle( CustomStyle.NodeTitle ) );
  343. }
  344. }
  345. public string GetFirstAvailableName()
  346. {
  347. string name = string.Empty;
  348. for( int i = 0; i < m_inputPorts.Count + 1; i++ )
  349. {
  350. name = DefaultInputNameStr + i;
  351. if( !m_usedNames.ContainsKey( name ) )
  352. {
  353. return name;
  354. }
  355. }
  356. Debug.LogWarning( "Could not find valid name" );
  357. return string.Empty;
  358. }
  359. public override void DrawProperties()
  360. {
  361. base.DrawProperties();
  362. NodeUtils.DrawPropertyGroup( ref m_propertiesFoldout, Constants.ParameterLabelStr, DrawBaseProperties );
  363. //NodeUtils.DrawPropertyGroup( ref m_visibleInputsFoldout, InputsStr, DrawInputs, DrawAddRemoveInputs );
  364. NodeUtils.DrawPropertyGroup( ref m_visibleInputsFoldout, InputsStr, DrawReordableInputs, DrawItemsAddRemoveInputs );
  365. EditorGUILayout.HelpBox( CustomExpressionInfo, MessageType.Info );
  366. }
  367. string WrapCodeInFunction( bool isTemplate, string functionName, bool expressionMode )
  368. {
  369. //Hack to be used util indent is properly used
  370. int currIndent = UIUtils.ShaderIndentLevel;
  371. UIUtils.ShaderIndentLevel = isTemplate ? 0 : 1;
  372. if( !isTemplate ) UIUtils.ShaderIndentLevel++;
  373. //string functionName = UIUtils.RemoveInvalidCharacters( m_customExpressionName );
  374. string returnType = ( m_mode == CustomExpressionMode.Call || m_voidMode ) ? "void" : UIUtils.PrecisionWirePortToCgType( m_currentPrecisionType, m_outputPorts[ 0 ].DataType );
  375. if( expressionMode )
  376. returnType = "inline " + returnType;
  377. string functionBody = UIUtils.ShaderIndentTabs + returnType + " " + functionName + "( ";
  378. int count = m_inputPorts.Count - m_firstAvailablePort;
  379. for( int i = 0; i < count; i++ )
  380. {
  381. int portIdx = i + m_firstAvailablePort;
  382. string qualifier = m_items[ i ].Qualifier == VariableQualifiers.In ? string.Empty : UIUtils.QualifierToCg( m_items[ i ].Qualifier ) + " ";
  383. PrecisionType precision = ( (int)m_items[ i ].Precision > (int)m_currentPrecisionType ) ? m_items[ i ].Precision : m_currentPrecisionType;
  384. string dataType = ( m_inputPorts[ portIdx ].DataType == WirePortDataType.OBJECT ) ? m_items[ i ].CustomType : UIUtils.PrecisionWirePortToCgType( precision, m_inputPorts[ portIdx ].DataType );
  385. functionBody += qualifier + dataType + " " + m_inputPorts[ portIdx ].Name;
  386. if( i < ( count - 1 ) )
  387. {
  388. functionBody += " , ";
  389. }
  390. }
  391. functionBody += " )\n" + UIUtils.ShaderIndentTabs + "{\n";
  392. UIUtils.ShaderIndentLevel++;
  393. {
  394. if( expressionMode )
  395. functionBody += UIUtils.ShaderIndentTabs + "return ";
  396. string[] codeLines = m_code.Split( IOUtils.LINE_TERMINATOR );
  397. for( int i = 0; i < codeLines.Length; i++ )
  398. {
  399. if( codeLines[ i ].Length > 0 )
  400. {
  401. functionBody += ( ( i == 0 && expressionMode ) ? string.Empty : UIUtils.ShaderIndentTabs ) + codeLines[ i ] + ( ( ( i == codeLines.Length - 1 ) && expressionMode ) ? string.Empty : "\n" );
  402. }
  403. }
  404. if( expressionMode )
  405. functionBody += ";\n";
  406. }
  407. UIUtils.ShaderIndentLevel--;
  408. functionBody += UIUtils.ShaderIndentTabs + "}\n";
  409. UIUtils.ShaderIndentLevel = currIndent;
  410. return functionBody;
  411. }
  412. void DrawBaseProperties()
  413. {
  414. EditorGUI.BeginChangeCheck();
  415. m_customExpressionName = EditorGUILayoutTextField( ExpressionNameLabelStr, m_customExpressionName );
  416. if( EditorGUI.EndChangeCheck() )
  417. {
  418. SetTimedUpdate( 2 );
  419. SetTitleText( m_customExpressionName );
  420. }
  421. EditorGUI.BeginChangeCheck();
  422. Mode = (CustomExpressionMode)EditorGUILayoutEnumPopup( FunctionCallModeStr, m_mode );
  423. if( EditorGUI.EndChangeCheck() )
  424. {
  425. if( CheckCallMode() )
  426. UIUtils.ShowMessage( "Call Mode cannot have return over is code.\nFalling back to Create Mode" );
  427. SetupCallMode();
  428. RecalculateInOutOutputPorts();
  429. }
  430. EditorGUILayout.LabelField( CodeTitleStr );
  431. EditorGUI.BeginChangeCheck();
  432. {
  433. m_code = EditorGUILayoutTextArea( m_code, UIUtils.MainSkin.textArea );
  434. }
  435. if( EditorGUI.EndChangeCheck() )
  436. {
  437. m_codeModified = true;
  438. m_lastTimeCodeModified = EditorApplication.timeSinceStartup;
  439. }
  440. if( m_mode == CustomExpressionMode.Create )
  441. {
  442. DrawPrecisionProperty();
  443. bool guiEnabled = GUI.enabled;
  444. GUI.enabled = !AutoRegisterMode;
  445. m_generateUniqueName = EditorGUILayoutToggle( GenerateUniqueNameStr, m_generateUniqueName ) && !AutoRegisterMode;
  446. GUI.enabled = !m_generateUniqueName;
  447. AutoRegisterMode = EditorGUILayoutToggle( AutoRegisterStr, AutoRegisterMode ) && !m_generateUniqueName;
  448. GUI.enabled = guiEnabled;
  449. EditorGUI.BeginChangeCheck();
  450. m_outputTypeIdx = EditorGUILayoutPopup( OutputTypeStr, m_outputTypeIdx, AvailableOutputWireTypesStr );
  451. if( EditorGUI.EndChangeCheck() )
  452. {
  453. bool oldVoidValue = m_voidMode;
  454. UpdateVoidMode();
  455. if( oldVoidValue != m_voidMode )
  456. {
  457. SetupCallMode();
  458. RecalculateInOutOutputPorts();
  459. }
  460. else
  461. {
  462. m_outputPorts[ 0 ].ChangeType( AvailableOutputWireTypes[ m_outputTypeIdx ], false );
  463. }
  464. }
  465. }
  466. NodeUtils.DrawNestedPropertyGroup( ref m_dependenciesFoldout, "Dependencies", DrawDependencies, DrawDependenciesAddRemoveInputs );
  467. }
  468. void UpdateVoidMode()
  469. {
  470. m_voidMode = ( m_outputTypeIdx == ( AvailableOutputWireTypesStr.Length - 1 ) );
  471. }
  472. void SetupCallMode()
  473. {
  474. if( m_mode == CustomExpressionMode.Call || m_voidMode )
  475. {
  476. if( m_firstAvailablePort != 1 )
  477. {
  478. m_firstAvailablePort = 1;
  479. AddInputPortAt( 0, WirePortDataType.FLOAT, false, DefaultInputNameStr );
  480. m_outputPorts[ 0 ].ChangeType( WirePortDataType.FLOAT, false );
  481. }
  482. }
  483. else
  484. {
  485. if( m_firstAvailablePort != 0 )
  486. {
  487. m_firstAvailablePort = 0;
  488. if( m_inputPorts[ 0 ].IsConnected )
  489. {
  490. m_containerGraph.DeleteConnection( true, UniqueId, m_inputPorts[ 0 ].PortId, false, true );
  491. }
  492. DeleteInputPortByArrayIdx( 0 );
  493. m_outputPorts[ 0 ].ChangeType( AvailableOutputWireTypes[ m_outputTypeIdx ], false );
  494. }
  495. }
  496. }
  497. void DrawItemsAddRemoveInputs()
  498. {
  499. if( m_inputPorts.Count == m_firstAvailablePort )
  500. m_visibleInputsFoldout = false;
  501. // Add new port
  502. if( GUILayoutButton( string.Empty, UIUtils.PlusStyle, GUILayout.Width( ButtonLayoutWidth ) ) )
  503. {
  504. AddPortAt( m_inputPorts.Count );
  505. m_visibleInputsFoldout = true;
  506. EditorGUI.FocusTextInControl( null );
  507. }
  508. //Remove port
  509. if( GUILayoutButton( string.Empty, UIUtils.MinusStyle, GUILayout.Width( ButtonLayoutWidth ) ) )
  510. {
  511. RemovePortAt( m_inputPorts.Count - 1 );
  512. EditorGUI.FocusTextInControl( null );
  513. }
  514. }
  515. void DrawDependenciesAddRemoveInputs()
  516. {
  517. // Add new port
  518. if( GUILayoutButton( string.Empty, UIUtils.PlusStyle, GUILayout.Width( ButtonLayoutWidth ) ) )
  519. {
  520. m_dependencies.Add( new CustomExpressionDependency() );
  521. EditorGUI.FocusTextInControl( null );
  522. }
  523. //Remove port
  524. if( GUILayoutButton( string.Empty, UIUtils.MinusStyle, GUILayout.Width( ButtonLayoutWidth ) ) )
  525. {
  526. m_dependencies.RemoveAt( m_dependencies.Count - 1 );
  527. }
  528. }
  529. void DrawDependencies()
  530. {
  531. if( m_dependenciesReordableList == null )
  532. {
  533. m_dependenciesReordableList = new ReorderableList( m_dependencies, typeof( CustomExpressionDependency ), true, false, false, false )
  534. {
  535. headerHeight = 0,
  536. footerHeight = 0,
  537. showDefaultBackground = false,
  538. drawElementCallback = ( Rect rect, int index, bool isActive, bool isFocused ) =>
  539. {
  540. if( m_dependencies[ index ] != null )
  541. {
  542. rect.xMin -= 1;
  543. Rect popupPos = new Rect( rect.x, rect.y, rect.width - 2 * Constants.PlusMinusButtonLayoutWidth, EditorGUIUtility.singleLineHeight );
  544. Rect buttonPlusPos = new Rect( rect.x + rect.width - 2 * Constants.PlusMinusButtonLayoutWidth, rect.y - 2, Constants.PlusMinusButtonLayoutWidth, Constants.PlusMinusButtonLayoutWidth );
  545. Rect buttonMinusPos = new Rect( rect.x + rect.width - Constants.PlusMinusButtonLayoutWidth, rect.y - 2, Constants.PlusMinusButtonLayoutWidth, Constants.PlusMinusButtonLayoutWidth );
  546. EditorGUI.BeginChangeCheck();
  547. m_dependencies[ index ].DependencyArrayIdx = EditorGUIPopup( popupPos, string.Empty, m_dependencies[ index ].DependencyArrayIdx, m_containerGraph.CustomExpressionOnFunctionMode.NodesArr );
  548. if( EditorGUI.EndChangeCheck() )
  549. {
  550. m_dependencies[ index ].DependencyNodeId = m_containerGraph.CustomExpressionOnFunctionMode.GetNode( m_dependencies[ index ].DependencyArrayIdx ).UniqueId;
  551. if( m_dependencies[ index ].DependencyNodeId == UniqueId )
  552. {
  553. m_dependencies[ index ].Reset();
  554. }
  555. }
  556. if( GUI.Button( buttonPlusPos, string.Empty, UIUtils.PlusStyle ) )
  557. {
  558. m_actionType = ReordableAction.Add;
  559. m_actionIndex = index;
  560. }
  561. if( GUI.Button( buttonMinusPos, string.Empty, UIUtils.MinusStyle ) )
  562. {
  563. m_actionType = ReordableAction.Remove;
  564. m_actionIndex = index;
  565. }
  566. }
  567. }
  568. };
  569. }
  570. if( m_dependenciesReordableList != null )
  571. {
  572. EditorGUILayout.Space();
  573. if( m_dependencies.Count == 0 )
  574. {
  575. EditorGUILayout.HelpBox( "Your list is Empty!\nUse the plus button to add one.", MessageType.Info );
  576. }
  577. else
  578. {
  579. m_dependenciesReordableList.DoLayoutList();
  580. }
  581. EditorGUILayout.Space();
  582. }
  583. if( m_actionType != ReordableAction.None )
  584. {
  585. switch( m_actionType )
  586. {
  587. case ReordableAction.Add:
  588. m_dependencies.Insert( m_actionIndex + 1, new CustomExpressionDependency() );
  589. break;
  590. case ReordableAction.Remove:
  591. m_dependencies.RemoveAt( m_actionIndex );
  592. break;
  593. }
  594. m_isDirty = true;
  595. m_actionType = ReordableAction.None;
  596. EditorGUI.FocusTextInControl( null );
  597. }
  598. }
  599. void DrawReordableInputs()
  600. {
  601. if( m_itemReordableList == null )
  602. {
  603. m_itemReordableList = new ReorderableList( m_items, typeof( CustomExpressionInputItem ), true, false, false, false )
  604. {
  605. headerHeight = 0,
  606. footerHeight = 0,
  607. showDefaultBackground = false,
  608. elementHeightCallback = ( int index ) =>
  609. {
  610. float lineHeight = EditorGUIUtility.singleLineHeight * LineAdjust;
  611. if( m_items[ index ].FoldoutFlag )
  612. {
  613. float size = 7 * lineHeight;
  614. if( m_inputPorts[ m_firstAvailablePort + index ].DataType == WirePortDataType.OBJECT )
  615. size += lineHeight;
  616. if( !m_inputPorts[ m_firstAvailablePort + index ].IsConnected )
  617. {
  618. switch( m_inputPorts[ m_firstAvailablePort + index ].DataType )
  619. {
  620. case WirePortDataType.INT:
  621. case WirePortDataType.FLOAT:
  622. size += 0;// lineHeight;
  623. break;
  624. case WirePortDataType.FLOAT2:
  625. case WirePortDataType.FLOAT3:
  626. case WirePortDataType.FLOAT4:
  627. size += lineHeight;//2 * lineHeight;
  628. break;
  629. case WirePortDataType.FLOAT3x3:
  630. size += 5 * lineHeight;//6 * lineHeight;
  631. break;
  632. case WirePortDataType.FLOAT4x4:
  633. size += 6 * lineHeight;//8 * lineHeight;
  634. break;
  635. }
  636. }
  637. return size;
  638. }
  639. else
  640. {
  641. return lineHeight;
  642. }
  643. },
  644. onReorderCallback = ( ReorderableList list ) =>
  645. {
  646. int realLastIndex = m_firstAvailablePort + m_lastIndex;
  647. int realCurrIndex = m_firstAvailablePort + list.index;
  648. SwapInputPorts( realLastIndex, realCurrIndex );
  649. },
  650. onSelectCallback = ( ReorderableList list ) =>
  651. {
  652. m_lastIndex = list.index;
  653. },
  654. drawElementCallback = ( Rect rect, int index, bool isActive, bool isFocused ) =>
  655. {
  656. if( m_items[ index ] != null )
  657. {
  658. float lineHeight = EditorGUIUtility.singleLineHeight;
  659. float lineSpacing = lineHeight * LineAdjust;
  660. rect.x -= IdentationAdjust;
  661. rect.height = lineHeight;
  662. int portIdx = index + m_firstAvailablePort;
  663. Rect foldoutRect = rect;
  664. if( !m_items[ index ].FoldoutFlag )
  665. {
  666. foldoutRect.width -= 2 * AddRemoveButtonLayoutWidth;
  667. }
  668. m_items[ index ].FoldoutFlag = EditorGUIFoldout( foldoutRect, m_items[ index ].FoldoutFlag, /*m_items[ index ].FoldoutLabel + " - " +*/ m_inputPorts[ portIdx ].Name );
  669. if( m_items[ index ].FoldoutFlag )
  670. {
  671. rect.x += IdentationAdjust;
  672. //Qualifier
  673. rect.y += lineSpacing;
  674. VariableQualifiers newQualifier = (VariableQualifiers)EditorGUIPopup( rect, InputQualifierStr, (int)m_items[ index ].Qualifier, QualifiersStr );
  675. if( newQualifier != m_items[ index ].Qualifier )
  676. {
  677. VariableQualifiers oldQualifier = m_items[ index ].Qualifier;
  678. m_items[ index ].Qualifier = newQualifier;
  679. if( newQualifier == VariableQualifiers.In )
  680. {
  681. RemoveOutputPort( CreateOutputId( m_inputPorts[ portIdx ].PortId ), false );
  682. }
  683. else if( oldQualifier == VariableQualifiers.In )
  684. {
  685. AddOutputPort( m_inputPorts[ portIdx ].DataType, m_inputPorts[ portIdx ].Name, CreateOutputId( m_inputPorts[ portIdx ].PortId ) );
  686. }
  687. m_inputPorts[ portIdx ].Visible = newQualifier != VariableQualifiers.Out;
  688. m_sizeIsDirty = true;
  689. RecalculateInOutOutputPorts();
  690. }
  691. // Precision
  692. rect.y += lineSpacing;
  693. m_items[ index ].Precision = (PrecisionType)EditorGUIPopup(rect, PrecisionContent.text, (int)m_items[ index ].Precision, PrecisionLabels );
  694. // Type
  695. rect.y += lineSpacing;
  696. int typeIdx = WireToIdx[ m_inputPorts[ portIdx ].DataType ];
  697. EditorGUI.BeginChangeCheck();
  698. {
  699. typeIdx = EditorGUIPopup( rect, InputTypeStr, typeIdx, AvailableWireTypesStr );
  700. }
  701. if( EditorGUI.EndChangeCheck() )
  702. {
  703. m_inputPorts[ portIdx ].ChangeType( AvailableWireTypes[ typeIdx ], false );
  704. if( m_items[ index ].Qualifier != VariableQualifiers.In )
  705. {
  706. OutputPort currOutPort = GetOutputPortByUniqueId( CreateOutputId( m_inputPorts[ portIdx ].PortId ) );
  707. currOutPort.ChangeType( AvailableWireTypes[ typeIdx ], false );
  708. }
  709. }
  710. if( AvailableWireTypes[ typeIdx ] == WirePortDataType.OBJECT )
  711. {
  712. rect.y += lineSpacing;
  713. m_items[ index ].CustomType = EditorGUITextField( rect, CustomTypeStr, m_items[ index ].CustomType );
  714. }
  715. //Name
  716. rect.y += lineSpacing;
  717. EditorGUI.BeginChangeCheck();
  718. {
  719. m_inputPorts[ portIdx ].Name = EditorGUITextField( rect, InputNameStr, m_inputPorts[ portIdx ].Name );
  720. }
  721. if( EditorGUI.EndChangeCheck() )
  722. {
  723. m_nameModified = true;
  724. m_lastTimeNameModified = EditorApplication.timeSinceStartup;
  725. m_inputPorts[ portIdx ].Name = UIUtils.RemoveInvalidCharacters( m_inputPorts[ portIdx ].Name );
  726. if( string.IsNullOrEmpty( m_inputPorts[ portIdx ].Name ) )
  727. {
  728. m_inputPorts[ portIdx ].Name = DefaultInputNameStr + index;
  729. }
  730. if( m_items[ index ].Qualifier != VariableQualifiers.In )
  731. {
  732. OutputPort currOutPort = GetOutputPortByUniqueId( CreateOutputId( m_inputPorts[ portIdx ].PortId ) );
  733. currOutPort.Name = m_inputPorts[ portIdx ].Name;
  734. }
  735. }
  736. // Port Data
  737. if( !m_inputPorts[ portIdx ].IsConnected )
  738. {
  739. rect.y += lineSpacing;
  740. m_inputPorts[ portIdx ].ShowInternalData( rect, this, true, InputValueStr );
  741. }
  742. //Buttons
  743. rect.x += rect.width - 2 * AddRemoveButtonLayoutWidth;
  744. rect.y += lineSpacing;
  745. if( !m_inputPorts[ m_firstAvailablePort + index ].IsConnected )
  746. {
  747. switch( m_inputPorts[ m_firstAvailablePort + index ].DataType )
  748. {
  749. case WirePortDataType.INT:
  750. case WirePortDataType.FLOAT:
  751. rect.y += 0;// lineSpacing;
  752. break;
  753. case WirePortDataType.FLOAT2:
  754. case WirePortDataType.FLOAT3:
  755. case WirePortDataType.FLOAT4:
  756. rect.y += lineSpacing;//2 * lineSpacing;
  757. break;
  758. case WirePortDataType.FLOAT3x3:
  759. rect.y += 5 * lineSpacing;//6 * lineSpacing;
  760. break;
  761. case WirePortDataType.FLOAT4x4:
  762. rect.y += 6 * lineSpacing;//8 * lineSpacing;
  763. break;
  764. }
  765. }
  766. rect.width = AddRemoveButtonLayoutWidth;
  767. if( GUI.Button( rect, string.Empty, UIUtils.PlusStyle ) )
  768. {
  769. m_actionType = ReordableAction.Add;
  770. m_actionIndex = index;
  771. }
  772. rect.x += AddRemoveButtonLayoutWidth;
  773. if( GUI.Button( rect, string.Empty, UIUtils.MinusStyle ) )
  774. {
  775. m_actionType = ReordableAction.Remove;
  776. m_actionIndex = index;
  777. }
  778. }
  779. else
  780. {
  781. //Buttons
  782. rect.x += IdentationAdjust + rect.width - 2 * AddRemoveButtonLayoutWidth;
  783. rect.width = AddRemoveButtonLayoutWidth;
  784. if( GUI.Button( rect, string.Empty, UIUtils.PlusStyle ) )
  785. {
  786. m_actionType = ReordableAction.Add;
  787. m_actionIndex = index;
  788. }
  789. rect.x += AddRemoveButtonLayoutWidth;
  790. if( GUI.Button( rect, string.Empty, UIUtils.MinusStyle ) )
  791. {
  792. m_actionType = ReordableAction.Remove;
  793. m_actionIndex = index;
  794. }
  795. }
  796. }
  797. }
  798. };
  799. }
  800. ///////////////////////////////////
  801. if( m_itemReordableList != null )
  802. {
  803. EditorGUILayout.Space();
  804. if( m_items.Count == 0 )
  805. {
  806. EditorGUILayout.HelpBox( "Your list is Empty!\nUse the plus button to add one.", MessageType.Info );
  807. }
  808. else
  809. {
  810. m_itemReordableList.DoLayoutList();
  811. }
  812. EditorGUILayout.Space();
  813. }
  814. if( m_actionType != ReordableAction.None )
  815. {
  816. switch( m_actionType )
  817. {
  818. case ReordableAction.Add:
  819. AddPortAt( m_firstAvailablePort + m_actionIndex + 1 );
  820. break;
  821. case ReordableAction.Remove:
  822. RemovePortAt( m_firstAvailablePort + m_actionIndex );
  823. break;
  824. }
  825. m_isDirty = true;
  826. m_actionType = ReordableAction.None;
  827. EditorGUI.FocusTextInControl( null );
  828. }
  829. }
  830. void DrawInputs()
  831. {
  832. int count = m_inputPorts.Count - m_firstAvailablePort;
  833. for( int i = 0; i < count; i++ )
  834. {
  835. int portIdx = i + m_firstAvailablePort;
  836. m_items[ i ].FoldoutFlag = EditorGUILayoutFoldout( m_items[ i ].FoldoutFlag, /*m_items[ i ].FoldoutLabel + " - " +*/ m_inputPorts[ portIdx ].Name );
  837. if( m_items[ i ].FoldoutFlag )
  838. {
  839. EditorGUI.indentLevel += 1;
  840. //Qualifier
  841. VariableQualifiers newQualifier = (VariableQualifiers)EditorGUILayoutPopup( InputQualifierStr, (int)m_items[ i ].Qualifier, QualifiersStr );
  842. if( newQualifier != m_items[ i ].Qualifier )
  843. {
  844. VariableQualifiers oldQualifier = m_items[ i ].Qualifier;
  845. m_items[ i ].Qualifier = newQualifier;
  846. if( newQualifier == VariableQualifiers.In )
  847. {
  848. RemoveOutputPort( CreateOutputId( m_inputPorts[ portIdx ].PortId ), false );
  849. }
  850. else if( oldQualifier == VariableQualifiers.In )
  851. {
  852. AddOutputPort( m_inputPorts[ portIdx ].DataType, m_inputPorts[ portIdx ].Name, CreateOutputId( m_inputPorts[ portIdx ].PortId ) );
  853. }
  854. RecalculateInOutOutputPorts();
  855. }
  856. // Type
  857. int typeIdx = WireToIdx[ m_inputPorts[ portIdx ].DataType ];
  858. EditorGUI.BeginChangeCheck();
  859. {
  860. typeIdx = EditorGUILayoutPopup( InputTypeStr, typeIdx, AvailableWireTypesStr );
  861. }
  862. if( EditorGUI.EndChangeCheck() )
  863. {
  864. m_inputPorts[ portIdx ].ChangeType( AvailableWireTypes[ typeIdx ], false );
  865. if( m_items[ i ].Qualifier != VariableQualifiers.In )
  866. {
  867. OutputPort currOutPort = GetOutputPortByUniqueId( CreateOutputId( m_inputPorts[ portIdx ].PortId ) );
  868. currOutPort.ChangeType( AvailableWireTypes[ typeIdx ], false );
  869. }
  870. }
  871. if( AvailableWireTypes[ typeIdx ] == WirePortDataType.OBJECT )
  872. {
  873. EditorGUI.indentLevel += 1;
  874. m_items[ i ].CustomType = EditorGUILayoutTextField( CustomTypeStr, m_items[ i ].CustomType );
  875. EditorGUI.indentLevel -= 1;
  876. }
  877. //Name
  878. EditorGUI.BeginChangeCheck();
  879. {
  880. m_inputPorts[ portIdx ].Name = EditorGUILayoutTextField( InputNameStr, m_inputPorts[ portIdx ].Name );
  881. }
  882. if( EditorGUI.EndChangeCheck() )
  883. {
  884. m_nameModified = true;
  885. m_lastTimeNameModified = EditorApplication.timeSinceStartup;
  886. m_inputPorts[ portIdx ].Name = UIUtils.RemoveInvalidCharacters( m_inputPorts[ portIdx ].Name );
  887. if( string.IsNullOrEmpty( m_inputPorts[ portIdx ].Name ) )
  888. {
  889. m_inputPorts[ portIdx ].Name = DefaultInputNameStr + i;
  890. }
  891. if( m_items[ i ].Qualifier != VariableQualifiers.In )
  892. {
  893. OutputPort currOutPort = GetOutputPortByUniqueId( CreateOutputId( m_inputPorts[ portIdx ].PortId ) );
  894. currOutPort.Name = m_inputPorts[ portIdx ].Name;
  895. }
  896. }
  897. // Port Data
  898. if( !m_inputPorts[ portIdx ].IsConnected )
  899. {
  900. m_inputPorts[ portIdx ].ShowInternalData( this, true, InputValueStr );
  901. }
  902. EditorGUILayout.BeginHorizontal();
  903. {
  904. GUILayout.Label( " " );
  905. // Add new port
  906. if( GUILayoutButton( string.Empty, UIUtils.PlusStyle, GUILayout.Width( ButtonLayoutWidth ) ) )
  907. {
  908. AddPortAt( portIdx );
  909. EditorGUI.FocusTextInControl( null );
  910. }
  911. //Remove port
  912. if( GUILayoutButton( string.Empty, UIUtils.MinusStyle, GUILayout.Width( ButtonLayoutWidth ) ) )
  913. {
  914. m_markedToDelete = portIdx;
  915. }
  916. }
  917. EditorGUILayout.EndHorizontal();
  918. EditorGUI.indentLevel -= 1;
  919. }
  920. }
  921. if( m_markedToDelete > -1 )
  922. {
  923. RemovePortAt( m_markedToDelete );
  924. m_markedToDelete = -1;
  925. EditorGUI.FocusTextInControl( null );
  926. }
  927. }
  928. void RecalculateInOutOutputPorts()
  929. {
  930. m_outputPorts.Sort( ( x, y ) => x.PortId.CompareTo( y.PortId ) );
  931. m_outputPortsDict.Clear();
  932. int count = m_inputPorts.Count - m_firstAvailablePort;
  933. int outputId = 1;
  934. for( int i = 0; i < count; i++ )
  935. {
  936. int idx = i + m_firstAvailablePort;
  937. if( m_items[ i ].Qualifier != VariableQualifiers.In )
  938. {
  939. m_outputPorts[ outputId ].ChangeProperties( m_inputPorts[ idx ].Name, m_inputPorts[ idx ].DataType, false );
  940. m_outputPorts[ outputId ].ChangePortId( CreateOutputId( m_inputPorts[ idx ].PortId ) );
  941. outputId++;
  942. }
  943. }
  944. int outCount = m_outputPorts.Count;
  945. for( int i = 0; i < outCount; i++ )
  946. {
  947. m_outputPortsDict.Add( m_outputPorts[ i ].PortId, m_outputPorts[ i ] );
  948. }
  949. }
  950. void AddPortAt( int idx )
  951. {
  952. AddInputPortAt( idx, WirePortDataType.FLOAT, false, GetFirstAvailableName() );
  953. m_items.Insert( idx - m_firstAvailablePort, new CustomExpressionInputItem( PrecisionType.Float, VariableQualifiers.In, string.Empty, true, string.Empty/* "[" + idx + "]"*/ ) );
  954. m_repopulateNameDictionary = true;
  955. }
  956. void RemovePortAt( int idx )
  957. {
  958. if( m_inputPorts.Count > m_firstAvailablePort )
  959. {
  960. bool recalculateOutputs = false;
  961. int varIdx = idx - m_firstAvailablePort;
  962. if( m_items[ varIdx ].Qualifier != VariableQualifiers.In )
  963. {
  964. RemoveOutputPort( CreateOutputId( m_inputPorts[ idx ].PortId ), false );
  965. recalculateOutputs = true;
  966. }
  967. DeleteInputPortByArrayIdx( idx );
  968. m_items.RemoveAt( varIdx );
  969. m_repopulateNameDictionary = true;
  970. if( recalculateOutputs )
  971. RecalculateInOutOutputPorts();
  972. }
  973. }
  974. public override void OnAfterDeserialize()
  975. {
  976. base.OnAfterDeserialize();
  977. m_repopulateNameDictionary = true;
  978. }
  979. public override string GenerateShaderForOutput( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar )
  980. {
  981. if( string.IsNullOrEmpty( m_code ) )
  982. {
  983. UIUtils.ShowMessage( "Custom Expression need to have code associated", MessageSeverity.Warning );
  984. return "0";
  985. }
  986. m_code = m_code.Replace( "\r\n", "\n" );
  987. bool codeContainsReturn = m_code.Contains( ReturnHelper );
  988. if( !codeContainsReturn && outputId != 0 && m_mode == CustomExpressionMode.Create && !m_voidMode )
  989. {
  990. UIUtils.ShowMessage( "Attempting to get value from inexisting inout/out variable", MessageSeverity.Warning );
  991. return "0";
  992. }
  993. int dependenciesCount = m_dependencies.Count;
  994. Dictionary<int, CustomExpressionNode> examinedNodes = new Dictionary<int, CustomExpressionNode>();
  995. for( int i = 0; i < dependenciesCount; i++ )
  996. {
  997. CustomExpressionNode node = m_containerGraph.GetNode( m_dependencies[ i ].DependencyNodeId ) as CustomExpressionNode;
  998. if( node != null )
  999. {
  1000. node.CheckDependencies( ref dataCollector, ref examinedNodes );
  1001. }
  1002. }
  1003. examinedNodes.Clear();
  1004. examinedNodes = null;
  1005. OutputPort outputPort = GetOutputPortByUniqueId( outputId );
  1006. if( outputPort.IsLocalValue( dataCollector.PortCategory ) )
  1007. return outputPort.LocalValue( dataCollector.PortCategory );
  1008. string expressionName = UIUtils.RemoveInvalidCharacters( m_customExpressionName );
  1009. string localVarName = "local" + expressionName;
  1010. if( m_generateUniqueName )
  1011. {
  1012. expressionName += OutputId;
  1013. }
  1014. localVarName += OutputId;
  1015. int count = m_inputPorts.Count;
  1016. if( count > 0 )
  1017. {
  1018. if( m_mode == CustomExpressionMode.Call || m_voidMode )
  1019. {
  1020. string mainData = m_inputPorts[ 0 ].GeneratePortInstructions( ref dataCollector );
  1021. RegisterLocalVariable( 0, string.Format( Constants.CodeWrapper, mainData ), ref dataCollector, localVarName );
  1022. }
  1023. if( codeContainsReturn )
  1024. {
  1025. string function = WrapCodeInFunction( dataCollector.IsTemplate, expressionName, false );
  1026. string functionCall = expressionName + "( ";
  1027. for( int i = m_firstAvailablePort; i < count; i++ )
  1028. {
  1029. string inputPortLocalVar = m_inputPorts[ i ].Name + OutputId;
  1030. string result = m_inputPorts[ i ].GeneratePortInstructions( ref dataCollector );
  1031. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, m_inputPorts[ i ].DataType, inputPortLocalVar, result );
  1032. int idx = i - m_firstAvailablePort;
  1033. if( m_items[ idx ].Qualifier != VariableQualifiers.In )
  1034. {
  1035. OutputPort currOutputPort = GetOutputPortByUniqueId( CreateOutputId( m_inputPorts[ i ].PortId ) );
  1036. currOutputPort.SetLocalValue( inputPortLocalVar, dataCollector.PortCategory );
  1037. }
  1038. functionCall += inputPortLocalVar;
  1039. if( i < ( count - 1 ) )
  1040. {
  1041. functionCall += " , ";
  1042. }
  1043. }
  1044. functionCall += " )";
  1045. if( m_mode == CustomExpressionMode.Call || m_voidMode )
  1046. {
  1047. dataCollector.AddLocalVariable( 0, functionCall + ";", true );
  1048. }
  1049. else
  1050. {
  1051. RegisterLocalVariable( 0, functionCall, ref dataCollector, localVarName );
  1052. }
  1053. dataCollector.AddFunction( expressionName, function );
  1054. }
  1055. else
  1056. {
  1057. string localCode = m_code;
  1058. if( m_mode == CustomExpressionMode.Call || m_voidMode )
  1059. {
  1060. for( int i = m_firstAvailablePort; i < count; i++ )
  1061. {
  1062. string inputPortLocalVar = m_inputPorts[ i ].Name + OutputId;
  1063. localCode = localCode.Replace( m_inputPorts[ i ].Name, inputPortLocalVar );
  1064. if( m_inputPorts[ i ].IsConnected )
  1065. {
  1066. string result = m_inputPorts[ i ].GenerateShaderForOutput( ref dataCollector, m_inputPorts[ i ].DataType, true, true );
  1067. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, m_inputPorts[ i ].DataType, inputPortLocalVar, result );
  1068. }
  1069. else
  1070. {
  1071. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, m_inputPorts[ i ].DataType, inputPortLocalVar, m_inputPorts[ i ].WrappedInternalData );
  1072. }
  1073. int idx = i - m_firstAvailablePort;
  1074. if( m_items[ idx ].Qualifier != VariableQualifiers.In )
  1075. {
  1076. OutputPort currOutputPort = GetOutputPortByUniqueId( CreateOutputId( m_inputPorts[ i ].PortId ) );
  1077. currOutputPort.SetLocalValue( inputPortLocalVar, dataCollector.PortCategory );
  1078. }
  1079. }
  1080. string[] codeLines = localCode.Split( '\n' );
  1081. for( int codeIdx = 0; codeIdx < codeLines.Length; codeIdx++ )
  1082. {
  1083. dataCollector.AddLocalVariable( 0, codeLines[ codeIdx ], true );
  1084. }
  1085. }
  1086. else
  1087. {
  1088. string function = WrapCodeInFunction( dataCollector.IsTemplate, expressionName, true );
  1089. string functionCall = expressionName + "( ";
  1090. for( int i = m_firstAvailablePort; i < count; i++ )
  1091. {
  1092. string inputPortLocalVar = m_inputPorts[ i ].Name + OutputId;
  1093. string result = m_inputPorts[ i ].GeneratePortInstructions( ref dataCollector );
  1094. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, m_inputPorts[ i ].DataType, inputPortLocalVar, result );
  1095. int idx = i - m_firstAvailablePort;
  1096. if( m_items[ idx ].Qualifier != VariableQualifiers.In )
  1097. {
  1098. OutputPort currOutputPort = GetOutputPortByUniqueId( CreateOutputId( m_inputPorts[ i ].PortId ) );
  1099. currOutputPort.SetLocalValue( inputPortLocalVar, dataCollector.PortCategory );
  1100. }
  1101. functionCall += inputPortLocalVar;
  1102. if( i < ( count - 1 ) )
  1103. {
  1104. functionCall += " , ";
  1105. }
  1106. }
  1107. functionCall += " )";
  1108. RegisterLocalVariable( 0, functionCall, ref dataCollector, localVarName );
  1109. dataCollector.AddFunction( expressionName, function );
  1110. }
  1111. }
  1112. return outputPort.LocalValue( dataCollector.PortCategory );
  1113. }
  1114. else
  1115. {
  1116. if( m_code.Contains( ReturnHelper ) )
  1117. {
  1118. string function = WrapCodeInFunction( dataCollector.IsTemplate, expressionName, false );
  1119. dataCollector.AddFunction( expressionName, function );
  1120. string functionCall = expressionName + "()";
  1121. RegisterLocalVariable( 0, functionCall, ref dataCollector, localVarName );
  1122. }
  1123. else
  1124. {
  1125. RegisterLocalVariable( 0, string.Format( Constants.CodeWrapper, m_code ), ref dataCollector, localVarName );
  1126. }
  1127. return m_outputPorts[ 0 ].LocalValue( dataCollector.PortCategory );
  1128. }
  1129. }
  1130. int CreateOutputId( int inputId )
  1131. {
  1132. return ( inputId + 1 );
  1133. }
  1134. int CreateInputId( int outputId )
  1135. {
  1136. return outputId - 1;
  1137. }
  1138. void UpdateOutputPorts()
  1139. {
  1140. int count = m_inputPorts.Count - m_firstAvailablePort;
  1141. for( int i = 0; i < count; i++ )
  1142. {
  1143. if( m_items[ i ].Qualifier != VariableQualifiers.In )
  1144. {
  1145. int portIdx = i + m_firstAvailablePort;
  1146. AddOutputPort( m_inputPorts[ portIdx ].DataType, m_inputPorts[ portIdx ].Name, CreateOutputId( m_inputPorts[ portIdx ].PortId ) );
  1147. }
  1148. }
  1149. }
  1150. public override void ReadFromString( ref string[] nodeParams )
  1151. {
  1152. // This node is, by default, created with one input port
  1153. base.ReadFromString( ref nodeParams );
  1154. m_code = GetCurrentParam( ref nodeParams );
  1155. m_code = m_code.Replace( LineFeedSeparator, '\n' );
  1156. m_code = m_code.Replace( Constants.SemiColonSeparator, ';' );
  1157. m_outputTypeIdx = Convert.ToInt32( GetCurrentParam( ref nodeParams ) );
  1158. if( m_outputTypeIdx >= AvailableWireTypes.Length )
  1159. {
  1160. UIUtils.ShowMessage( "Sampler types were removed as a valid output custom expression type" );
  1161. m_outputTypeIdx = 1;
  1162. }
  1163. UpdateVoidMode();
  1164. m_outputPorts[ 0 ].ChangeType( AvailableWireTypes[ m_outputTypeIdx ], false );
  1165. if( UIUtils.CurrentShaderVersion() > 12001 )
  1166. {
  1167. bool mode = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) );
  1168. m_mode = mode ? CustomExpressionMode.Call : CustomExpressionMode.Create;
  1169. if( m_mode == CustomExpressionMode.Call || m_voidMode )
  1170. {
  1171. m_firstAvailablePort = 1;
  1172. AddInputPortAt( 0, WirePortDataType.FLOAT, false, DefaultInputNameStr );
  1173. }
  1174. }
  1175. if( m_mode == CustomExpressionMode.Call )
  1176. m_containerGraph.CustomExpressionOnFunctionMode.RemoveNode( this );
  1177. int count = Convert.ToInt32( GetCurrentParam( ref nodeParams ) );
  1178. if( count == 0 )
  1179. {
  1180. DeleteInputPortByArrayIdx( 0 );
  1181. m_items.Clear();
  1182. }
  1183. else
  1184. {
  1185. for( int i = 0; i < count; i++ )
  1186. {
  1187. bool foldoutValue = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) );
  1188. string name = GetCurrentParam( ref nodeParams );
  1189. WirePortDataType type = (WirePortDataType)Enum.Parse( typeof( WirePortDataType ), GetCurrentParam( ref nodeParams ) );
  1190. string internalData = GetCurrentParam( ref nodeParams );
  1191. VariableQualifiers qualifier = VariableQualifiers.In;
  1192. if( UIUtils.CurrentShaderVersion() > 12001 )
  1193. {
  1194. qualifier = (VariableQualifiers)Enum.Parse( typeof( VariableQualifiers ), GetCurrentParam( ref nodeParams ) );
  1195. }
  1196. string customType = string.Empty;
  1197. if( UIUtils.CurrentShaderVersion() > 15311 )
  1198. {
  1199. customType = GetCurrentParam( ref nodeParams );
  1200. }
  1201. PrecisionType precision = PrecisionType.Float;
  1202. if( UIUtils.CurrentShaderVersion() > 15607 )
  1203. {
  1204. precision = (PrecisionType)Enum.Parse( typeof( PrecisionType ), GetCurrentParam( ref nodeParams ));
  1205. }
  1206. int portIdx = i + m_firstAvailablePort;
  1207. if( i == 0 )
  1208. {
  1209. m_inputPorts[ portIdx ].ChangeProperties( name, type, false );
  1210. m_inputPorts[ portIdx ].Visible = qualifier != VariableQualifiers.Out;
  1211. m_items[ 0 ].Qualifier = qualifier;
  1212. m_items[ 0 ].FoldoutFlag = foldoutValue;
  1213. m_items[ 0 ].CustomType = customType;
  1214. m_items[ 0 ].Precision = precision;
  1215. }
  1216. else
  1217. {
  1218. m_items.Add( new CustomExpressionInputItem( precision, qualifier, customType, foldoutValue, string.Empty/*"[" + i + "]"*/ ) );
  1219. AddInputPort( type, false, name );
  1220. m_inputPorts[ m_inputPorts.Count -1 ].Visible = qualifier != VariableQualifiers.Out;
  1221. }
  1222. m_inputPorts[ i ].InternalData = internalData;
  1223. }
  1224. }
  1225. if( UIUtils.CurrentShaderVersion() > 7205 )
  1226. {
  1227. m_customExpressionName = GetCurrentParam( ref nodeParams );
  1228. SetTitleText( m_customExpressionName );
  1229. }
  1230. if( UIUtils.CurrentShaderVersion() > 14401 )
  1231. {
  1232. m_generateUniqueName = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) );
  1233. }
  1234. if( UIUtils.CurrentShaderVersion() > 15102 )
  1235. {
  1236. m_autoRegisterMode = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) );
  1237. }
  1238. if( UIUtils.CurrentShaderVersion() > 15403 )
  1239. {
  1240. int dependencyCount = Convert.ToInt32( GetCurrentParam( ref nodeParams ) );
  1241. for( int i = 0; i < dependencyCount; i++ )
  1242. {
  1243. m_dependencies.Add( new CustomExpressionDependency( GetCurrentParam( ref nodeParams ) ) );
  1244. }
  1245. }
  1246. if( m_mode == CustomExpressionMode.Create )
  1247. {
  1248. m_containerGraph.CustomExpressionOnFunctionMode.AddNode( this );
  1249. }
  1250. UpdateOutputPorts();
  1251. m_repopulateNameDictionary = true;
  1252. m_functionMode = m_code.Contains( ReturnHelper );
  1253. CheckCallMode();
  1254. }
  1255. public override void WriteToString( ref string nodeInfo, ref string connectionsInfo )
  1256. {
  1257. base.WriteToString( ref nodeInfo, ref connectionsInfo );
  1258. m_code = m_code.Replace( "\r\n", "\n" );
  1259. string parsedCode = m_code.Replace( '\n', LineFeedSeparator );
  1260. parsedCode = parsedCode.Replace( ';', Constants.SemiColonSeparator );
  1261. IOUtils.AddFieldValueToString( ref nodeInfo, parsedCode );
  1262. IOUtils.AddFieldValueToString( ref nodeInfo, m_outputTypeIdx );
  1263. IOUtils.AddFieldValueToString( ref nodeInfo, m_mode == CustomExpressionMode.Call );
  1264. int count = m_inputPorts.Count - m_firstAvailablePort;
  1265. IOUtils.AddFieldValueToString( ref nodeInfo, count );
  1266. for( int i = 0; i < count; i++ )
  1267. {
  1268. int portIdx = m_firstAvailablePort + i;
  1269. IOUtils.AddFieldValueToString( ref nodeInfo, m_items[ i ].FoldoutFlag );
  1270. IOUtils.AddFieldValueToString( ref nodeInfo, m_inputPorts[ portIdx ].Name );
  1271. IOUtils.AddFieldValueToString( ref nodeInfo, m_inputPorts[ portIdx ].DataType );
  1272. IOUtils.AddFieldValueToString( ref nodeInfo, m_inputPorts[ portIdx ].InternalData );
  1273. IOUtils.AddFieldValueToString( ref nodeInfo, m_items[ i ].Qualifier );
  1274. IOUtils.AddFieldValueToString( ref nodeInfo, m_items[ i ].CustomType );
  1275. IOUtils.AddFieldValueToString( ref nodeInfo, m_items[ i ].Precision );
  1276. }
  1277. IOUtils.AddFieldValueToString( ref nodeInfo, m_customExpressionName );
  1278. IOUtils.AddFieldValueToString( ref nodeInfo, m_generateUniqueName );
  1279. IOUtils.AddFieldValueToString( ref nodeInfo, m_autoRegisterMode );
  1280. count = m_dependencies.Count;
  1281. IOUtils.AddFieldValueToString( ref nodeInfo, count );
  1282. for( int i = 0; i < count; i++ )
  1283. {
  1284. IOUtils.AddFieldValueToString( ref nodeInfo, m_dependencies[ i ].DependencyNodeId );
  1285. }
  1286. }
  1287. public override void Destroy()
  1288. {
  1289. base.Destroy();
  1290. if( m_mode == CustomExpressionMode.Create )
  1291. {
  1292. m_containerGraph.CustomExpressionOnFunctionMode.RemoveNode( this );
  1293. }
  1294. m_items.Clear();
  1295. m_items = null;
  1296. m_dependencies.Clear();
  1297. m_dependencies = null;
  1298. m_itemReordableList = null;
  1299. }
  1300. public void CheckDependencies( ref MasterNodeDataCollector dataCollector, ref Dictionary<int, CustomExpressionNode> examinedNodes )
  1301. {
  1302. if( !examinedNodes.ContainsKey( UniqueId ) && m_mode == CustomExpressionMode.Create && !m_generateUniqueName )
  1303. {
  1304. int dependencyCount = m_dependencies.Count;
  1305. for( int d = 0; d < dependencyCount; d++ )
  1306. {
  1307. if( !examinedNodes.ContainsKey( m_dependencies[ d ].DependencyNodeId ) )
  1308. {
  1309. CustomExpressionNode dNode = m_containerGraph.GetNode( m_dependencies[ d ].DependencyNodeId ) as CustomExpressionNode;
  1310. if( dNode != null )
  1311. {
  1312. dNode.CheckDependencies( ref dataCollector, ref examinedNodes );
  1313. }
  1314. }
  1315. }
  1316. dataCollector.AddFunction( ExpressionName, EncapsulatedCode( dataCollector.IsTemplate ) );
  1317. examinedNodes.Add( UniqueId, this );
  1318. }
  1319. }
  1320. public string EncapsulatedCode( bool isTemplate )
  1321. {
  1322. string functionName = UIUtils.RemoveInvalidCharacters( m_customExpressionName );
  1323. if( m_generateUniqueName )
  1324. {
  1325. functionName += OutputId;
  1326. }
  1327. return WrapCodeInFunction( isTemplate, functionName, false );
  1328. }
  1329. public CustomExpressionMode Mode
  1330. {
  1331. get { return m_mode; }
  1332. set
  1333. {
  1334. if( m_mode != value )
  1335. {
  1336. m_mode = value;
  1337. if( m_mode == CustomExpressionMode.Call )
  1338. {
  1339. AutoRegisterMode = false;
  1340. m_generateUniqueName = false;
  1341. m_containerGraph.CustomExpressionOnFunctionMode.RemoveNode( this );
  1342. }
  1343. else
  1344. {
  1345. m_containerGraph.CustomExpressionOnFunctionMode.AddNode( this );
  1346. }
  1347. }
  1348. }
  1349. }
  1350. public string ExpressionName
  1351. {
  1352. get
  1353. {
  1354. string expressionName = UIUtils.RemoveInvalidCharacters( m_customExpressionName );
  1355. if( m_generateUniqueName )
  1356. {
  1357. expressionName += OutputId;
  1358. }
  1359. return expressionName;
  1360. }
  1361. }
  1362. public override string DataToArray { get { return m_customExpressionName; } }
  1363. public bool AutoRegisterMode
  1364. {
  1365. get { return m_autoRegisterMode; }
  1366. set
  1367. {
  1368. if( value != m_autoRegisterMode )
  1369. {
  1370. m_autoRegisterMode = value;
  1371. }
  1372. }
  1373. }
  1374. public override void RefreshExternalReferences()
  1375. {
  1376. base.RefreshExternalReferences();
  1377. int portCount = m_inputPorts.Count;
  1378. for( int i = 0; i < portCount; i++ )
  1379. {
  1380. if( m_inputPorts[ i ].DataType == WirePortDataType.COLOR )
  1381. {
  1382. m_inputPorts[ i ].ChangeType( WirePortDataType.FLOAT4, false ); ;
  1383. }
  1384. }
  1385. int dependencyCount = m_dependencies.Count;
  1386. for( int i = 0; i < dependencyCount; i++ )
  1387. {
  1388. m_dependencies[ i ].DependencyArrayIdx = m_containerGraph.CustomExpressionOnFunctionMode.GetNodeRegisterIdx( m_dependencies[ i ].DependencyNodeId );
  1389. }
  1390. }
  1391. public override void FireTimedUpdate()
  1392. {
  1393. m_containerGraph.CustomExpressionOnFunctionMode.UpdateDataOnNode( UniqueId, m_customExpressionName );
  1394. }
  1395. public List<CustomExpressionDependency> Dependencies { get { return m_dependencies; } }
  1396. }
  1397. }