123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- import { EventDispatcher } from 'three';
- import { NodeUpdateType } from './constants.js';
- import { getNodeChildren, getCacheKey } from './NodeUtils.js';
- import { MathUtils } from 'three';
- const NodeClasses = new Map();
- let _nodeId = 0;
- class Node extends EventDispatcher {
- constructor( nodeType = null ) {
- super();
- this.nodeType = nodeType;
- this.updateType = NodeUpdateType.NONE;
- this.updateBeforeType = NodeUpdateType.NONE;
- this.updateAfterType = NodeUpdateType.NONE;
- this.uuid = MathUtils.generateUUID();
- this.version = 0;
- this._cacheKey = null;
- this._cacheKeyVersion = 0;
- this.global = false;
- this.isNode = true;
- Object.defineProperty( this, 'id', { value: _nodeId ++ } );
- }
- set needsUpdate( value ) {
- if ( value === true ) {
- this.version ++;
- }
- }
- get type() {
- return this.constructor.type;
- }
- onUpdate( callback, updateType ) {
- this.updateType = updateType;
- this.update = callback.bind( this.getSelf() );
- return this;
- }
- onFrameUpdate( callback ) {
- return this.onUpdate( callback, NodeUpdateType.FRAME );
- }
- onRenderUpdate( callback ) {
- return this.onUpdate( callback, NodeUpdateType.RENDER );
- }
- onObjectUpdate( callback ) {
- return this.onUpdate( callback, NodeUpdateType.OBJECT );
- }
- onReference( callback ) {
- this.updateReference = callback.bind( this.getSelf() );
- return this;
- }
- getSelf() {
- // Returns non-node object.
- return this.self || this;
- }
- updateReference( /*state*/ ) {
- return this;
- }
- isGlobal( /*builder*/ ) {
- return this.global;
- }
- * getChildren() {
- for ( const { childNode } of getNodeChildren( this ) ) {
- yield childNode;
- }
- }
- dispose() {
- this.dispatchEvent( { type: 'dispose' } );
- }
- traverse( callback ) {
- callback( this );
- for ( const childNode of this.getChildren() ) {
- childNode.traverse( callback );
- }
- }
- getCacheKey( force = false ) {
- force = force || this.version !== this._cacheKeyVersion;
- if ( force === true || this._cacheKey === null ) {
- this._cacheKey = getCacheKey( this, force );
- this._cacheKeyVersion = this.version;
- }
- return this._cacheKey;
- }
- getHash( /*builder*/ ) {
- return this.uuid;
- }
- getUpdateType() {
- return this.updateType;
- }
- getUpdateBeforeType() {
- return this.updateBeforeType;
- }
- getUpdateAfterType() {
- return this.updateAfterType;
- }
- getElementType( builder ) {
- const type = this.getNodeType( builder );
- const elementType = builder.getElementType( type );
- return elementType;
- }
- getNodeType( builder ) {
- const nodeProperties = builder.getNodeProperties( this );
- if ( nodeProperties.outputNode ) {
- return nodeProperties.outputNode.getNodeType( builder );
- }
- return this.nodeType;
- }
- getShared( builder ) {
- const hash = this.getHash( builder );
- const nodeFromHash = builder.getNodeFromHash( hash );
- return nodeFromHash || this;
- }
- setup( builder ) {
- const nodeProperties = builder.getNodeProperties( this );
- let index = 0;
- for ( const childNode of this.getChildren() ) {
- nodeProperties[ 'node' + index ++ ] = childNode;
- }
- // return a outputNode if exists
- return null;
- }
- construct( builder ) { // @deprecated, r157
- console.warn( 'THREE.Node: construct() is deprecated. Use setup() instead.' );
- return this.setup( builder );
- }
- increaseUsage( builder ) {
- const nodeData = builder.getDataFromNode( this );
- nodeData.usageCount = nodeData.usageCount === undefined ? 1 : nodeData.usageCount + 1;
- return nodeData.usageCount;
- }
- analyze( builder ) {
- const usageCount = this.increaseUsage( builder );
- if ( usageCount === 1 ) {
- // node flow children
- const nodeProperties = builder.getNodeProperties( this );
- for ( const childNode of Object.values( nodeProperties ) ) {
- if ( childNode && childNode.isNode === true ) {
- childNode.build( builder );
- }
- }
- }
- }
- generate( builder, output ) {
- const { outputNode } = builder.getNodeProperties( this );
- if ( outputNode && outputNode.isNode === true ) {
- return outputNode.build( builder, output );
- }
- }
- updateBefore( /*frame*/ ) {
- console.warn( 'Abstract function.' );
- }
- updateAfter( /*frame*/ ) {
- console.warn( 'Abstract function.' );
- }
- update( /*frame*/ ) {
- console.warn( 'Abstract function.' );
- }
- build( builder, output = null ) {
- const refNode = this.getShared( builder );
- if ( this !== refNode ) {
- return refNode.build( builder, output );
- }
- builder.addNode( this );
- builder.addChain( this );
- /* Build stages expected results:
- - "setup" -> Node
- - "analyze" -> null
- - "generate" -> String
- */
- let result = null;
- const buildStage = builder.getBuildStage();
- if ( buildStage === 'setup' ) {
- this.updateReference( builder );
- const properties = builder.getNodeProperties( this );
- if ( properties.initialized !== true ) {
- const stackNodesBeforeSetup = builder.stack.nodes.length;
- properties.initialized = true;
- properties.outputNode = this.setup( builder );
- if ( properties.outputNode !== null && builder.stack.nodes.length !== stackNodesBeforeSetup ) {
- properties.outputNode = builder.stack;
- }
- for ( const childNode of Object.values( properties ) ) {
- if ( childNode && childNode.isNode === true ) {
- childNode.build( builder );
- }
- }
- }
- } else if ( buildStage === 'analyze' ) {
- this.analyze( builder );
- } else if ( buildStage === 'generate' ) {
- const isGenerateOnce = this.generate.length === 1;
- if ( isGenerateOnce ) {
- const type = this.getNodeType( builder );
- const nodeData = builder.getDataFromNode( this );
- result = nodeData.snippet;
- if ( result === undefined ) {
- result = this.generate( builder ) || '';
- nodeData.snippet = result;
- }
- result = builder.format( result, type, output );
- } else {
- result = this.generate( builder, output ) || '';
- }
- }
- builder.removeChain( this );
- return result;
- }
- getSerializeChildren() {
- return getNodeChildren( this );
- }
- serialize( json ) {
- const nodeChildren = this.getSerializeChildren();
- const inputNodes = {};
- for ( const { property, index, childNode } of nodeChildren ) {
- if ( index !== undefined ) {
- if ( inputNodes[ property ] === undefined ) {
- inputNodes[ property ] = Number.isInteger( index ) ? [] : {};
- }
- inputNodes[ property ][ index ] = childNode.toJSON( json.meta ).uuid;
- } else {
- inputNodes[ property ] = childNode.toJSON( json.meta ).uuid;
- }
- }
- if ( Object.keys( inputNodes ).length > 0 ) {
- json.inputNodes = inputNodes;
- }
- }
- deserialize( json ) {
- if ( json.inputNodes !== undefined ) {
- const nodes = json.meta.nodes;
- for ( const property in json.inputNodes ) {
- if ( Array.isArray( json.inputNodes[ property ] ) ) {
- const inputArray = [];
- for ( const uuid of json.inputNodes[ property ] ) {
- inputArray.push( nodes[ uuid ] );
- }
- this[ property ] = inputArray;
- } else if ( typeof json.inputNodes[ property ] === 'object' ) {
- const inputObject = {};
- for ( const subProperty in json.inputNodes[ property ] ) {
- const uuid = json.inputNodes[ property ][ subProperty ];
- inputObject[ subProperty ] = nodes[ uuid ];
- }
- this[ property ] = inputObject;
- } else {
- const uuid = json.inputNodes[ property ];
- this[ property ] = nodes[ uuid ];
- }
- }
- }
- }
- toJSON( meta ) {
- const { uuid, type } = this;
- const isRoot = ( meta === undefined || typeof meta === 'string' );
- if ( isRoot ) {
- meta = {
- textures: {},
- images: {},
- nodes: {}
- };
- }
- // serialize
- let data = meta.nodes[ uuid ];
- if ( data === undefined ) {
- data = {
- uuid,
- type,
- meta,
- metadata: {
- version: 4.6,
- type: 'Node',
- generator: 'Node.toJSON'
- }
- };
- if ( isRoot !== true ) meta.nodes[ data.uuid ] = data;
- this.serialize( data );
- delete data.meta;
- }
- // TODO: Copied from Object3D.toJSON
- function extractFromCache( cache ) {
- const values = [];
- for ( const key in cache ) {
- const data = cache[ key ];
- delete data.metadata;
- values.push( data );
- }
- return values;
- }
- if ( isRoot ) {
- const textures = extractFromCache( meta.textures );
- const images = extractFromCache( meta.images );
- const nodes = extractFromCache( meta.nodes );
- if ( textures.length > 0 ) data.textures = textures;
- if ( images.length > 0 ) data.images = images;
- if ( nodes.length > 0 ) data.nodes = nodes;
- }
- return data;
- }
- }
- export default Node;
- export function addNodeClass( type, nodeClass ) {
- if ( typeof nodeClass !== 'function' || ! type ) throw new Error( `Node class ${ type } is not a class` );
- if ( NodeClasses.has( type ) ) {
- console.warn( `Redefinition of node class ${ type }` );
- return;
- }
- NodeClasses.set( type, nodeClass );
- nodeClass.type = type;
- }
- export function createNodeFromType( type ) {
- const Class = NodeClasses.get( type );
- if ( Class !== undefined ) {
- return new Class();
- }
- }
|