Skip to content

Commit 47a70dc

Browse files
authored
TSL: Add renderer context node (global) (#32295)
1 parent ab2018c commit 47a70dc

File tree

12 files changed

+174
-27
lines changed

12 files changed

+174
-27
lines changed

src/Three.TSL.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ export const remapClamp = TSL.remapClamp;
464464
export const renderGroup = TSL.renderGroup;
465465
export const renderOutput = TSL.renderOutput;
466466
export const rendererReference = TSL.rendererReference;
467+
export const replaceDefaultUV = TSL.replaceDefaultUV;
467468
export const rotate = TSL.rotate;
468469
export const rotateUV = TSL.rotateUV;
469470
export const roughness = TSL.roughness;

src/materials/nodes/NodeMaterial.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { modelViewMatrix } from '../../nodes/accessors/ModelNode.js';
2525
import { vertexColor } from '../../nodes/accessors/VertexColorNode.js';
2626
import { premultiplyAlpha } from '../../nodes/display/BlendModes.js';
2727
import { subBuild } from '../../nodes/core/SubBuildNode.js';
28-
import { warn } from '../../utils.js';
28+
import { error, warn } from '../../utils.js';
2929

3030
/**
3131
* Base class for all node materials.
@@ -382,6 +382,14 @@ class NodeMaterial extends Material {
382382
*/
383383
this.vertexNode = null;
384384

385+
/**
386+
* This node can be used as a global context management component for this material.
387+
*
388+
* @type {?ContextNode}
389+
* @default null
390+
*/
391+
this.contextNode = null;
392+
385393
// Deprecated properties
386394

387395
Object.defineProperty( this, 'shadowPositionNode', { // @deprecated, r176
@@ -489,6 +497,32 @@ class NodeMaterial extends Material {
489497
const renderer = builder.renderer;
490498
const renderTarget = renderer.getRenderTarget();
491499

500+
// < CONTEXT >
501+
502+
if ( renderer.contextNode.isContextNode === true ) {
503+
504+
builder.context = { ...builder.context, ...renderer.contextNode.getFlowContextData() };
505+
506+
} else {
507+
508+
error( 'NodeMaterial: "renderer.contextNode" must be an instance of `context()`.' );
509+
510+
}
511+
512+
if ( this.contextNode !== null ) {
513+
514+
if ( this.contextNode.isContextNode === true ) {
515+
516+
builder.context = { ...builder.context, ...this.contextNode.getFlowContextData() };
517+
518+
} else {
519+
520+
error( 'NodeMaterial: "material.contextNode" must be an instance of `context()`.' );
521+
522+
}
523+
524+
}
525+
492526
// < VERTEX STAGE >
493527

494528
builder.addStack();
@@ -558,6 +592,14 @@ class NodeMaterial extends Material {
558592

559593
if ( isCustomOutput ) resultNode = this.outputNode;
560594

595+
//
596+
597+
if ( builder.context.getOutput ) {
598+
599+
resultNode = builder.context.getOutput( resultNode, builder );
600+
601+
}
602+
561603
// MRT
562604

563605
if ( renderTarget !== null ) {
@@ -1301,6 +1343,8 @@ class NodeMaterial extends Material {
13011343
this.fragmentNode = source.fragmentNode;
13021344
this.vertexNode = source.vertexNode;
13031345

1346+
this.contextNode = source.contextNode;
1347+
13041348
return super.copy( source );
13051349

13061350
}

src/materials/nodes/manager/NodeMaterialObserver.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ class NodeMaterialObserver {
259259

260260
}
261261

262-
if ( builder.renderer.overrideNodes.modelViewMatrix !== null || builder.renderer.overrideNodes.modelNormalViewMatrix !== null )
262+
if ( builder.context.modelViewMatrix || builder.context.modelNormalViewMatrix )
263263
return true;
264264

265265
return false;

src/nodes/accessors/ModelNode.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export const modelWorldMatrixInverse = /*@__PURE__*/ uniform( new Matrix4() ).on
123123
*/
124124
export const modelViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => {
125125

126-
return builder.renderer.overrideNodes.modelViewMatrix || mediumpModelViewMatrix;
126+
return builder.context.modelViewMatrix || mediumpModelViewMatrix;
127127

128128
} ).once() )().toVar( 'modelViewMatrix' );
129129

src/nodes/accessors/Normal.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ export const transformNormal = /*@__PURE__*/ Fn( ( [ normal, matrix = modelWorld
182182
*/
183183
export const transformNormalToView = /*@__PURE__*/ Fn( ( [ normal ], builder ) => {
184184

185-
const modelNormalViewMatrix = builder.renderer.overrideNodes.modelNormalViewMatrix;
185+
const modelNormalViewMatrix = builder.context.modelNormalViewMatrix;
186186

187-
if ( modelNormalViewMatrix !== null ) {
187+
if ( modelNormalViewMatrix ) {
188188

189189
return modelNormalViewMatrix.transformDirection( normal );
190190

src/nodes/core/ContextNode.js

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Node from './Node.js';
2-
import { addMethodChaining, nodeProxy } from '../tsl/TSLCore.js';
2+
import { addMethodChaining } from '../tsl/TSLCore.js';
33
import { warn } from '../../utils.js';
44

55
/**
@@ -9,6 +9,12 @@ import { warn } from '../../utils.js';
99
*
1010
* ```js
1111
*node.context( { getUV: () => customCoord } );
12+
*\// or
13+
*material.contextNode = context( { getUV: () => customCoord } );
14+
*\// or
15+
*renderer.contextNode = context( { getUV: () => customCoord } );
16+
*\// or
17+
*scenePass.contextNode = context( { getUV: () => customCoord } );
1218
*```
1319
* @augments Node
1420
*/
@@ -26,7 +32,7 @@ class ContextNode extends Node {
2632
* @param {Node} node - The node whose context should be modified.
2733
* @param {Object} [value={}] - The modified context data.
2834
*/
29-
constructor( node, value = {} ) {
35+
constructor( node = null, value = {} ) {
3036

3137
super();
3238

@@ -79,6 +85,29 @@ class ContextNode extends Node {
7985

8086
}
8187

88+
/**
89+
* Gathers the context data from all parent context nodes.
90+
*
91+
* @return {Object} The gathered context data.
92+
*/
93+
getFlowContextData() {
94+
95+
const children = [];
96+
97+
this.traverse( ( node ) => {
98+
99+
if ( node.isContextNode === true ) {
100+
101+
children.push( node.value );
102+
103+
}
104+
105+
} );
106+
107+
return Object.assign( {}, ...children );
108+
109+
}
110+
82111
/**
83112
* This method is overwritten to ensure it returns the member type of {@link ContextNode#node}.
84113
*
@@ -133,11 +162,24 @@ export default ContextNode;
133162
*
134163
* @tsl
135164
* @function
136-
* @param {Node} node - The node whose context should be modified.
165+
* @param {Node|Object} [nodeOrValue={}] - The node whose context should be modified or the modified context data.
137166
* @param {Object} [value={}] - The modified context data.
138167
* @returns {ContextNode}
139168
*/
140-
export const context = /*@__PURE__*/ nodeProxy( ContextNode ).setParameterLength( 1, 2 );
169+
export const context = /*@__PURE__*/ ( nodeOrValue = null, value = {} ) => {
170+
171+
let node = nodeOrValue;
172+
173+
if ( node === null || node.isNode !== true ) {
174+
175+
value = node || value;
176+
node = null;
177+
178+
}
179+
180+
return new ContextNode( node, value );
181+
182+
};
141183

142184
/**
143185
* TSL function for defining a uniformFlow context value for a given node.

src/nodes/display/PassNode.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import TempNode from '../core/TempNode.js';
22
import { default as TextureNode/*, texture*/ } from '../accessors/TextureNode.js';
33
import { NodeUpdateType } from '../core/constants.js';
4-
import { nodeObject } from '../tsl/TSLBase.js';
4+
import { nodeObject, context } from '../tsl/TSLBase.js';
55
import { uniform } from '../core/UniformNode.js';
66
import { viewZToOrthographicDepth, perspectiveDepthToViewZ } from './ViewportDepthNode.js';
77

@@ -248,6 +248,22 @@ class PassNode extends TempNode {
248248
*/
249249
this.renderTarget = renderTarget;
250250

251+
/**
252+
* An optional global context for the pass.
253+
*
254+
* @type {ContextNode|null}
255+
*/
256+
this.contextNode = null;
257+
258+
/**
259+
* A cache for the context node.
260+
*
261+
* @private
262+
* @type {?Object}
263+
* @default null
264+
*/
265+
this._contextNodeCache = null;
266+
251267
/**
252268
* A dictionary holding the internal result textures.
253269
*
@@ -739,6 +755,7 @@ class PassNode extends TempNode {
739755
const currentMRT = renderer.getMRT();
740756
const currentAutoClear = renderer.autoClear;
741757
const currentMask = camera.layers.mask;
758+
const currentContextNode = renderer.contextNode;
742759

743760
this._cameraNear.value = camera.near;
744761
this._cameraFar.value = camera.far;
@@ -759,6 +776,21 @@ class PassNode extends TempNode {
759776
renderer.setMRT( this._mrt );
760777
renderer.autoClear = true;
761778

779+
if ( this.contextNode !== null ) {
780+
781+
if ( this._contextNodeCache === null || this._contextNodeCache.version !== this.version ) {
782+
783+
this._contextNodeCache = {
784+
version: this.version,
785+
context: context( { ...renderer.contextNode.value, ...this.contextNode.getFlowContextData() } )
786+
};
787+
788+
}
789+
790+
renderer.contextNode = this._contextNodeCache.context;
791+
792+
}
793+
762794
const currentSceneName = scene.name;
763795

764796
scene.name = this.name ? this.name : scene.name;
@@ -770,6 +802,7 @@ class PassNode extends TempNode {
770802
renderer.setRenderTarget( currentRenderTarget );
771803
renderer.setMRT( currentMRT );
772804
renderer.autoClear = currentAutoClear;
805+
renderer.contextNode = currentContextNode;
773806

774807
camera.layers.mask = currentMask;
775808

src/nodes/pmrem/PMREMNode.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ class PMREMNode extends TempNode {
308308

309309
if ( uvNode === null && builder.context.getUV ) {
310310

311-
uvNode = builder.context.getUV( this );
311+
uvNode = builder.context.getUV( this, builder );
312312

313313
}
314314

src/nodes/utils/UVUtils.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
11
import { Fn, vec2 } from '../tsl/TSLBase.js';
22
import { rotate } from './RotateNode.js';
3+
import { context } from '../core/ContextNode.js';
4+
5+
/**
6+
* Replaces the default UV coordinates used in texture lookups.
7+
*
8+
* ```js
9+
*material.contextNode = replaceDefaultUV( ( textureNode ) => {
10+
*
11+
* // ...
12+
* return customUVCoordinates;
13+
*
14+
*} );
15+
*```
16+
*
17+
* @tsl
18+
* @function
19+
* @param {function(Node):Node<vec2>} callback - A callback that receives the texture node
20+
* and must return the new uv coordinates.
21+
* @param {Node} [node=null] - An optional node to which the context will be applied.
22+
* @return {ContextNode} A context node that replaces the default UV coordinates.
23+
*/
24+
export function replaceDefaultUV( callback, node = null ) {
25+
26+
return context( node, { getUV: callback } );
27+
28+
}
329

430
/**
531
* Rotates the given uv coordinates around a center point

src/renderers/common/Background.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import DataMap from './DataMap.js';
22
import Color4 from './Color4.js';
3-
import { vec4, context, normalWorldGeometry, backgroundBlurriness, backgroundIntensity, backgroundRotation, positionLocal, cameraProjectionMatrix, modelViewMatrix, div } from '../../nodes/TSL.js';
3+
import { vec4, normalWorldGeometry, backgroundBlurriness, backgroundIntensity, backgroundRotation, positionLocal, cameraProjectionMatrix, modelViewMatrix, div } from '../../nodes/TSL.js';
44
import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
55

66
import { Mesh } from '../../objects/Mesh.js';
@@ -88,7 +88,7 @@ class Background extends DataMap {
8888

8989
if ( backgroundMesh === undefined ) {
9090

91-
const backgroundMeshNode = context( vec4( backgroundNode ).mul( backgroundIntensity ), {
91+
const backgroundMeshNode = vec4( backgroundNode ).mul( backgroundIntensity ).context( {
9292
// @TODO: Add Texture2D support using node context
9393
getUV: () => backgroundRotation.mul( normalWorldGeometry ),
9494
getTextureLevel: () => backgroundBlurriness

0 commit comments

Comments
 (0)