Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NodeEditor: Adds support for exporting Node Chaining, Materials and Objects3D (individually) #25553

Merged
merged 8 commits into from
Feb 27, 2023
26 changes: 14 additions & 12 deletions examples/jsm/node-editor/NodeEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { PointsEditor } from './scene/PointsEditor.js';
import { MeshEditor } from './scene/MeshEditor.js';
import { FileEditor } from './core/FileEditor.js';
import { FileURLEditor } from './core/FileURLEditor.js';
import { exportJSON } from './NodeEditorUtils.js';
import { EventDispatcher } from 'three';

Styles.icons.unlink = 'ti ti-unlink';
Expand Down Expand Up @@ -540,14 +541,7 @@ export class NodeEditor extends EventDispatcher {

saveButton.onClick( () => {

const json = JSON.stringify( this.canvas.toJSON() );

const a = document.createElement( 'a' );
const file = new Blob( [ json ], { type: 'text/plain' } );

a.href = URL.createObjectURL( file );
a.download = 'node_editor.json';
a.click();
exportJSON( this.canvas.toJSON(), 'node_editor' );

} );

Expand Down Expand Up @@ -863,10 +857,14 @@ export class NodeEditor extends EventDispatcher {

} else if ( key === 'Enter' ) {

nodeButtonsVisible[ nodeButtonsIndex ].dom.click();
if ( nodeButtonsVisible[ nodeButtonsIndex ] !== undefined ) {

e.preventDefault();
e.stopImmediatePropagation();
nodeButtonsVisible[ nodeButtonsIndex ].dom.click();

e.preventDefault();
e.stopImmediatePropagation();

}

}

Expand Down Expand Up @@ -911,7 +909,11 @@ export class NodeEditor extends EventDispatcher {

}

nodeButtonsVisible[ nodeButtonsIndex ].setSelected( true );
if ( nodeButtonsVisible[ nodeButtonsIndex ] !== undefined ) {

nodeButtonsVisible[ nodeButtonsIndex ].setSelected( true );

}

} );

Expand Down
12 changes: 12 additions & 0 deletions examples/jsm/node-editor/NodeEditorUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const exportJSON = ( object, name ) => {

const json = JSON.stringify( object );

const a = document.createElement( 'a' );
const file = new Blob( [ json ], { type: 'text/plain' } );

a.href = URL.createObjectURL( file );
a.download = name + '.json';
a.click();

};
34 changes: 27 additions & 7 deletions examples/jsm/node-editor/core/BaseNode.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Node, ButtonInput, TitleElement, ContextMenu } from '../../libs/flow.module.js';
import { exportJSON } from '../NodeEditorUtils.js';

export const onNodeValidElement = ( inputElement, outputElement ) => {

Expand Down Expand Up @@ -31,24 +32,43 @@ export class BaseNode extends Node {
.setSerializable( false )
.setOutput( outputLength );

const closeButton = new ButtonInput().onClick( () => {
const contextButton = new ButtonInput().onClick( () => {

context.open();

} ).setIcon( 'ti ti-dots' );

const context = new ContextMenu( this.dom );
context.add( new ButtonInput( 'Remove' ).setIcon( 'ti ti-trash' ).onClick( () => {
const onAddButtons = () => {

context.removeEventListener( 'show', onAddButtons );

if ( this.value && typeof this.value.toJSON === 'function' ) {

this.context.add( new ButtonInput( 'Export' ).setIcon( 'ti ti-download' ).onClick( () => {

exportJSON( this.value.toJSON(), this.constructor.name );

} ) );

}

this.dispose();
context.add( new ButtonInput( 'Remove' ).setIcon( 'ti ti-trash' ).onClick( () => {

} ) );
this.dispose();

} ) );

};

const context = new ContextMenu( this.dom );
context.addEventListener( 'show', onAddButtons );

this.title = title;
this.closeButton = closeButton;

this.contextButton = contextButton;
this.context = context;

title.addButton( closeButton );
title.addButton( contextButton );

this.add( title );

Expand Down
10 changes: 3 additions & 7 deletions examples/jsm/node-editor/materials/BasicMaterialEditor.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { ColorInput, SliderInput, LabelElement } from '../../libs/flow.module.js';
import { BaseNode } from '../core/BaseNode.js';
import { MaterialEditor } from './MaterialEditor.js';
import { MeshBasicNodeMaterial } from 'three/nodes';

export class BasicMaterialEditor extends BaseNode {
export class BasicMaterialEditor extends MaterialEditor {

constructor() {

const material = new MeshBasicNodeMaterial();

super( 'Basic Material', 1, material );

this.setWidth( 300 );
super( 'Basic Material', material );

const color = new LabelElement( 'color' ).setInput( 3 );
const opacity = new LabelElement( 'opacity' ).setInput( 1 );
Expand Down Expand Up @@ -42,8 +40,6 @@ export class BasicMaterialEditor extends BaseNode {
this.opacity = opacity;
this.position = position;

this.material = material;

this.update();

}
Expand Down
17 changes: 17 additions & 0 deletions examples/jsm/node-editor/materials/MaterialEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { BaseNode } from '../core/BaseNode.js';

export class MaterialEditor extends BaseNode {

constructor( name, material, width = 300 ) {

super( name, 1, material, width );

}

get material() {

return this.value;

}

}
10 changes: 3 additions & 7 deletions examples/jsm/node-editor/materials/PointsMaterialEditor.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { ColorInput, ToggleInput, SliderInput, LabelElement } from '../../libs/flow.module.js';
import { BaseNode } from '../core/BaseNode.js';
import { MaterialEditor } from './MaterialEditor.js';
import { PointsNodeMaterial } from 'three/nodes';
import * as THREE from 'three';

export class PointsMaterialEditor extends BaseNode {
export class PointsMaterialEditor extends MaterialEditor {

constructor() {

const material = new PointsNodeMaterial();

super( 'Points Material', 1, material );

this.setWidth( 300 );
super( 'Points Material', material );

const color = new LabelElement( 'color' ).setInput( 3 );
const opacity = new LabelElement( 'opacity' ).setInput( 1 );
Expand Down Expand Up @@ -57,8 +55,6 @@ export class PointsMaterialEditor extends BaseNode {
this.position = position;
this.sizeAttenuation = sizeAttenuation;

this.material = material;

this.update();

}
Expand Down
10 changes: 3 additions & 7 deletions examples/jsm/node-editor/materials/StandardMaterialEditor.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { ColorInput, SliderInput, LabelElement } from '../../libs/flow.module.js';
import { BaseNode } from '../core/BaseNode.js';
import { MaterialEditor } from './MaterialEditor.js';
import { MeshStandardNodeMaterial } from 'three/nodes';

export class StandardMaterialEditor extends BaseNode {
export class StandardMaterialEditor extends MaterialEditor {

constructor() {

const material = new MeshStandardNodeMaterial();

super( 'Standard Material', 1, material );

this.setWidth( 300 );
super( 'Standard Material', material );

const color = new LabelElement( 'color' ).setInput( 3 );
const opacity = new LabelElement( 'opacity' ).setInput( 1 );
Expand Down Expand Up @@ -70,8 +68,6 @@ export class StandardMaterialEditor extends BaseNode {
this.normal = normal;
this.position = position;

this.material = material;

this.update();

}
Expand Down
38 changes: 34 additions & 4 deletions examples/jsm/nodes/core/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,21 @@ class Node {

for ( const property of nodeKeys ) {

inputNodes[ property ] = this[ property ].toJSON( json.meta ).uuid;
if ( Array.isArray( this[ property ] ) ) {

inputNodes[ property ] = [];

for ( const node of this[ property ] ) {

inputNodes[ property ].push( node.toJSON( json.meta ).uuid );

}

} else {

inputNodes[ property ] = this[ property ].toJSON( json.meta ).uuid;

}

}

Expand All @@ -291,9 +305,25 @@ class Node {

for ( const property in json.inputNodes ) {

const uuid = json.inputNodes[ property ];
if ( Array.isArray( json.inputNodes[ property ] ) ) {

const inputArray = [];

this[ property ] = nodes[ uuid ];
for ( const uuid of json.inputNodes[ property ] ) {

inputArray.push( nodes[ uuid ] );

}

this[ property ] = inputArray;

} else {

const uuid = json.inputNodes[ property ];

this[ property ] = nodes[ uuid ];

}

}

Expand Down Expand Up @@ -333,7 +363,7 @@ class Node {
}
};

meta.nodes[ data.uuid ] = data;
if ( isRoot !== true ) meta.nodes[ data.uuid ] = data;

this.serialize( data );

Expand Down
28 changes: 26 additions & 2 deletions examples/jsm/nodes/core/NodeUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,23 @@ export const getCacheKey = ( object ) => {

for ( const property of getNodesKeys( object ) ) {

cacheKey += `${ property }:${ object[ property ].getCacheKey() },`;
const node = object[ property ];

// @TODO: Think about implement NodeArray and NodeObject.

if ( Array.isArray( node ) ) {

for ( const subNode of node ) {

cacheKey += `${ property }:${ subNode.getCacheKey() },`;

}

} else {

cacheKey += `${ property }:${ node.getCacheKey() },`;

}

}

Expand All @@ -30,7 +46,15 @@ export const getNodesKeys = ( object ) => {

const value = object[ name ];

if ( value && value.isNode === true ) {
if ( Array.isArray( value ) ) {

if ( value[ 0 ] && value[ 0 ].isNode === true ) {

props.push( name );

}

} else if ( value && value.isNode === true ) {

props.push( name );

Expand Down
2 changes: 1 addition & 1 deletion examples/jsm/nodes/loaders/NodeLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class NodeLoader extends Loader {
const node = nodeObject( createNodeFromType( type ) );
node.uuid = json.uuid;

const nodes = this.parseNodes( json.inputNodes );
const nodes = this.parseNodes( json.nodes );
const meta = { nodes, textures: this.textures };

json.meta = meta;
Expand Down