Node.js 21 KB

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