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.

516 lines
15 KiB

  1. // Amplify Shader Editor - Visual Shader Editing Tool
  2. // Copyright (c) Amplify Creations, Lda <info@amplify.pt>
  3. using UnityEngine;
  4. using UnityEditor;
  5. using System;
  6. using System.Collections.Generic;
  7. namespace AmplifyShaderEditor
  8. {
  9. [Serializable]
  10. public class DynamicTypeNode : ParentNode
  11. {
  12. protected string m_inputA = string.Empty;
  13. protected string m_inputB = string.Empty;
  14. protected List<string> m_extensibleInputResults;
  15. protected bool m_dynamicOutputType = true;
  16. protected bool m_extensibleInputPorts = false;
  17. protected bool m_allowMatrixCheck = false;
  18. protected bool m_vectorMatrixOps = false;
  19. //[SerializeField]
  20. private int m_inputCount = 2;
  21. //[SerializeField]
  22. private int m_lastInputCount = 2;
  23. private bool m_previouslyDragging = false;
  24. private int m_beforePreviewCount = 0;
  25. [UnityEngine.SerializeField]
  26. protected WirePortDataType m_mainDataType = WirePortDataType.FLOAT;
  27. protected WirePortDataType[] m_dynamicRestrictions =
  28. {
  29. WirePortDataType.OBJECT,
  30. WirePortDataType.FLOAT,
  31. WirePortDataType.FLOAT2,
  32. WirePortDataType.FLOAT3,
  33. WirePortDataType.FLOAT4,
  34. WirePortDataType.COLOR,
  35. WirePortDataType.INT
  36. };
  37. protected override void CommonInit( int uniqueId )
  38. {
  39. base.CommonInit( uniqueId );
  40. m_useInternalPortData = true;
  41. m_textLabelWidth = 35;
  42. AddPorts();
  43. }
  44. protected virtual void AddPorts()
  45. {
  46. AddInputPort( WirePortDataType.FLOAT, false, "A" );
  47. AddInputPort( WirePortDataType.FLOAT, false, "B" );
  48. AddOutputPort( WirePortDataType.FLOAT, Constants.EmptyPortValue );
  49. m_inputPorts[ 0 ].CreatePortRestrictions( m_dynamicRestrictions );
  50. m_inputPorts[ 1 ].CreatePortRestrictions( m_dynamicRestrictions );
  51. }
  52. public override void OnConnectedOutputNodeChanges( int inputPortId, int otherNodeId, int otherPortId, string name, WirePortDataType type )
  53. {
  54. UpdateConnection( inputPortId );
  55. }
  56. public override void OnInputPortConnected( int portId, int otherNodeId, int otherPortId, bool activateNode = true )
  57. {
  58. base.OnInputPortConnected( portId, otherNodeId, otherPortId, activateNode );
  59. UpdateConnection( portId );
  60. }
  61. public override void OnInputPortDisconnected( int portId )
  62. {
  63. base.OnInputPortDisconnected( portId );
  64. UpdateDisconnectedConnection( portId );
  65. UpdateConnection( portId );
  66. UpdateEmptyInputPorts( true );
  67. }
  68. void UpdateDisconnectedConnection( int portId )
  69. {
  70. if( m_extensibleInputPorts || m_allowMatrixCheck )
  71. {
  72. int higher = 0;
  73. int groupOneType = 0;
  74. int groupTwoType = 0;
  75. for( int i = 0; i < m_inputPorts.Count; i++ )
  76. {
  77. if( m_inputPorts[ i ].IsConnected )
  78. {
  79. int currentPriority = UIUtils.GetPriority( m_inputPorts[ i ].DataType );
  80. if( !m_vectorMatrixOps && currentPriority < 3 )
  81. currentPriority += 7;
  82. if( currentPriority > higher && currentPriority > 2 )
  83. {
  84. higher = currentPriority;
  85. m_mainDataType = m_inputPorts[ i ].DataType;
  86. }
  87. switch( m_inputPorts[ i ].DataType )
  88. {
  89. case WirePortDataType.FLOAT2:
  90. case WirePortDataType.FLOAT3:
  91. case WirePortDataType.FLOAT4:
  92. case WirePortDataType.COLOR:
  93. {
  94. groupOneType++;
  95. groupTwoType++;
  96. }
  97. break;
  98. case WirePortDataType.FLOAT3x3:
  99. {
  100. groupOneType++;
  101. }
  102. break;
  103. case WirePortDataType.FLOAT4x4:
  104. {
  105. groupTwoType++;
  106. }
  107. break;
  108. }
  109. }
  110. }
  111. for( int i = 0; i < m_inputPorts.Count; i++ )
  112. {
  113. if( !m_inputPorts[ i ].IsConnected )
  114. {
  115. m_inputPorts[ i ].ChangeType( m_mainDataType, false );
  116. }
  117. }
  118. if( groupOneType > 0 && m_mainDataType == WirePortDataType.FLOAT4x4 )
  119. {
  120. m_errorMessageTooltip = "Doing this operation with FLOAT4x4 value only works against other FLOAT4x4 or FLOAT values";
  121. m_showErrorMessage = true;
  122. }
  123. else if( groupTwoType > 0 && m_mainDataType == WirePortDataType.FLOAT3x3 )
  124. {
  125. m_errorMessageTooltip = "Doing this operation with FLOAT3x3 value only works against other FLOAT3x3 or FLOAT values";
  126. m_showErrorMessage = true;
  127. }
  128. else
  129. {
  130. m_showErrorMessage = false;
  131. }
  132. if( m_dynamicOutputType )
  133. m_outputPorts[ 0 ].ChangeType( m_mainDataType, false );
  134. }
  135. else
  136. if( m_inputPorts[ 0 ].DataType != m_inputPorts[ 1 ].DataType )
  137. {
  138. int otherPortId = ( portId + 1 ) % 2;
  139. if( m_inputPorts[ otherPortId ].IsConnected )
  140. {
  141. m_mainDataType = m_inputPorts[ otherPortId ].DataType;
  142. m_inputPorts[ portId ].ChangeType( m_mainDataType, false );
  143. if( m_dynamicOutputType )
  144. m_outputPorts[ 0 ].ChangeType( m_mainDataType, false );
  145. }
  146. else
  147. {
  148. if( UIUtils.GetPriority( m_inputPorts[ 0 ].DataType ) > UIUtils.GetPriority( m_inputPorts[ 1 ].DataType ) )
  149. {
  150. m_mainDataType = m_inputPorts[ 0 ].DataType;
  151. m_inputPorts[ 1 ].ChangeType( m_mainDataType, false );
  152. }
  153. else
  154. {
  155. m_mainDataType = m_inputPorts[ 1 ].DataType;
  156. m_inputPorts[ 0 ].ChangeType( m_mainDataType, false );
  157. }
  158. if( m_dynamicOutputType )
  159. {
  160. if( m_mainDataType != m_outputPorts[ 0 ].DataType )
  161. {
  162. m_outputPorts[ 0 ].ChangeType( m_mainDataType, false );
  163. }
  164. }
  165. }
  166. }
  167. }
  168. void UpdateConnection( int portId )
  169. {
  170. if( m_extensibleInputPorts || m_allowMatrixCheck )
  171. {
  172. m_inputPorts[ portId ].MatchPortToConnection();
  173. int higher = 0;
  174. int groupOneType = 0;
  175. int groupTwoType = 0;
  176. for( int i = 0; i < m_inputPorts.Count; i++ )
  177. {
  178. if( m_inputPorts[ i ].IsConnected )
  179. {
  180. int currentPriority = UIUtils.GetPriority( m_inputPorts[ i ].DataType );
  181. if( !m_vectorMatrixOps && currentPriority < 3 )
  182. currentPriority += 7;
  183. if( currentPriority > higher )
  184. {
  185. higher = currentPriority;
  186. m_mainDataType = m_inputPorts[ i ].DataType;
  187. }
  188. switch( m_inputPorts[ i ].DataType )
  189. {
  190. case WirePortDataType.FLOAT2:
  191. case WirePortDataType.FLOAT3:
  192. case WirePortDataType.FLOAT4:
  193. case WirePortDataType.COLOR:
  194. {
  195. groupOneType++;
  196. groupTwoType++;
  197. }
  198. break;
  199. case WirePortDataType.FLOAT3x3:
  200. {
  201. groupOneType++;
  202. }
  203. break;
  204. case WirePortDataType.FLOAT4x4:
  205. {
  206. groupTwoType++;
  207. }
  208. break;
  209. }
  210. }
  211. }
  212. for( int i = 0; i < m_inputPorts.Count; i++ )
  213. {
  214. if( !m_inputPorts[ i ].IsConnected )
  215. {
  216. m_inputPorts[ i ].ChangeType( m_mainDataType, false );
  217. }
  218. }
  219. if( groupOneType > 0 && m_mainDataType == WirePortDataType.FLOAT4x4 )
  220. {
  221. m_errorMessageTooltip = "Doing this operation with FLOAT4x4 value only works against other FLOAT4x4 or FLOAT values";
  222. m_showErrorMessage = true;
  223. }
  224. else if( groupTwoType > 0 && m_mainDataType == WirePortDataType.FLOAT3x3 )
  225. {
  226. m_errorMessageTooltip = "Doing this operation with FLOAT3x3 value only works against other FLOAT3x3 or FLOAT values";
  227. m_showErrorMessage = true;
  228. }
  229. else
  230. {
  231. m_showErrorMessage = false;
  232. }
  233. if( m_dynamicOutputType )
  234. m_outputPorts[ 0 ].ChangeType( m_mainDataType, false );
  235. }
  236. else
  237. {
  238. m_inputPorts[ portId ].MatchPortToConnection();
  239. int otherPortId = ( portId + 1 ) % 2;
  240. if( !m_inputPorts[ otherPortId ].IsConnected )
  241. {
  242. m_inputPorts[ otherPortId ].ChangeType( m_inputPorts[ portId ].DataType, false );
  243. }
  244. if( m_inputPorts[ 0 ].DataType == m_inputPorts[ 1 ].DataType )
  245. {
  246. m_mainDataType = m_inputPorts[ 0 ].DataType;
  247. if( m_dynamicOutputType )
  248. m_outputPorts[ 0 ].ChangeType( InputPorts[ 0 ].DataType, false );
  249. }
  250. else
  251. {
  252. if( UIUtils.GetPriority( m_inputPorts[ 0 ].DataType ) > UIUtils.GetPriority( m_inputPorts[ 1 ].DataType ) )
  253. {
  254. m_mainDataType = m_inputPorts[ 0 ].DataType;
  255. }
  256. else
  257. {
  258. m_mainDataType = m_inputPorts[ 1 ].DataType;
  259. }
  260. if( m_dynamicOutputType )
  261. {
  262. if( m_mainDataType != m_outputPorts[ 0 ].DataType )
  263. {
  264. m_outputPorts[ 0 ].ChangeType( m_mainDataType, false );
  265. }
  266. }
  267. }
  268. }
  269. }
  270. public override void OnNodeLayout( DrawInfo drawInfo )
  271. {
  272. base.OnNodeLayout( drawInfo );
  273. if( !m_extensibleInputPorts )
  274. return;
  275. if( m_previouslyDragging != m_containerGraph.ParentWindow.WireReferenceUtils.OutputPortReference.IsValid && m_containerGraph.ParentWindow.WireReferenceUtils.OutputPortReference.NodeId != UniqueId )
  276. {
  277. if( m_containerGraph.ParentWindow.WireReferenceUtils.OutputPortReference.IsValid )
  278. {
  279. m_beforePreviewCount = 2;
  280. for( int i = 2; i < m_inputPorts.Count; i++ )
  281. {
  282. if( m_inputPorts[ i ].IsConnected )
  283. {
  284. m_beforePreviewCount++;
  285. }
  286. }
  287. m_inputCount = m_beforePreviewCount + 1;
  288. if( m_inputCount <= 10 )
  289. {
  290. if( m_inputCount > m_lastInputCount )
  291. {
  292. Undo.RegisterCompleteObjectUndo( m_containerGraph.ParentWindow, Constants.UndoCreateDynamicPortId );
  293. RecordObject( Constants.UndoCreateDynamicPortId );
  294. AddInputPort( m_mainDataType, false, ( ( char ) ( 'A' + m_inputCount - 1 ) ).ToString() );
  295. m_inputPorts[ m_inputCount - 1 ].CreatePortRestrictions( m_dynamicRestrictions );
  296. if( Selected && ContainerGraph.ParentWindow.ParametersWindow.IsMaximized )
  297. Event.current.type = EventType.Used;
  298. }
  299. m_lastInputCount = m_inputCount;
  300. m_sizeIsDirty = true;
  301. m_isDirty = true;
  302. SetSaveIsDirty();
  303. }
  304. }
  305. else
  306. {
  307. bool hasEmpty = CheckValidConnections();
  308. if( hasEmpty )
  309. UpdateEmptyInputPorts( false );
  310. }
  311. m_previouslyDragging = m_containerGraph.ParentWindow.WireReferenceUtils.OutputPortReference.IsValid;
  312. }
  313. UpdateEmptyInputPorts( false );
  314. }
  315. private bool CheckValidConnections()
  316. {
  317. if( !m_extensibleInputPorts )
  318. return false;
  319. bool hasEmptyConnections = false;
  320. bool hasMatrix = m_inputPorts[ 0 ].DataType == WirePortDataType.FLOAT3x3 || m_inputPorts[ 0 ].DataType == WirePortDataType.FLOAT4x4 || m_inputPorts[ 1 ].DataType == WirePortDataType.FLOAT3x3 || m_inputPorts[ 1 ].DataType == WirePortDataType.FLOAT4x4;
  321. if( m_inputPorts.Count != m_beforePreviewCount )
  322. {
  323. if( hasMatrix )
  324. {
  325. bool showError = false;
  326. for( int i = m_inputPorts.Count - 1; i >= 2; i-- )
  327. {
  328. if( m_inputPorts[ i ].IsConnected )
  329. {
  330. showError = true;
  331. m_inputPorts[ i ].FullDeleteConnections();
  332. }
  333. hasEmptyConnections = true;
  334. }
  335. if( showError )
  336. m_containerGraph.ParentWindow.ShowMessage( "Matrix operations are only valid for the first two inputs to prevent errors" );
  337. }
  338. else
  339. {
  340. for( int i = m_inputPorts.Count - 1; i >= 2; i-- )
  341. {
  342. if( m_inputPorts[ i ].DataType == WirePortDataType.FLOAT3x3 || m_inputPorts[ i ].DataType == WirePortDataType.FLOAT4x4 )
  343. {
  344. m_containerGraph.ParentWindow.ShowMessage( "Matrix operations are only valid for the first two inputs to prevent errors" );
  345. m_inputPorts[ i ].FullDeleteConnections();
  346. hasEmptyConnections = true;
  347. }
  348. else if( !m_inputPorts[ i ].IsConnected )
  349. {
  350. hasEmptyConnections = true;
  351. }
  352. }
  353. }
  354. }
  355. return hasEmptyConnections;
  356. }
  357. private void UpdateEmptyInputPorts( bool recordUndo )
  358. {
  359. if( !m_extensibleInputPorts )
  360. return;
  361. if( !m_containerGraph.ParentWindow.WireReferenceUtils.OutputPortReference.IsValid )
  362. {
  363. if( recordUndo )
  364. {
  365. Undo.RegisterCompleteObjectUndo( m_containerGraph.ParentWindow, Constants.UndoDeleteDynamicPortId );
  366. RecordObject( Constants.UndoDeleteDynamicPortId );
  367. }
  368. bool hasDeleted = false;
  369. m_inputCount = 2;
  370. for( int i = m_inputPorts.Count - 1; i >= 2; i-- )
  371. {
  372. if( !m_inputPorts[ i ].IsConnected )
  373. {
  374. hasDeleted = true;
  375. DeleteInputPortByArrayIdx( i );
  376. }
  377. else
  378. {
  379. m_inputCount++;
  380. }
  381. }
  382. if( hasDeleted || m_inputCount != m_lastInputCount )
  383. {
  384. for( int i = 2; i < m_inputPorts.Count; i++ )
  385. {
  386. m_inputPorts[ i ].Name = ( ( char ) ( 'A' + i ) ).ToString();
  387. }
  388. m_beforePreviewCount = m_inputPorts.Count;
  389. m_inputCount = m_beforePreviewCount;
  390. m_lastInputCount = m_inputCount;
  391. m_sizeIsDirty = true;
  392. m_isDirty = true;
  393. SetSaveIsDirty();
  394. }
  395. }
  396. m_inputCount = Mathf.Clamp( m_inputCount, 2, 10 );
  397. }
  398. public virtual string BuildResults( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar )
  399. {
  400. if( !m_extensibleInputPorts )
  401. SetInputData( outputId, ref dataCollector, ignoreLocalvar );
  402. else
  403. SetExtensibleInputData( outputId, ref dataCollector, ignoreLocalvar );
  404. return string.Empty;
  405. }
  406. public override string GenerateShaderForOutput( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar )
  407. {
  408. string result = BuildResults( outputId, ref dataCollector, ignoreLocalvar );
  409. return CreateOutputLocalVariable( 0, result, ref dataCollector );
  410. }
  411. protected void SetInputData( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar )
  412. {
  413. m_inputA = m_inputPorts[ 0 ].GeneratePortInstructions( ref dataCollector );
  414. if( m_inputPorts[ 0 ].DataType != m_mainDataType )
  415. {
  416. m_inputA = UIUtils.CastPortType( ref dataCollector, m_currentPrecisionType, new NodeCastInfo( UniqueId, outputId ), m_inputA, m_inputPorts[ 0 ].DataType, m_mainDataType, m_inputA );
  417. }
  418. m_inputB = m_inputPorts[ 1 ].GeneratePortInstructions( ref dataCollector );
  419. if( m_inputPorts[ 1 ].DataType != m_mainDataType )
  420. {
  421. m_inputB = UIUtils.CastPortType( ref dataCollector, m_currentPrecisionType, new NodeCastInfo( UniqueId, outputId ), m_inputB, m_inputPorts[ 1 ].DataType, m_mainDataType, m_inputB );
  422. }
  423. }
  424. protected void SetExtensibleInputData( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar )
  425. {
  426. m_extensibleInputResults = new List<string>();
  427. for( int i = 0; i < m_inputPorts.Count; i++ )
  428. {
  429. m_extensibleInputResults.Add( m_inputPorts[ i ].GeneratePortInstructions( ref dataCollector ) );
  430. if( m_inputPorts[ i ].DataType != m_mainDataType && m_inputPorts[ i ].DataType != WirePortDataType.FLOAT && m_inputPorts[ i ].DataType != WirePortDataType.INT )
  431. {
  432. m_extensibleInputResults[ i ] = UIUtils.CastPortType( ref dataCollector, m_currentPrecisionType, new NodeCastInfo( UniqueId, outputId ), m_extensibleInputResults[ i ], m_inputPorts[ i ].DataType, m_mainDataType, m_extensibleInputResults[ i ] );
  433. }
  434. }
  435. }
  436. void UpdatePorts()
  437. {
  438. m_lastInputCount = Mathf.Clamp( m_inputCount, 2, 10 );
  439. for( int i = 2; i < m_inputCount; i++ )
  440. {
  441. AddInputPort( m_mainDataType, false, ( ( char ) ( 'A' + i ) ).ToString() );
  442. m_inputPorts[ i ].CreatePortRestrictions( m_dynamicRestrictions );
  443. }
  444. m_sizeIsDirty = true;
  445. SetSaveIsDirty();
  446. }
  447. public override void ReadFromString( ref string[] nodeParams )
  448. {
  449. base.ReadFromString( ref nodeParams );
  450. if( m_extensibleInputPorts && UIUtils.CurrentShaderVersion() > 10005 )
  451. {
  452. m_inputCount = Convert.ToInt32( GetCurrentParam( ref nodeParams ) );
  453. UpdatePorts();
  454. }
  455. }
  456. public override void WriteToString( ref string nodeInfo, ref string connectionsInfo )
  457. {
  458. base.WriteToString( ref nodeInfo, ref connectionsInfo );
  459. if( m_extensibleInputPorts )
  460. IOUtils.AddFieldValueToString( ref nodeInfo, m_inputCount );
  461. }
  462. }
  463. }