Browse Source

NodeEditor: Support to file, textures and updates - PR 1/2 (#23376)

* update flow.ui lib

* CheckerEditor: cleanup

* TextureNode: rename to .uv to uvNode and .bias to .biasNode

* NodeEditor: FileEditor and TextureEditor support

* cleanup
sunag 3 years ago
parent
commit
378a2ba501

File diff suppressed because it is too large
+ 0 - 0
examples/jsm/libs/flow.module.js


+ 48 - 7
examples/jsm/node-editor/NodeEditor.js

@@ -16,6 +16,7 @@ import { Vector3Editor } from './inputs/Vector3Editor.js';
 import { Vector4Editor } from './inputs/Vector4Editor.js';
 import { SliderEditor } from './inputs/SliderEditor.js';
 import { ColorEditor } from './inputs/ColorEditor.js';
+import { TextureEditor } from './inputs/TextureEditor.js';
 import { BlendEditor } from './display/BlendEditor.js';
 import { UVEditor } from './accessors/UVEditor.js';
 import { PositionEditor } from './accessors/PositionEditor.js';
@@ -27,6 +28,7 @@ import { JoinEditor } from './utils/JoinEditor.js';
 import { CheckerEditor } from './procedural/CheckerEditor.js';
 import { PointsEditor } from './scene/PointsEditor.js';
 import { MeshEditor } from './scene/MeshEditor.js';
+import { FileEditor } from './core/FileEditor.js';
 import { EventDispatcher } from 'three';
 
 Styles.icons.unlink = 'ti ti-unlink';
@@ -65,6 +67,11 @@ export const NodeList = [
 				name: 'Color',
 				icon: 'palette',
 				nodeClass: ColorEditor
+			},
+			{
+				name: 'Texture',
+				icon: 'photo',
+				nodeClass: TextureEditor
 			}
 		]
 	},
@@ -241,6 +248,7 @@ export const ClassLib = {
 	Vector4Editor,
 	SliderEditor,
 	ColorEditor,
+	TextureEditor,
 	BlendEditor,
 	UVEditor,
 	PositionEditor,
@@ -271,6 +279,7 @@ export class NodeEditor extends EventDispatcher {
 		this.nodesContext = null;
 		this.examplesContext = null;
 
+		this._initUpload();
 		this._initTips();
 		this._initMenu();
 		this._initSearch();
@@ -333,11 +342,13 @@ export class NodeEditor extends EventDispatcher {
 
 	loadJSON( json ) {
 
-		this.canvas.clear();
+		const canvas = this.canvas;
+
+		canvas.clear();
 
-		this.canvas.deserialize( json );
+		canvas.deserialize( json );
 
-		for ( const node of this.canvas.nodes ) {
+		for ( const node of canvas.nodes ) {
 
 			this.add( node );
 
@@ -347,6 +358,36 @@ export class NodeEditor extends EventDispatcher {
 
 	}
 
+	_initUpload() {
+
+		const canvas = this.canvas;
+
+		canvas.onDrop( () => {
+
+			for ( const item of canvas.droppedItems ) {
+
+				if ( /^image\//.test( item.type ) === true ) {
+
+					const { relativeClientX, relativeClientY } = canvas;
+
+					const file = item.getAsFile();
+					const fileEditor = new FileEditor( file );
+
+					fileEditor.setPosition(
+						relativeClientX - ( fileEditor.getWidth() / 2 ),
+						relativeClientY - 20
+					);
+
+					this.add( fileEditor );
+
+				}
+
+			}
+
+		} );
+
+	}
+
 	_initTips() {
 
 		this.tips = new Tips();
@@ -622,8 +663,8 @@ export class NodeEditor extends EventDispatcher {
 			if ( isContext ) {
 
 				node.setPosition(
-					contextPosition.x,
-					contextPosition.y
+					Math.round( contextPosition.x ),
+					Math.round( contextPosition.y )
 				);
 
 			} else {
@@ -648,8 +689,8 @@ export class NodeEditor extends EventDispatcher {
 
 			const { relativeClientX, relativeClientY } = this.canvas;
 
-			contextPosition.x = relativeClientX;
-			contextPosition.y = relativeClientY;
+			contextPosition.x = Math.round( relativeClientX );
+			contextPosition.y = Math.round( relativeClientY );
 
 		} );
 

+ 17 - 11
examples/jsm/node-editor/core/BaseNode.js

@@ -1,5 +1,17 @@
 import { ObjectNode } from '../../libs/flow.module.js';
 
+export const onNodeValidElement = ( inputElement, outputElement ) => {
+
+	const outputObject = outputElement.getObject();
+
+	if ( ! outputObject || ! outputObject.isNode ) {
+
+		return false;
+
+	}
+
+};
+
 export class BaseNode extends ObjectNode {
 
 	constructor( name, inputLength, value = null, width = 300 ) {
@@ -18,17 +30,7 @@ export class BaseNode extends ObjectNode {
 
 		this.value = value;
 
-		this.onValidElement = ( inputElement, outputElement ) => {
-
-			const outputObject = outputElement.getObject();
-
-			if ( ! outputObject || ! outputObject.isNode ) {
-
-				return false;
-
-			}
-
-		};
+		this.onValidElement = onNodeValidElement;
 
 	}
 
@@ -68,6 +70,10 @@ export class BaseNode extends ObjectNode {
 
 			return 'orange';
 
+		} else if ( value instanceof File ) {
+
+			return 'aqua';
+
 		}
 
 	}

+ 17 - 0
examples/jsm/node-editor/core/FileEditor.js

@@ -0,0 +1,17 @@
+import { StringInput, Element } from '../../libs/flow.module.js';
+import { BaseNode } from '../core/BaseNode.js';
+
+export class FileEditor extends BaseNode {
+
+	constructor( file ) {
+
+		super( 'File', 1, file, 250 );
+
+		this.file = file;
+		this.nameInput = new StringInput( file.name ).setReadOnly( true );
+
+		this.add( new Element().add( this.nameInput ) );
+
+	}
+
+}

+ 145 - 0
examples/jsm/node-editor/inputs/TextureEditor.js

@@ -0,0 +1,145 @@
+import { LabelElement, ToggleInput, SelectInput } from '../../libs/flow.module.js';
+import { BaseNode, onNodeValidElement } from '../core/BaseNode.js';
+import { TextureNode, UVNode } from '../../renderers/nodes/Nodes.js';
+import { Texture, TextureLoader, RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping } from 'three';
+
+const fileTexture = new WeakMap();
+const textureLoader = new TextureLoader();
+const defaultTexture = new Texture();
+const defaultUV = new UVNode();
+
+const getTextureFromFile = ( file ) => {
+
+	let texture = fileTexture.get( file );
+
+	if ( texture === undefined ) {
+
+		texture = textureLoader.load( URL.createObjectURL( file ) );
+
+		fileTexture.set( file, texture );
+
+	}
+
+	return texture;
+
+};
+
+export class TextureEditor extends BaseNode {
+
+	constructor() {
+
+		const node = new TextureNode( defaultTexture );
+
+		super( 'Texture', 4, node, 250 );
+
+		this.texture = null;
+
+		this._initFile();
+		this._initParams();
+
+		this.onValidElement = () => {};
+
+	}
+
+	_initFile() {
+
+		const fileElement = new LabelElement( 'File' ).setInputColor( 'aqua' ).setInput( 1 );
+
+		fileElement.onValid( ( source, target, stage ) => {
+
+			const object = target.getObject();
+
+			if ( object && ( object instanceof File ) === false ) {
+
+				if ( stage === 'dragged' ) {
+
+					const name = target.node.getName();
+
+					this.editor.tips.error( `"${name}" is not a File.` );
+
+				}
+
+				return false;
+
+			}
+
+		} ).onConnect( () => {
+
+			const file = fileElement.getLinkedObject();
+			const node = this.value;
+
+			this.texture = file ? getTextureFromFile( file ) : null;
+
+			node.value = this.texture || defaultTexture;
+
+			this.update();
+
+		} );
+
+		this.add( fileElement );
+
+	}
+
+	_initParams() {
+
+		const uvField = new LabelElement( 'UV' ).setInput( 2 );
+
+		uvField.onValid( onNodeValidElement ).onConnect( () => {
+
+			const node = this.value;
+
+			node.uvNode = uvField.getLinkedObject() || defaultUV;
+
+		} );
+
+		this.wrapSInput = new SelectInput( [
+			{ name: 'Repeat Wrapping', value: RepeatWrapping },
+			{ name: 'Clamp To Edge Wrapping', value: ClampToEdgeWrapping },
+			{ name: 'Mirrored Repeat Wrapping', value: MirroredRepeatWrapping }
+		], RepeatWrapping ).onChange( () => {
+
+			this.update();
+
+		} );
+
+		this.wrapTInput = new SelectInput( [
+			{ name: 'Repeat Wrapping', value: RepeatWrapping },
+			{ name: 'Clamp To Edge Wrapping', value: ClampToEdgeWrapping },
+			{ name: 'Mirrored Repeat Wrapping', value: MirroredRepeatWrapping }
+		], RepeatWrapping ).onChange( () => {
+
+			this.update();
+
+		} );
+
+		this.flipYInput = new ToggleInput( false ).onClick( () => {
+
+			this.update();
+
+		} );
+
+		this.add( uvField )
+			.add( new LabelElement( 'Wrap S' ).add( this.wrapSInput ) )
+			.add( new LabelElement( 'Wrap T' ).add( this.wrapTInput ) )
+			.add( new LabelElement( 'Flip Y' ).add( this.flipYInput ) );
+
+	}
+
+	update() {
+
+		const texture = this.texture;
+
+		if ( texture ) {
+
+			texture.wrapS = Number( this.wrapSInput.getValue() );
+			texture.wrapT = Number( this.wrapTInput.getValue() );
+			texture.flipY = this.flipYInput.getValue();
+			texture.needsUpdate = true;
+
+			this.invalidate();
+
+		}
+
+	}
+
+}

+ 3 - 3
examples/jsm/node-editor/procedural/CheckerEditor.js

@@ -2,13 +2,13 @@ import { LabelElement } from '../../libs/flow.module.js';
 import { BaseNode } from '../core/BaseNode.js';
 import { CheckerNode, UVNode } from '../../renderers/nodes/Nodes.js';
 
-const DEFAULT_UV = new UVNode();
+const defaultUV = new UVNode();
 
 export class CheckerEditor extends BaseNode {
 
 	constructor() {
 
-		const node = new CheckerNode( DEFAULT_UV );
+		const node = new CheckerNode( defaultUV );
 
 		super( 'Checker', 1, node, 200 );
 
@@ -16,7 +16,7 @@ export class CheckerEditor extends BaseNode {
 
 		field.onConnect( () => {
 
-			node.uvNode = field.getLinkedObject() || DEFAULT_UV;
+			node.uvNode = field.getLinkedObject() || defaultUV;
 
 		} );
 

+ 7 - 7
examples/jsm/renderers/nodes/inputs/TextureNode.js

@@ -3,13 +3,13 @@ import UVNode from '../accessors/UVNode.js';
 
 class TextureNode extends InputNode {
 
-	constructor( value = null, uv = new UVNode(), bias = null ) {
+	constructor( value = null, uvNode = new UVNode(), biasNode = null ) {
 
 		super( 'texture' );
 
 		this.value = value;
-		this.uv = uv;
-		this.bias = bias;
+		this.uvNode = uvNode;
+		this.biasNode = biasNode;
 
 	}
 
@@ -43,14 +43,14 @@ class TextureNode extends InputNode {
 
 			if ( snippet === undefined ) {
 
-				const uvSnippet = this.uv.build( builder, 'vec2' );
-				const bias = this.bias;
+				const uvSnippet = this.uvNode.build( builder, 'vec2' );
+				const biasNode = this.biasNode;
 
 				let biasSnippet = null;
 
-				if ( bias !== null ) {
+				if ( biasNode !== null ) {
 
-					biasSnippet = bias.build( builder, 'float' );
+					biasSnippet = biasNode.build( builder, 'float' );
 
 				}
 

Some files were not shown because too many files changed in this diff