Node.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  1. import { NodeUpdateType } from './constants.js';
  2. import { hash, hashArray, hashString } from './NodeUtils.js';
  3. import { EventDispatcher } from '../../core/EventDispatcher.js';
  4. import { MathUtils } from '../../math/MathUtils.js';
  5. import { warn, error } from '../../utils.js';
  6. const _parentBuildStage = {
  7. analyze: 'setup',
  8. generate: 'analyze'
  9. };
  10. let _nodeId = 0;
  11. /**
  12. * Base class for all nodes.
  13. *
  14. * @augments EventDispatcher
  15. */
  16. class Node extends EventDispatcher {
  17. static get type() {
  18. return 'Node';
  19. }
  20. /**
  21. * Constructs a new node.
  22. *
  23. * @param {?string} nodeType - The node type.
  24. */
  25. constructor( nodeType = null ) {
  26. super();
  27. /**
  28. * The node type. This represents the result type of the node (e.g. `float` or `vec3`).
  29. *
  30. * @type {?string}
  31. * @default null
  32. */
  33. this.nodeType = nodeType;
  34. /**
  35. * The update type of the node's {@link Node#update} method. Possible values are listed in {@link NodeUpdateType}.
  36. *
  37. * @type {string}
  38. * @default 'none'
  39. */
  40. this.updateType = NodeUpdateType.NONE;
  41. /**
  42. * The update type of the node's {@link Node#updateBefore} method. Possible values are listed in {@link NodeUpdateType}.
  43. *
  44. * @type {string}
  45. * @default 'none'
  46. */
  47. this.updateBeforeType = NodeUpdateType.NONE;
  48. /**
  49. * The update type of the node's {@link Node#updateAfter} method. Possible values are listed in {@link NodeUpdateType}.
  50. *
  51. * @type {string}
  52. * @default 'none'
  53. */
  54. this.updateAfterType = NodeUpdateType.NONE;
  55. /**
  56. * The UUID of the node.
  57. *
  58. * @type {string}
  59. * @readonly
  60. */
  61. this.uuid = MathUtils.generateUUID();
  62. /**
  63. * The version of the node. The version automatically is increased when {@link Node#needsUpdate} is set to `true`.
  64. *
  65. * @type {number}
  66. * @readonly
  67. * @default 0
  68. */
  69. this.version = 0;
  70. /**
  71. * The name of the node.
  72. *
  73. * @type {string}
  74. * @default ''
  75. */
  76. this.name = '';
  77. /**
  78. * Whether this node is global or not. This property is relevant for the internal
  79. * node caching system. All nodes which should be declared just once should
  80. * set this flag to `true` (a typical example is {@link AttributeNode}).
  81. *
  82. * @type {boolean}
  83. * @default false
  84. */
  85. this.global = false;
  86. /**
  87. * Create a list of parents for this node during the build process.
  88. *
  89. * @type {boolean}
  90. * @default false
  91. */
  92. this.parents = false;
  93. /**
  94. * This flag can be used for type testing.
  95. *
  96. * @type {boolean}
  97. * @readonly
  98. * @default true
  99. */
  100. this.isNode = true;
  101. // private
  102. this._beforeNodes = null;
  103. /**
  104. * The cache key of this node.
  105. *
  106. * @private
  107. * @type {?number}
  108. * @default null
  109. */
  110. this._cacheKey = null;
  111. /**
  112. * The cache key 's version.
  113. *
  114. * @private
  115. * @type {number}
  116. * @default 0
  117. */
  118. this._cacheKeyVersion = 0;
  119. Object.defineProperty( this, 'id', { value: _nodeId ++ } );
  120. }
  121. /**
  122. * Set this property to `true` when the node should be regenerated.
  123. *
  124. * @type {boolean}
  125. * @default false
  126. * @param {boolean} value
  127. */
  128. set needsUpdate( value ) {
  129. if ( value === true ) {
  130. this.version ++;
  131. }
  132. }
  133. /**
  134. * The type of the class. The value is usually the constructor name.
  135. *
  136. * @type {string}
  137. * @readonly
  138. */
  139. get type() {
  140. return this.constructor.type;
  141. }
  142. /**
  143. * Convenient method for defining {@link Node#update}.
  144. *
  145. * @param {Function} callback - The update method.
  146. * @param {string} updateType - The update type.
  147. * @return {Node} A reference to this node.
  148. */
  149. onUpdate( callback, updateType ) {
  150. this.updateType = updateType;
  151. this.update = callback.bind( this );
  152. return this;
  153. }
  154. /**
  155. * Convenient method for defining {@link Node#update}. Similar to {@link Node#onUpdate}, but
  156. * this method automatically sets the update type to `FRAME`.
  157. *
  158. * @param {Function} callback - The update method.
  159. * @return {Node} A reference to this node.
  160. */
  161. onFrameUpdate( callback ) {
  162. return this.onUpdate( callback, NodeUpdateType.FRAME );
  163. }
  164. /**
  165. * Convenient method for defining {@link Node#update}. Similar to {@link Node#onUpdate}, but
  166. * this method automatically sets the update type to `RENDER`.
  167. *
  168. * @param {Function} callback - The update method.
  169. * @return {Node} A reference to this node.
  170. */
  171. onRenderUpdate( callback ) {
  172. return this.onUpdate( callback, NodeUpdateType.RENDER );
  173. }
  174. /**
  175. * Convenient method for defining {@link Node#update}. Similar to {@link Node#onUpdate}, but
  176. * this method automatically sets the update type to `OBJECT`.
  177. *
  178. * @param {Function} callback - The update method.
  179. * @return {Node} A reference to this node.
  180. */
  181. onObjectUpdate( callback ) {
  182. return this.onUpdate( callback, NodeUpdateType.OBJECT );
  183. }
  184. /**
  185. * Convenient method for defining {@link Node#updateReference}.
  186. *
  187. * @param {Function} callback - The update method.
  188. * @return {Node} A reference to this node.
  189. */
  190. onReference( callback ) {
  191. this.updateReference = callback.bind( this );
  192. return this;
  193. }
  194. /**
  195. * Nodes might refer to other objects like materials. This method allows to dynamically update the reference
  196. * to such objects based on a given state (e.g. the current node frame or builder).
  197. *
  198. * @param {any} state - This method can be invocated in different contexts so `state` can refer to any object type.
  199. * @return {any} The updated reference.
  200. */
  201. updateReference( /*state*/ ) {
  202. return this;
  203. }
  204. /**
  205. * By default this method returns the value of the {@link Node#global} flag. This method
  206. * can be overwritten in derived classes if an analytical way is required to determine the
  207. * global cache referring to the current shader-stage.
  208. *
  209. * @param {NodeBuilder} builder - The current node builder.
  210. * @return {boolean} Whether this node is global or not.
  211. */
  212. isGlobal( /*builder*/ ) {
  213. return this.global;
  214. }
  215. /**
  216. * Generator function that can be used to iterate over the child nodes.
  217. *
  218. * @generator
  219. * @yields {Node} A child node.
  220. */
  221. * getChildren() {
  222. for ( const { childNode } of this._getChildren() ) {
  223. yield childNode;
  224. }
  225. }
  226. /**
  227. * Calling this method dispatches the `dispose` event. This event can be used
  228. * to register event listeners for clean up tasks.
  229. */
  230. dispose() {
  231. this.dispatchEvent( { type: 'dispose' } );
  232. }
  233. /**
  234. * Callback for {@link Node#traverse}.
  235. *
  236. * @callback traverseCallback
  237. * @param {Node} node - The current node.
  238. */
  239. /**
  240. * Can be used to traverse through the node's hierarchy.
  241. *
  242. * @param {traverseCallback} callback - A callback that is executed per node.
  243. */
  244. traverse( callback ) {
  245. callback( this );
  246. for ( const childNode of this.getChildren() ) {
  247. childNode.traverse( callback );
  248. }
  249. }
  250. /**
  251. * Returns the child nodes of this node.
  252. *
  253. * @private
  254. * @param {Set<Node>} [ignores=new Set()] - A set of nodes to ignore during the search to avoid circular references.
  255. * @returns {Array<Object>} An array of objects describing the child nodes.
  256. */
  257. _getChildren( ignores = new Set() ) {
  258. const children = [];
  259. // avoid circular references
  260. ignores.add( this );
  261. for ( const property of Object.getOwnPropertyNames( this ) ) {
  262. const object = this[ property ];
  263. // Ignore private properties and ignored nodes.
  264. if ( property.startsWith( '_' ) === true || ignores.has( object ) ) continue;
  265. if ( Array.isArray( object ) === true ) {
  266. for ( let i = 0; i < object.length; i ++ ) {
  267. const child = object[ i ];
  268. if ( child && child.isNode === true ) {
  269. children.push( { property, index: i, childNode: child } );
  270. }
  271. }
  272. } else if ( object && object.isNode === true ) {
  273. children.push( { property, childNode: object } );
  274. } else if ( object && Object.getPrototypeOf( object ) === Object.prototype ) {
  275. for ( const subProperty in object ) {
  276. // Ignore private sub-properties.
  277. if ( subProperty.startsWith( '_' ) === true ) continue;
  278. const child = object[ subProperty ];
  279. if ( child && child.isNode === true ) {
  280. children.push( { property, index: subProperty, childNode: child } );
  281. }
  282. }
  283. }
  284. }
  285. //
  286. return children;
  287. }
  288. /**
  289. * Returns the cache key for this node.
  290. *
  291. * @param {boolean} [force=false] - When set to `true`, a recomputation of the cache key is forced.
  292. * @param {Set<Node>} [ignores=null] - A set of nodes to ignore during the computation of the cache key.
  293. * @return {number} The cache key of the node.
  294. */
  295. getCacheKey( force = false, ignores = null ) {
  296. force = force || this.version !== this._cacheKeyVersion;
  297. if ( force === true || this._cacheKey === null ) {
  298. if ( ignores === null ) ignores = new Set();
  299. //
  300. const values = [ this.id ];
  301. for ( const { property, childNode } of this._getChildren( ignores ) ) {
  302. values.push( hashString( property.slice( 0, - 4 ) ), childNode.getCacheKey( force, ignores ) );
  303. }
  304. //
  305. this._cacheKey = hash( hashArray( values ), this.customCacheKey() );
  306. this._cacheKeyVersion = this.version;
  307. }
  308. return this._cacheKey;
  309. }
  310. /**
  311. * Generate a custom cache key for this node.
  312. *
  313. * @return {number} The cache key of the node.
  314. */
  315. customCacheKey() {
  316. return 0;
  317. }
  318. /**
  319. * Returns the references to this node which is by default `this`.
  320. *
  321. * @return {Node} A reference to this node.
  322. */
  323. getScope() {
  324. return this;
  325. }
  326. /**
  327. * Returns the hash of the node which is used to identify the node. By default it's
  328. * the {@link Node#uuid} however derived node classes might have to overwrite this method
  329. * depending on their implementation.
  330. *
  331. * @param {NodeBuilder} builder - The current node builder.
  332. * @return {string} The hash.
  333. */
  334. getHash( /*builder*/ ) {
  335. return this.uuid;
  336. }
  337. /**
  338. * Returns the update type of {@link Node#update}.
  339. *
  340. * @return {NodeUpdateType} The update type.
  341. */
  342. getUpdateType() {
  343. return this.updateType;
  344. }
  345. /**
  346. * Returns the update type of {@link Node#updateBefore}.
  347. *
  348. * @return {NodeUpdateType} The update type.
  349. */
  350. getUpdateBeforeType() {
  351. return this.updateBeforeType;
  352. }
  353. /**
  354. * Returns the update type of {@link Node#updateAfter}.
  355. *
  356. * @return {NodeUpdateType} The update type.
  357. */
  358. getUpdateAfterType() {
  359. return this.updateAfterType;
  360. }
  361. /**
  362. * Certain types are composed of multiple elements. For example a `vec3`
  363. * is composed of three `float` values. This method returns the type of
  364. * these elements.
  365. *
  366. * @param {NodeBuilder} builder - The current node builder.
  367. * @return {string} The type of the node.
  368. */
  369. getElementType( builder ) {
  370. const type = this.getNodeType( builder );
  371. const elementType = builder.getElementType( type );
  372. return elementType;
  373. }
  374. /**
  375. * Returns the node member type for the given name.
  376. *
  377. * @param {NodeBuilder} builder - The current node builder.
  378. * @param {string} name - The name of the member.
  379. * @return {string} The type of the node.
  380. */
  381. getMemberType( /*builder, name*/ ) {
  382. return 'void';
  383. }
  384. /**
  385. * Returns the node's type.
  386. *
  387. * @param {NodeBuilder} builder - The current node builder.
  388. * @return {string} The type of the node.
  389. */
  390. getNodeType( builder ) {
  391. const nodeProperties = builder.getNodeProperties( this );
  392. if ( nodeProperties.outputNode ) {
  393. return nodeProperties.outputNode.getNodeType( builder );
  394. }
  395. return this.nodeType;
  396. }
  397. /**
  398. * This method is used during the build process of a node and ensures
  399. * equal nodes are not built multiple times but just once. For example if
  400. * `attribute( 'uv' )` is used multiple times by the user, the build
  401. * process makes sure to process just the first node.
  402. *
  403. * @param {NodeBuilder} builder - The current node builder.
  404. * @return {Node} The shared node if possible. Otherwise `this` is returned.
  405. */
  406. getShared( builder ) {
  407. const hash = this.getHash( builder );
  408. const nodeFromHash = builder.getNodeFromHash( hash );
  409. return nodeFromHash || this;
  410. }
  411. /**
  412. * Returns the number of elements in the node array.
  413. *
  414. * @param {NodeBuilder} builder - The current node builder.
  415. * @return {?number} The number of elements in the node array.
  416. */
  417. getArrayCount( /*builder*/ ) {
  418. return null;
  419. }
  420. /**
  421. * Represents the setup stage which is the first step of the build process, see {@link Node#build} method.
  422. * This method is often overwritten in derived modules to prepare the node which is used as a node's output/result.
  423. * If an output node is prepared, then it must be returned in the `return` statement of the derived module's setup function.
  424. *
  425. * @param {NodeBuilder} builder - The current node builder.
  426. * @return {?Node} The output node.
  427. */
  428. setup( builder ) {
  429. const nodeProperties = builder.getNodeProperties( this );
  430. let index = 0;
  431. for ( const childNode of this.getChildren() ) {
  432. nodeProperties[ 'node' + index ++ ] = childNode;
  433. }
  434. // return a outputNode if exists or null
  435. return nodeProperties.outputNode || null;
  436. }
  437. /**
  438. * Represents the analyze stage which is the second step of the build process, see {@link Node#build} method.
  439. * This stage analyzes the node hierarchy and ensures descendent nodes are built.
  440. *
  441. * @param {NodeBuilder} builder - The current node builder.
  442. * @param {?Node} output - The target output node.
  443. */
  444. analyze( builder, output = null ) {
  445. const usageCount = builder.increaseUsage( this );
  446. if ( this.parents === true ) {
  447. const nodeData = builder.getDataFromNode( this, 'any' );
  448. nodeData.stages = nodeData.stages || {};
  449. nodeData.stages[ builder.shaderStage ] = nodeData.stages[ builder.shaderStage ] || [];
  450. nodeData.stages[ builder.shaderStage ].push( output );
  451. }
  452. if ( usageCount === 1 ) {
  453. // node flow children
  454. const nodeProperties = builder.getNodeProperties( this );
  455. for ( const childNode of Object.values( nodeProperties ) ) {
  456. if ( childNode && childNode.isNode === true ) {
  457. childNode.build( builder, this );
  458. }
  459. }
  460. }
  461. }
  462. /**
  463. * Represents the generate stage which is the third step of the build process, see {@link Node#build} method.
  464. * This state builds the output node and returns the resulting shader string.
  465. *
  466. * @param {NodeBuilder} builder - The current node builder.
  467. * @param {?string} [output] - Can be used to define the output type.
  468. * @return {?string} The generated shader string.
  469. */
  470. generate( builder, output ) {
  471. const { outputNode } = builder.getNodeProperties( this );
  472. if ( outputNode && outputNode.isNode === true ) {
  473. return outputNode.build( builder, output );
  474. }
  475. }
  476. /**
  477. * The method can be implemented to update the node's internal state before it is used to render an object.
  478. * The {@link Node#updateBeforeType} property defines how often the update is executed.
  479. *
  480. * @abstract
  481. * @param {NodeFrame} frame - A reference to the current node frame.
  482. * @return {?boolean} An optional bool that indicates whether the implementation actually performed an update or not (e.g. due to caching).
  483. */
  484. updateBefore( /*frame*/ ) {
  485. warn( 'Abstract function.' );
  486. }
  487. /**
  488. * The method can be implemented to update the node's internal state after it was used to render an object.
  489. * The {@link Node#updateAfterType} property defines how often the update is executed.
  490. *
  491. * @abstract
  492. * @param {NodeFrame} frame - A reference to the current node frame.
  493. * @return {?boolean} An optional bool that indicates whether the implementation actually performed an update or not (e.g. due to caching).
  494. */
  495. updateAfter( /*frame*/ ) {
  496. warn( 'Abstract function.' );
  497. }
  498. /**
  499. * The method can be implemented to update the node's internal state when it is used to render an object.
  500. * The {@link Node#updateType} property defines how often the update is executed.
  501. *
  502. * @abstract
  503. * @param {NodeFrame} frame - A reference to the current node frame.
  504. * @return {?boolean} An optional bool that indicates whether the implementation actually performed an update or not (e.g. due to caching).
  505. */
  506. update( /*frame*/ ) {
  507. warn( 'Abstract function.' );
  508. }
  509. before( node ) {
  510. if ( this._beforeNodes === null ) this._beforeNodes = [];
  511. this._beforeNodes.push( node );
  512. return this;
  513. }
  514. /**
  515. * This method performs the build of a node. The behavior and return value depend on the current build stage:
  516. * - **setup**: Prepares the node and its children for the build process. This process can also create new nodes. Returns the node itself or a variant.
  517. * - **analyze**: Analyzes the node hierarchy for optimizations in the code generation stage. Returns `null`.
  518. * - **generate**: Generates the shader code for the node. Returns the generated shader string.
  519. *
  520. * @param {NodeBuilder} builder - The current node builder.
  521. * @param {?(string|Node)} [output=null] - Can be used to define the output type.
  522. * @return {?(Node|string)} The result of the build process, depending on the build stage.
  523. */
  524. build( builder, output = null ) {
  525. const refNode = this.getShared( builder );
  526. if ( this !== refNode ) {
  527. return refNode.build( builder, output );
  528. }
  529. //
  530. if ( this._beforeNodes !== null ) {
  531. const currentBeforeNodes = this._beforeNodes;
  532. this._beforeNodes = null;
  533. for ( const beforeNode of currentBeforeNodes ) {
  534. beforeNode.build( builder, output );
  535. }
  536. this._beforeNodes = currentBeforeNodes;
  537. }
  538. //
  539. const nodeData = builder.getDataFromNode( this );
  540. nodeData.buildStages = nodeData.buildStages || {};
  541. nodeData.buildStages[ builder.buildStage ] = true;
  542. const parentBuildStage = _parentBuildStage[ builder.buildStage ];
  543. if ( parentBuildStage && nodeData.buildStages[ parentBuildStage ] !== true ) {
  544. // force parent build stage (setup or analyze)
  545. const previousBuildStage = builder.getBuildStage();
  546. builder.setBuildStage( parentBuildStage );
  547. this.build( builder );
  548. builder.setBuildStage( previousBuildStage );
  549. }
  550. //
  551. builder.addNode( this );
  552. builder.addChain( this );
  553. /* Build stages expected results:
  554. - "setup" -> Node
  555. - "analyze" -> null
  556. - "generate" -> String
  557. */
  558. let result = null;
  559. const buildStage = builder.getBuildStage();
  560. if ( buildStage === 'setup' ) {
  561. this.updateReference( builder );
  562. const properties = builder.getNodeProperties( this );
  563. if ( properties.initialized !== true ) {
  564. //const stackNodesBeforeSetup = builder.stack.nodes.length;
  565. properties.initialized = true;
  566. properties.outputNode = this.setup( builder ) || properties.outputNode || null;
  567. /*if ( isNodeOutput && builder.stack.nodes.length !== stackNodesBeforeSetup ) {
  568. // !! no outputNode !!
  569. //outputNode = builder.stack;
  570. }*/
  571. for ( const childNode of Object.values( properties ) ) {
  572. if ( childNode && childNode.isNode === true ) {
  573. if ( childNode.parents === true ) {
  574. const childProperties = builder.getNodeProperties( childNode );
  575. childProperties.parents = childProperties.parents || [];
  576. childProperties.parents.push( this );
  577. }
  578. childNode.build( builder );
  579. }
  580. }
  581. }
  582. result = properties.outputNode;
  583. } else if ( buildStage === 'analyze' ) {
  584. this.analyze( builder, output );
  585. } else if ( buildStage === 'generate' ) {
  586. // If generate has just one argument, it means the output type is not required.
  587. // This means that the node does not handle output conversions internally,
  588. // so the value is stored in a cache and the builder handles the conversion
  589. // for all requested output types.
  590. const isGenerateOnce = this.generate.length < 2;
  591. if ( isGenerateOnce ) {
  592. const type = this.getNodeType( builder );
  593. const nodeData = builder.getDataFromNode( this );
  594. result = nodeData.snippet;
  595. if ( result === undefined ) {
  596. if ( nodeData.generated === undefined ) {
  597. nodeData.generated = true;
  598. result = this.generate( builder ) || '';
  599. nodeData.snippet = result;
  600. } else {
  601. warn( 'Node: Recursion detected.', this );
  602. result = '/* Recursion detected. */';
  603. }
  604. } else if ( nodeData.flowCodes !== undefined && builder.context.nodeBlock !== undefined ) {
  605. builder.addFlowCodeHierarchy( this, builder.context.nodeBlock );
  606. }
  607. result = builder.format( result, type, output );
  608. } else {
  609. result = this.generate( builder, output ) || '';
  610. }
  611. if ( result === '' && output !== null && output !== 'void' && output !== 'OutputType' ) {
  612. // if no snippet is generated, return a default value
  613. error( `TSL: Invalid generated code, expected a "${ output }".` );
  614. result = builder.generateConst( output );
  615. }
  616. }
  617. builder.removeChain( this );
  618. builder.addSequentialNode( this );
  619. return result;
  620. }
  621. /**
  622. * Returns the child nodes as a JSON object.
  623. *
  624. * @return {Generator<Object>} An iterable list of serialized child objects as JSON.
  625. */
  626. getSerializeChildren() {
  627. return this._getChildren();
  628. }
  629. /**
  630. * Serializes the node to JSON.
  631. *
  632. * @param {Object} json - The output JSON object.
  633. */
  634. serialize( json ) {
  635. const nodeChildren = this.getSerializeChildren();
  636. const inputNodes = {};
  637. for ( const { property, index, childNode } of nodeChildren ) {
  638. if ( index !== undefined ) {
  639. if ( inputNodes[ property ] === undefined ) {
  640. inputNodes[ property ] = Number.isInteger( index ) ? [] : {};
  641. }
  642. inputNodes[ property ][ index ] = childNode.toJSON( json.meta ).uuid;
  643. } else {
  644. inputNodes[ property ] = childNode.toJSON( json.meta ).uuid;
  645. }
  646. }
  647. if ( Object.keys( inputNodes ).length > 0 ) {
  648. json.inputNodes = inputNodes;
  649. }
  650. }
  651. /**
  652. * Deserializes the node from the given JSON.
  653. *
  654. * @param {Object} json - The JSON object.
  655. */
  656. deserialize( json ) {
  657. if ( json.inputNodes !== undefined ) {
  658. const nodes = json.meta.nodes;
  659. for ( const property in json.inputNodes ) {
  660. if ( Array.isArray( json.inputNodes[ property ] ) ) {
  661. const inputArray = [];
  662. for ( const uuid of json.inputNodes[ property ] ) {
  663. inputArray.push( nodes[ uuid ] );
  664. }
  665. this[ property ] = inputArray;
  666. } else if ( typeof json.inputNodes[ property ] === 'object' ) {
  667. const inputObject = {};
  668. for ( const subProperty in json.inputNodes[ property ] ) {
  669. const uuid = json.inputNodes[ property ][ subProperty ];
  670. inputObject[ subProperty ] = nodes[ uuid ];
  671. }
  672. this[ property ] = inputObject;
  673. } else {
  674. const uuid = json.inputNodes[ property ];
  675. this[ property ] = nodes[ uuid ];
  676. }
  677. }
  678. }
  679. }
  680. /**
  681. * Serializes the node into the three.js JSON Object/Scene format.
  682. *
  683. * @param {?Object} meta - An optional JSON object that already holds serialized data from other scene objects.
  684. * @return {Object} The serialized node.
  685. */
  686. toJSON( meta ) {
  687. const { uuid, type } = this;
  688. const isRoot = ( meta === undefined || typeof meta === 'string' );
  689. if ( isRoot ) {
  690. meta = {
  691. textures: {},
  692. images: {},
  693. nodes: {}
  694. };
  695. }
  696. // serialize
  697. let data = meta.nodes[ uuid ];
  698. if ( data === undefined ) {
  699. data = {
  700. uuid,
  701. type,
  702. meta,
  703. metadata: {
  704. version: 4.7,
  705. type: 'Node',
  706. generator: 'Node.toJSON'
  707. }
  708. };
  709. if ( isRoot !== true ) meta.nodes[ data.uuid ] = data;
  710. this.serialize( data );
  711. delete data.meta;
  712. }
  713. // TODO: Copied from Object3D.toJSON
  714. function extractFromCache( cache ) {
  715. const values = [];
  716. for ( const key in cache ) {
  717. const data = cache[ key ];
  718. delete data.metadata;
  719. values.push( data );
  720. }
  721. return values;
  722. }
  723. if ( isRoot ) {
  724. const textures = extractFromCache( meta.textures );
  725. const images = extractFromCache( meta.images );
  726. const nodes = extractFromCache( meta.nodes );
  727. if ( textures.length > 0 ) data.textures = textures;
  728. if ( images.length > 0 ) data.images = images;
  729. if ( nodes.length > 0 ) data.nodes = nodes;
  730. }
  731. return data;
  732. }
  733. }
  734. export default Node;
粤ICP备19079148号