NodeEditor.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import { Canvas, CircleMenu, ButtonInput, ContextMenu, Loader } from '../libs/flow.module.js';
  2. import { StandardMaterialEditor } from './materials/StandardMaterialEditor.js';
  3. import { OperatorEditor } from './math/OperatorEditor.js';
  4. import { FloatEditor } from './inputs/FloatEditor.js';
  5. import { ColorEditor } from './inputs/ColorEditor.js';
  6. import { UVEditor } from './accessors/UVEditor.js';
  7. import { PositionEditor } from './accessors/PositionEditor.js';
  8. import { NormalEditor } from './accessors/NormalEditor.js';
  9. import { CheckerEditor } from './procedural/CheckerEditor.js';
  10. import { EventDispatcher } from 'three';
  11. export const ClassLib = {
  12. 'StandardMaterialEditor': StandardMaterialEditor,
  13. 'OperatorEditor': OperatorEditor,
  14. 'FloatEditor': FloatEditor,
  15. 'ColorEditor': ColorEditor,
  16. 'UVEditor': UVEditor,
  17. 'PositionEditor': PositionEditor,
  18. 'NormalEditor': NormalEditor,
  19. 'CheckerEditor': CheckerEditor
  20. };
  21. export class NodeEditor extends EventDispatcher {
  22. constructor() {
  23. super();
  24. const domElement = document.createElement( 'flow' );
  25. const canvas = new Canvas();
  26. domElement.appendChild( canvas.dom );
  27. this.canvas = canvas;
  28. this.domElement = domElement;
  29. this._initMenu();
  30. this._initContextMenu();
  31. }
  32. add( node ) {
  33. this.canvas.add( node );
  34. return this;
  35. }
  36. get nodes() {
  37. return this.canvas.nodes;
  38. }
  39. _initMenu() {
  40. const menu = new CircleMenu();
  41. const menuButton = new ButtonInput().setIcon( 'ti ti-menu-2' );
  42. const newButton = new ButtonInput().setIcon( 'ti ti-file' ).setToolTip( 'New' );
  43. const openButton = new ButtonInput().setIcon( 'ti ti-upload' ).setToolTip( 'Open' );
  44. const saveButton = new ButtonInput().setIcon( 'ti ti-download' ).setToolTip( 'Save' );
  45. menuButton.onClick( () => {
  46. this.context.show( 50, 50 );
  47. } );
  48. newButton.onClick( () => {
  49. this.canvas.clear();
  50. this.dispatchEvent( { type: 'new' } );
  51. } );
  52. openButton.onClick( () => {
  53. this.context.hide();
  54. const input = document.createElement( 'input' );
  55. input.type = 'file';
  56. input.onchange = e => {
  57. const file = e.target.files[ 0 ];
  58. const reader = new FileReader();
  59. reader.readAsText( file, 'UTF-8' );
  60. reader.onload = readerEvent => {
  61. const json = Loader.parseObjects( JSON.parse( readerEvent.target.result ), ClassLib );
  62. this.canvas.clear();
  63. this.canvas.deserialize( json );
  64. this.dispatchEvent( { type: 'load' } );
  65. };
  66. };
  67. input.click();
  68. } );
  69. saveButton.onClick( () => {
  70. this.context.hide();
  71. const json = JSON.stringify( this.canvas.toJSON() );
  72. const a = document.createElement( 'a' );
  73. const file = new Blob( [ json ], { type: 'text/plain' } );
  74. a.href = URL.createObjectURL( file );
  75. a.download = 'node_editor.json';
  76. a.click();
  77. } );
  78. menu.add( menuButton );
  79. menu.add( newButton );
  80. menu.add( openButton );
  81. menu.add( saveButton );
  82. this.domElement.appendChild( menu.dom );
  83. this.menu = menu;
  84. }
  85. _initContextMenu() {
  86. const context = new ContextMenu( this.domElement );
  87. const add = ( node ) => {
  88. const canvas = this.canvas;
  89. const canvasRect = canvas.rect;
  90. node.setPosition(
  91. ( canvas.relativeX + ( canvasRect.width / 2 ) ) - ( 350 / 2 ),
  92. ( canvas.relativeY + ( canvasRect.height / 2 ) ) - 20
  93. );
  94. context.hide();
  95. this.add( node );
  96. this.canvas.select( node );
  97. };
  98. //**************//
  99. //* INPUTS
  100. //**************//
  101. const inputsContext = new ContextMenu();
  102. const floatInput = new ButtonInput( 'Float' ).setIcon( 'ti ti-box-multiple-1' )
  103. .onClick( () => add( new FloatEditor() ) );
  104. //const vec2Input = new ButtonInput( 'Vector 2' ).setIcon( 'ti ti-box-multiple-2' );
  105. //const vec3Input = new ButtonInput( 'Vector 3' ).setIcon( 'ti ti-box-multiple-3' );
  106. //const vec4Input = new ButtonInput( 'Vector 4' ).setIcon( 'ti ti-box-multiple-4' );
  107. const colorInput = new ButtonInput( 'Color' ).setIcon( 'ti ti-palette' )
  108. .onClick( () => add( new ColorEditor() ) );
  109. //const mapInput = new ButtonInput( 'Map' ).setIcon( 'ti ti-photo' );
  110. //const cubeMapInput = new ButtonInput( 'Cube Map' ).setIcon( 'ti ti-box' );
  111. //const sliderInput = new ButtonInput( 'Slider' ).setIcon( 'ti ti-adjustments-horizontal' );
  112. //const integerInput = new ButtonInput( 'Integer' ).setIcon( 'ti ti-list-numbers' );
  113. inputsContext
  114. .add( floatInput )
  115. //.add( vec2Input )
  116. //.add( vec3Input )
  117. //.add( vec4Input )
  118. .add( colorInput );
  119. //.add( sliderInput );
  120. //**************//
  121. //* MATH
  122. //**************//
  123. const mathContext = new ContextMenu();
  124. const operatorsNode = new ButtonInput( 'Operators' ).setIcon( 'ti ti-math-symbols' )
  125. .onClick( () => add( new OperatorEditor() ) );
  126. mathContext
  127. .add( operatorsNode );
  128. //**************//
  129. //* ACCESSORS
  130. //**************//
  131. const accessorsContext = new ContextMenu();
  132. const uvNode = new ButtonInput( 'UV' ).setIcon( 'ti ti-details' )
  133. .onClick( () => add( new UVEditor() ) );
  134. const positionNode = new ButtonInput( 'Position' ).setIcon( 'ti ti-hierarchy' )
  135. .onClick( () => add( new PositionEditor() ) );
  136. const normalNode = new ButtonInput( 'Normal' ).setIcon( 'ti ti-fold-up' )
  137. .onClick( () => add( new NormalEditor() ) );
  138. accessorsContext
  139. .add( uvNode )
  140. .add( positionNode )
  141. .add( normalNode );
  142. //**************//
  143. //* PROCEDURAL
  144. //**************//
  145. const proceduralContext = new ContextMenu();
  146. const checkerNode = new ButtonInput( 'Checker' ).setIcon( 'ti ti-border-outer' )
  147. .onClick( () => add( new CheckerEditor() ) );
  148. proceduralContext
  149. .add( checkerNode );
  150. //**************//
  151. //* MAIN
  152. //**************//
  153. context.add( new ButtonInput( 'Inputs' ).setIcon( 'ti ti-forms' ), inputsContext );
  154. context.add( new ButtonInput( 'Accessors' ).setIcon( 'ti ti-vector-triangle' ), accessorsContext );
  155. context.add( new ButtonInput( 'Math' ).setIcon( 'ti ti-calculator' ), mathContext );
  156. context.add( new ButtonInput( 'Procedural' ).setIcon( 'ti ti-infinity' ), proceduralContext );
  157. this.domElement.appendChild( context.dom );
  158. this.context = context;
  159. }
  160. }