浏览代码

NodeEditor: add Basic and Points Material (#23339)

sunag 3 年之前
父节点
当前提交
add8fad8cf

+ 39 - 5
examples/jsm/node-editor/NodeEditor.js

@@ -1,5 +1,7 @@
 import { Styles, Canvas, CircleMenu, ButtonInput, ContextMenu, Tips, Search, Loader } from '../libs/flow.module.js';
 import { Styles, Canvas, CircleMenu, ButtonInput, ContextMenu, Tips, Search, Loader } from '../libs/flow.module.js';
+import { BasicMaterialEditor } from './materials/BasicMaterialEditor.js';
 import { StandardMaterialEditor } from './materials/StandardMaterialEditor.js';
 import { StandardMaterialEditor } from './materials/StandardMaterialEditor.js';
+import { PointsMaterialEditor } from './materials/PointsMaterialEditor.js';
 import { OperatorEditor } from './math/OperatorEditor.js';
 import { OperatorEditor } from './math/OperatorEditor.js';
 import { NormalizeEditor } from './math/NormalizeEditor.js';
 import { NormalizeEditor } from './math/NormalizeEditor.js';
 import { InvertEditor } from './math/InvertEditor.js';
 import { InvertEditor } from './math/InvertEditor.js';
@@ -23,6 +25,7 @@ import { OscillatorEditor } from './utils/OscillatorEditor.js';
 import { SplitEditor } from './utils/SplitEditor.js';
 import { SplitEditor } from './utils/SplitEditor.js';
 import { JoinEditor } from './utils/JoinEditor.js';
 import { JoinEditor } from './utils/JoinEditor.js';
 import { CheckerEditor } from './procedural/CheckerEditor.js';
 import { CheckerEditor } from './procedural/CheckerEditor.js';
+import { PointsEditor } from './scene/PointsEditor.js';
 import { MeshEditor } from './scene/MeshEditor.js';
 import { MeshEditor } from './scene/MeshEditor.js';
 import { EventDispatcher } from 'three';
 import { EventDispatcher } from 'three';
 
 
@@ -199,17 +202,30 @@ export const NodeList = [
 		name: 'Material',
 		name: 'Material',
 		icon: 'circles',
 		icon: 'circles',
 		children: [
 		children: [
+			{
+				name: 'Basic Material',
+				icon: 'circle',
+				nodeClass: BasicMaterialEditor
+			},
 			{
 			{
 				name: 'Standard Material',
 				name: 'Standard Material',
 				icon: 'circle',
 				icon: 'circle',
 				nodeClass: StandardMaterialEditor
 				nodeClass: StandardMaterialEditor
+			},
+			{
+				name: 'Points Material',
+				icon: 'circle-dotted',
+				nodeClass: PointsMaterialEditor
 			}
 			}
 		]
 		]
 	}
 	}
 ];
 ];
 
 
 export const ClassLib = {
 export const ClassLib = {
+	BasicMaterialEditor,
 	StandardMaterialEditor,
 	StandardMaterialEditor,
+	PointsMaterialEditor,
+	PointsEditor,
 	MeshEditor,
 	MeshEditor,
 	OperatorEditor,
 	OperatorEditor,
 	NormalizeEditor,
 	NormalizeEditor,
@@ -524,17 +540,35 @@ export class NodeEditor extends EventDispatcher {
 
 
 				object3d.traverse( ( obj3d ) => {
 				object3d.traverse( ( obj3d ) => {
 
 
-					if ( obj3d.isMesh === true ) {
+					if ( obj3d.isMesh === true || obj3d.isPoints === true ) {
+
+						let prefix = null;
+						let icon = null;
+						let editorClass = null;
+
+						if ( obj3d.isMesh === true ) {
+
+							prefix = 'Mesh';
+							icon = 'ti ti-3d-cube-sphere';
+							editorClass = MeshEditor;
+
+						} else if ( obj3d.isPoints === true ) {
+
+							prefix = 'Points';
+							icon = 'ti ti-border-none';
+							editorClass = PointsEditor;
+
+						}
 
 
-						const button = new ButtonInput( `Mesh - ${obj3d.name}` );
-						button.setIcon( 'ti ti-3d-cube-sphere' );
+						const button = new ButtonInput( `${prefix} - ${obj3d.name}` );
+						button.setIcon( icon );
 						button.addEventListener( 'complete', () => {
 						button.addEventListener( 'complete', () => {
 
 
 							for ( const node of this.canvas.nodes ) {
 							for ( const node of this.canvas.nodes ) {
 
 
 								if ( node.value === obj3d ) {
 								if ( node.value === obj3d ) {
 
 
-									// not duplicated node
+									// prevent duplicated node
 
 
 									this.canvas.select( node );
 									this.canvas.select( node );
 
 
@@ -544,7 +578,7 @@ export class NodeEditor extends EventDispatcher {
 
 
 							}
 							}
 
 
-							const node = new MeshEditor( obj3d );
+							const node = new editorClass( obj3d );
 
 
 							this.add( node );
 							this.add( node );
 
 

+ 87 - 0
examples/jsm/node-editor/materials/BasicMaterialEditor.js

@@ -0,0 +1,87 @@
+import { ColorInput, SliderInput, LabelElement } from '../../libs/flow.module.js';
+import { BaseNode } from '../core/BaseNode.js';
+import { MeshBasicNodeMaterial } from '../../renderers/nodes/Nodes.js';
+import * as THREE from 'three';
+
+export class BasicMaterialEditor extends BaseNode {
+
+	constructor() {
+
+		const material = new MeshBasicNodeMaterial();
+
+		super( 'Basic Material', 1, material );
+
+		this.setWidth( 300 );
+
+		const color = new LabelElement( 'color' ).setInput( 3 );
+		const opacity = new LabelElement( 'opacity' ).setInput( 1 );
+		const position = new LabelElement( 'position' ).setInput( 3 );
+
+		color.add( new ColorInput( material.color.getHex() ).onChange( ( input ) => {
+
+			material.color.setHex( input.getValue() );
+
+		} ) );
+
+		opacity.add( new SliderInput( material.opacity, 0, 1 ).onChange( ( input ) => {
+
+			material.opacity = input.getValue();
+
+			this.updateTransparent();
+
+		} ) );
+
+		color.onConnect( () => this.update(), true );
+		opacity.onConnect( () => this.update(), true );
+		position.onConnect( () => this.update(), true );
+
+		this.add( color )
+			.add( opacity )
+			.add( position );
+
+		this.color = color;
+		this.opacity = opacity;
+		this.position = position;
+
+		this.material = material;
+
+		this.update();
+
+	}
+
+	update() {
+
+		const { material, color, opacity, position } = this;
+
+		color.setEnabledInputs( ! color.getLinkedObject() );
+		opacity.setEnabledInputs( ! opacity.getLinkedObject() );
+
+		material.colorNode = color.getLinkedObject();
+		material.opacityNode = opacity.getLinkedObject() || null;
+
+		material.positionNode = position.getLinkedObject() || null;
+
+		material.dispose();
+
+		this.updateTransparent();
+
+		// TODO: Fix on NodeMaterial System
+		material.customProgramCacheKey = () => {
+
+			return THREE.MathUtils.generateUUID();
+
+		};
+
+	}
+
+	updateTransparent() {
+
+		const { material, opacity } = this;
+
+		material.transparent = opacity.getLinkedObject() || material.opacity < 1 ? true : false;
+
+		opacity.setIcon( material.transparent ? 'ti ti-layers-intersect' : 'ti ti-layers-subtract' );
+
+	}
+
+}

+ 97 - 0
examples/jsm/node-editor/materials/PointsMaterialEditor.js

@@ -0,0 +1,97 @@
+import { ColorInput, SliderInput, LabelElement } from '../../libs/flow.module.js';
+import { BaseNode } from '../core/BaseNode.js';
+import { PointsNodeMaterial } from '../../renderers/nodes/Nodes.js';
+import * as THREE from 'three';
+
+export class PointsMaterialEditor extends BaseNode {
+
+	constructor() {
+
+		const material = new PointsNodeMaterial( {
+			depthWrite: false,
+			transparent: true,
+			sizeAttenuation: true,
+			blending: THREE.AdditiveBlending
+		} );
+
+		super( 'Points Material', 1, material );
+
+		this.setWidth( 300 );
+
+		const color = new LabelElement( 'color' ).setInput( 3 );
+		const opacity = new LabelElement( 'opacity' ).setInput( 1 );
+		const size = new LabelElement( 'size' ).setInput( 1 );
+		const position = new LabelElement( 'position' ).setInput( 3 );
+
+		color.add( new ColorInput( material.color.getHex() ).onChange( ( input ) => {
+
+			material.color.setHex( input.getValue() );
+
+		} ) );
+
+		opacity.add( new SliderInput( material.opacity, 0, 1 ).onChange( ( input ) => {
+
+			material.opacity = input.getValue();
+
+			this.updateTransparent();
+
+		} ) );
+
+		color.onConnect( () => this.update(), true );
+		opacity.onConnect( () => this.update(), true );
+		size.onConnect(() => this.update(), true );
+		position.onConnect(() => this.update(), true );
+
+		this.add( color )
+			.add( opacity )
+			.add( size )
+			.add( position );
+
+		this.color = color;
+		this.opacity = opacity;
+		this.size = size;
+		this.position = position;
+
+		this.material = material;
+
+		this.update();
+
+	}
+
+	update() {
+
+		const { material, color, opacity, size, position } = this;
+
+		color.setEnabledInputs( ! color.getLinkedObject() );
+		opacity.setEnabledInputs( ! opacity.getLinkedObject() );
+
+		material.colorNode = color.getLinkedObject();
+		material.opacityNode = opacity.getLinkedObject() || null;
+
+		material.sizeNode = size.getLinkedObject() || null;
+		material.positionNode = position.getLinkedObject() || null;
+
+		material.dispose();
+
+		this.updateTransparent();
+
+		// TODO: Fix on NodeMaterial System
+		material.customProgramCacheKey = () => {
+
+			return THREE.MathUtils.generateUUID();
+
+		};
+
+	}
+
+	updateTransparent() {
+
+		const { material, opacity } = this;
+
+		material.transparent = opacity.getLinkedObject() || material.opacity < 1 ? true : false;
+
+		opacity.setIcon( material.transparent ? 'ti ti-layers-intersect' : 'ti ti-layers-subtract' );
+
+	}
+
+}

+ 12 - 121
examples/jsm/node-editor/scene/MeshEditor.js

@@ -1,8 +1,8 @@
-import { NumberInput, StringInput, LabelElement } from '../../libs/flow.module.js';
-import { BaseNode } from '../core/BaseNode.js';
-import { Mesh, MathUtils, Vector3 } from 'three';
+import { LabelElement } from '../../libs/flow.module.js';
+import { Object3DEditor } from './Object3DEditor.js';
+import { Mesh } from 'three';
 
 
-export class MeshEditor extends BaseNode {
+export class MeshEditor extends Object3DEditor {
 
 
 	constructor( mesh = null ) {
 	constructor( mesh = null ) {
 
 
@@ -12,50 +12,18 @@ export class MeshEditor extends BaseNode {
 
 
 		}
 		}
 
 
-		super( 'Mesh', 1, mesh );
+		super( mesh, 'Mesh' );
 
 
 		this.material = null;
 		this.material = null;
 
 
 		this.defaultMaterial = null;
 		this.defaultMaterial = null;
-		this.defaultPosition = new Vector3();
-		this.defaultRotation = new Vector3();
-		this.defaultScale = new Vector3( 100, 100, 100 );
 
 
-		this._initTags();
-		this._initTransform();
 		this._initMaterial();
 		this._initMaterial();
 
 
 		this.updateDefault();
 		this.updateDefault();
+		this.restoreDefault();
 		this.update();
 		this.update();
 
 
-		this.onValidElement = () => {};
-
-	}
-
-	setEditor( editor ) {
-
-		if ( this.editor ) {
-
-			this._restoreDefault();
-
-		}
-
-		super.setEditor( editor );
-
-		if ( editor ) {
-
-			const name = this.nameInput.getValue();
-			const mesh = editor.scene.getObjectByName( name );
-
-			this.value = mesh;
-
-			this.updateDefault();
-			this.update();
-
-		}
-
-		return this;
-
 	}
 	}
 
 
 	get mesh() {
 	get mesh() {
@@ -98,71 +66,14 @@ export class MeshEditor extends BaseNode {
 
 
 	}
 	}
 
 
-	_initTags() {
-
-		this.nameInput = new StringInput( this.mesh.name ).setReadOnly( true )
-			.onChange( () => this.mesh.name = this.nameInput.getValue() );
-
-		this.add( new LabelElement( 'Name' ).add( this.nameInput ) );
-
-	}
-
-	_initTransform() {
-
-		const update = () => this.update();
-
-		const posX = new NumberInput().setTagColor( 'red' ).onChange( update );
-		const posY = new NumberInput().setTagColor( 'green' ).onChange( update );
-		const posZ = new NumberInput().setTagColor( 'blue' ).onChange( update );
-
-		const rotationStep = 1;
-
-		const rotX = new NumberInput().setTagColor( 'red' ).setStep( rotationStep ).onChange( update );
-		const rotY = new NumberInput().setTagColor( 'green' ).setStep( rotationStep ).onChange( update );
-		const rotZ = new NumberInput().setTagColor( 'blue' ).setStep( rotationStep ).onChange( update );
-
-		const scaleX = new NumberInput( 100 ).setTagColor( 'red' ).setStep( rotationStep ).onChange( update );
-		const scaleY = new NumberInput( 100 ).setTagColor( 'green' ).setStep( rotationStep ).onChange( update );
-		const scaleZ = new NumberInput( 100 ).setTagColor( 'blue' ).setStep( rotationStep ).onChange( update );
-
-		this.add( new LabelElement( 'Position' ).add( posX ).add( posY ).add( posZ ) )
-			.add( new LabelElement( 'Rotation' ).add( rotX ).add( rotY ).add( rotZ ) )
-			.add( new LabelElement( 'Scale' ).add( scaleX ).add( scaleY ).add( scaleZ ) );
-
-		this.posX = posX;
-		this.posY = posY;
-		this.posZ = posZ;
-
-		this.rotX = rotX;
-		this.rotY = rotY;
-		this.rotZ = rotZ;
-
-		this.scaleX = scaleX;
-		this.scaleY = scaleY;
-		this.scaleZ = scaleZ;
-
-	}
-
 	update() {
 	update() {
 
 
+		super.update();
+
 		const mesh = this.mesh;
 		const mesh = this.mesh;
 
 
 		if ( mesh ) {
 		if ( mesh ) {
 
 
-			const { position, rotation, scale } = mesh;
-
-			position.x = this.posX.getValue();
-			position.y = this.posY.getValue();
-			position.z = this.posZ.getValue();
-
-			rotation.x = MathUtils.degToRad( this.rotX.getValue() );
-			rotation.y = MathUtils.degToRad( this.rotY.getValue() );
-			rotation.z = MathUtils.degToRad( this.rotZ.getValue() );
-
-			scale.x = this.scaleX.getValue() / 100;
-			scale.y = this.scaleY.getValue() / 100;
-			scale.z = this.scaleZ.getValue() / 100;
-
 			mesh.material = this.material || this.defaultMaterial;
 			mesh.material = this.material || this.defaultMaterial;
 
 
 		}
 		}
@@ -171,35 +82,15 @@ export class MeshEditor extends BaseNode {
 
 
 	updateDefault() {
 	updateDefault() {
 
 
-		const { material, position, rotation, scale } = this.mesh;
+		super.updateDefault();
 
 
-		this.defaultMaterial = material;
-
-		this.defaultPosition = position.clone();
-		this.defaultRotation = new Vector3( MathUtils.radToDeg( rotation.x ), MathUtils.radToDeg( rotation.y ), MathUtils.radToDeg( rotation.z ) );
-		this.defaultScale = scale.clone().multiplyScalar( 100 );
-
-		this._restoreDefault();
+		this.defaultMaterial = this.mesh.material;
 
 
 	}
 	}
 
 
-	_restoreDefault() {
-
-		const position = this.defaultPosition;
-		const rotation = this.defaultRotation;
-		const scale = this.defaultScale;
-
-		this.posX.setValue( position.x );
-		this.posY.setValue( position.y );
-		this.posZ.setValue( position.z );
-
-		this.rotX.setValue( rotation.x );
-		this.rotY.setValue( rotation.y );
-		this.rotZ.setValue( rotation.z );
+	restoreDefault() {
 
 
-		this.scaleX.setValue( scale.x );
-		this.scaleY.setValue( scale.y );
-		this.scaleZ.setValue( scale.z );
+		super.restoreDefault();
 
 
 		this.mesh.material = this.defaultMaterial;
 		this.mesh.material = this.defaultMaterial;
 
 

+ 160 - 0
examples/jsm/node-editor/scene/Object3DEditor.js

@@ -0,0 +1,160 @@
+import { NumberInput, StringInput, LabelElement } from '../../libs/flow.module.js';
+import { BaseNode } from '../core/BaseNode.js';
+import { Group, MathUtils, Vector3 } from 'three';
+
+export class Object3DEditor extends BaseNode {
+
+	constructor( object3d = null, name = 'Object 3D' ) {
+
+		if ( object3d === null ) {
+
+			object3d = new Group();
+
+		}
+
+		super( name, 1, object3d );
+
+		this.defaultPosition = new Vector3();
+		this.defaultRotation = new Vector3();
+		this.defaultScale = new Vector3( 100, 100, 100 );
+
+		this._initTags();
+		this._initTransform();
+
+		this.onValidElement = () => {};
+
+	}
+
+	setEditor( editor ) {
+
+		if ( this.editor ) {
+
+			this.restoreDefault();
+
+		}
+
+		super.setEditor( editor );
+
+		if ( editor ) {
+
+			const name = this.nameInput.getValue();
+			const object3d = editor.scene.getObjectByName( name );
+
+			this.value = object3d;
+
+			this.updateDefault();
+			this.restoreDefault();
+			this.update();
+
+		}
+
+		return this;
+
+	}
+
+	get object3d() {
+
+		return this.value;
+
+	}
+
+	_initTags() {
+
+		this.nameInput = new StringInput( this.object3d.name ).setReadOnly( true )
+			.onChange( () => this.object3d.name = this.nameInput.getValue() );
+
+		this.add( new LabelElement( 'Name' ).add( this.nameInput ) );
+
+	}
+
+	_initTransform() {
+
+		const update = () => this.update();
+
+		const posX = new NumberInput().setTagColor( 'red' ).onChange( update );
+		const posY = new NumberInput().setTagColor( 'green' ).onChange( update );
+		const posZ = new NumberInput().setTagColor( 'blue' ).onChange( update );
+
+		const rotationStep = 1;
+
+		const rotX = new NumberInput().setTagColor( 'red' ).setStep( rotationStep ).onChange( update );
+		const rotY = new NumberInput().setTagColor( 'green' ).setStep( rotationStep ).onChange( update );
+		const rotZ = new NumberInput().setTagColor( 'blue' ).setStep( rotationStep ).onChange( update );
+
+		const scaleX = new NumberInput( 100 ).setTagColor( 'red' ).setStep( rotationStep ).onChange( update );
+		const scaleY = new NumberInput( 100 ).setTagColor( 'green' ).setStep( rotationStep ).onChange( update );
+		const scaleZ = new NumberInput( 100 ).setTagColor( 'blue' ).setStep( rotationStep ).onChange( update );
+
+		this.add( new LabelElement( 'Position' ).add( posX ).add( posY ).add( posZ ) )
+			.add( new LabelElement( 'Rotation' ).add( rotX ).add( rotY ).add( rotZ ) )
+			.add( new LabelElement( 'Scale' ).add( scaleX ).add( scaleY ).add( scaleZ ) );
+
+		this.posX = posX;
+		this.posY = posY;
+		this.posZ = posZ;
+
+		this.rotX = rotX;
+		this.rotY = rotY;
+		this.rotZ = rotZ;
+
+		this.scaleX = scaleX;
+		this.scaleY = scaleY;
+		this.scaleZ = scaleZ;
+
+	}
+
+	update() {
+
+		const object3d = this.object3d;
+
+		if ( object3d ) {
+
+			const { position, rotation, scale } = object3d;
+
+			position.x = this.posX.getValue();
+			position.y = this.posY.getValue();
+			position.z = this.posZ.getValue();
+
+			rotation.x = MathUtils.degToRad( this.rotX.getValue() );
+			rotation.y = MathUtils.degToRad( this.rotY.getValue() );
+			rotation.z = MathUtils.degToRad( this.rotZ.getValue() );
+
+			scale.x = this.scaleX.getValue() / 100;
+			scale.y = this.scaleY.getValue() / 100;
+			scale.z = this.scaleZ.getValue() / 100;
+
+		}
+
+	}
+
+	updateDefault() {
+
+		const { position, rotation, scale } = this.object3d;
+
+		this.defaultPosition = position.clone();
+		this.defaultRotation = new Vector3( MathUtils.radToDeg( rotation.x ), MathUtils.radToDeg( rotation.y ), MathUtils.radToDeg( rotation.z ) );
+		this.defaultScale = scale.clone().multiplyScalar( 100 );
+
+	}
+
+	restoreDefault() {
+
+		const position = this.defaultPosition;
+		const rotation = this.defaultRotation;
+		const scale = this.defaultScale;
+
+		this.posX.setValue( position.x );
+		this.posY.setValue( position.y );
+		this.posZ.setValue( position.z );
+
+		this.rotX.setValue( rotation.x );
+		this.rotY.setValue( rotation.y );
+		this.rotZ.setValue( rotation.z );
+
+		this.scaleX.setValue( scale.x );
+		this.scaleY.setValue( scale.y );
+		this.scaleZ.setValue( scale.z );
+
+	}
+
+}

+ 99 - 0
examples/jsm/node-editor/scene/PointsEditor.js

@@ -0,0 +1,99 @@
+import { LabelElement } from '../../libs/flow.module.js';
+import { Object3DEditor } from './Object3DEditor.js';
+import { Points } from 'three';
+
+export class PointsEditor extends Object3DEditor {
+
+	constructor( points = null ) {
+
+		if ( points === null ) {
+
+			points = new Points();
+
+		}
+
+		super( points, 'Points' );
+
+		this.material = null;
+
+		this.defaultMaterial = null;
+
+		this._initMaterial();
+
+		this.updateDefault();
+		this.restoreDefault();
+		this.update();
+
+	}
+
+	get points() {
+
+		return this.value;
+
+	}
+
+	_initMaterial() {
+
+		const materialElement = new LabelElement( 'Material' ).setInputColor( 'forestgreen' ).setInput( 1 );
+
+		materialElement.onValid( ( source, target, stage ) => {
+
+			const object = target.getObject();
+
+			if ( object && object.isMaterial !== true ) {
+
+				if ( stage === 'dragged' ) {
+
+					const name = target.node.getName();
+
+					this.editor.tips.error( `"${name}" is not a Material.` );
+
+				}
+
+				return false;
+
+			}
+
+		} ).onConnect( () => {
+
+			this.material = materialElement.getLinkedObject() || this.defaultMaterial;
+
+			this.update();
+
+		} );
+
+		this.add( materialElement );
+
+	}
+
+	update() {
+
+		super.update();
+
+		const points = this.points;
+
+		if ( points ) {
+
+			points.material = this.material || this.defaultMaterial;
+
+		}
+
+	}
+
+	updateDefault() {
+
+		super.updateDefault();
+
+		this.defaultMaterial = this.points.material;
+
+	}
+
+	restoreDefault() {
+
+		super.restoreDefault();
+
+		this.points.material = this.defaultMaterial;
+
+	}
+
+}

+ 10 - 2
examples/webgl_nodes_playground.html

@@ -133,16 +133,24 @@
 				const defaultMaterial = new Nodes.MeshBasicNodeMaterial();
 				const defaultMaterial = new Nodes.MeshBasicNodeMaterial();
 				defaultMaterial.colorNode = new Nodes.FloatNode( 0 );
 				defaultMaterial.colorNode = new Nodes.FloatNode( 0 );
 
 
-				const sphere = new THREE.Mesh( new THREE.SphereGeometry( 200, 32, 16 ), defaultMaterial  ) ;
+				const sphere = new THREE.Mesh( new THREE.SphereGeometry( 200, 32, 16 ), defaultMaterial ) ;
 				sphere.name = 'Sphere';
 				sphere.name = 'Sphere';
 				sphere.position.set( 500, 0, -500 );
 				sphere.position.set( 500, 0, -500 );
 				scene.add( sphere );
 				scene.add( sphere );
 
 
-				const box = new THREE.Mesh( new THREE.BoxGeometry( 200, 200, 200 ), defaultMaterial  ) ;
+				const box = new THREE.Mesh( new THREE.BoxGeometry( 200, 200, 200 ), defaultMaterial ) ;
 				box.name = 'Box';
 				box.name = 'Box';
 				box.position.set( -500, 0, -500 );
 				box.position.set( -500, 0, -500 );
 				scene.add( box );
 				scene.add( box );
 
 
+				const defaultPointsMaterial = new Nodes.PointsNodeMaterial();
+				defaultPointsMaterial.colorNode = new Nodes.FloatNode( 0 );
+
+				const torusKnot = new THREE.Points( new THREE.TorusKnotGeometry( 100, 30, 100, 16 ), defaultPointsMaterial ) ;
+				torusKnot.name = 'Torus Knot ( Points )';
+				torusKnot.position.set( 0, 0, -500 );
+				scene.add( torusKnot );
+
 				model = object.children[ 0 ];
 				model = object.children[ 0 ];
 				model.position.set( 0, 0, 10 );
 				model.position.set( 0, 0, 10 );
 				model.scale.setScalar( 1 );
 				model.scale.setScalar( 1 );

+ 8 - 0
examples/webgpu_nodes_playground.html

@@ -174,6 +174,14 @@
 					box.position.set( -500, 0, -500 );
 					box.position.set( -500, 0, -500 );
 					scene.add( box );
 					scene.add( box );
 
 
+					const defaultPointsMaterial = new Nodes.PointsNodeMaterial();
+					defaultPointsMaterial.colorNode = new Nodes.FloatNode( 0 );
+
+					const torusKnot = new THREE.Points( new THREE.TorusKnotGeometry( 100, 30, 100, 16 ), defaultPointsMaterial ) ;
+					torusKnot.name = 'Torus Knot ( Points )';
+					torusKnot.position.set( 0, 0, -500 );
+					scene.add( torusKnot );
+
 					model = object.children[ 0 ];
 					model = object.children[ 0 ];
 					model.position.set( 0, 0, 10 );
 					model.position.set( 0, 0, 10 );
 					model.scale.setScalar( 1 );
 					model.scale.setScalar( 1 );