first app vibe

This commit is contained in:
2025-07-28 22:43:55 +01:00
parent d70b6714c3
commit af090f5bf0
2530 changed files with 1410652 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
import { normalView } from './Normal.js';
import { tangentView } from './Tangent.js';
import { bitangentView } from './Bitangent.js';
import { Fn, mat3 } from '../tsl/TSLBase.js';
import { mix } from '../math/MathNode.js';
import { anisotropy, anisotropyB, roughness } from '../core/PropertyNode.js';
import { positionViewDirection } from './Position.js';
/**
* TSL object that represents the TBN matrix in view space.
*
* @tsl
* @type {Node<mat3>}
*/
export const TBNViewMatrix = /*@__PURE__*/ mat3( tangentView, bitangentView, normalView ).toVar( 'TBNViewMatrix' );
/**
* TSL object that represents the parallax direction.
*
* @tsl
* @type {Node<mat3>}
*/
export const parallaxDirection = /*@__PURE__*/ positionViewDirection.mul( TBNViewMatrix )/*.normalize()*/;
/**
* TSL function for computing parallax uv coordinates.
*
* @tsl
* @function
* @param {Node<vec2>} uv - A uv node.
* @param {Node<vec2>} scale - A scale node.
* @returns {Node<vec2>} Parallax uv coordinates.
*/
export const parallaxUV = ( uv, scale ) => uv.sub( parallaxDirection.mul( scale ) );
/**
* TSL function for computing bent normals.
*
* @tsl
* @function
* @returns {Node<vec3>} Bent normals.
*/
export const bentNormalView = /*@__PURE__*/ ( Fn( () => {
// https://google.github.io/filament/Filament.md.html#lighting/imagebasedlights/anisotropy
let bentNormal = anisotropyB.cross( positionViewDirection );
bentNormal = bentNormal.cross( anisotropyB ).normalize();
bentNormal = mix( bentNormal, normalView, anisotropy.mul( roughness.oneMinus() ).oneMinus().pow2().pow2() ).normalize();
return bentNormal;
} ).once() )();

68
app/node_modules/three/src/nodes/accessors/Arrays.js generated vendored Normal file
View File

@@ -0,0 +1,68 @@
import StorageInstancedBufferAttribute from '../../renderers/common/StorageInstancedBufferAttribute.js';
import StorageBufferAttribute from '../../renderers/common/StorageBufferAttribute.js';
import { storage } from './StorageBufferNode.js';
import { getLengthFromType, getTypedArrayFromType } from '../core/NodeUtils.js';
/**
* TSL function for creating a storage buffer node with a configured `StorageBufferAttribute`.
*
* @tsl
* @function
* @param {number|TypedArray} count - The data count. It is also valid to pass a typed array as an argument.
* @param {string|Struct} [type='float'] - The data type.
* @returns {StorageBufferNode}
*/
export const attributeArray = ( count, type = 'float' ) => {
let itemSize, typedArray;
if ( type.isStruct === true ) {
itemSize = type.layout.getLength();
typedArray = getTypedArrayFromType( 'float' );
} else {
itemSize = getLengthFromType( type );
typedArray = getTypedArrayFromType( type );
}
const buffer = new StorageBufferAttribute( count, itemSize, typedArray );
const node = storage( buffer, type, count );
return node;
};
/**
* TSL function for creating a storage buffer node with a configured `StorageInstancedBufferAttribute`.
*
* @tsl
* @function
* @param {number|TypedArray} count - The data count. It is also valid to pass a typed array as an argument.
* @param {string|Struct} [type='float'] - The data type.
* @returns {StorageBufferNode}
*/
export const instancedArray = ( count, type = 'float' ) => {
let itemSize, typedArray;
if ( type.isStruct === true ) {
itemSize = type.layout.getLength();
typedArray = getTypedArrayFromType( 'float' );
} else {
itemSize = getLengthFromType( type );
typedArray = getTypedArrayFromType( type );
}
const buffer = new StorageInstancedBufferAttribute( count, itemSize, typedArray );
const node = storage( buffer, type, count );
return node;
};

163
app/node_modules/three/src/nodes/accessors/BatchNode.js generated vendored Normal file
View File

@@ -0,0 +1,163 @@
import Node from '../core/Node.js';
import { normalLocal } from './Normal.js';
import { positionLocal } from './Position.js';
import { nodeProxy, vec3, mat3, mat4, int, ivec2, float, Fn } from '../tsl/TSLBase.js';
import { textureLoad } from './TextureNode.js';
import { textureSize } from './TextureSizeNode.js';
import { tangentLocal } from './Tangent.js';
import { instanceIndex, drawIndex } from '../core/IndexNode.js';
import { varyingProperty } from '../core/PropertyNode.js';
/**
* This node implements the vertex shader logic which is required
* when rendering 3D objects via batching. `BatchNode` must be used
* with instances of {@link BatchedMesh}.
*
* @augments Node
*/
class BatchNode extends Node {
static get type() {
return 'BatchNode';
}
/**
* Constructs a new batch node.
*
* @param {BatchedMesh} batchMesh - A reference to batched mesh.
*/
constructor( batchMesh ) {
super( 'void' );
/**
* A reference to batched mesh.
*
* @type {BatchedMesh}
*/
this.batchMesh = batchMesh;
/**
* The batching index node.
*
* @type {?IndexNode}
* @default null
*/
this.batchingIdNode = null;
}
/**
* Setups the internal buffers and nodes and assigns the transformed vertex data
* to predefined node variables for accumulation. That follows the same patterns
* like with morph and skinning nodes.
*
* @param {NodeBuilder} builder - The current node builder.
*/
setup( builder ) {
if ( this.batchingIdNode === null ) {
if ( builder.getDrawIndex() === null ) {
this.batchingIdNode = instanceIndex;
} else {
this.batchingIdNode = drawIndex;
}
}
const getIndirectIndex = Fn( ( [ id ] ) => {
const size = int( textureSize( textureLoad( this.batchMesh._indirectTexture ), 0 ).x );
const x = int( id ).mod( size );
const y = int( id ).div( size );
return textureLoad( this.batchMesh._indirectTexture, ivec2( x, y ) ).x;
} ).setLayout( {
name: 'getIndirectIndex',
type: 'uint',
inputs: [
{ name: 'id', type: 'int' }
]
} );
const indirectId = getIndirectIndex( int( this.batchingIdNode ) );
const matricesTexture = this.batchMesh._matricesTexture;
const size = int( textureSize( textureLoad( matricesTexture ), 0 ).x );
const j = float( indirectId ).mul( 4 ).toInt().toVar();
const x = j.mod( size );
const y = j.div( size );
const batchingMatrix = mat4(
textureLoad( matricesTexture, ivec2( x, y ) ),
textureLoad( matricesTexture, ivec2( x.add( 1 ), y ) ),
textureLoad( matricesTexture, ivec2( x.add( 2 ), y ) ),
textureLoad( matricesTexture, ivec2( x.add( 3 ), y ) )
);
const colorsTexture = this.batchMesh._colorsTexture;
if ( colorsTexture !== null ) {
const getBatchingColor = Fn( ( [ id ] ) => {
const size = int( textureSize( textureLoad( colorsTexture ), 0 ).x );
const j = id;
const x = j.mod( size );
const y = j.div( size );
return textureLoad( colorsTexture, ivec2( x, y ) ).rgb;
} ).setLayout( {
name: 'getBatchingColor',
type: 'vec3',
inputs: [
{ name: 'id', type: 'int' }
]
} );
const color = getBatchingColor( indirectId );
varyingProperty( 'vec3', 'vBatchColor' ).assign( color );
}
const bm = mat3( batchingMatrix );
positionLocal.assign( batchingMatrix.mul( positionLocal ) );
const transformedNormal = normalLocal.div( vec3( bm[ 0 ].dot( bm[ 0 ] ), bm[ 1 ].dot( bm[ 1 ] ), bm[ 2 ].dot( bm[ 2 ] ) ) );
const batchingNormal = bm.mul( transformedNormal ).xyz;
normalLocal.assign( batchingNormal );
if ( builder.hasGeometryAttribute( 'tangent' ) ) {
tangentLocal.mulAssign( bm );
}
}
}
export default BatchNode;
/**
* TSL function for creating a batch node.
*
* @tsl
* @function
* @param {BatchedMesh} batchMesh - A reference to batched mesh.
* @returns {BatchNode}
*/
export const batch = /*@__PURE__*/ nodeProxy( BatchNode ).setParameterLength( 1 );

View File

@@ -0,0 +1,82 @@
import { Fn } from '../tsl/TSLCore.js';
import { normalGeometry, normalLocal, normalView, normalWorld } from './Normal.js';
import { tangentGeometry, tangentLocal, tangentView, tangentWorld } from './Tangent.js';
import { bitangentViewFrame } from './TangentUtils.js';
import { directionToFaceDirection } from '../display/FrontFacingNode.js';
/**
* Returns the bitangent node and assigns it to a varying if the material is not flat shaded.
*
* @tsl
* @private
* @param {Node<vec3>} crossNormalTangent - The cross product of the normal and tangent vectors.
* @param {string} varyingName - The name of the varying to assign the bitangent to.
* @returns {Node<vec3>} The bitangent node.
*/
const getBitangent = /*@__PURE__*/ Fn( ( [ crossNormalTangent, varyingName ], { subBuildFn, material } ) => {
let bitangent = crossNormalTangent.mul( tangentGeometry.w ).xyz;
if ( subBuildFn === 'NORMAL' && material.flatShading !== true ) {
bitangent = bitangent.toVarying( varyingName );
}
return bitangent;
} ).once( [ 'NORMAL' ] );
/**
* TSL object that represents the bitangent attribute of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const bitangentGeometry = /*@__PURE__*/ getBitangent( normalGeometry.cross( tangentGeometry ), 'v_bitangentGeometry' ).normalize().toVar( 'bitangentGeometry' );
/**
* TSL object that represents the vertex bitangent in local space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const bitangentLocal = /*@__PURE__*/ getBitangent( normalLocal.cross( tangentLocal ), 'v_bitangentLocal' ).normalize().toVar( 'bitangentLocal' );
/**
* TSL object that represents the vertex bitangent in view space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const bitangentView = /*@__PURE__*/ ( Fn( ( { subBuildFn, geometry, material } ) => {
let node;
if ( subBuildFn === 'VERTEX' || geometry.hasAttribute( 'tangent' ) ) {
node = getBitangent( normalView.cross( tangentView ), 'v_bitangentView' ).normalize();
} else {
node = bitangentViewFrame;
}
if ( material.flatShading !== true ) {
node = directionToFaceDirection( node );
}
return node;
}, 'vec3' ).once( [ 'NORMAL', 'VERTEX' ] ) )().toVar( 'bitangentView' );
/**
* TSL object that represents the vertex bitangent in world space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const bitangentWorld = /*@__PURE__*/ getBitangent( normalWorld.cross( tangentWorld ), 'v_bitangentWorld' ).normalize().toVar( 'bitangentWorld' );

View File

@@ -0,0 +1,340 @@
import InputNode from '../core/InputNode.js';
import { nodeObject, addMethodChaining } from '../tsl/TSLCore.js';
import { varying } from '../core/VaryingNode.js';
import { InterleavedBufferAttribute } from '../../core/InterleavedBufferAttribute.js';
import { InterleavedBuffer } from '../../core/InterleavedBuffer.js';
import { StaticDrawUsage, DynamicDrawUsage } from '../../constants.js';
/**
* In earlier `three.js` versions it was only possible to define attribute data
* on geometry level. With `BufferAttributeNode`, it is also possible to do this
* on the node level.
* ```js
* const geometry = new THREE.PlaneGeometry();
* const positionAttribute = geometry.getAttribute( 'position' );
*
* const colors = [];
* for ( let i = 0; i < position.count; i ++ ) {
* colors.push( 1, 0, 0 );
* }
*
* material.colorNode = bufferAttribute( new THREE.Float32BufferAttribute( colors, 3 ) );
* ```
* This new approach is especially interesting when geometry data are generated via
* compute shaders. The below line converts a storage buffer into an attribute node.
* ```js
* material.positionNode = positionBuffer.toAttribute();
* ```
* @augments InputNode
*/
class BufferAttributeNode extends InputNode {
static get type() {
return 'BufferAttributeNode';
}
/**
* Constructs a new buffer attribute node.
*
* @param {BufferAttribute|InterleavedBuffer|TypedArray} value - The attribute data.
* @param {?string} [bufferType=null] - The buffer type (e.g. `'vec3'`).
* @param {number} [bufferStride=0] - The buffer stride.
* @param {number} [bufferOffset=0] - The buffer offset.
*/
constructor( value, bufferType = null, bufferStride = 0, bufferOffset = 0 ) {
super( value, bufferType );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isBufferNode = true;
/**
* The buffer type (e.g. `'vec3'`).
*
* @type {?string}
* @default null
*/
this.bufferType = bufferType;
/**
* The buffer stride.
*
* @type {number}
* @default 0
*/
this.bufferStride = bufferStride;
/**
* The buffer offset.
*
* @type {number}
* @default 0
*/
this.bufferOffset = bufferOffset;
/**
* The usage property. Set this to `THREE.DynamicDrawUsage` via `.setUsage()`,
* if you are planning to update the attribute data per frame.
*
* @type {number}
* @default StaticDrawUsage
*/
this.usage = StaticDrawUsage;
/**
* Whether the attribute is instanced or not.
*
* @type {boolean}
* @default false
*/
this.instanced = false;
/**
* A reference to the buffer attribute.
*
* @type {?BufferAttribute}
* @default null
*/
this.attribute = null;
/**
* `BufferAttributeNode` sets this property to `true` by default.
*
* @type {boolean}
* @default true
*/
this.global = true;
if ( value && value.isBufferAttribute === true ) {
this.attribute = value;
this.usage = value.usage;
this.instanced = value.isInstancedBufferAttribute;
}
}
/**
* This method is overwritten since the attribute data might be shared
* and thus the hash should be shared as well.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The hash.
*/
getHash( builder ) {
if ( this.bufferStride === 0 && this.bufferOffset === 0 ) {
let bufferData = builder.globalCache.getData( this.value );
if ( bufferData === undefined ) {
bufferData = {
node: this
};
builder.globalCache.setData( this.value, bufferData );
}
return bufferData.node.uuid;
}
return this.uuid;
}
/**
* This method is overwritten since the node type is inferred from
* the buffer attribute.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The node type.
*/
getNodeType( builder ) {
if ( this.bufferType === null ) {
this.bufferType = builder.getTypeFromAttribute( this.attribute );
}
return this.bufferType;
}
/**
* Depending on which value was passed to the node, `setup()` behaves
* differently. If no instance of `BufferAttribute` was passed, the method
* creates an internal attribute and configures it respectively.
*
* @param {NodeBuilder} builder - The current node builder.
*/
setup( builder ) {
if ( this.attribute !== null ) return;
const type = this.getNodeType( builder );
const array = this.value;
const itemSize = builder.getTypeLength( type );
const stride = this.bufferStride || itemSize;
const offset = this.bufferOffset;
const buffer = array.isInterleavedBuffer === true ? array : new InterleavedBuffer( array, stride );
const bufferAttribute = new InterleavedBufferAttribute( buffer, itemSize, offset );
buffer.setUsage( this.usage );
this.attribute = bufferAttribute;
this.attribute.isInstancedBufferAttribute = this.instanced; // @TODO: Add a possible: InstancedInterleavedBufferAttribute
}
/**
* Generates the code snippet of the buffer attribute node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The generated code snippet.
*/
generate( builder ) {
const nodeType = this.getNodeType( builder );
const nodeAttribute = builder.getBufferAttributeFromNode( this, nodeType );
const propertyName = builder.getPropertyName( nodeAttribute );
let output = null;
if ( builder.shaderStage === 'vertex' || builder.shaderStage === 'compute' ) {
this.name = propertyName;
output = propertyName;
} else {
const nodeVarying = varying( this );
output = nodeVarying.build( builder, nodeType );
}
return output;
}
/**
* Overwrites the default implementation to return a fixed value `'bufferAttribute'`.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The input type.
*/
getInputType( /*builder*/ ) {
return 'bufferAttribute';
}
/**
* Sets the `usage` property to the given value.
*
* @param {number} value - The usage to set.
* @return {BufferAttributeNode} A reference to this node.
*/
setUsage( value ) {
this.usage = value;
if ( this.attribute && this.attribute.isBufferAttribute === true ) {
this.attribute.usage = value;
}
return this;
}
/**
* Sets the `instanced` property to the given value.
*
* @param {boolean} value - The value to set.
* @return {BufferAttributeNode} A reference to this node.
*/
setInstanced( value ) {
this.instanced = value;
return this;
}
}
export default BufferAttributeNode;
/**
* TSL function for creating a buffer attribute node.
*
* @tsl
* @function
* @param {BufferAttribute|InterleavedBuffer|TypedArray} array - The attribute data.
* @param {?string} [type=null] - The buffer type (e.g. `'vec3'`).
* @param {number} [stride=0] - The buffer stride.
* @param {number} [offset=0] - The buffer offset.
* @returns {BufferAttributeNode}
*/
export const bufferAttribute = ( array, type = null, stride = 0, offset = 0 ) => nodeObject( new BufferAttributeNode( array, type, stride, offset ) );
/**
* TSL function for creating a buffer attribute node but with dynamic draw usage.
* Use this function if attribute data are updated per frame.
*
* @tsl
* @function
* @param {BufferAttribute|InterleavedBuffer|TypedArray} array - The attribute data.
* @param {?string} [type=null] - The buffer type (e.g. `'vec3'`).
* @param {number} [stride=0] - The buffer stride.
* @param {number} [offset=0] - The buffer offset.
* @returns {BufferAttributeNode}
*/
export const dynamicBufferAttribute = ( array, type = null, stride = 0, offset = 0 ) => bufferAttribute( array, type, stride, offset ).setUsage( DynamicDrawUsage );
/**
* TSL function for creating a buffer attribute node but with enabled instancing
*
* @tsl
* @function
* @param {BufferAttribute|InterleavedBuffer|TypedArray} array - The attribute data.
* @param {?string} [type=null] - The buffer type (e.g. `'vec3'`).
* @param {number} [stride=0] - The buffer stride.
* @param {number} [offset=0] - The buffer offset.
* @returns {BufferAttributeNode}
*/
export const instancedBufferAttribute = ( array, type = null, stride = 0, offset = 0 ) => bufferAttribute( array, type, stride, offset ).setInstanced( true );
/**
* TSL function for creating a buffer attribute node but with dynamic draw usage and enabled instancing
*
* @tsl
* @function
* @param {BufferAttribute|InterleavedBuffer|TypedArray} array - The attribute data.
* @param {?string} [type=null] - The buffer type (e.g. `'vec3'`).
* @param {number} [stride=0] - The buffer stride.
* @param {number} [offset=0] - The buffer offset.
* @returns {BufferAttributeNode}
*/
export const instancedDynamicBufferAttribute = ( array, type = null, stride = 0, offset = 0 ) => dynamicBufferAttribute( array, type, stride, offset ).setInstanced( true );
addMethodChaining( 'toAttribute', ( bufferNode ) => bufferAttribute( bufferNode.value ) );

View File

@@ -0,0 +1,101 @@
import UniformNode from '../core/UniformNode.js';
import { nodeObject } from '../tsl/TSLBase.js';
/**
* A special type of uniform node which represents array-like data
* as uniform buffers. The access usually happens via `element()`
* which returns an instance of {@link ArrayElementNode}. For example:
*
* ```js
* const bufferNode = buffer( array, 'mat4', count );
* const matrixNode = bufferNode.element( index ); // access a matrix from the buffer
* ```
* In general, it is recommended to use the more managed {@link UniformArrayNode}
* since it handles more input types and automatically cares about buffer paddings.
*
* @augments UniformNode
*/
class BufferNode extends UniformNode {
static get type() {
return 'BufferNode';
}
/**
* Constructs a new buffer node.
*
* @param {Array<number>} value - Array-like buffer data.
* @param {string} bufferType - The data type of the buffer.
* @param {number} [bufferCount=0] - The count of buffer elements.
*/
constructor( value, bufferType, bufferCount = 0 ) {
super( value, bufferType );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isBufferNode = true;
/**
* The data type of the buffer.
*
* @type {string}
*/
this.bufferType = bufferType;
/**
* The uniform node that holds the value of the reference node.
*
* @type {number}
* @default 0
*/
this.bufferCount = bufferCount;
}
/**
* The data type of the buffer elements.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The element type.
*/
getElementType( builder ) {
return this.getNodeType( builder );
}
/**
* Overwrites the default implementation to return a fixed value `'buffer'`.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The input type.
*/
getInputType( /*builder*/ ) {
return 'buffer';
}
}
export default BufferNode;
/**
* TSL function for creating a buffer node.
*
* @tsl
* @function
* @param {Array} value - Array-like buffer data.
* @param {string} type - The data type of a buffer element.
* @param {number} count - The count of buffer elements.
* @returns {BufferNode}
*/
export const buffer = ( value, type, count ) => nodeObject( new BufferNode( value, type, count ) );

View File

@@ -0,0 +1,63 @@
import Node from '../core/Node.js';
import { nodeProxy } from '../tsl/TSLBase.js';
/**
* The node allows to set values for built-in shader variables. That is
* required for features like hardware-accelerated vertex clipping.
*
* @augments Node
*/
class BuiltinNode extends Node {
/**
* Constructs a new builtin node.
*
* @param {string} name - The name of the built-in shader variable.
*/
constructor( name ) {
super( 'float' );
/**
* The name of the built-in shader variable.
*
* @type {string}
*/
this.name = name;
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isBuiltinNode = true;
}
/**
* Generates the code snippet of the builtin node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The generated code snippet.
*/
generate( /* builder */ ) {
return this.name;
}
}
export default BuiltinNode;
/**
* TSL function for creating a builtin node.
*
* @tsl
* @function
* @param {string} name - The name of the built-in shader variable.
* @returns {BuiltinNode}
*/
export const builtin = nodeProxy( BuiltinNode ).setParameterLength( 1 );

156
app/node_modules/three/src/nodes/accessors/Camera.js generated vendored Normal file
View File

@@ -0,0 +1,156 @@
import { uniform } from '../core/UniformNode.js';
import { renderGroup, sharedUniformGroup } from '../core/UniformGroupNode.js';
import { Vector3 } from '../../math/Vector3.js';
import { Fn } from '../tsl/TSLBase.js';
import { uniformArray } from './UniformArrayNode.js';
import { builtin } from './BuiltinNode.js';
/**
* TSL object that represents the current `index` value of the camera if used ArrayCamera.
*
* @tsl
* @type {UniformNode<uint>}
*/
export const cameraIndex = /*@__PURE__*/ uniform( 0, 'uint' ).label( 'u_cameraIndex' ).setGroup( sharedUniformGroup( 'cameraIndex' ) ).toVarying( 'v_cameraIndex' );
/**
* TSL object that represents the `near` value of the camera used for the current render.
*
* @tsl
* @type {UniformNode<float>}
*/
export const cameraNear = /*@__PURE__*/ uniform( 'float' ).label( 'cameraNear' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.near );
/**
* TSL object that represents the `far` value of the camera used for the current render.
*
* @tsl
* @type {UniformNode<float>}
*/
export const cameraFar = /*@__PURE__*/ uniform( 'float' ).label( 'cameraFar' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.far );
/**
* TSL object that represents the projection matrix of the camera used for the current render.
*
* @tsl
* @type {UniformNode<mat4>}
*/
export const cameraProjectionMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => {
let cameraProjectionMatrix;
if ( camera.isArrayCamera && camera.cameras.length > 0 ) {
const matrices = [];
for ( const subCamera of camera.cameras ) {
matrices.push( subCamera.projectionMatrix );
}
const cameraProjectionMatrices = uniformArray( matrices ).setGroup( renderGroup ).label( 'cameraProjectionMatrices' );
cameraProjectionMatrix = cameraProjectionMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraProjectionMatrix' );
} else {
cameraProjectionMatrix = uniform( 'mat4' ).label( 'cameraProjectionMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix );
}
return cameraProjectionMatrix;
} ).once() )();
/**
* TSL object that represents the inverse projection matrix of the camera used for the current render.
*
* @tsl
* @type {UniformNode<mat4>}
*/
export const cameraProjectionMatrixInverse = /*@__PURE__*/ ( Fn( ( { camera } ) => {
let cameraProjectionMatrixInverse;
if ( camera.isArrayCamera && camera.cameras.length > 0 ) {
const matrices = [];
for ( const subCamera of camera.cameras ) {
matrices.push( subCamera.projectionMatrixInverse );
}
const cameraProjectionMatricesInverse = uniformArray( matrices ).setGroup( renderGroup ).label( 'cameraProjectionMatricesInverse' );
cameraProjectionMatrixInverse = cameraProjectionMatricesInverse.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraProjectionMatrixInverse' );
} else {
cameraProjectionMatrixInverse = uniform( 'mat4' ).label( 'cameraProjectionMatrixInverse' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrixInverse );
}
return cameraProjectionMatrixInverse;
} ).once() )();
/**
* TSL object that represents the view matrix of the camera used for the current render.
*
* @tsl
* @type {UniformNode<mat4>}
*/
export const cameraViewMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => {
let cameraViewMatrix;
if ( camera.isArrayCamera && camera.cameras.length > 0 ) {
const matrices = [];
for ( const subCamera of camera.cameras ) {
matrices.push( subCamera.matrixWorldInverse );
}
const cameraViewMatrices = uniformArray( matrices ).setGroup( renderGroup ).label( 'cameraViewMatrices' );
cameraViewMatrix = cameraViewMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ).toVar( 'cameraViewMatrix' );
} else {
cameraViewMatrix = uniform( 'mat4' ).label( 'cameraViewMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse );
}
return cameraViewMatrix;
} ).once() )();
/**
* TSL object that represents the world matrix of the camera used for the current render.
*
* @tsl
* @type {UniformNode<mat4>}
*/
export const cameraWorldMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraWorldMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld );
/**
* TSL object that represents the normal matrix of the camera used for the current render.
*
* @tsl
* @type {UniformNode<mat3>}
*/
export const cameraNormalMatrix = /*@__PURE__*/ uniform( 'mat3' ).label( 'cameraNormalMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix );
/**
* TSL object that represents the position in world space of the camera used for the current render.
*
* @tsl
* @type {UniformNode<vec3>}
*/
export const cameraPosition = /*@__PURE__*/ uniform( new Vector3() ).label( 'cameraPosition' ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) );

View File

@@ -0,0 +1,254 @@
import Node from '../core/Node.js';
import { nodeObject, Fn, bool, float } from '../tsl/TSLBase.js';
import { positionView } from './Position.js';
import { diffuseColor } from '../core/PropertyNode.js';
import { Loop } from '../utils/LoopNode.js';
import { smoothstep } from '../math/MathNode.js';
import { uniformArray } from './UniformArrayNode.js';
import { builtin } from './BuiltinNode.js';
/**
* This node is used in {@link NodeMaterial} to setup the clipping
* which can happen hardware-accelerated (if supported) and optionally
* use alpha-to-coverage for anti-aliasing clipped edges.
*
* @augments Node
*/
class ClippingNode extends Node {
static get type() {
return 'ClippingNode';
}
/**
* Constructs a new clipping node.
*
* @param {('default'|'hardware'|'alphaToCoverage')} [scope='default'] - The node's scope. Similar to other nodes,
* the selected scope influences the behavior of the node and what type of code is generated.
*/
constructor( scope = ClippingNode.DEFAULT ) {
super();
/**
* The node's scope. Similar to other nodes, the selected scope influences
* the behavior of the node and what type of code is generated.
*
* @type {('default'|'hardware'|'alphaToCoverage')}
*/
this.scope = scope;
}
/**
* Setups the node depending on the selected scope.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node} The result node.
*/
setup( builder ) {
super.setup( builder );
const clippingContext = builder.clippingContext;
const { intersectionPlanes, unionPlanes } = clippingContext;
this.hardwareClipping = builder.material.hardwareClipping;
if ( this.scope === ClippingNode.ALPHA_TO_COVERAGE ) {
return this.setupAlphaToCoverage( intersectionPlanes, unionPlanes );
} else if ( this.scope === ClippingNode.HARDWARE ) {
return this.setupHardwareClipping( unionPlanes, builder );
} else {
return this.setupDefault( intersectionPlanes, unionPlanes );
}
}
/**
* Setups alpha to coverage.
*
* @param {Array<Vector4>} intersectionPlanes - The intersection planes.
* @param {Array<Vector4>} unionPlanes - The union planes.
* @return {Node} The result node.
*/
setupAlphaToCoverage( intersectionPlanes, unionPlanes ) {
return Fn( () => {
const distanceToPlane = float().toVar( 'distanceToPlane' );
const distanceGradient = float().toVar( 'distanceToGradient' );
const clipOpacity = float( 1 ).toVar( 'clipOpacity' );
const numUnionPlanes = unionPlanes.length;
if ( this.hardwareClipping === false && numUnionPlanes > 0 ) {
const clippingPlanes = uniformArray( unionPlanes );
Loop( numUnionPlanes, ( { i } ) => {
const plane = clippingPlanes.element( i );
distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );
clipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ) );
} );
}
const numIntersectionPlanes = intersectionPlanes.length;
if ( numIntersectionPlanes > 0 ) {
const clippingPlanes = uniformArray( intersectionPlanes );
const intersectionClipOpacity = float( 1 ).toVar( 'intersectionClipOpacity' );
Loop( numIntersectionPlanes, ( { i } ) => {
const plane = clippingPlanes.element( i );
distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );
intersectionClipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ).oneMinus() );
} );
clipOpacity.mulAssign( intersectionClipOpacity.oneMinus() );
}
diffuseColor.a.mulAssign( clipOpacity );
diffuseColor.a.equal( 0.0 ).discard();
} )();
}
/**
* Setups the default clipping.
*
* @param {Array<Vector4>} intersectionPlanes - The intersection planes.
* @param {Array<Vector4>} unionPlanes - The union planes.
* @return {Node} The result node.
*/
setupDefault( intersectionPlanes, unionPlanes ) {
return Fn( () => {
const numUnionPlanes = unionPlanes.length;
if ( this.hardwareClipping === false && numUnionPlanes > 0 ) {
const clippingPlanes = uniformArray( unionPlanes );
Loop( numUnionPlanes, ( { i } ) => {
const plane = clippingPlanes.element( i );
positionView.dot( plane.xyz ).greaterThan( plane.w ).discard();
} );
}
const numIntersectionPlanes = intersectionPlanes.length;
if ( numIntersectionPlanes > 0 ) {
const clippingPlanes = uniformArray( intersectionPlanes );
const clipped = bool( true ).toVar( 'clipped' );
Loop( numIntersectionPlanes, ( { i } ) => {
const plane = clippingPlanes.element( i );
clipped.assign( positionView.dot( plane.xyz ).greaterThan( plane.w ).and( clipped ) );
} );
clipped.discard();
}
} )();
}
/**
* Setups hardware clipping.
*
* @param {Array<Vector4>} unionPlanes - The union planes.
* @param {NodeBuilder} builder - The current node builder.
* @return {Node} The result node.
*/
setupHardwareClipping( unionPlanes, builder ) {
const numUnionPlanes = unionPlanes.length;
builder.enableHardwareClipping( numUnionPlanes );
return Fn( () => {
const clippingPlanes = uniformArray( unionPlanes );
const hw_clip_distances = builtin( builder.getClipDistance() );
Loop( numUnionPlanes, ( { i } ) => {
const plane = clippingPlanes.element( i );
const distance = positionView.dot( plane.xyz ).sub( plane.w ).negate();
hw_clip_distances.element( i ).assign( distance );
} );
} )();
}
}
ClippingNode.ALPHA_TO_COVERAGE = 'alphaToCoverage';
ClippingNode.DEFAULT = 'default';
ClippingNode.HARDWARE = 'hardware';
export default ClippingNode;
/**
* TSL function for setting up the default clipping logic.
*
* @tsl
* @function
* @returns {ClippingNode}
*/
export const clipping = () => nodeObject( new ClippingNode() );
/**
* TSL function for setting up alpha to coverage.
*
* @tsl
* @function
* @returns {ClippingNode}
*/
export const clippingAlpha = () => nodeObject( new ClippingNode( ClippingNode.ALPHA_TO_COVERAGE ) );
/**
* TSL function for setting up hardware-based clipping.
*
* @tsl
* @function
* @returns {ClippingNode}
*/
export const hardwareClipping = () => nodeObject( new ClippingNode( ClippingNode.HARDWARE ) );

View File

@@ -0,0 +1,189 @@
import TextureNode from './TextureNode.js';
import { reflectVector, refractVector } from './ReflectVector.js';
import { nodeObject, nodeProxy, vec3 } from '../tsl/TSLBase.js';
import { CubeReflectionMapping, CubeRefractionMapping, WebGPUCoordinateSystem } from '../../constants.js';
import { materialEnvRotation } from './MaterialProperties.js';
import { CubeTexture } from '../../textures/CubeTexture.js';
const EmptyTexture = /*@__PURE__*/ new CubeTexture();
/**
* This type of uniform node represents a cube texture.
*
* @augments TextureNode
*/
class CubeTextureNode extends TextureNode {
static get type() {
return 'CubeTextureNode';
}
/**
* Constructs a new cube texture node.
*
* @param {CubeTexture} value - The cube texture.
* @param {?Node<vec3>} [uvNode=null] - The uv node.
* @param {?Node<int>} [levelNode=null] - The level node.
* @param {?Node<float>} [biasNode=null] - The bias node.
*/
constructor( value, uvNode = null, levelNode = null, biasNode = null ) {
super( value, uvNode, levelNode, biasNode );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isCubeTextureNode = true;
}
/**
* Overwrites the default implementation to return a fixed value `'cubeTexture'`.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The input type.
*/
getInputType( /*builder*/ ) {
return 'cubeTexture';
}
/**
* Returns a default uvs based on the mapping type of the cube texture.
*
* @return {Node<vec3>} The default uv attribute.
*/
getDefaultUV() {
const texture = this.value;
if ( texture.mapping === CubeReflectionMapping ) {
return reflectVector;
} else if ( texture.mapping === CubeRefractionMapping ) {
return refractVector;
} else {
console.error( 'THREE.CubeTextureNode: Mapping "%s" not supported.', texture.mapping );
return vec3( 0, 0, 0 );
}
}
/**
* Overwritten with an empty implementation since the `updateMatrix` flag is ignored
* for cube textures. The uv transformation matrix is not applied to cube textures.
*
* @param {boolean} value - The update toggle.
*/
setUpdateMatrix( /*updateMatrix*/ ) { } // Ignore .updateMatrix for CubeTextureNode
/**
* Setups the uv node. Depending on the backend as well as the texture type, it might be necessary
* to modify the uv node for correct sampling.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {Node} uvNode - The uv node to setup.
* @return {Node} The updated uv node.
*/
setupUV( builder, uvNode ) {
const texture = this.value;
if ( builder.renderer.coordinateSystem === WebGPUCoordinateSystem || ! texture.isRenderTargetTexture ) {
uvNode = vec3( uvNode.x.negate(), uvNode.yz );
}
return materialEnvRotation.mul( uvNode );
}
/**
* Generates the uv code snippet.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {Node} cubeUV - The uv node to generate code for.
* @return {string} The generated code snippet.
*/
generateUV( builder, cubeUV ) {
return cubeUV.build( builder, 'vec3' );
}
}
export default CubeTextureNode;
/**
* TSL function for creating a cube texture node.
*
* @tsl
* @function
* @param {CubeTexture} value - The cube texture.
* @param {?Node<vec3>} [uvNode=null] - The uv node.
* @param {?Node<int>} [levelNode=null] - The level node.
* @param {?Node<float>} [biasNode=null] - The bias node.
* @returns {CubeTextureNode}
*/
export const cubeTextureBase = /*@__PURE__*/ nodeProxy( CubeTextureNode ).setParameterLength( 1, 4 ).setName( 'cubeTexture' );
/**
* TSL function for creating a cube texture uniform node.
*
* @tsl
* @function
* @param {?CubeTexture|CubeTextureNode} [value=EmptyTexture] - The cube texture.
* @param {?Node<vec3>} [uvNode=null] - The uv node.
* @param {?Node<int>} [levelNode=null] - The level node.
* @param {?Node<float>} [biasNode=null] - The bias node.
* @returns {CubeTextureNode}
*/
export const cubeTexture = ( value = EmptyTexture, uvNode = null, levelNode = null, biasNode = null ) => {
let textureNode;
if ( value && value.isCubeTextureNode === true ) {
textureNode = nodeObject( value.clone() );
textureNode.referenceNode = value.getSelf(); // Ensure the reference is set to the original node
if ( uvNode !== null ) textureNode.uvNode = nodeObject( uvNode );
if ( levelNode !== null ) textureNode.levelNode = nodeObject( levelNode );
if ( biasNode !== null ) textureNode.biasNode = nodeObject( biasNode );
} else {
textureNode = cubeTextureBase( value, uvNode, levelNode, biasNode );
}
return textureNode;
};
/**
* TSL function for creating a uniform cube texture node.
*
* @tsl
* @function
* @param {?CubeTexture} [value=EmptyTexture] - The cube texture.
* @returns {CubeTextureNode}
*/
export const uniformCubeTexture = ( value = EmptyTexture ) => cubeTextureBase( value );

View File

@@ -0,0 +1,224 @@
import Node from '../core/Node.js';
import { varyingProperty } from '../core/PropertyNode.js';
import { instancedBufferAttribute, instancedDynamicBufferAttribute } from './BufferAttributeNode.js';
import { normalLocal, transformNormal } from './Normal.js';
import { positionLocal } from './Position.js';
import { nodeProxy, vec3, mat4 } from '../tsl/TSLBase.js';
import { NodeUpdateType } from '../core/constants.js';
import { buffer } from '../accessors/BufferNode.js';
import { instanceIndex } from '../core/IndexNode.js';
import { InstancedInterleavedBuffer } from '../../core/InstancedInterleavedBuffer.js';
import { InstancedBufferAttribute } from '../../core/InstancedBufferAttribute.js';
import { DynamicDrawUsage } from '../../constants.js';
/**
* This node implements the vertex shader logic which is required
* when rendering 3D objects via instancing. The code makes sure
* vertex positions, normals and colors can be modified via instanced
* data.
*
* @augments Node
*/
class InstanceNode extends Node {
static get type() {
return 'InstanceNode';
}
/**
* Constructs a new instance node.
*
* @param {number} count - The number of instances.
* @param {InstancedBufferAttribute} instanceMatrix - Instanced buffer attribute representing the instance transformations.
* @param {?InstancedBufferAttribute} instanceColor - Instanced buffer attribute representing the instance colors.
*/
constructor( count, instanceMatrix, instanceColor = null ) {
super( 'void' );
/**
* The number of instances.
*
* @type {number}
*/
this.count = count;
/**
* Instanced buffer attribute representing the transformation of instances.
*
* @type {InstancedBufferAttribute}
*/
this.instanceMatrix = instanceMatrix;
/**
* Instanced buffer attribute representing the color of instances.
*
* @type {InstancedBufferAttribute}
*/
this.instanceColor = instanceColor;
/**
* The node that represents the instance matrix data.
*
* @type {?Node}
*/
this.instanceMatrixNode = null;
/**
* The node that represents the instance color data.
*
* @type {?Node}
* @default null
*/
this.instanceColorNode = null;
/**
* The update type is set to `frame` since an update
* of instanced buffer data must be checked per frame.
*
* @type {string}
* @default 'frame'
*/
this.updateType = NodeUpdateType.FRAME;
/**
* A reference to a buffer that is used by `instanceMatrixNode`.
*
* @type {?InstancedInterleavedBuffer}
*/
this.buffer = null;
/**
* A reference to a buffer that is used by `instanceColorNode`.
*
* @type {?InstancedBufferAttribute}
*/
this.bufferColor = null;
}
/**
* Setups the internal buffers and nodes and assigns the transformed vertex data
* to predefined node variables for accumulation. That follows the same patterns
* like with morph and skinning nodes.
*
* @param {NodeBuilder} builder - The current node builder.
*/
setup( builder ) {
const { count, instanceMatrix, instanceColor } = this;
let { instanceMatrixNode, instanceColorNode } = this;
if ( instanceMatrixNode === null ) {
// Both WebGPU and WebGL backends have UBO max limited to 64kb. Matrix count number bigger than 1000 ( 16 * 4 * 1000 = 64kb ) will fallback to attribute.
if ( count <= 1000 ) {
instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', Math.max( count, 1 ) ).element( instanceIndex );
} else {
const buffer = new InstancedInterleavedBuffer( instanceMatrix.array, 16, 1 );
this.buffer = buffer;
const bufferFn = instanceMatrix.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;
const instanceBuffers = [
// F.Signature -> bufferAttribute( array, type, stride, offset )
bufferFn( buffer, 'vec4', 16, 0 ),
bufferFn( buffer, 'vec4', 16, 4 ),
bufferFn( buffer, 'vec4', 16, 8 ),
bufferFn( buffer, 'vec4', 16, 12 )
];
instanceMatrixNode = mat4( ...instanceBuffers );
}
this.instanceMatrixNode = instanceMatrixNode;
}
if ( instanceColor && instanceColorNode === null ) {
const buffer = new InstancedBufferAttribute( instanceColor.array, 3 );
const bufferFn = instanceColor.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;
this.bufferColor = buffer;
instanceColorNode = vec3( bufferFn( buffer, 'vec3', 3, 0 ) );
this.instanceColorNode = instanceColorNode;
}
// POSITION
const instancePosition = instanceMatrixNode.mul( positionLocal ).xyz;
positionLocal.assign( instancePosition );
// NORMAL
if ( builder.hasGeometryAttribute( 'normal' ) ) {
const instanceNormal = transformNormal( normalLocal, instanceMatrixNode );
// ASSIGNS
normalLocal.assign( instanceNormal );
}
// COLOR
if ( this.instanceColorNode !== null ) {
varyingProperty( 'vec3', 'vInstanceColor' ).assign( this.instanceColorNode );
}
}
/**
* Checks if the internal buffers required an update.
*
* @param {NodeFrame} frame - The current node frame.
*/
update( /*frame*/ ) {
if ( this.instanceMatrix.usage !== DynamicDrawUsage && this.buffer !== null && this.instanceMatrix.version !== this.buffer.version ) {
this.buffer.version = this.instanceMatrix.version;
}
if ( this.instanceColor && this.instanceColor.usage !== DynamicDrawUsage && this.bufferColor !== null && this.instanceColor.version !== this.bufferColor.version ) {
this.bufferColor.version = this.instanceColor.version;
}
}
}
export default InstanceNode;
/**
* TSL function for creating an instance node.
*
* @tsl
* @function
* @param {number} count - The number of instances.
* @param {InstancedBufferAttribute} instanceMatrix - Instanced buffer attribute representing the instance transformations.
* @param {?InstancedBufferAttribute} instanceColor - Instanced buffer attribute representing the instance colors.
* @returns {InstanceNode}
*/
export const instance = /*@__PURE__*/ nodeProxy( InstanceNode ).setParameterLength( 2, 3 );

View File

@@ -0,0 +1,50 @@
import InstanceNode from './InstanceNode.js';
import { nodeProxy } from '../tsl/TSLBase.js';
/**
* This is a special version of `InstanceNode` which requires the usage of {@link InstancedMesh}.
* It allows an easier setup of the instance node.
*
* @augments InstanceNode
*/
class InstancedMeshNode extends InstanceNode {
static get type() {
return 'InstancedMeshNode';
}
/**
* Constructs a new instanced mesh node.
*
* @param {InstancedMesh} instancedMesh - The instanced mesh.
*/
constructor( instancedMesh ) {
const { count, instanceMatrix, instanceColor } = instancedMesh;
super( count, instanceMatrix, instanceColor );
/**
* A reference to the instanced mesh.
*
* @type {InstancedMesh}
*/
this.instancedMesh = instancedMesh;
}
}
export default InstancedMeshNode;
/**
* TSL function for creating an instanced mesh node.
*
* @tsl
* @function
* @param {InstancedMesh} instancedMesh - The instancedMesh.
* @returns {InstancedMeshNode}
*/
export const instancedMesh = /*@__PURE__*/ nodeProxy( InstancedMeshNode ).setParameterLength( 1 );

129
app/node_modules/three/src/nodes/accessors/Lights.js generated vendored Normal file
View File

@@ -0,0 +1,129 @@
import { uniform } from '../core/UniformNode.js';
import { renderGroup } from '../core/UniformGroupNode.js';
import { Vector3 } from '../../math/Vector3.js';
import { cameraViewMatrix } from './Camera.js';
import { positionWorld } from './Position.js';
let uniformsLib;
function getLightData( light ) {
uniformsLib = uniformsLib || new WeakMap();
let uniforms = uniformsLib.get( light );
if ( uniforms === undefined ) uniformsLib.set( light, uniforms = {} );
return uniforms;
}
/**
* TSL function for getting a shadow matrix uniform node for the given light.
*
* @tsl
* @function
* @param {Light} light -The light source.
* @returns {UniformNode<mat4>} The shadow matrix uniform node.
*/
export function lightShadowMatrix( light ) {
const data = getLightData( light );
return data.shadowMatrix || ( data.shadowMatrix = uniform( 'mat4' ).setGroup( renderGroup ).onRenderUpdate( ( frame ) => {
if ( light.castShadow !== true || frame.renderer.shadowMap.enabled === false ) {
light.shadow.updateMatrices( light );
}
return light.shadow.matrix;
} ) );
}
/**
* TSL function for getting projected uv coordinates for the given light.
* Relevant when using maps with spot lights.
*
* @tsl
* @function
* @param {Light} light -The light source.
* @param {Node<vec3>} [position=positionWorld] -The position to project.
* @returns {Node<vec3>} The projected uvs.
*/
export function lightProjectionUV( light, position = positionWorld ) {
const spotLightCoord = lightShadowMatrix( light ).mul( position );
const projectionUV = spotLightCoord.xyz.div( spotLightCoord.w );
return projectionUV;
}
/**
* TSL function for getting the position in world space for the given light.
*
* @tsl
* @function
* @param {Light} light -The light source.
* @returns {UniformNode<vec3>} The light's position in world space.
*/
export function lightPosition( light ) {
const data = getLightData( light );
return data.position || ( data.position = uniform( new Vector3() ).setGroup( renderGroup ).onRenderUpdate( ( _, self ) => self.value.setFromMatrixPosition( light.matrixWorld ) ) );
}
/**
* TSL function for getting the light target position in world space for the given light.
*
* @tsl
* @function
* @param {Light} light -The light source.
* @returns {UniformNode<vec3>} The light target position in world space.
*/
export function lightTargetPosition( light ) {
const data = getLightData( light );
return data.targetPosition || ( data.targetPosition = uniform( new Vector3() ).setGroup( renderGroup ).onRenderUpdate( ( _, self ) => self.value.setFromMatrixPosition( light.target.matrixWorld ) ) );
}
/**
* TSL function for getting the position in view space for the given light.
*
* @tsl
* @function
* @param {Light} light - The light source.
* @returns {UniformNode<vec3>} The light's position in view space.
*/
export function lightViewPosition( light ) {
const data = getLightData( light );
return data.viewPosition || ( data.viewPosition = uniform( new Vector3() ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => {
self.value = self.value || new Vector3();
self.value.setFromMatrixPosition( light.matrixWorld );
self.value.applyMatrix4( camera.matrixWorldInverse );
} ) );
}
/**
* TSL function for getting the light target direction for the given light.
*
* @tsl
* @function
* @param {Light} light -The light source.
* @returns {Node<vec3>} The light's target direction.
*/
export const lightTargetDirection = ( light ) => cameraViewMatrix.transformDirection( lightPosition( light ).sub( lightTargetPosition( light ) ) );

View File

@@ -0,0 +1,775 @@
import Node from '../core/Node.js';
import { reference } from './ReferenceNode.js';
import { materialReference } from './MaterialReferenceNode.js';
import { normalView } from './Normal.js';
import { nodeImmutable, float, vec2, vec3, mat2 } from '../tsl/TSLBase.js';
import { uniform } from '../core/UniformNode.js';
import { normalMap } from '../display/NormalMapNode.js';
import { bumpMap } from '../display/BumpMapNode.js';
import { Vector2 } from '../../math/Vector2.js';
const _propertyCache = new Map();
/**
* This class should simplify the node access to material properties.
* It internal uses reference nodes to make sure changes to material
* properties are automatically reflected to predefined TSL objects
* like e.g. `materialColor`.
*
* @augments Node
*/
class MaterialNode extends Node {
static get type() {
return 'MaterialNode';
}
/**
* Constructs a new material node.
*
* @param {string} scope - The scope defines what kind of material property is referred by the node.
*/
constructor( scope ) {
super();
/**
* The scope defines what material property is referred by the node.
*
* @type {string}
*/
this.scope = scope;
}
/**
* Returns a cached reference node for the given property and type.
*
* @param {string} property - The name of the material property.
* @param {string} type - The uniform type of the property.
* @return {MaterialReferenceNode} A material reference node representing the property access.
*/
getCache( property, type ) {
let node = _propertyCache.get( property );
if ( node === undefined ) {
node = materialReference( property, type );
_propertyCache.set( property, node );
}
return node;
}
/**
* Returns a float-typed material reference node for the given property name.
*
* @param {string} property - The name of the material property.
* @return {MaterialReferenceNode<float>} A material reference node representing the property access.
*/
getFloat( property ) {
return this.getCache( property, 'float' );
}
/**
* Returns a color-typed material reference node for the given property name.
*
* @param {string} property - The name of the material property.
* @return {MaterialReferenceNode<color>} A material reference node representing the property access.
*/
getColor( property ) {
return this.getCache( property, 'color' );
}
/**
* Returns a texture-typed material reference node for the given property name.
*
* @param {string} property - The name of the material property.
* @return {MaterialReferenceNode} A material reference node representing the property access.
*/
getTexture( property ) {
return this.getCache( property === 'map' ? 'map' : property + 'Map', 'texture' );
}
/**
* The node setup is done depending on the selected scope. Multiple material properties
* might be grouped into a single node composition if they logically belong together.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node} The node representing the selected scope.
*/
setup( builder ) {
const material = builder.context.material;
const scope = this.scope;
let node = null;
if ( scope === MaterialNode.COLOR ) {
const colorNode = material.color !== undefined ? this.getColor( scope ) : vec3();
if ( material.map && material.map.isTexture === true ) {
node = colorNode.mul( this.getTexture( 'map' ) );
} else {
node = colorNode;
}
} else if ( scope === MaterialNode.OPACITY ) {
const opacityNode = this.getFloat( scope );
if ( material.alphaMap && material.alphaMap.isTexture === true ) {
node = opacityNode.mul( this.getTexture( 'alpha' ) );
} else {
node = opacityNode;
}
} else if ( scope === MaterialNode.SPECULAR_STRENGTH ) {
if ( material.specularMap && material.specularMap.isTexture === true ) {
node = this.getTexture( 'specular' ).r;
} else {
node = float( 1 );
}
} else if ( scope === MaterialNode.SPECULAR_INTENSITY ) {
const specularIntensityNode = this.getFloat( scope );
if ( material.specularIntensityMap && material.specularIntensityMap.isTexture === true ) {
node = specularIntensityNode.mul( this.getTexture( scope ).a );
} else {
node = specularIntensityNode;
}
} else if ( scope === MaterialNode.SPECULAR_COLOR ) {
const specularColorNode = this.getColor( scope );
if ( material.specularColorMap && material.specularColorMap.isTexture === true ) {
node = specularColorNode.mul( this.getTexture( scope ).rgb );
} else {
node = specularColorNode;
}
} else if ( scope === MaterialNode.ROUGHNESS ) { // TODO: cleanup similar branches
const roughnessNode = this.getFloat( scope );
if ( material.roughnessMap && material.roughnessMap.isTexture === true ) {
node = roughnessNode.mul( this.getTexture( scope ).g );
} else {
node = roughnessNode;
}
} else if ( scope === MaterialNode.METALNESS ) {
const metalnessNode = this.getFloat( scope );
if ( material.metalnessMap && material.metalnessMap.isTexture === true ) {
node = metalnessNode.mul( this.getTexture( scope ).b );
} else {
node = metalnessNode;
}
} else if ( scope === MaterialNode.EMISSIVE ) {
const emissiveIntensityNode = this.getFloat( 'emissiveIntensity' );
const emissiveNode = this.getColor( scope ).mul( emissiveIntensityNode );
if ( material.emissiveMap && material.emissiveMap.isTexture === true ) {
node = emissiveNode.mul( this.getTexture( scope ) );
} else {
node = emissiveNode;
}
} else if ( scope === MaterialNode.NORMAL ) {
if ( material.normalMap ) {
node = normalMap( this.getTexture( 'normal' ), this.getCache( 'normalScale', 'vec2' ) );
node.normalMapType = material.normalMapType;
} else if ( material.bumpMap ) {
node = bumpMap( this.getTexture( 'bump' ).r, this.getFloat( 'bumpScale' ) );
} else {
node = normalView;
}
} else if ( scope === MaterialNode.CLEARCOAT ) {
const clearcoatNode = this.getFloat( scope );
if ( material.clearcoatMap && material.clearcoatMap.isTexture === true ) {
node = clearcoatNode.mul( this.getTexture( scope ).r );
} else {
node = clearcoatNode;
}
} else if ( scope === MaterialNode.CLEARCOAT_ROUGHNESS ) {
const clearcoatRoughnessNode = this.getFloat( scope );
if ( material.clearcoatRoughnessMap && material.clearcoatRoughnessMap.isTexture === true ) {
node = clearcoatRoughnessNode.mul( this.getTexture( scope ).r );
} else {
node = clearcoatRoughnessNode;
}
} else if ( scope === MaterialNode.CLEARCOAT_NORMAL ) {
if ( material.clearcoatNormalMap ) {
node = normalMap( this.getTexture( scope ), this.getCache( scope + 'Scale', 'vec2' ) );
} else {
node = normalView;
}
} else if ( scope === MaterialNode.SHEEN ) {
const sheenNode = this.getColor( 'sheenColor' ).mul( this.getFloat( 'sheen' ) ); // Move this mul() to CPU
if ( material.sheenColorMap && material.sheenColorMap.isTexture === true ) {
node = sheenNode.mul( this.getTexture( 'sheenColor' ).rgb );
} else {
node = sheenNode;
}
} else if ( scope === MaterialNode.SHEEN_ROUGHNESS ) {
const sheenRoughnessNode = this.getFloat( scope );
if ( material.sheenRoughnessMap && material.sheenRoughnessMap.isTexture === true ) {
node = sheenRoughnessNode.mul( this.getTexture( scope ).a );
} else {
node = sheenRoughnessNode;
}
node = node.clamp( 0.07, 1.0 );
} else if ( scope === MaterialNode.ANISOTROPY ) {
if ( material.anisotropyMap && material.anisotropyMap.isTexture === true ) {
const anisotropyPolar = this.getTexture( scope );
const anisotropyMat = mat2( materialAnisotropyVector.x, materialAnisotropyVector.y, materialAnisotropyVector.y.negate(), materialAnisotropyVector.x );
node = anisotropyMat.mul( anisotropyPolar.rg.mul( 2.0 ).sub( vec2( 1.0 ) ).normalize().mul( anisotropyPolar.b ) );
} else {
node = materialAnisotropyVector;
}
} else if ( scope === MaterialNode.IRIDESCENCE_THICKNESS ) {
const iridescenceThicknessMaximum = reference( '1', 'float', material.iridescenceThicknessRange );
if ( material.iridescenceThicknessMap ) {
const iridescenceThicknessMinimum = reference( '0', 'float', material.iridescenceThicknessRange );
node = iridescenceThicknessMaximum.sub( iridescenceThicknessMinimum ).mul( this.getTexture( scope ).g ).add( iridescenceThicknessMinimum );
} else {
node = iridescenceThicknessMaximum;
}
} else if ( scope === MaterialNode.TRANSMISSION ) {
const transmissionNode = this.getFloat( scope );
if ( material.transmissionMap ) {
node = transmissionNode.mul( this.getTexture( scope ).r );
} else {
node = transmissionNode;
}
} else if ( scope === MaterialNode.THICKNESS ) {
const thicknessNode = this.getFloat( scope );
if ( material.thicknessMap ) {
node = thicknessNode.mul( this.getTexture( scope ).g );
} else {
node = thicknessNode;
}
} else if ( scope === MaterialNode.IOR ) {
node = this.getFloat( scope );
} else if ( scope === MaterialNode.LIGHT_MAP ) {
node = this.getTexture( scope ).rgb.mul( this.getFloat( 'lightMapIntensity' ) );
} else if ( scope === MaterialNode.AO ) {
node = this.getTexture( scope ).r.sub( 1.0 ).mul( this.getFloat( 'aoMapIntensity' ) ).add( 1.0 );
} else if ( scope === MaterialNode.LINE_DASH_OFFSET ) {
node = ( material.dashOffset ) ? this.getFloat( scope ) : float( 0 );
} else {
const outputType = this.getNodeType( builder );
node = this.getCache( scope, outputType );
}
return node;
}
}
MaterialNode.ALPHA_TEST = 'alphaTest';
MaterialNode.COLOR = 'color';
MaterialNode.OPACITY = 'opacity';
MaterialNode.SHININESS = 'shininess';
MaterialNode.SPECULAR = 'specular';
MaterialNode.SPECULAR_STRENGTH = 'specularStrength';
MaterialNode.SPECULAR_INTENSITY = 'specularIntensity';
MaterialNode.SPECULAR_COLOR = 'specularColor';
MaterialNode.REFLECTIVITY = 'reflectivity';
MaterialNode.ROUGHNESS = 'roughness';
MaterialNode.METALNESS = 'metalness';
MaterialNode.NORMAL = 'normal';
MaterialNode.CLEARCOAT = 'clearcoat';
MaterialNode.CLEARCOAT_ROUGHNESS = 'clearcoatRoughness';
MaterialNode.CLEARCOAT_NORMAL = 'clearcoatNormal';
MaterialNode.EMISSIVE = 'emissive';
MaterialNode.ROTATION = 'rotation';
MaterialNode.SHEEN = 'sheen';
MaterialNode.SHEEN_ROUGHNESS = 'sheenRoughness';
MaterialNode.ANISOTROPY = 'anisotropy';
MaterialNode.IRIDESCENCE = 'iridescence';
MaterialNode.IRIDESCENCE_IOR = 'iridescenceIOR';
MaterialNode.IRIDESCENCE_THICKNESS = 'iridescenceThickness';
MaterialNode.IOR = 'ior';
MaterialNode.TRANSMISSION = 'transmission';
MaterialNode.THICKNESS = 'thickness';
MaterialNode.ATTENUATION_DISTANCE = 'attenuationDistance';
MaterialNode.ATTENUATION_COLOR = 'attenuationColor';
MaterialNode.LINE_SCALE = 'scale';
MaterialNode.LINE_DASH_SIZE = 'dashSize';
MaterialNode.LINE_GAP_SIZE = 'gapSize';
MaterialNode.LINE_WIDTH = 'linewidth';
MaterialNode.LINE_DASH_OFFSET = 'dashOffset';
MaterialNode.POINT_SIZE = 'size';
MaterialNode.DISPERSION = 'dispersion';
MaterialNode.LIGHT_MAP = 'light';
MaterialNode.AO = 'ao';
export default MaterialNode;
/**
* TSL object that represents alpha test of the current material.
*
* @tsl
* @type {Node<float>}
*/
export const materialAlphaTest = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ALPHA_TEST );
/**
* TSL object that represents the diffuse color of the current material.
* The value is composed via `color` * `map`.
*
* @tsl
* @type {Node<vec3>}
*/
export const materialColor = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.COLOR );
/**
* TSL object that represents the shininess of the current material.
*
* @tsl
* @type {Node<float>}
*/
export const materialShininess = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SHININESS );
/**
* TSL object that represents the emissive color of the current material.
* The value is composed via `emissive` * `emissiveIntensity` * `emissiveMap`.
*
* @tsl
* @type {Node<vec3>}
*/
export const materialEmissive = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.EMISSIVE );
/**
* TSL object that represents the opacity of the current material.
* The value is composed via `opacity` * `alphaMap`.
*
* @tsl
* @type {Node<float>}
*/
export const materialOpacity = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.OPACITY );
/**
* TSL object that represents the specular of the current material.
*
* @tsl
* @type {Node<vec3>}
*/
export const materialSpecular = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SPECULAR );
/**
* TSL object that represents the specular intensity of the current material.
* The value is composed via `specularIntensity` * `specularMap.a`.
*
* @tsl
* @type {Node<float>}
*/
export const materialSpecularIntensity = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SPECULAR_INTENSITY );
/**
* TSL object that represents the specular color of the current material.
* The value is composed via `specularColor` * `specularMap.rgb`.
*
* @tsl
* @type {Node<vec3>}
*/
export const materialSpecularColor = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SPECULAR_COLOR );
/**
* TSL object that represents the specular strength of the current material.
* The value is composed via `specularMap.r`.
*
* @tsl
* @type {Node<float>}
*/
export const materialSpecularStrength = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SPECULAR_STRENGTH );
/**
* TSL object that represents the reflectivity of the current material.
*
* @tsl
* @type {Node<float>}
*/
export const materialReflectivity = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.REFLECTIVITY );
/**
* TSL object that represents the roughness of the current material.
* The value is composed via `roughness` * `roughnessMap.g`.
*
* @tsl
* @type {Node<float>}
*/
export const materialRoughness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ROUGHNESS );
/**
* TSL object that represents the metalness of the current material.
* The value is composed via `metalness` * `metalnessMap.b`.
*
* @tsl
* @type {Node<float>}
*/
export const materialMetalness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.METALNESS );
/**
* TSL object that represents the normal of the current material.
* The value will be either `normalMap` * `normalScale`, `bumpMap` * `bumpScale` or `normalView`.
*
* @tsl
* @type {Node<vec3>}
*/
export const materialNormal = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.NORMAL );
/**
* TSL object that represents the clearcoat of the current material.
* The value is composed via `clearcoat` * `clearcoatMap.r`
*
* @tsl
* @type {Node<float>}
*/
export const materialClearcoat = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.CLEARCOAT );
/**
* TSL object that represents the clearcoat roughness of the current material.
* The value is composed via `clearcoatRoughness` * `clearcoatRoughnessMap.r`.
*
* @tsl
* @type {Node<float>}
*/
export const materialClearcoatRoughness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.CLEARCOAT_ROUGHNESS );
/**
* TSL object that represents the clearcoat normal of the current material.
* The value will be either `clearcoatNormalMap` or `normalView`.
*
* @tsl
* @type {Node<vec3>}
*/
export const materialClearcoatNormal = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.CLEARCOAT_NORMAL );
/**
* TSL object that represents the rotation of the current sprite material.
*
* @tsl
* @type {Node<float>}
*/
export const materialRotation = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ROTATION );
/**
* TSL object that represents the sheen color of the current material.
* The value is composed via `sheen` * `sheenColor` * `sheenColorMap`.
*
* @tsl
* @type {Node<vec3>}
*/
export const materialSheen = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SHEEN );
/**
* TSL object that represents the sheen roughness of the current material.
* The value is composed via `sheenRoughness` * `sheenRoughnessMap.a`.
*
* @tsl
* @type {Node<float>}
*/
export const materialSheenRoughness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SHEEN_ROUGHNESS );
/**
* TSL object that represents the anisotropy of the current material.
*
* @tsl
* @type {Node<vec2>}
*/
export const materialAnisotropy = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ANISOTROPY );
/**
* TSL object that represents the iridescence of the current material.
*
* @tsl
* @type {Node<float>}
*/
export const materialIridescence = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE );
/**
* TSL object that represents the iridescence IOR of the current material.
*
* @tsl
* @type {Node<float>}
*/
export const materialIridescenceIOR = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_IOR );
/**
* TSL object that represents the iridescence thickness of the current material.
*
* @tsl
* @type {Node<float>}
*/
export const materialIridescenceThickness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_THICKNESS );
/**
* TSL object that represents the transmission of the current material.
* The value is composed via `transmission` * `transmissionMap.r`.
*
* @tsl
* @type {Node<float>}
*/
export const materialTransmission = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.TRANSMISSION );
/**
* TSL object that represents the thickness of the current material.
* The value is composed via `thickness` * `thicknessMap.g`.
*
* @tsl
* @type {Node<float>}
*/
export const materialThickness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.THICKNESS );
/**
* TSL object that represents the IOR of the current material.
*
* @tsl
* @type {Node<float>}
*/
export const materialIOR = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.IOR );
/**
* TSL object that represents the attenuation distance of the current material.
*
* @tsl
* @type {Node<float>}
*/
export const materialAttenuationDistance = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ATTENUATION_DISTANCE );
/**
* TSL object that represents the attenuation color of the current material.
*
* @tsl
* @type {Node<vec3>}
*/
export const materialAttenuationColor = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ATTENUATION_COLOR );
/**
* TSL object that represents the scale of the current dashed line material.
*
* @tsl
* @type {Node<float>}
*/
export const materialLineScale = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_SCALE );
/**
* TSL object that represents the dash size of the current dashed line material.
*
* @tsl
* @type {Node<float>}
*/
export const materialLineDashSize = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_DASH_SIZE );
/**
* TSL object that represents the gap size of the current dashed line material.
*
* @tsl
* @type {Node<float>}
*/
export const materialLineGapSize = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_GAP_SIZE );
/**
* TSL object that represents the line width of the current line material.
*
* @tsl
* @type {Node<float>}
*/
export const materialLineWidth = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_WIDTH );
/**
* TSL object that represents the dash offset of the current line material.
*
* @tsl
* @type {Node<float>}
*/
export const materialLineDashOffset = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_DASH_OFFSET );
/**
* TSL object that represents the point size of the current points material.
*
* @tsl
* @type {Node<float>}
*/
export const materialPointSize = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.POINT_SIZE );
/**
* TSL object that represents the dispersion of the current material.
*
* @tsl
* @type {Node<float>}
*/
export const materialDispersion = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.DISPERSION );
/**
* TSL object that represents the light map of the current material.
* The value is composed via `lightMapIntensity` * `lightMap.rgb`.
*
* @tsl
* @type {Node<vec3>}
*/
export const materialLightMap = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LIGHT_MAP );
/**
* TSL object that represents the ambient occlusion map of the current material.
* The value is composed via `aoMap.r` - 1 * `aoMapIntensity` + 1.
*
* @tsl
* @type {Node<float>}
*/
export const materialAO = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.AO );
/**
* TSL object that represents the anisotropy vector of the current material.
*
* @tsl
* @type {Node<vec2>}
*/
export const materialAnisotropyVector = /*@__PURE__*/ uniform( new Vector2() ).onReference( function ( frame ) {
return frame.material;
} ).onRenderUpdate( function ( { material } ) {
this.value.set( material.anisotropy * Math.cos( material.anisotropyRotation ), material.anisotropy * Math.sin( material.anisotropyRotation ) );
} );

View File

@@ -0,0 +1,59 @@
import { Euler } from '../../math/Euler.js';
import { Matrix4 } from '../../math/Matrix4.js';
import { uniform } from '../core/UniformNode.js';
const _e1 = /*@__PURE__*/ new Euler();
const _m1 = /*@__PURE__*/ new Matrix4();
/**
* TSL object that represents the refraction ratio of the material used for rendering the current object.
*
* @tsl
* @type {UniformNode<float>}
*/
export const materialRefractionRatio = /*@__PURE__*/ uniform( 0 ).onReference( ( { material } ) => material ).onObjectUpdate( ( { material } ) => material.refractionRatio );
/**
* TSL object that represents the intensity of environment maps of PBR materials.
* When `material.envMap` is set, the value is `material.envMapIntensity` otherwise `scene.environmentIntensity`.
*
* @tsl
* @type {Node<float>}
*/
export const materialEnvIntensity = /*@__PURE__*/ uniform( 1 ).onReference( ( { material } ) => material ).onObjectUpdate( function ( { material, scene } ) {
return material.envMap ? material.envMapIntensity : scene.environmentIntensity;
} );
/**
* TSL object that represents the rotation of environment maps.
* When `material.envMap` is set, the value is `material.envMapRotation`. `scene.environmentRotation` controls the
* rotation of `scene.environment` instead.
*
* @tsl
* @type {Node<mat4>}
*/
export const materialEnvRotation = /*@__PURE__*/ uniform( new Matrix4() ).onReference( function ( frame ) {
return frame.material;
} ).onObjectUpdate( function ( { material, scene } ) {
const rotation = ( scene.environment !== null && material.envMap === null ) ? scene.environmentRotation : material.envMapRotation;
if ( rotation ) {
_e1.copy( rotation );
_m1.makeRotationFromEuler( _e1 );
} else {
_m1.identity();
}
return _m1;
} );

View File

@@ -0,0 +1,85 @@
import ReferenceNode from './ReferenceNode.js';
import { nodeObject } from '../tsl/TSLBase.js';
/**
* This node is a special type of reference node which is intended
* for linking material properties with node values.
* ```js
* const opacityNode = materialReference( 'opacity', 'float', material );
* ```
* When changing `material.opacity`, the node value of `opacityNode` will
* automatically be updated.
*
* @augments ReferenceNode
*/
class MaterialReferenceNode extends ReferenceNode {
static get type() {
return 'MaterialReferenceNode';
}
/**
* Constructs a new material reference node.
*
* @param {string} property - The name of the property the node refers to.
* @param {string} inputType - The uniform type that should be used to represent the property value.
* @param {?Material} [material=null] - The material the property belongs to. When no material is set,
* the node refers to the material of the current rendered object.
*/
constructor( property, inputType, material = null ) {
super( property, inputType, material );
/**
* The material the property belongs to. When no material is set,
* the node refers to the material of the current rendered object.
*
* @type {?Material}
* @default null
*/
this.material = material;
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isMaterialReferenceNode = true;
}
/**
* Updates the reference based on the given state. The state is only evaluated
* {@link MaterialReferenceNode#material} is not set.
*
* @param {(NodeFrame|NodeBuilder)} state - The current state.
* @return {Object} The updated reference.
*/
updateReference( state ) {
this.reference = this.material !== null ? this.material : state.material;
return this.reference;
}
}
export default MaterialReferenceNode;
/**
* TSL function for creating a material reference node.
*
* @tsl
* @function
* @param {string} name - The name of the property the node refers to.
* @param {string} type - The uniform type that should be used to represent the property value.
* @param {?Material} [material=null] - The material the property belongs to.
* When no material is set, the node refers to the material of the current rendered object.
* @returns {MaterialReferenceNode}
*/
export const materialReference = ( name, type, material = null ) => nodeObject( new MaterialReferenceNode( name, type, material ) );

184
app/node_modules/three/src/nodes/accessors/ModelNode.js generated vendored Normal file
View File

@@ -0,0 +1,184 @@
import Object3DNode from './Object3DNode.js';
import { Fn, nodeImmutable } from '../tsl/TSLBase.js';
import { uniform } from '../core/UniformNode.js';
import { Matrix4 } from '../../math/Matrix4.js';
import { cameraViewMatrix } from './Camera.js';
import { Matrix3 } from '../../math/Matrix3.js';
/**
* This type of node is a specialized version of `Object3DNode`
* with larger set of model related metrics. Unlike `Object3DNode`,
* `ModelNode` extracts the reference to the 3D object from the
* current node frame state.
*
* @augments Object3DNode
*/
class ModelNode extends Object3DNode {
static get type() {
return 'ModelNode';
}
/**
* Constructs a new object model node.
*
* @param {('position'|'viewPosition'|'direction'|'scale'|'worldMatrix')} scope - The node represents a different type of transformation depending on the scope.
*/
constructor( scope ) {
super( scope );
}
/**
* Extracts the model reference from the frame state and then
* updates the uniform value depending on the scope.
*
* @param {NodeFrame} frame - The current node frame.
*/
update( frame ) {
this.object3d = frame.object;
super.update( frame );
}
}
export default ModelNode;
/**
* TSL object that represents the object's direction in world space.
*
* @tsl
* @type {ModelNode<vec3>}
*/
export const modelDirection = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.DIRECTION );
/**
* TSL object that represents the object's world matrix.
*
* @tsl
* @type {ModelNode<mat4>}
*/
export const modelWorldMatrix = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.WORLD_MATRIX );
/**
* TSL object that represents the object's position in world space.
*
* @tsl
* @type {ModelNode<vec3>}
*/
export const modelPosition = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.POSITION );
/**
* TSL object that represents the object's scale in world space.
*
* @tsl
* @type {ModelNode<vec3>}
*/
export const modelScale = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.SCALE );
/**
* TSL object that represents the object's position in view/camera space.
*
* @tsl
* @type {ModelNode<vec3>}
*/
export const modelViewPosition = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.VIEW_POSITION );
/**
* TSL object that represents the object's radius.
*
* @tsl
* @type {ModelNode<float>}
*/
export const modelRadius = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.RADIUS );
/**
* TSL object that represents the object's normal matrix.
*
* @tsl
* @type {UniformNode<mat3>}
*/
export const modelNormalMatrix = /*@__PURE__*/ uniform( new Matrix3() ).onObjectUpdate( ( { object }, self ) => self.value.getNormalMatrix( object.matrixWorld ) );
/**
* TSL object that represents the object's inverse world matrix.
*
* @tsl
* @type {UniformNode<mat4>}
*/
export const modelWorldMatrixInverse = /*@__PURE__*/ uniform( new Matrix4() ).onObjectUpdate( ( { object }, self ) => self.value.copy( object.matrixWorld ).invert() );
/**
* TSL object that represents the object's model view matrix.
*
* @tsl
* @type {Node<mat4>}
*/
export const modelViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => {
return builder.renderer.overrideNodes.modelViewMatrix || mediumpModelViewMatrix;
} ).once() )().toVar( 'modelViewMatrix' );
// GPU Precision
/**
* TSL object that represents the object's model view in `mediump` precision.
*
* @tsl
* @type {Node<mat4>}
*/
export const mediumpModelViewMatrix = /*@__PURE__*/ cameraViewMatrix.mul( modelWorldMatrix );
// CPU Precision
/**
* TSL object that represents the object's model view in `highp` precision
* which is achieved by computing the matrix in JS and not in the shader.
*
* @tsl
* @type {Node<mat4>}
*/
export const highpModelViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => {
builder.context.isHighPrecisionModelViewMatrix = true;
return uniform( 'mat4' ).onObjectUpdate( ( { object, camera } ) => {
return object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
} );
} ).once() )().toVar( 'highpModelViewMatrix' );
/**
* TSL object that represents the object's model normal view in `highp` precision
* which is achieved by computing the matrix in JS and not in the shader.
*
* @tsl
* @type {Node<mat3>}
*/
export const highpModelNormalViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => {
const isHighPrecisionModelViewMatrix = builder.context.isHighPrecisionModelViewMatrix;
return uniform( 'mat3' ).onObjectUpdate( ( { object, camera } ) => {
if ( isHighPrecisionModelViewMatrix !== true ) {
object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
}
return object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
} );
} ).once() )().toVar( 'highpModelNormalViewMatrix' );

View File

@@ -0,0 +1,13 @@
import { Fn } from '../tsl/TSLCore.js';
/**
* TSL object that represents the position in clip space after the model-view-projection transform of the current rendered object.
*
* @tsl
* @type {VaryingNode<vec4>}
*/
export const modelViewProjection = /*@__PURE__*/ ( Fn( ( builder ) => {
return builder.context.setupModelViewProjection();
}, 'vec4' ).once() )().toVarying( 'v_modelViewProjection' );

310
app/node_modules/three/src/nodes/accessors/MorphNode.js generated vendored Normal file
View File

@@ -0,0 +1,310 @@
import Node from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { float, nodeProxy, Fn, ivec2, int, If } from '../tsl/TSLBase.js';
import { uniform } from '../core/UniformNode.js';
import { reference } from './ReferenceNode.js';
import { positionLocal } from './Position.js';
import { normalLocal } from './Normal.js';
import { textureLoad } from './TextureNode.js';
import { instanceIndex, vertexIndex } from '../core/IndexNode.js';
import { Loop } from '../utils/LoopNode.js';
import { DataArrayTexture } from '../../textures/DataArrayTexture.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector4 } from '../../math/Vector4.js';
import { FloatType } from '../../constants.js';
const _morphTextures = /*@__PURE__*/ new WeakMap();
const _morphVec4 = /*@__PURE__*/ new Vector4();
const getMorph = /*@__PURE__*/ Fn( ( { bufferMap, influence, stride, width, depth, offset } ) => {
const texelIndex = int( vertexIndex ).mul( stride ).add( offset );
const y = texelIndex.div( width );
const x = texelIndex.sub( y.mul( width ) );
const bufferAttrib = textureLoad( bufferMap, ivec2( x, y ) ).depth( depth ).xyz;
return bufferAttrib.mul( influence );
} );
function getEntry( geometry ) {
const hasMorphPosition = geometry.morphAttributes.position !== undefined;
const hasMorphNormals = geometry.morphAttributes.normal !== undefined;
const hasMorphColors = geometry.morphAttributes.color !== undefined;
// instead of using attributes, the WebGL 2 code path encodes morph targets
// into an array of data textures. Each layer represents a single morph target.
const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;
const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;
let entry = _morphTextures.get( geometry );
if ( entry === undefined || entry.count !== morphTargetsCount ) {
if ( entry !== undefined ) entry.texture.dispose();
const morphTargets = geometry.morphAttributes.position || [];
const morphNormals = geometry.morphAttributes.normal || [];
const morphColors = geometry.morphAttributes.color || [];
let vertexDataCount = 0;
if ( hasMorphPosition === true ) vertexDataCount = 1;
if ( hasMorphNormals === true ) vertexDataCount = 2;
if ( hasMorphColors === true ) vertexDataCount = 3;
let width = geometry.attributes.position.count * vertexDataCount;
let height = 1;
const maxTextureSize = 4096; // @TODO: Use 'capabilities.maxTextureSize'
if ( width > maxTextureSize ) {
height = Math.ceil( width / maxTextureSize );
width = maxTextureSize;
}
const buffer = new Float32Array( width * height * 4 * morphTargetsCount );
const bufferTexture = new DataArrayTexture( buffer, width, height, morphTargetsCount );
bufferTexture.type = FloatType;
bufferTexture.needsUpdate = true;
// fill buffer
const vertexDataStride = vertexDataCount * 4;
for ( let i = 0; i < morphTargetsCount; i ++ ) {
const morphTarget = morphTargets[ i ];
const morphNormal = morphNormals[ i ];
const morphColor = morphColors[ i ];
const offset = width * height * 4 * i;
for ( let j = 0; j < morphTarget.count; j ++ ) {
const stride = j * vertexDataStride;
if ( hasMorphPosition === true ) {
_morphVec4.fromBufferAttribute( morphTarget, j );
buffer[ offset + stride + 0 ] = _morphVec4.x;
buffer[ offset + stride + 1 ] = _morphVec4.y;
buffer[ offset + stride + 2 ] = _morphVec4.z;
buffer[ offset + stride + 3 ] = 0;
}
if ( hasMorphNormals === true ) {
_morphVec4.fromBufferAttribute( morphNormal, j );
buffer[ offset + stride + 4 ] = _morphVec4.x;
buffer[ offset + stride + 5 ] = _morphVec4.y;
buffer[ offset + stride + 6 ] = _morphVec4.z;
buffer[ offset + stride + 7 ] = 0;
}
if ( hasMorphColors === true ) {
_morphVec4.fromBufferAttribute( morphColor, j );
buffer[ offset + stride + 8 ] = _morphVec4.x;
buffer[ offset + stride + 9 ] = _morphVec4.y;
buffer[ offset + stride + 10 ] = _morphVec4.z;
buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? _morphVec4.w : 1;
}
}
}
entry = {
count: morphTargetsCount,
texture: bufferTexture,
stride: vertexDataCount,
size: new Vector2( width, height )
};
_morphTextures.set( geometry, entry );
function disposeTexture() {
bufferTexture.dispose();
_morphTextures.delete( geometry );
geometry.removeEventListener( 'dispose', disposeTexture );
}
geometry.addEventListener( 'dispose', disposeTexture );
}
return entry;
}
/**
* This node implements the vertex transformation shader logic which is required
* for morph target animation.
*
* @augments Node
*/
class MorphNode extends Node {
static get type() {
return 'MorphNode';
}
/**
* Constructs a new morph node.
*
* @param {Mesh} mesh - The mesh holding the morph targets.
*/
constructor( mesh ) {
super( 'void' );
/**
* The mesh holding the morph targets.
*
* @type {Mesh}
*/
this.mesh = mesh;
/**
* A uniform node which represents the morph base influence value.
*
* @type {UniformNode<float>}
*/
this.morphBaseInfluence = uniform( 1 );
/**
* The update type overwritten since morph nodes are updated per object.
*
* @type {string}
*/
this.updateType = NodeUpdateType.OBJECT;
}
/**
* Setups the morph node by assigning the transformed vertex data to predefined node variables.
*
* @param {NodeBuilder} builder - The current node builder.
*/
setup( builder ) {
const { geometry } = builder;
const hasMorphPosition = geometry.morphAttributes.position !== undefined;
const hasMorphNormals = geometry.hasAttribute( 'normal' ) && geometry.morphAttributes.normal !== undefined;
const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;
const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;
// nodes
const { texture: bufferMap, stride, size } = getEntry( geometry );
if ( hasMorphPosition === true ) positionLocal.mulAssign( this.morphBaseInfluence );
if ( hasMorphNormals === true ) normalLocal.mulAssign( this.morphBaseInfluence );
const width = int( size.width );
Loop( morphTargetsCount, ( { i } ) => {
const influence = float( 0 ).toVar();
if ( this.mesh.count > 1 && ( this.mesh.morphTexture !== null && this.mesh.morphTexture !== undefined ) ) {
influence.assign( textureLoad( this.mesh.morphTexture, ivec2( int( i ).add( 1 ), int( instanceIndex ) ) ).r );
} else {
influence.assign( reference( 'morphTargetInfluences', 'float' ).element( i ).toVar() );
}
If( influence.notEqual( 0 ), () => {
if ( hasMorphPosition === true ) {
positionLocal.addAssign( getMorph( {
bufferMap,
influence,
stride,
width,
depth: i,
offset: int( 0 )
} ) );
}
if ( hasMorphNormals === true ) {
normalLocal.addAssign( getMorph( {
bufferMap,
influence,
stride,
width,
depth: i,
offset: int( 1 )
} ) );
}
} );
} );
}
/**
* Updates the state of the morphed mesh by updating the base influence.
*
* @param {NodeFrame} frame - The current node frame.
*/
update( /*frame*/ ) {
const morphBaseInfluence = this.morphBaseInfluence;
if ( this.mesh.geometry.morphTargetsRelative ) {
morphBaseInfluence.value = 1;
} else {
morphBaseInfluence.value = 1 - this.mesh.morphTargetInfluences.reduce( ( a, b ) => a + b, 0 );
}
}
}
export default MorphNode;
/**
* TSL function for creating a morph node.
*
* @tsl
* @function
* @param {Mesh} mesh - The mesh holding the morph targets.
* @returns {MorphNode}
*/
export const morphReference = /*@__PURE__*/ nodeProxy( MorphNode ).setParameterLength( 1 );

242
app/node_modules/three/src/nodes/accessors/Normal.js generated vendored Normal file
View File

@@ -0,0 +1,242 @@
import { attribute } from '../core/AttributeNode.js';
import { cameraViewMatrix } from './Camera.js';
import { modelNormalMatrix, modelWorldMatrix } from './ModelNode.js';
import { mat3, vec3, Fn } from '../tsl/TSLBase.js';
import { positionView } from './Position.js';
import { directionToFaceDirection } from '../display/FrontFacingNode.js';
/**
* TSL object that represents the normal attribute of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const normalGeometry = /*@__PURE__*/ attribute( 'normal', 'vec3' );
/**
* TSL object that represents the vertex normal in local space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const normalLocal = /*@__PURE__*/ ( Fn( ( builder ) => {
if ( builder.geometry.hasAttribute( 'normal' ) === false ) {
console.warn( 'THREE.TSL: Vertex attribute "normal" not found on geometry.' );
return vec3( 0, 1, 0 );
}
return normalGeometry;
}, 'vec3' ).once() )().toVar( 'normalLocal' );
/**
* TSL object that represents the flat vertex normal in view space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const normalFlat = /*@__PURE__*/ positionView.dFdx().cross( positionView.dFdy() ).normalize().toVar( 'normalFlat' );
/**
* TSL object that represents the vertex normal in view space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const normalViewGeometry = /*@__PURE__*/ ( Fn( ( builder ) => {
let node;
if ( builder.material.flatShading === true ) {
node = normalFlat;
} else {
node = transformNormalToView( normalLocal ).toVarying( 'v_normalViewGeometry' ).normalize();
}
return node;
}, 'vec3' ).once() )().toVar( 'normalViewGeometry' );
/**
* TSL object that represents the vertex normal in world space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const normalWorldGeometry = /*@__PURE__*/ ( Fn( ( builder ) => {
let normal = normalViewGeometry.transformDirection( cameraViewMatrix );
if ( builder.material.flatShading !== true ) {
normal = normal.toVarying( 'v_normalWorldGeometry' );
}
return normal.normalize().toVar( 'normalWorldGeometry' );
}, 'vec3' ).once() )();
/**
* TSL object that represents the transformed vertex normal in view space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const normalView = /*@__PURE__*/ ( Fn( ( { subBuildFn, material, context } ) => {
let node;
if ( subBuildFn === 'NORMAL' || subBuildFn === 'VERTEX' ) {
node = normalViewGeometry;
if ( material.flatShading !== true ) {
node = directionToFaceDirection( node );
}
} else {
// Use getUV context to avoid side effects from nodes overwriting getUV in the context (e.g. EnvironmentNode)
node = context.setupNormal().context( { getUV: null } );
}
return node;
}, 'vec3' ).once( [ 'NORMAL', 'VERTEX' ] ) )().toVar( 'normalView' );
/**
* TSL object that represents the transformed vertex normal in world space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const normalWorld = /*@__PURE__*/ normalView.transformDirection( cameraViewMatrix ).toVar( 'normalWorld' );
/**
* TSL object that represents the transformed clearcoat vertex normal in view space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const clearcoatNormalView = /*@__PURE__*/ ( Fn( ( { subBuildFn, context } ) => {
let node;
if ( subBuildFn === 'NORMAL' || subBuildFn === 'VERTEX' ) {
node = normalView;
} else {
// Use getUV context to avoid side effects from nodes overwriting getUV in the context (e.g. EnvironmentNode)
node = context.setupClearcoatNormal().context( { getUV: null } );
}
return node;
}, 'vec3' ).once( [ 'NORMAL', 'VERTEX' ] ) )().toVar( 'clearcoatNormalView' );
/**
* Transforms the normal with the given matrix.
*
* @tsl
* @function
* @param {Node<vec3>} normal - The normal.
* @param {Node<mat3>} [matrix=modelWorldMatrix] - The matrix.
* @return {Node<vec3>} The transformed normal.
*/
export const transformNormal = /*@__PURE__*/ Fn( ( [ normal, matrix = modelWorldMatrix ] ) => {
const m = mat3( matrix );
const transformedNormal = normal.div( vec3( m[ 0 ].dot( m[ 0 ] ), m[ 1 ].dot( m[ 1 ] ), m[ 2 ].dot( m[ 2 ] ) ) );
return m.mul( transformedNormal ).xyz;
} );
/**
* Transforms the given normal from local to view space.
*
* @tsl
* @function
* @param {Node<vec3>} normal - The normal.
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec3>} The transformed normal.
*/
export const transformNormalToView = /*@__PURE__*/ Fn( ( [ normal ], builder ) => {
const modelNormalViewMatrix = builder.renderer.overrideNodes.modelNormalViewMatrix;
if ( modelNormalViewMatrix !== null ) {
return modelNormalViewMatrix.transformDirection( normal );
}
//
const transformedNormal = modelNormalMatrix.mul( normal );
return cameraViewMatrix.transformDirection( transformedNormal );
} );
// Deprecated
/**
* TSL object that represents the transformed vertex normal in view space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
* @deprecated since r178. Use `normalView` instead.
*/
export const transformedNormalView = ( Fn( () => { // @deprecated, r177
console.warn( 'THREE.TSL: "transformedNormalView" is deprecated. Use "normalView" instead.' );
return normalView;
} ).once( [ 'NORMAL', 'VERTEX' ] ) )();
/**
* TSL object that represents the transformed vertex normal in world space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
* @deprecated since r178. Use `normalWorld` instead.
*/
export const transformedNormalWorld = ( Fn( () => { // @deprecated, r177
console.warn( 'THREE.TSL: "transformedNormalWorld" is deprecated. Use "normalWorld" instead.' );
return normalWorld;
} ).once( [ 'NORMAL', 'VERTEX' ] ) )();
/**
* TSL object that represents the transformed clearcoat vertex normal in view space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
* @deprecated since r178. Use `clearcoatNormalView` instead.
*/
export const transformedClearcoatNormalView = ( Fn( () => { // @deprecated, r177
console.warn( 'THREE.TSL: "transformedClearcoatNormalView" is deprecated. Use "clearcoatNormalView" instead.' );
return clearcoatNormalView;
} ).once( [ 'NORMAL', 'VERTEX' ] ) )();

View File

@@ -0,0 +1,268 @@
import Node from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import UniformNode from '../core/UniformNode.js';
import { nodeProxy } from '../tsl/TSLBase.js';
import { Vector3 } from '../../math/Vector3.js';
import { Sphere } from '../../math/Sphere.js';
const _sphere = /*@__PURE__*/ new Sphere();
/**
* This node can be used to access transformation related metrics of 3D objects.
* Depending on the selected scope, a different metric is represented as a uniform
* in the shader. The following scopes are supported:
*
* - `POSITION`: The object's position in world space.
* - `VIEW_POSITION`: The object's position in view/camera space.
* - `DIRECTION`: The object's direction in world space.
* - `SCALE`: The object's scale in world space.
* - `WORLD_MATRIX`: The object's matrix in world space.
*
* @augments Node
*/
class Object3DNode extends Node {
static get type() {
return 'Object3DNode';
}
/**
* Constructs a new object 3D node.
*
* @param {('position'|'viewPosition'|'direction'|'scale'|'worldMatrix')} scope - The node represents a different type of transformation depending on the scope.
* @param {?Object3D} [object3d=null] - The 3D object.
*/
constructor( scope, object3d = null ) {
super();
/**
* The node reports a different type of transformation depending on the scope.
*
* @type {('position'|'viewPosition'|'direction'|'scale'|'worldMatrix')}
*/
this.scope = scope;
/**
* The 3D object.
*
* @type {?Object3D}
* @default null
*/
this.object3d = object3d;
/**
* Overwritten since this type of node is updated per object.
*
* @type {string}
* @default 'object'
*/
this.updateType = NodeUpdateType.OBJECT;
/**
* Holds the value of the node as a uniform.
*
* @type {UniformNode}
*/
this.uniformNode = new UniformNode( null );
}
/**
* Overwritten since the node type is inferred from the scope.
*
* @return {string} The node type.
*/
getNodeType() {
const scope = this.scope;
if ( scope === Object3DNode.WORLD_MATRIX ) {
return 'mat4';
} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION || scope === Object3DNode.SCALE ) {
return 'vec3';
} else if ( scope === Object3DNode.RADIUS ) {
return 'float';
}
}
/**
* Updates the uniform value depending on the scope.
*
* @param {NodeFrame} frame - The current node frame.
*/
update( frame ) {
const object = this.object3d;
const uniformNode = this.uniformNode;
const scope = this.scope;
if ( scope === Object3DNode.WORLD_MATRIX ) {
uniformNode.value = object.matrixWorld;
} else if ( scope === Object3DNode.POSITION ) {
uniformNode.value = uniformNode.value || new Vector3();
uniformNode.value.setFromMatrixPosition( object.matrixWorld );
} else if ( scope === Object3DNode.SCALE ) {
uniformNode.value = uniformNode.value || new Vector3();
uniformNode.value.setFromMatrixScale( object.matrixWorld );
} else if ( scope === Object3DNode.DIRECTION ) {
uniformNode.value = uniformNode.value || new Vector3();
object.getWorldDirection( uniformNode.value );
} else if ( scope === Object3DNode.VIEW_POSITION ) {
const camera = frame.camera;
uniformNode.value = uniformNode.value || new Vector3();
uniformNode.value.setFromMatrixPosition( object.matrixWorld );
uniformNode.value.applyMatrix4( camera.matrixWorldInverse );
} else if ( scope === Object3DNode.RADIUS ) {
const geometry = frame.object.geometry;
if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
_sphere.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld );
uniformNode.value = _sphere.radius;
}
}
/**
* Generates the code snippet of the uniform node. The node type of the uniform
* node also depends on the selected scope.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The generated code snippet.
*/
generate( builder ) {
const scope = this.scope;
if ( scope === Object3DNode.WORLD_MATRIX ) {
this.uniformNode.nodeType = 'mat4';
} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION || scope === Object3DNode.SCALE ) {
this.uniformNode.nodeType = 'vec3';
} else if ( scope === Object3DNode.RADIUS ) {
this.uniformNode.nodeType = 'float';
}
return this.uniformNode.build( builder );
}
serialize( data ) {
super.serialize( data );
data.scope = this.scope;
}
deserialize( data ) {
super.deserialize( data );
this.scope = data.scope;
}
}
Object3DNode.WORLD_MATRIX = 'worldMatrix';
Object3DNode.POSITION = 'position';
Object3DNode.SCALE = 'scale';
Object3DNode.VIEW_POSITION = 'viewPosition';
Object3DNode.DIRECTION = 'direction';
Object3DNode.RADIUS = 'radius';
export default Object3DNode;
/**
* TSL function for creating an object 3D node that represents the object's direction in world space.
*
* @tsl
* @function
* @param {?Object3D} [object3d] - The 3D object.
* @returns {Object3DNode<vec3>}
*/
export const objectDirection = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.DIRECTION ).setParameterLength( 1 );
/**
* TSL function for creating an object 3D node that represents the object's world matrix.
*
* @tsl
* @function
* @param {?Object3D} [object3d] - The 3D object.
* @returns {Object3DNode<mat4>}
*/
export const objectWorldMatrix = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.WORLD_MATRIX ).setParameterLength( 1 );
/**
* TSL function for creating an object 3D node that represents the object's position in world space.
*
* @tsl
* @function
* @param {?Object3D} [object3d] - The 3D object.
* @returns {Object3DNode<vec3>}
*/
export const objectPosition = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.POSITION ).setParameterLength( 1 );
/**
* TSL function for creating an object 3D node that represents the object's scale in world space.
*
* @tsl
* @function
* @param {?Object3D} [object3d] - The 3D object.
* @returns {Object3DNode<vec3>}
*/
export const objectScale = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.SCALE ).setParameterLength( 1 );
/**
* TSL function for creating an object 3D node that represents the object's position in view/camera space.
*
* @tsl
* @function
* @param {?Object3D} [object3d] - The 3D object.
* @returns {Object3DNode<vec3>}
*/
export const objectViewPosition = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.VIEW_POSITION ).setParameterLength( 1 );
/**
* TSL function for creating an object 3D node that represents the object's radius.
*
* @tsl
* @function
* @param {?Object3D} [object3d] - The 3D object.
* @returns {Object3DNode<float>}
*/
export const objectRadius = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.RADIUS ).setParameterLength( 1 );

View File

@@ -0,0 +1,55 @@
import Node from '../core/Node.js';
import { nodeImmutable } from '../tsl/TSLBase.js';
/**
* A node for representing the uv coordinates of points.
*
* Can only be used with a WebGL backend. In WebGPU, point
* primitives always have the size of one pixel and can thus
* can't be used as sprite-like objects that display textures.
*
* @augments Node
*/
class PointUVNode extends Node {
static get type() {
return 'PointUVNode';
}
/**
* Constructs a new point uv node.
*/
constructor() {
super( 'vec2' );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isPointUVNode = true;
}
generate( /*builder*/ ) {
return 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
}
}
export default PointUVNode;
/**
* TSL object that represents the uv coordinates of points.
*
* @tsl
* @type {PointUVNode}
*/
export const pointUV = /*@__PURE__*/ nodeImmutable( PointUVNode );

74
app/node_modules/three/src/nodes/accessors/Position.js generated vendored Normal file
View File

@@ -0,0 +1,74 @@
import { attribute } from '../core/AttributeNode.js';
import { Fn } from '../tsl/TSLCore.js';
import { modelWorldMatrix } from './ModelNode.js';
/**
* TSL object that represents the position attribute of the current rendered object.
*
* @tsl
* @type {AttributeNode<vec3>}
*/
export const positionGeometry = /*@__PURE__*/ attribute( 'position', 'vec3' );
/**
* TSL object that represents the vertex position in local space of the current rendered object.
*
* @tsl
* @type {AttributeNode<vec3>}
*/
export const positionLocal = /*@__PURE__*/ positionGeometry.toVarying( 'positionLocal' );
/**
* TSL object that represents the previous vertex position in local space of the current rendered object.
* Used in context of {@link VelocityNode} for rendering motion vectors.
*
* @tsl
* @type {AttributeNode<vec3>}
*/
export const positionPrevious = /*@__PURE__*/ positionGeometry.toVarying( 'positionPrevious' );
/**
* TSL object that represents the vertex position in world space of the current rendered object.
*
* @tsl
* @type {VaryingNode<vec3>}
*/
export const positionWorld = /*@__PURE__*/ ( Fn( ( builder ) => {
return modelWorldMatrix.mul( positionLocal ).xyz.toVarying( builder.getSubBuildProperty( 'v_positionWorld' ) );
}, 'vec3' ).once( [ 'POSITION' ] ) )();
/**
* TSL object that represents the position world direction of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const positionWorldDirection = /*@__PURE__*/ ( Fn( () => {
const vertexPWD = positionLocal.transformDirection( modelWorldMatrix ).toVarying( 'v_positionWorldDirection' );
return vertexPWD.normalize().toVar( 'positionWorldDirection' );
}, 'vec3' ).once( [ 'POSITION' ] ) )();
/**
* TSL object that represents the vertex position in view space of the current rendered object.
*
* @tsl
* @type {VaryingNode<vec3>}
*/
export const positionView = /*@__PURE__*/ ( Fn( ( builder ) => {
return builder.context.setupPositionView().toVarying( 'v_positionView' );
}, 'vec3' ).once( [ 'POSITION' ] ) )();
/**
* TSL object that represents the position view direction of the current rendered object.
*
* @tsl
* @type {VaryingNode<vec3>}
*/
export const positionViewDirection = /*@__PURE__*/ positionView.negate().toVarying( 'v_positionViewDirection' ).normalize().toVar( 'positionViewDirection' );

View File

@@ -0,0 +1,357 @@
import Node from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { uniform } from '../core/UniformNode.js';
import { nodeObject } from '../tsl/TSLCore.js';
import ArrayElementNode from '../utils/ArrayElementNode.js';
// TODO: Avoid duplicated code and ues only ReferenceBaseNode or ReferenceNode
/**
* This class is only relevant if the referenced property is array-like.
* In this case, `ReferenceElementNode` allows to refer to a specific
* element inside the data structure via an index.
*
* @augments ArrayElementNode
*/
class ReferenceElementNode extends ArrayElementNode {
static get type() {
return 'ReferenceElementNode';
}
/**
* Constructs a new reference element node.
*
* @param {ReferenceBaseNode} referenceNode - The reference node.
* @param {Node} indexNode - The index node that defines the element access.
*/
constructor( referenceNode, indexNode ) {
super( referenceNode, indexNode );
/**
* Similar to {@link ReferenceBaseNode#reference}, an additional
* property references to the current node.
*
* @type {?ReferenceBaseNode}
* @default null
*/
this.referenceNode = referenceNode;
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isReferenceElementNode = true;
}
/**
* This method is overwritten since the node type is inferred from
* the uniform type of the reference node.
*
* @return {string} The node type.
*/
getNodeType() {
return this.referenceNode.uniformType;
}
generate( builder ) {
const snippet = super.generate( builder );
const arrayType = this.referenceNode.getNodeType();
const elementType = this.getNodeType();
return builder.format( snippet, arrayType, elementType );
}
}
/**
* Base class for nodes which establishes a reference to a property of another object.
* In this way, the value of the node is automatically linked to the value of
* referenced object. Reference nodes internally represent the linked value
* as a uniform.
*
* @augments Node
*/
class ReferenceBaseNode extends Node {
static get type() {
return 'ReferenceBaseNode';
}
/**
* Constructs a new reference base node.
*
* @param {string} property - The name of the property the node refers to.
* @param {string} uniformType - The uniform type that should be used to represent the property value.
* @param {?Object} [object=null] - The object the property belongs to.
* @param {?number} [count=null] - When the linked property is an array-like, this parameter defines its length.
*/
constructor( property, uniformType, object = null, count = null ) {
super();
/**
* The name of the property the node refers to.
*
* @type {string}
*/
this.property = property;
/**
* The uniform type that should be used to represent the property value.
*
* @type {string}
*/
this.uniformType = uniformType;
/**
* The object the property belongs to.
*
* @type {?Object}
* @default null
*/
this.object = object;
/**
* When the linked property is an array, this parameter defines its length.
*
* @type {?number}
* @default null
*/
this.count = count;
/**
* The property name might have dots so nested properties can be referred.
* The hierarchy of the names is stored inside this array.
*
* @type {Array<string>}
*/
this.properties = property.split( '.' );
/**
* Points to the current referred object. This property exists next to {@link ReferenceNode#object}
* since the final reference might be updated from calling code.
*
* @type {?Object}
* @default null
*/
this.reference = object;
/**
* The uniform node that holds the value of the reference node.
*
* @type {UniformNode}
* @default null
*/
this.node = null;
/**
* The uniform group of the internal uniform.
*
* @type {UniformGroupNode}
* @default null
*/
this.group = null;
/**
* Overwritten since reference nodes are updated per object.
*
* @type {string}
* @default 'object'
*/
this.updateType = NodeUpdateType.OBJECT;
}
/**
* Sets the uniform group for this reference node.
*
* @param {UniformGroupNode} group - The uniform group to set.
* @return {ReferenceBaseNode} A reference to this node.
*/
setGroup( group ) {
this.group = group;
return this;
}
/**
* When the referred property is array-like, this method can be used
* to access elements via an index node.
*
* @param {IndexNode} indexNode - indexNode.
* @return {ReferenceElementNode} A reference to an element.
*/
element( indexNode ) {
return nodeObject( new ReferenceElementNode( this, nodeObject( indexNode ) ) );
}
/**
* Sets the node type which automatically defines the internal
* uniform type.
*
* @param {string} uniformType - The type to set.
*/
setNodeType( uniformType ) {
const node = uniform( null, uniformType ).getSelf();
if ( this.group !== null ) {
node.setGroup( this.group );
}
this.node = node;
}
/**
* This method is overwritten since the node type is inferred from
* the type of the reference node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The node type.
*/
getNodeType( builder ) {
if ( this.node === null ) {
this.updateReference( builder );
this.updateValue();
}
return this.node.getNodeType( builder );
}
/**
* Returns the property value from the given referred object.
*
* @param {Object} [object=this.reference] - The object to retrieve the property value from.
* @return {any} The value.
*/
getValueFromReference( object = this.reference ) {
const { properties } = this;
let value = object[ properties[ 0 ] ];
for ( let i = 1; i < properties.length; i ++ ) {
value = value[ properties[ i ] ];
}
return value;
}
/**
* Allows to update the reference based on the given state. The state is only
* evaluated {@link ReferenceBaseNode#object} is not set.
*
* @param {(NodeFrame|NodeBuilder)} state - The current state.
* @return {Object} The updated reference.
*/
updateReference( state ) {
this.reference = this.object !== null ? this.object : state.object;
return this.reference;
}
/**
* The output of the reference node is the internal uniform node.
*
* @return {UniformNode} The output node.
*/
setup() {
this.updateValue();
return this.node;
}
/**
* Overwritten to update the internal uniform value.
*
* @param {NodeFrame} frame - A reference to the current node frame.
*/
update( /*frame*/ ) {
this.updateValue();
}
/**
* Retrieves the value from the referred object property and uses it
* to updated the internal uniform.
*/
updateValue() {
if ( this.node === null ) this.setNodeType( this.uniformType );
const value = this.getValueFromReference();
if ( Array.isArray( value ) ) {
this.node.array = value;
} else {
this.node.value = value;
}
}
}
export default ReferenceBaseNode;
/**
* TSL function for creating a reference base node.
*
* @tsl
* @function
* @param {string} name - The name of the property the node refers to.
* @param {string} type - The uniform type that should be used to represent the property value.
* @param {Object} object - The object the property belongs to.
* @returns {ReferenceBaseNode}
*/
export const reference = ( name, type, object ) => nodeObject( new ReferenceBaseNode( name, type, object ) );
/**
* TSL function for creating a reference base node. Use this function if you want need a reference
* to an array-like property that should be represented as a uniform buffer.
*
* @tsl
* @function
* @param {string} name - The name of the property the node refers to.
* @param {string} type - The uniform type that should be used to represent the property value.
* @param {number} count - The number of value inside the array-like object.
* @param {Object} [object] - An array-like object the property belongs to.
* @returns {ReferenceBaseNode}
*/
export const referenceBuffer = ( name, type, count, object ) => nodeObject( new ReferenceBaseNode( name, type, object, count ) );

View File

@@ -0,0 +1,408 @@
import Node from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { uniform } from '../core/UniformNode.js';
import { texture } from './TextureNode.js';
import { cubeTexture } from './CubeTextureNode.js';
import { buffer } from './BufferNode.js';
import { nodeObject } from '../tsl/TSLBase.js';
import { uniformArray } from './UniformArrayNode.js';
import ArrayElementNode from '../utils/ArrayElementNode.js';
// TODO: Avoid duplicated code and ues only ReferenceBaseNode or ReferenceNode
/**
* This class is only relevant if the referenced property is array-like.
* In this case, `ReferenceElementNode` allows to refer to a specific
* element inside the data structure via an index.
*
* @augments ArrayElementNode
*/
class ReferenceElementNode extends ArrayElementNode {
static get type() {
return 'ReferenceElementNode';
}
/**
* Constructs a new reference element node.
*
* @param {?ReferenceNode} referenceNode - The reference node.
* @param {Node} indexNode - The index node that defines the element access.
*/
constructor( referenceNode, indexNode ) {
super( referenceNode, indexNode );
/**
* Similar to {@link ReferenceNode#reference}, an additional
* property references to the current node.
*
* @type {?ReferenceNode}
* @default null
*/
this.referenceNode = referenceNode;
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isReferenceElementNode = true;
}
/**
* This method is overwritten since the node type is inferred from
* the uniform type of the reference node.
*
* @return {string} The node type.
*/
getNodeType() {
return this.referenceNode.uniformType;
}
generate( builder ) {
const snippet = super.generate( builder );
const arrayType = this.referenceNode.getNodeType();
const elementType = this.getNodeType();
return builder.format( snippet, arrayType, elementType );
}
}
/**
* This type of node establishes a reference to a property of another object.
* In this way, the value of the node is automatically linked to the value of
* referenced object. Reference nodes internally represent the linked value
* as a uniform.
*
* @augments Node
*/
class ReferenceNode extends Node {
static get type() {
return 'ReferenceNode';
}
/**
* Constructs a new reference node.
*
* @param {string} property - The name of the property the node refers to.
* @param {string} uniformType - The uniform type that should be used to represent the property value.
* @param {?Object} [object=null] - The object the property belongs to.
* @param {?number} [count=null] - When the linked property is an array-like, this parameter defines its length.
*/
constructor( property, uniformType, object = null, count = null ) {
super();
/**
* The name of the property the node refers to.
*
* @type {string}
*/
this.property = property;
/**
* The uniform type that should be used to represent the property value.
*
* @type {string}
*/
this.uniformType = uniformType;
/**
* The object the property belongs to.
*
* @type {?Object}
* @default null
*/
this.object = object;
/**
* When the linked property is an array, this parameter defines its length.
*
* @type {?number}
* @default null
*/
this.count = count;
/**
* The property name might have dots so nested properties can be referred.
* The hierarchy of the names is stored inside this array.
*
* @type {Array<string>}
*/
this.properties = property.split( '.' );
/**
* Points to the current referred object. This property exists next to {@link ReferenceNode#object}
* since the final reference might be updated from calling code.
*
* @type {?Object}
* @default null
*/
this.reference = object;
/**
* The uniform node that holds the value of the reference node.
*
* @type {UniformNode}
* @default null
*/
this.node = null;
/**
* The uniform group of the internal uniform.
*
* @type {UniformGroupNode}
* @default null
*/
this.group = null;
/**
* An optional label of the internal uniform node.
*
* @type {?string}
* @default null
*/
this.name = null;
/**
* Overwritten since reference nodes are updated per object.
*
* @type {string}
* @default 'object'
*/
this.updateType = NodeUpdateType.OBJECT;
}
/**
* When the referred property is array-like, this method can be used
* to access elements via an index node.
*
* @param {IndexNode} indexNode - indexNode.
* @return {ReferenceElementNode} A reference to an element.
*/
element( indexNode ) {
return nodeObject( new ReferenceElementNode( this, nodeObject( indexNode ) ) );
}
/**
* Sets the uniform group for this reference node.
*
* @param {UniformGroupNode} group - The uniform group to set.
* @return {ReferenceNode} A reference to this node.
*/
setGroup( group ) {
this.group = group;
return this;
}
/**
* Sets the label for the internal uniform.
*
* @param {string} name - The label to set.
* @return {ReferenceNode} A reference to this node.
*/
label( name ) {
this.name = name;
return this;
}
/**
* Sets the node type which automatically defines the internal
* uniform type.
*
* @param {string} uniformType - The type to set.
*/
setNodeType( uniformType ) {
let node = null;
if ( this.count !== null ) {
node = buffer( null, uniformType, this.count );
} else if ( Array.isArray( this.getValueFromReference() ) ) {
node = uniformArray( null, uniformType );
} else if ( uniformType === 'texture' ) {
node = texture( null );
} else if ( uniformType === 'cubeTexture' ) {
node = cubeTexture( null );
} else {
node = uniform( null, uniformType );
}
if ( this.group !== null ) {
node.setGroup( this.group );
}
if ( this.name !== null ) node.label( this.name );
this.node = node.getSelf();
}
/**
* This method is overwritten since the node type is inferred from
* the type of the reference node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The node type.
*/
getNodeType( builder ) {
if ( this.node === null ) {
this.updateReference( builder );
this.updateValue();
}
return this.node.getNodeType( builder );
}
/**
* Returns the property value from the given referred object.
*
* @param {Object} [object=this.reference] - The object to retrieve the property value from.
* @return {any} The value.
*/
getValueFromReference( object = this.reference ) {
const { properties } = this;
let value = object[ properties[ 0 ] ];
for ( let i = 1; i < properties.length; i ++ ) {
value = value[ properties[ i ] ];
}
return value;
}
/**
* Allows to update the reference based on the given state. The state is only
* evaluated {@link ReferenceNode#object} is not set.
*
* @param {(NodeFrame|NodeBuilder)} state - The current state.
* @return {Object} The updated reference.
*/
updateReference( state ) {
this.reference = this.object !== null ? this.object : state.object;
return this.reference;
}
/**
* The output of the reference node is the internal uniform node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {UniformNode} The output node.
*/
setup( /* builder */ ) {
this.updateValue();
return this.node;
}
/**
* Overwritten to update the internal uniform value.
*
* @param {NodeFrame} frame - A reference to the current node frame.
*/
update( /*frame*/ ) {
this.updateValue();
}
/**
* Retrieves the value from the referred object property and uses it
* to updated the internal uniform.
*/
updateValue() {
if ( this.node === null ) this.setNodeType( this.uniformType );
const value = this.getValueFromReference();
if ( Array.isArray( value ) ) {
this.node.array = value;
} else {
this.node.value = value;
}
}
}
export default ReferenceNode;
/**
* TSL function for creating a reference node.
*
* @tsl
* @function
* @param {string} name - The name of the property the node refers to.
* @param {string} type - The uniform type that should be used to represent the property value.
* @param {?Object} [object] - The object the property belongs to.
* @returns {ReferenceNode}
*/
export const reference = ( name, type, object ) => nodeObject( new ReferenceNode( name, type, object ) );
/**
* TSL function for creating a reference node. Use this function if you want need a reference
* to an array-like property that should be represented as a uniform buffer.
*
* @tsl
* @function
* @param {string} name - The name of the property the node refers to.
* @param {string} type - The uniform type that should be used to represent the property value.
* @param {number} count - The number of value inside the array-like object.
* @param {Object} object - An array-like object the property belongs to.
* @returns {ReferenceNode}
*/
export const referenceBuffer = ( name, type, count, object ) => nodeObject( new ReferenceNode( name, type, object, count ) );

View File

@@ -0,0 +1,36 @@
import { cameraViewMatrix } from './Camera.js';
import { normalView } from './Normal.js';
import { positionViewDirection } from './Position.js';
import { materialRefractionRatio } from './MaterialProperties.js';
/**
* The reflect vector in view space.
*
* @tsl
* @type {Node<vec3>}
*/
export const reflectView = /*@__PURE__*/ positionViewDirection.negate().reflect( normalView );
/**
* The refract vector in view space.
*
* @tsl
* @type {Node<vec3>}
*/
export const refractView = /*@__PURE__*/ positionViewDirection.negate().refract( normalView, materialRefractionRatio );
/**
* Used for sampling cube maps when using cube reflection mapping.
*
* @tsl
* @type {Node<vec3>}
*/
export const reflectVector = /*@__PURE__*/ reflectView.transformDirection( cameraViewMatrix ).toVar( 'reflectVector' );
/**
* Used for sampling cube maps when using cube refraction mapping.
*
* @tsl
* @type {Node<vec3>}
*/
export const refractVector = /*@__PURE__*/ refractView.transformDirection( cameraViewMatrix ).toVar( 'reflectVector' );

View File

@@ -0,0 +1,79 @@
import ReferenceBaseNode from './ReferenceBaseNode.js';
import { nodeObject } from '../tsl/TSLCore.js';
import { renderGroup } from '../core/UniformGroupNode.js';
/**
* This node is a special type of reference node which is intended
* for linking renderer properties with node values.
* ```js
* const exposureNode = rendererReference( 'toneMappingExposure', 'float', renderer );
* ```
* When changing `renderer.toneMappingExposure`, the node value of `exposureNode` will
* automatically be updated.
*
* @augments ReferenceBaseNode
*/
class RendererReferenceNode extends ReferenceBaseNode {
static get type() {
return 'RendererReferenceNode';
}
/**
* Constructs a new renderer reference node.
*
* @param {string} property - The name of the property the node refers to.
* @param {string} inputType - The uniform type that should be used to represent the property value.
* @param {?Renderer} [renderer=null] - The renderer the property belongs to. When no renderer is set,
* the node refers to the renderer of the current state.
*/
constructor( property, inputType, renderer = null ) {
super( property, inputType, renderer );
/**
* The renderer the property belongs to. When no renderer is set,
* the node refers to the renderer of the current state.
*
* @type {?Renderer}
* @default null
*/
this.renderer = renderer;
this.setGroup( renderGroup );
}
/**
* Updates the reference based on the given state. The state is only evaluated
* {@link RendererReferenceNode#renderer} is not set.
*
* @param {(NodeFrame|NodeBuilder)} state - The current state.
* @return {Object} The updated reference.
*/
updateReference( state ) {
this.reference = this.renderer !== null ? this.renderer : state.renderer;
return this.reference;
}
}
export default RendererReferenceNode;
/**
* TSL function for creating a renderer reference node.
*
* @tsl
* @function
* @param {string} name - The name of the property the node refers to.
* @param {string} type - The uniform type that should be used to represent the property value.
* @param {?Renderer} [renderer=null] - The renderer the property belongs to. When no renderer is set,
* the node refers to the renderer of the current state.
* @returns {RendererReferenceNode}
*/
export const rendererReference = ( name, type, renderer = null ) => nodeObject( new RendererReferenceNode( name, type, renderer ) );

144
app/node_modules/three/src/nodes/accessors/SceneNode.js generated vendored Normal file
View File

@@ -0,0 +1,144 @@
import { UVMapping } from '../../constants.js';
import { Euler } from '../../math/Euler.js';
import { Matrix4 } from '../../math/Matrix4.js';
import Node from '../core/Node.js';
import { renderGroup } from '../core/UniformGroupNode.js';
import { nodeImmutable, uniform } from '../tsl/TSLBase.js';
import { reference } from './ReferenceNode.js';
const _e1 = /*@__PURE__*/ new Euler();
const _m1 = /*@__PURE__*/ new Matrix4();
/**
* This module allows access to a collection of scene properties. The following predefined TSL objects
* are available for easier use:
*
* - `backgroundBlurriness`: A node that represents the scene's background blurriness.
* - `backgroundIntensity`: A node that represents the scene's background intensity.
* - `backgroundRotation`: A node that represents the scene's background rotation.
*
* @augments Node
*/
class SceneNode extends Node {
static get type() {
return 'SceneNode';
}
/**
* Constructs a new scene node.
*
* @param {('backgroundBlurriness'|'backgroundIntensity'|'backgroundRotation')} scope - The scope defines the type of scene property that is accessed.
* @param {?Scene} [scene=null] - A reference to the scene.
*/
constructor( scope = SceneNode.BACKGROUND_BLURRINESS, scene = null ) {
super();
/**
* The scope defines the type of scene property that is accessed.
*
* @type {('backgroundBlurriness'|'backgroundIntensity'|'backgroundRotation')}
*/
this.scope = scope;
/**
* A reference to the scene that is going to be accessed.
*
* @type {?Scene}
* @default null
*/
this.scene = scene;
}
/**
* Depending on the scope, the method returns a different type of node that represents
* the respective scene property.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node} The output node.
*/
setup( builder ) {
const scope = this.scope;
const scene = this.scene !== null ? this.scene : builder.scene;
let output;
if ( scope === SceneNode.BACKGROUND_BLURRINESS ) {
output = reference( 'backgroundBlurriness', 'float', scene );
} else if ( scope === SceneNode.BACKGROUND_INTENSITY ) {
output = reference( 'backgroundIntensity', 'float', scene );
} else if ( scope === SceneNode.BACKGROUND_ROTATION ) {
output = uniform( 'mat4' ).label( 'backgroundRotation' ).setGroup( renderGroup ).onRenderUpdate( () => {
const background = scene.background;
if ( background !== null && background.isTexture && background.mapping !== UVMapping ) {
_e1.copy( scene.backgroundRotation );
// accommodate left-handed frame
_e1.x *= - 1; _e1.y *= - 1; _e1.z *= - 1;
_m1.makeRotationFromEuler( _e1 );
} else {
_m1.identity();
}
return _m1;
} );
} else {
console.error( 'THREE.SceneNode: Unknown scope:', scope );
}
return output;
}
}
SceneNode.BACKGROUND_BLURRINESS = 'backgroundBlurriness';
SceneNode.BACKGROUND_INTENSITY = 'backgroundIntensity';
SceneNode.BACKGROUND_ROTATION = 'backgroundRotation';
export default SceneNode;
/**
* TSL object that represents the scene's background blurriness.
*
* @tsl
* @type {SceneNode}
*/
export const backgroundBlurriness = /*@__PURE__*/ nodeImmutable( SceneNode, SceneNode.BACKGROUND_BLURRINESS );
/**
* TSL object that represents the scene's background intensity.
*
* @tsl
* @type {SceneNode}
*/
export const backgroundIntensity = /*@__PURE__*/ nodeImmutable( SceneNode, SceneNode.BACKGROUND_INTENSITY );
/**
* TSL object that represents the scene's background rotation.
*
* @tsl
* @type {SceneNode}
*/
export const backgroundRotation = /*@__PURE__*/ nodeImmutable( SceneNode, SceneNode.BACKGROUND_ROTATION );

View File

@@ -0,0 +1,327 @@
import Node from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { nodeObject } from '../tsl/TSLBase.js';
import { attribute } from '../core/AttributeNode.js';
import { reference, referenceBuffer } from './ReferenceNode.js';
import { add } from '../math/OperatorNode.js';
import { normalLocal } from './Normal.js';
import { positionLocal, positionPrevious } from './Position.js';
import { tangentLocal } from './Tangent.js';
import { uniform } from '../core/UniformNode.js';
import { buffer } from './BufferNode.js';
import { getDataFromObject } from '../core/NodeUtils.js';
import { storage } from './StorageBufferNode.js';
import { InstancedBufferAttribute } from '../../core/InstancedBufferAttribute.js';
import { instanceIndex } from '../core/IndexNode.js';
const _frameId = new WeakMap();
/**
* This node implements the vertex transformation shader logic which is required
* for skinning/skeletal animation.
*
* @augments Node
*/
class SkinningNode extends Node {
static get type() {
return 'SkinningNode';
}
/**
* Constructs a new skinning node.
*
* @param {SkinnedMesh} skinnedMesh - The skinned mesh.
*/
constructor( skinnedMesh ) {
super( 'void' );
/**
* The skinned mesh.
*
* @type {SkinnedMesh}
*/
this.skinnedMesh = skinnedMesh;
/**
* The update type overwritten since skinning nodes are updated per object.
*
* @type {string}
*/
this.updateType = NodeUpdateType.OBJECT;
//
/**
* The skin index attribute.
*
* @type {AttributeNode}
*/
this.skinIndexNode = attribute( 'skinIndex', 'uvec4' );
/**
* The skin weight attribute.
*
* @type {AttributeNode}
*/
this.skinWeightNode = attribute( 'skinWeight', 'vec4' );
/**
* The bind matrix node.
*
* @type {Node<mat4>}
*/
this.bindMatrixNode = reference( 'bindMatrix', 'mat4' );
/**
* The bind matrix inverse node.
*
* @type {Node<mat4>}
*/
this.bindMatrixInverseNode = reference( 'bindMatrixInverse', 'mat4' );
/**
* The bind matrices as a uniform buffer node.
*
* @type {Node}
*/
this.boneMatricesNode = referenceBuffer( 'skeleton.boneMatrices', 'mat4', skinnedMesh.skeleton.bones.length );
/**
* The current vertex position in local space.
*
* @type {Node<vec3>}
*/
this.positionNode = positionLocal;
/**
* The result of vertex position in local space.
*
* @type {Node<vec3>}
*/
this.toPositionNode = positionLocal;
/**
* The previous bind matrices as a uniform buffer node.
* Required for computing motion vectors.
*
* @type {?Node}
* @default null
*/
this.previousBoneMatricesNode = null;
}
/**
* Transforms the given vertex position via skinning.
*
* @param {Node} [boneMatrices=this.boneMatricesNode] - The bone matrices
* @param {Node<vec3>} [position=this.positionNode] - The vertex position in local space.
* @return {Node<vec3>} The transformed vertex position.
*/
getSkinnedPosition( boneMatrices = this.boneMatricesNode, position = this.positionNode ) {
const { skinIndexNode, skinWeightNode, bindMatrixNode, bindMatrixInverseNode } = this;
const boneMatX = boneMatrices.element( skinIndexNode.x );
const boneMatY = boneMatrices.element( skinIndexNode.y );
const boneMatZ = boneMatrices.element( skinIndexNode.z );
const boneMatW = boneMatrices.element( skinIndexNode.w );
// POSITION
const skinVertex = bindMatrixNode.mul( position );
const skinned = add(
boneMatX.mul( skinWeightNode.x ).mul( skinVertex ),
boneMatY.mul( skinWeightNode.y ).mul( skinVertex ),
boneMatZ.mul( skinWeightNode.z ).mul( skinVertex ),
boneMatW.mul( skinWeightNode.w ).mul( skinVertex )
);
return bindMatrixInverseNode.mul( skinned ).xyz;
}
/**
* Transforms the given vertex normal via skinning.
*
* @param {Node} [boneMatrices=this.boneMatricesNode] - The bone matrices
* @param {Node<vec3>} [normal=normalLocal] - The vertex normal in local space.
* @return {Node<vec3>} The transformed vertex normal.
*/
getSkinnedNormal( boneMatrices = this.boneMatricesNode, normal = normalLocal ) {
const { skinIndexNode, skinWeightNode, bindMatrixNode, bindMatrixInverseNode } = this;
const boneMatX = boneMatrices.element( skinIndexNode.x );
const boneMatY = boneMatrices.element( skinIndexNode.y );
const boneMatZ = boneMatrices.element( skinIndexNode.z );
const boneMatW = boneMatrices.element( skinIndexNode.w );
// NORMAL
let skinMatrix = add(
skinWeightNode.x.mul( boneMatX ),
skinWeightNode.y.mul( boneMatY ),
skinWeightNode.z.mul( boneMatZ ),
skinWeightNode.w.mul( boneMatW )
);
skinMatrix = bindMatrixInverseNode.mul( skinMatrix ).mul( bindMatrixNode );
return skinMatrix.transformDirection( normal ).xyz;
}
/**
* Computes the transformed/skinned vertex position of the previous frame.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec3>} The skinned position from the previous frame.
*/
getPreviousSkinnedPosition( builder ) {
const skinnedMesh = builder.object;
if ( this.previousBoneMatricesNode === null ) {
skinnedMesh.skeleton.previousBoneMatrices = new Float32Array( skinnedMesh.skeleton.boneMatrices );
this.previousBoneMatricesNode = referenceBuffer( 'skeleton.previousBoneMatrices', 'mat4', skinnedMesh.skeleton.bones.length );
}
return this.getSkinnedPosition( this.previousBoneMatricesNode, positionPrevious );
}
/**
* Returns `true` if bone matrices from the previous frame are required. Relevant
* when computing motion vectors with {@link VelocityNode}.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {boolean} Whether bone matrices from the previous frame are required or not.
*/
needsPreviousBoneMatrices( builder ) {
const mrt = builder.renderer.getMRT();
return ( mrt && mrt.has( 'velocity' ) ) || getDataFromObject( builder.object ).useVelocity === true;
}
/**
* Setups the skinning node by assigning the transformed vertex data to predefined node variables.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec3>} The transformed vertex position.
*/
setup( builder ) {
if ( this.needsPreviousBoneMatrices( builder ) ) {
positionPrevious.assign( this.getPreviousSkinnedPosition( builder ) );
}
const skinPosition = this.getSkinnedPosition();
if ( this.toPositionNode ) this.toPositionNode.assign( skinPosition );
//
if ( builder.hasGeometryAttribute( 'normal' ) ) {
const skinNormal = this.getSkinnedNormal();
normalLocal.assign( skinNormal );
if ( builder.hasGeometryAttribute( 'tangent' ) ) {
tangentLocal.assign( skinNormal );
}
}
return skinPosition;
}
/**
* Generates the code snippet of the skinning node.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {string} output - The current output.
* @return {string} The generated code snippet.
*/
generate( builder, output ) {
if ( output !== 'void' ) {
return super.generate( builder, output );
}
}
/**
* Updates the state of the skinned mesh by updating the skeleton once per frame.
*
* @param {NodeFrame} frame - The current node frame.
*/
update( frame ) {
const skeleton = frame.object && frame.object.skeleton ? frame.object.skeleton : this.skinnedMesh.skeleton;
if ( _frameId.get( skeleton ) === frame.frameId ) return;
_frameId.set( skeleton, frame.frameId );
if ( this.previousBoneMatricesNode !== null ) skeleton.previousBoneMatrices.set( skeleton.boneMatrices );
skeleton.update();
}
}
export default SkinningNode;
/**
* TSL function for creating a skinning node.
*
* @tsl
* @function
* @param {SkinnedMesh} skinnedMesh - The skinned mesh.
* @returns {SkinningNode}
*/
export const skinning = ( skinnedMesh ) => nodeObject( new SkinningNode( skinnedMesh ) );
/**
* TSL function for computing skinning.
*
* @tsl
* @function
* @param {SkinnedMesh} skinnedMesh - The skinned mesh.
* @param {Node<vec3>} [toPosition=null] - The target position.
* @returns {SkinningNode}
*/
export const computeSkinning = ( skinnedMesh, toPosition = null ) => {
const node = new SkinningNode( skinnedMesh );
node.positionNode = storage( new InstancedBufferAttribute( skinnedMesh.geometry.getAttribute( 'position' ).array, 3 ), 'vec3' ).setPBO( true ).toReadOnly().element( instanceIndex ).toVar();
node.skinIndexNode = storage( new InstancedBufferAttribute( new Uint32Array( skinnedMesh.geometry.getAttribute( 'skinIndex' ).array ), 4 ), 'uvec4' ).setPBO( true ).toReadOnly().element( instanceIndex ).toVar();
node.skinWeightNode = storage( new InstancedBufferAttribute( skinnedMesh.geometry.getAttribute( 'skinWeight' ).array, 4 ), 'vec4' ).setPBO( true ).toReadOnly().element( instanceIndex ).toVar();
node.bindMatrixNode = uniform( skinnedMesh.bindMatrix, 'mat4' );
node.bindMatrixInverseNode = uniform( skinnedMesh.bindMatrixInverse, 'mat4' );
node.boneMatricesNode = buffer( skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length );
node.toPositionNode = toPosition;
return nodeObject( node );
};

View File

@@ -0,0 +1,415 @@
import BufferNode from './BufferNode.js';
import { bufferAttribute } from './BufferAttributeNode.js';
import { nodeObject, varying } from '../tsl/TSLBase.js';
import { storageElement } from '../utils/StorageArrayElementNode.js';
import { NodeAccess } from '../core/constants.js';
import { getTypeFromLength } from '../core/NodeUtils.js';
/**
* This node is used in context of compute shaders and allows to define a
* storage buffer for data. A typical workflow is to create instances of
* this node with the convenience functions `attributeArray()` or `instancedArray()`,
* setup up a compute shader that writes into the buffers and then convert
* the storage buffers to attribute nodes for rendering.
*
* ```js
* const positionBuffer = instancedArray( particleCount, 'vec3' ); // the storage buffer node
*
* const computeInit = Fn( () => { // the compute shader
*
* const position = positionBuffer.element( instanceIndex );
*
* // compute position data
*
* position.x = 1;
* position.y = 1;
* position.z = 1;
*
* } )().compute( particleCount );
*
* const particleMaterial = new THREE.SpriteNodeMaterial();
* particleMaterial.positionNode = positionBuffer.toAttribute();
*
* renderer.computeAsync( computeInit );
*
* ```
*
* @augments BufferNode
*/
class StorageBufferNode extends BufferNode {
static get type() {
return 'StorageBufferNode';
}
/**
* Constructs a new storage buffer node.
*
* @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
* @param {?(string|Struct)} [bufferType=null] - The buffer type (e.g. `'vec3'`).
* @param {number} [bufferCount=0] - The buffer count.
*/
constructor( value, bufferType = null, bufferCount = 0 ) {
let nodeType, structTypeNode = null;
if ( bufferType && bufferType.isStruct ) {
nodeType = 'struct';
structTypeNode = bufferType.layout;
if ( value.isStorageBufferAttribute || value.isStorageInstancedBufferAttribute ) {
bufferCount = value.count;
}
} else if ( bufferType === null && ( value.isStorageBufferAttribute || value.isStorageInstancedBufferAttribute ) ) {
nodeType = getTypeFromLength( value.itemSize );
bufferCount = value.count;
} else {
nodeType = bufferType;
}
super( value, nodeType, bufferCount );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isStorageBufferNode = true;
/**
* The buffer struct type.
*
* @type {?StructTypeNode}
* @default null
*/
this.structTypeNode = structTypeNode;
/**
* The access type of the texture node.
*
* @type {string}
* @default 'readWrite'
*/
this.access = NodeAccess.READ_WRITE;
/**
* Whether the node is atomic or not.
*
* @type {boolean}
* @default false
*/
this.isAtomic = false;
/**
* Whether the node represents a PBO or not.
* Only relevant for WebGL.
*
* @type {boolean}
* @default false
*/
this.isPBO = false;
/**
* A reference to the internal buffer attribute node.
*
* @type {?BufferAttributeNode}
* @default null
*/
this._attribute = null;
/**
* A reference to the internal varying node.
*
* @type {?VaryingNode}
* @default null
*/
this._varying = null;
/**
* `StorageBufferNode` sets this property to `true` by default.
*
* @type {boolean}
* @default true
*/
this.global = true;
if ( value.isStorageBufferAttribute !== true && value.isStorageInstancedBufferAttribute !== true ) {
// TODO: Improve it, possibly adding a new property to the BufferAttribute to identify it as a storage buffer read-only attribute in Renderer
if ( value.isInstancedBufferAttribute ) value.isStorageInstancedBufferAttribute = true;
else value.isStorageBufferAttribute = true;
}
}
/**
* This method is overwritten since the buffer data might be shared
* and thus the hash should be shared as well.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The hash.
*/
getHash( builder ) {
if ( this.bufferCount === 0 ) {
let bufferData = builder.globalCache.getData( this.value );
if ( bufferData === undefined ) {
bufferData = {
node: this
};
builder.globalCache.setData( this.value, bufferData );
}
return bufferData.node.uuid;
}
return this.uuid;
}
/**
* Overwrites the default implementation to return a fixed value `'indirectStorageBuffer'` or `'storageBuffer'`.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The input type.
*/
getInputType( /*builder*/ ) {
return this.value.isIndirectStorageBufferAttribute ? 'indirectStorageBuffer' : 'storageBuffer';
}
/**
* Enables element access with the given index node.
*
* @param {IndexNode} indexNode - The index node.
* @return {StorageArrayElementNode} A node representing the element access.
*/
element( indexNode ) {
return storageElement( this, indexNode );
}
/**
* Defines whether this node is a PBO or not. Only relevant for WebGL.
*
* @param {boolean} value - The value so set.
* @return {StorageBufferNode} A reference to this node.
*/
setPBO( value ) {
this.isPBO = value;
return this;
}
/**
* Returns the `isPBO` value.
*
* @return {boolean} Whether the node represents a PBO or not.
*/
getPBO() {
return this.isPBO;
}
/**
* Defines the node access.
*
* @param {string} value - The node access.
* @return {StorageBufferNode} A reference to this node.
*/
setAccess( value ) {
this.access = value;
return this;
}
/**
* Convenience method for configuring a read-only node access.
*
* @return {StorageBufferNode} A reference to this node.
*/
toReadOnly() {
return this.setAccess( NodeAccess.READ_ONLY );
}
/**
* Defines whether the node is atomic or not.
*
* @param {boolean} value - The atomic flag.
* @return {StorageBufferNode} A reference to this node.
*/
setAtomic( value ) {
this.isAtomic = value;
return this;
}
/**
* Convenience method for making this node atomic.
*
* @return {StorageBufferNode} A reference to this node.
*/
toAtomic() {
return this.setAtomic( true );
}
/**
* Returns attribute data for this storage buffer node.
*
* @return {{attribute: BufferAttributeNode, varying: VaryingNode}} The attribute data.
*/
getAttributeData() {
if ( this._attribute === null ) {
this._attribute = bufferAttribute( this.value );
this._varying = varying( this._attribute );
}
return {
attribute: this._attribute,
varying: this._varying
};
}
/**
* This method is overwritten since the node type from the availability of storage buffers
* and the attribute data.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The node type.
*/
getNodeType( builder ) {
if ( this.structTypeNode !== null ) {
return this.structTypeNode.getNodeType( builder );
}
if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) {
return super.getNodeType( builder );
}
const { attribute } = this.getAttributeData();
return attribute.getNodeType( builder );
}
/**
* Returns the type of a member of the struct.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {string} name - The name of the member.
* @return {string} The type of the member.
*/
getMemberType( builder, name ) {
if ( this.structTypeNode !== null ) {
return this.structTypeNode.getMemberType( builder, name );
}
return 'void';
}
/**
* Generates the code snippet of the storage buffer node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The generated code snippet.
*/
generate( builder ) {
if ( this.structTypeNode !== null ) this.structTypeNode.build( builder );
if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) {
return super.generate( builder );
}
const { attribute, varying } = this.getAttributeData();
const output = varying.build( builder );
builder.registerTransform( output, attribute );
return output;
}
}
export default StorageBufferNode;
/**
* TSL function for creating a storage buffer node.
*
* @tsl
* @function
* @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
* @param {?(string|Struct)} [type=null] - The buffer type (e.g. `'vec3'`).
* @param {number} [count=0] - The buffer count.
* @returns {StorageBufferNode}
*/
export const storage = ( value, type = null, count = 0 ) => nodeObject( new StorageBufferNode( value, type, count ) );
/**
* @tsl
* @function
* @deprecated since r171. Use `storage().setPBO( true )` instead.
*
* @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
* @param {?string} type - The buffer type (e.g. `'vec3'`).
* @param {number} count - The buffer count.
* @returns {StorageBufferNode}
*/
export const storageObject = ( value, type, count ) => { // @deprecated, r171
console.warn( 'THREE.TSL: "storageObject()" is deprecated. Use "storage().setPBO( true )" instead.' );
return storage( value, type, count ).setPBO( true );
};

View File

@@ -0,0 +1,242 @@
import TextureNode from './TextureNode.js';
import { nodeProxy } from '../tsl/TSLBase.js';
import { NodeAccess } from '../core/constants.js';
/**
* This special version of a texture node can be used to
* write data into a storage texture with a compute shader.
*
* ```js
* const storageTexture = new THREE.StorageTexture( width, height );
*
* const computeTexture = Fn( ( { storageTexture } ) => {
*
* const posX = instanceIndex.mod( width );
* const posY = instanceIndex.div( width );
* const indexUV = uvec2( posX, posY );
*
* // generate RGB values
*
* const r = 1;
* const g = 1;
* const b = 1;
*
* textureStore( storageTexture, indexUV, vec4( r, g, b, 1 ) ).toWriteOnly();
*
* } );
*
* const computeNode = computeTexture( { storageTexture } ).compute( width * height );
* renderer.computeAsync( computeNode );
* ```
*
* This node can only be used with a WebGPU backend.
*
* @augments TextureNode
*/
class StorageTextureNode extends TextureNode {
static get type() {
return 'StorageTextureNode';
}
/**
* Constructs a new storage texture node.
*
* @param {StorageTexture} value - The storage texture.
* @param {Node<vec2|vec3>} uvNode - The uv node.
* @param {?Node} [storeNode=null] - The value node that should be stored in the texture.
*/
constructor( value, uvNode, storeNode = null ) {
super( value, uvNode );
/**
* The value node that should be stored in the texture.
*
* @type {?Node}
* @default null
*/
this.storeNode = storeNode;
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isStorageTextureNode = true;
/**
* The access type of the texture node.
*
* @type {string}
* @default 'writeOnly'
*/
this.access = NodeAccess.WRITE_ONLY;
}
/**
* Overwrites the default implementation to return a fixed value `'storageTexture'`.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The input type.
*/
getInputType( /*builder*/ ) {
return 'storageTexture';
}
setup( builder ) {
super.setup( builder );
const properties = builder.getNodeProperties( this );
properties.storeNode = this.storeNode;
return properties;
}
/**
* Defines the node access.
*
* @param {string} value - The node access.
* @return {StorageTextureNode} A reference to this node.
*/
setAccess( value ) {
this.access = value;
return this;
}
/**
* Generates the code snippet of the storage node. If no `storeNode`
* is defined, the texture node is generated as normal texture.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {string} output - The current output.
* @return {string} The generated code snippet.
*/
generate( builder, output ) {
let snippet;
if ( this.storeNode !== null ) {
snippet = this.generateStore( builder );
} else {
snippet = super.generate( builder, output );
}
return snippet;
}
/**
* Convenience method for configuring a read/write node access.
*
* @return {StorageTextureNode} A reference to this node.
*/
toReadWrite() {
return this.setAccess( NodeAccess.READ_WRITE );
}
/**
* Convenience method for configuring a read-only node access.
*
* @return {StorageTextureNode} A reference to this node.
*/
toReadOnly() {
return this.setAccess( NodeAccess.READ_ONLY );
}
/**
* Convenience method for configuring a write-only node access.
*
* @return {StorageTextureNode} A reference to this node.
*/
toWriteOnly() {
return this.setAccess( NodeAccess.WRITE_ONLY );
}
/**
* Generates the code snippet of the storage texture node.
*
* @param {NodeBuilder} builder - The current node builder.
*/
generateStore( builder ) {
const properties = builder.getNodeProperties( this );
const { uvNode, storeNode, depthNode } = properties;
const textureProperty = super.generate( builder, 'property' );
const uvSnippet = uvNode.build( builder, 'uvec2' );
const storeSnippet = storeNode.build( builder, 'vec4' );
const depthSnippet = depthNode ? depthNode.build( builder, 'int' ) : null;
const snippet = builder.generateTextureStore( builder, textureProperty, uvSnippet, depthSnippet, storeSnippet );
builder.addLineFlowCode( snippet, this );
}
clone() {
const newNode = super.clone();
newNode.storeNode = this.storeNode;
return newNode;
}
}
export default StorageTextureNode;
/**
* TSL function for creating a storage texture node.
*
* @tsl
* @function
* @param {StorageTexture} value - The storage texture.
* @param {?Node<vec2|vec3>} uvNode - The uv node.
* @param {?Node} [storeNode=null] - The value node that should be stored in the texture.
* @returns {StorageTextureNode}
*/
export const storageTexture = /*@__PURE__*/ nodeProxy( StorageTextureNode ).setParameterLength( 1, 3 );
/**
* TODO: Explain difference to `storageTexture()`.
*
* @tsl
* @function
* @param {StorageTexture} value - The storage texture.
* @param {Node<vec2|vec3>} uvNode - The uv node.
* @param {?Node} [storeNode=null] - The value node that should be stored in the texture.
* @returns {StorageTextureNode}
*/
export const textureStore = ( value, uvNode, storeNode ) => {
const node = storageTexture( value, uvNode, storeNode );
if ( storeNode !== null ) node.toStack();
return node;
};

70
app/node_modules/three/src/nodes/accessors/Tangent.js generated vendored Normal file
View File

@@ -0,0 +1,70 @@
import { attribute } from '../core/AttributeNode.js';
import { cameraViewMatrix } from './Camera.js';
import { modelViewMatrix } from './ModelNode.js';
import { Fn, vec4 } from '../tsl/TSLBase.js';
import { tangentViewFrame } from './TangentUtils.js';
import { directionToFaceDirection } from '../display/FrontFacingNode.js';
/**
* TSL object that represents the tangent attribute of the current rendered object.
*
* @tsl
* @type {Node<vec4>}
*/
export const tangentGeometry = /*@__PURE__*/ Fn( ( builder ) => {
if ( builder.geometry.hasAttribute( 'tangent' ) === false ) {
builder.geometry.computeTangents();
}
return attribute( 'tangent', 'vec4' );
} )();
/**
* TSL object that represents the vertex tangent in local space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const tangentLocal = /*@__PURE__*/ tangentGeometry.xyz.toVar( 'tangentLocal' );
/**
* TSL object that represents the vertex tangent in view space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const tangentView = /*@__PURE__*/ ( Fn( ( { subBuildFn, geometry, material } ) => {
let node;
if ( subBuildFn === 'VERTEX' || geometry.hasAttribute( 'tangent' ) ) {
node = modelViewMatrix.mul( vec4( tangentLocal, 0 ) ).xyz.toVarying( 'v_tangentView' ).normalize();
} else {
node = tangentViewFrame;
}
if ( material.flatShading !== true ) {
node = directionToFaceDirection( node );
}
return node;
}, 'vec3' ).once( [ 'NORMAL', 'VERTEX' ] ) )().toVar( 'tangentView' );
/**
* TSL object that represents the vertex tangent in world space of the current rendered object.
*
* @tsl
* @type {Node<vec3>}
*/
export const tangentWorld = /*@__PURE__*/ tangentView.transformDirection( cameraViewMatrix ).toVarying( 'v_tangentWorld' ).normalize().toVar( 'tangentWorld' );

View File

@@ -0,0 +1,46 @@
import { uv as getUV } from './UV.js';
import { positionView } from './Position.js';
import { normalView } from './Normal.js';
// Normal Mapping Without Precomputed Tangents
// http://www.thetenthplanet.de/archives/1180
const uv = getUV();
const q0 = positionView.dFdx();
const q1 = positionView.dFdy();
const st0 = uv.dFdx();
const st1 = uv.dFdy();
const N = normalView;
const q1perp = q1.cross( N );
const q0perp = N.cross( q0 );
const T = q1perp.mul( st0.x ).add( q0perp.mul( st1.x ) );
const B = q1perp.mul( st0.y ).add( q0perp.mul( st1.y ) );
const det = T.dot( T ).max( B.dot( B ) );
const scale = det.equal( 0.0 ).select( 0.0, det.inverseSqrt() );
/**
* Tangent vector in view space, computed dynamically from geometry and UV derivatives.
* Useful for normal mapping without precomputed tangents.
*
* Reference: http://www.thetenthplanet.de/archives/1180
*
* @tsl
* @type {Node<vec3>}
*/
export const tangentViewFrame = /*@__PURE__*/ T.mul( scale ).toVar( 'tangentViewFrame' );
/**
* Bitangent vector in view space, computed dynamically from geometry and UV derivatives.
* Complements the tangentViewFrame for constructing the tangent space basis.
*
* Reference: http://www.thetenthplanet.de/archives/1180
*
* @tsl
* @type {Node<vec3>}
*/
export const bitangentViewFrame = /*@__PURE__*/ B.mul( scale ).toVar( 'bitangentViewFrame' );

View File

@@ -0,0 +1,186 @@
import TextureNode from './TextureNode.js';
import { nodeProxy, vec3, Fn, If, int } from '../tsl/TSLBase.js';
import { textureSize } from './TextureSizeNode.js';
const normal = Fn( ( { texture, uv } ) => {
const epsilon = 0.0001;
const ret = vec3().toVar();
If( uv.x.lessThan( epsilon ), () => {
ret.assign( vec3( 1, 0, 0 ) );
} ).ElseIf( uv.y.lessThan( epsilon ), () => {
ret.assign( vec3( 0, 1, 0 ) );
} ).ElseIf( uv.z.lessThan( epsilon ), () => {
ret.assign( vec3( 0, 0, 1 ) );
} ).ElseIf( uv.x.greaterThan( 1 - epsilon ), () => {
ret.assign( vec3( - 1, 0, 0 ) );
} ).ElseIf( uv.y.greaterThan( 1 - epsilon ), () => {
ret.assign( vec3( 0, - 1, 0 ) );
} ).ElseIf( uv.z.greaterThan( 1 - epsilon ), () => {
ret.assign( vec3( 0, 0, - 1 ) );
} ).Else( () => {
const step = 0.01;
const x = texture.sample( uv.add( vec3( - step, 0.0, 0.0 ) ) ).r.sub( texture.sample( uv.add( vec3( step, 0.0, 0.0 ) ) ).r );
const y = texture.sample( uv.add( vec3( 0.0, - step, 0.0 ) ) ).r.sub( texture.sample( uv.add( vec3( 0.0, step, 0.0 ) ) ).r );
const z = texture.sample( uv.add( vec3( 0.0, 0.0, - step ) ) ).r.sub( texture.sample( uv.add( vec3( 0.0, 0.0, step ) ) ).r );
ret.assign( vec3( x, y, z ) );
} );
return ret.normalize();
} );
/**
* This type of uniform node represents a 3D texture.
*
* @augments TextureNode
*/
class Texture3DNode extends TextureNode {
static get type() {
return 'Texture3DNode';
}
/**
* Constructs a new 3D texture node.
*
* @param {Data3DTexture} value - The 3D texture.
* @param {?Node<vec2|vec3>} [uvNode=null] - The uv node.
* @param {?Node<int>} [levelNode=null] - The level node.
*/
constructor( value, uvNode = null, levelNode = null ) {
super( value, uvNode, levelNode );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isTexture3DNode = true;
}
/**
* Overwrites the default implementation to return a fixed value `'texture3D'`.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The input type.
*/
getInputType( /*builder*/ ) {
return 'texture3D';
}
/**
* Returns a default uv node which is in context of 3D textures a three-dimensional
* uv node.
*
* @return {Node<vec3>} The default uv node.
*/
getDefaultUV() {
return vec3( 0.5, 0.5, 0.5 );
}
/**
* Overwritten with an empty implementation since the `updateMatrix` flag is ignored
* for 3D textures. The uv transformation matrix is not applied to 3D textures.
*
* @param {boolean} value - The update toggle.
*/
setUpdateMatrix( /*value*/ ) { } // Ignore .updateMatrix for 3d TextureNode
/**
* Overwrites the default implementation to return the unmodified uv node.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {Node} uvNode - The uv node to setup.
* @return {Node} The unmodified uv node.
*/
setupUV( builder, uvNode ) {
const texture = this.value;
if ( builder.isFlipY() && ( texture.isRenderTargetTexture === true || texture.isFramebufferTexture === true ) ) {
if ( this.sampler ) {
uvNode = uvNode.flipY();
} else {
uvNode = uvNode.setY( int( textureSize( this, this.levelNode ).y ).sub( uvNode.y ).sub( 1 ) );
}
}
return uvNode;
}
/**
* Generates the uv code snippet.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {Node} uvNode - The uv node to generate code for.
* @return {string} The generated code snippet.
*/
generateUV( builder, uvNode ) {
return uvNode.build( builder, 'vec3' );
}
/**
* TODO.
*
* @param {Node<vec3>} uvNode - The uv node .
* @return {Node<vec3>} TODO.
*/
normal( uvNode ) {
return normal( { texture: this, uv: uvNode } );
}
}
export default Texture3DNode;
/**
* TSL function for creating a 3D texture node.
*
* @tsl
* @function
* @param {Data3DTexture} value - The 3D texture.
* @param {?Node<vec2|vec3>} [uvNode=null] - The uv node.
* @param {?Node<int>} [levelNode=null] - The level node.
* @returns {Texture3DNode}
*/
export const texture3D = /*@__PURE__*/ nodeProxy( Texture3DNode ).setParameterLength( 1, 3 );

View File

@@ -0,0 +1,92 @@
import { add, mul, div } from '../math/OperatorNode.js';
import { floor, ceil, fract, pow } from '../math/MathNode.js';
import { Fn, vec2, vec4, int } from '../tsl/TSLBase.js';
import { maxMipLevel } from '../utils/MaxMipLevelNode.js';
// Mipped Bicubic Texture Filtering by N8
// https://www.shadertoy.com/view/Dl2SDW
const bC = 1.0 / 6.0;
const w0 = ( a ) => mul( bC, mul( a, mul( a, a.negate().add( 3.0 ) ).sub( 3.0 ) ).add( 1.0 ) );
const w1 = ( a ) => mul( bC, mul( a, mul( a, mul( 3.0, a ).sub( 6.0 ) ) ).add( 4.0 ) );
const w2 = ( a ) => mul( bC, mul( a, mul( a, mul( - 3.0, a ).add( 3.0 ) ).add( 3.0 ) ).add( 1.0 ) );
const w3 = ( a ) => mul( bC, pow( a, 3 ) );
const g0 = ( a ) => w0( a ).add( w1( a ) );
const g1 = ( a ) => w2( a ).add( w3( a ) );
// h0 and h1 are the two offset functions
const h0 = ( a ) => add( - 1.0, w1( a ).div( w0( a ).add( w1( a ) ) ) );
const h1 = ( a ) => add( 1.0, w3( a ).div( w2( a ).add( w3( a ) ) ) );
const bicubic = ( textureNode, texelSize, lod ) => {
const uv = textureNode.uvNode;
const uvScaled = mul( uv, texelSize.zw ).add( 0.5 );
const iuv = floor( uvScaled );
const fuv = fract( uvScaled );
const g0x = g0( fuv.x );
const g1x = g1( fuv.x );
const h0x = h0( fuv.x );
const h1x = h1( fuv.x );
const h0y = h0( fuv.y );
const h1y = h1( fuv.y );
const p0 = vec2( iuv.x.add( h0x ), iuv.y.add( h0y ) ).sub( 0.5 ).mul( texelSize.xy );
const p1 = vec2( iuv.x.add( h1x ), iuv.y.add( h0y ) ).sub( 0.5 ).mul( texelSize.xy );
const p2 = vec2( iuv.x.add( h0x ), iuv.y.add( h1y ) ).sub( 0.5 ).mul( texelSize.xy );
const p3 = vec2( iuv.x.add( h1x ), iuv.y.add( h1y ) ).sub( 0.5 ).mul( texelSize.xy );
const a = g0( fuv.y ).mul( add( g0x.mul( textureNode.sample( p0 ).level( lod ) ), g1x.mul( textureNode.sample( p1 ).level( lod ) ) ) );
const b = g1( fuv.y ).mul( add( g0x.mul( textureNode.sample( p2 ).level( lod ) ), g1x.mul( textureNode.sample( p3 ).level( lod ) ) ) );
return a.add( b );
};
/**
* Applies mipped bicubic texture filtering to the given texture node.
*
* @tsl
* @function
* @param {TextureNode} textureNode - The texture node that should be filtered.
* @param {Node<float>} lodNode - Defines the LOD to sample from.
* @return {Node} The filtered texture sample.
*/
export const textureBicubicLevel = /*@__PURE__*/ Fn( ( [ textureNode, lodNode ] ) => {
const fLodSize = vec2( textureNode.size( int( lodNode ) ) );
const cLodSize = vec2( textureNode.size( int( lodNode.add( 1.0 ) ) ) );
const fLodSizeInv = div( 1.0, fLodSize );
const cLodSizeInv = div( 1.0, cLodSize );
const fSample = bicubic( textureNode, vec4( fLodSizeInv, fLodSize ), floor( lodNode ) );
const cSample = bicubic( textureNode, vec4( cLodSizeInv, cLodSize ), ceil( lodNode ) );
return fract( lodNode ).mix( fSample, cSample );
} );
/**
* Applies mipped bicubic texture filtering to the given texture node.
*
* @tsl
* @function
* @param {TextureNode} textureNode - The texture node that should be filtered.
* @param {Node<float>} [strength] - Defines the strength of the bicubic filtering.
* @return {Node} The filtered texture sample.
*/
export const textureBicubic = /*@__PURE__*/ Fn( ( [ textureNode, strength ] ) => {
const lod = strength.mul( maxMipLevel( textureNode ) );
return textureBicubicLevel( textureNode, lod );
} );

View File

@@ -0,0 +1,829 @@
import UniformNode, { uniform } from '../core/UniformNode.js';
import { uv } from './UV.js';
import { textureSize } from './TextureSizeNode.js';
import { colorSpaceToWorking } from '../display/ColorSpaceNode.js';
import { expression } from '../code/ExpressionNode.js';
import { maxMipLevel } from '../utils/MaxMipLevelNode.js';
import { nodeProxy, vec3, nodeObject, int } from '../tsl/TSLBase.js';
import { NodeUpdateType } from '../core/constants.js';
import { IntType, NearestFilter, UnsignedIntType } from '../../constants.js';
import { Texture } from '../../textures/Texture.js';
const EmptyTexture = /*@__PURE__*/ new Texture();
/**
* This type of uniform node represents a 2D texture.
*
* @augments UniformNode
*/
class TextureNode extends UniformNode {
static get type() {
return 'TextureNode';
}
/**
* Constructs a new texture node.
*
* @param {Texture} [value=EmptyTexture] - The texture.
* @param {?Node<vec2|vec3>} [uvNode=null] - The uv node.
* @param {?Node<int>} [levelNode=null] - The level node.
* @param {?Node<float>} [biasNode=null] - The bias node.
*/
constructor( value = EmptyTexture, uvNode = null, levelNode = null, biasNode = null ) {
super( value );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isTextureNode = true;
/**
* Represents the texture coordinates.
*
* @type {?Node<vec2|vec3>}
* @default null
*/
this.uvNode = uvNode;
/**
* Represents the mip level that should be selected.
*
* @type {?Node<int>}
* @default null
*/
this.levelNode = levelNode;
/**
* Represents the bias to be applied during level-of-detail computation.
*
* @type {?Node<float>}
* @default null
*/
this.biasNode = biasNode;
/**
* Represents a reference value a texture sample is compared to.
*
* @type {?Node<float>}
* @default null
*/
this.compareNode = null;
/**
* When using texture arrays, the depth node defines the layer to select.
*
* @type {?Node<int>}
* @default null
*/
this.depthNode = null;
/**
* When defined, a texture is sampled using explicit gradients.
*
* @type {?Array<Node<vec2>>}
* @default null
*/
this.gradNode = null;
/**
* Whether texture values should be sampled or fetched.
*
* @type {boolean}
* @default true
*/
this.sampler = true;
/**
* Whether the uv transformation matrix should be
* automatically updated or not. Use `setUpdateMatrix()`
* if you want to change the value of the property.
*
* @type {boolean}
* @default false
*/
this.updateMatrix = false;
/**
* By default the `update()` method is not executed. `setUpdateMatrix()`
* sets the value to `frame` when the uv transformation matrix should
* automatically be updated.
*
* @type {string}
* @default 'none'
*/
this.updateType = NodeUpdateType.NONE;
/**
* The reference node.
*
* @type {?Node}
* @default null
*/
this.referenceNode = null;
/**
* The texture value is stored in a private property.
*
* @private
* @type {Texture}
*/
this._value = value;
/**
* The uniform node that represents the uv transformation matrix.
*
* @private
* @type {?UniformNode<mat3>}
*/
this._matrixUniform = null;
this.setUpdateMatrix( uvNode === null );
}
set value( value ) {
if ( this.referenceNode ) {
this.referenceNode.value = value;
} else {
this._value = value;
}
}
/**
* The texture value.
*
* @type {Texture}
*/
get value() {
return this.referenceNode ? this.referenceNode.value : this._value;
}
/**
* Overwritten since the uniform hash is defined by the texture's UUID.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The uniform hash.
*/
getUniformHash( /*builder*/ ) {
return this.value.uuid;
}
/**
* Overwritten since the node type is inferred from the texture type.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The node type.
*/
getNodeType( /*builder*/ ) {
if ( this.value.isDepthTexture === true ) return 'float';
if ( this.value.type === UnsignedIntType ) {
return 'uvec4';
} else if ( this.value.type === IntType ) {
return 'ivec4';
}
return 'vec4';
}
/**
* Overwrites the default implementation to return a fixed value `'texture'`.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The input type.
*/
getInputType( /*builder*/ ) {
return 'texture';
}
/**
* Returns a default uvs based on the current texture's channel.
*
* @return {AttributeNode<vec2>} The default uvs.
*/
getDefaultUV() {
return uv( this.value.channel );
}
/**
* Overwritten to always return the texture reference of the node.
*
* @param {any} state - This method can be invocated in different contexts so `state` can refer to any object type.
* @return {Texture} The texture reference.
*/
updateReference( /*state*/ ) {
return this.value;
}
/**
* Transforms the given uv node with the texture transformation matrix.
*
* @param {Node} uvNode - The uv node to transform.
* @return {Node} The transformed uv node.
*/
getTransformedUV( uvNode ) {
if ( this._matrixUniform === null ) this._matrixUniform = uniform( this.value.matrix );
return this._matrixUniform.mul( vec3( uvNode, 1 ) ).xy;
}
/**
* Defines whether the uv transformation matrix should automatically be updated or not.
*
* @param {boolean} value - The update toggle.
* @return {TextureNode} A reference to this node.
*/
setUpdateMatrix( value ) {
this.updateMatrix = value;
this.updateType = value ? NodeUpdateType.OBJECT : NodeUpdateType.NONE;
return this;
}
/**
* Setups the uv node. Depending on the backend as well as texture's image and type, it might be necessary
* to modify the uv node for correct sampling.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {Node} uvNode - The uv node to setup.
* @return {Node} The updated uv node.
*/
setupUV( builder, uvNode ) {
const texture = this.value;
if ( builder.isFlipY() && ( ( texture.image instanceof ImageBitmap && texture.flipY === true ) || texture.isRenderTargetTexture === true || texture.isFramebufferTexture === true || texture.isDepthTexture === true ) ) {
if ( this.sampler ) {
uvNode = uvNode.flipY();
} else {
uvNode = uvNode.setY( int( textureSize( this, this.levelNode ).y ).sub( uvNode.y ).sub( 1 ) );
}
}
return uvNode;
}
/**
* Setups texture node by preparing the internal nodes for code generation.
*
* @param {NodeBuilder} builder - The current node builder.
*/
setup( builder ) {
const properties = builder.getNodeProperties( this );
properties.referenceNode = this.referenceNode;
//
const texture = this.value;
if ( ! texture || texture.isTexture !== true ) {
throw new Error( 'THREE.TSL: `texture( value )` function expects a valid instance of THREE.Texture().' );
}
//
let uvNode = this.uvNode;
if ( ( uvNode === null || builder.context.forceUVContext === true ) && builder.context.getUV ) {
uvNode = builder.context.getUV( this, builder );
}
if ( ! uvNode ) uvNode = this.getDefaultUV();
if ( this.updateMatrix === true ) {
uvNode = this.getTransformedUV( uvNode );
}
uvNode = this.setupUV( builder, uvNode );
//
let levelNode = this.levelNode;
if ( levelNode === null && builder.context.getTextureLevel ) {
levelNode = builder.context.getTextureLevel( this );
}
//
properties.uvNode = uvNode;
properties.levelNode = levelNode;
properties.biasNode = this.biasNode;
properties.compareNode = this.compareNode;
properties.gradNode = this.gradNode;
properties.depthNode = this.depthNode;
}
/**
* Generates the uv code snippet.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {Node} uvNode - The uv node to generate code for.
* @return {string} The generated code snippet.
*/
generateUV( builder, uvNode ) {
return uvNode.build( builder, this.sampler === true ? 'vec2' : 'ivec2' );
}
/**
* Generates the snippet for the texture sampling.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {string} textureProperty - The texture property.
* @param {string} uvSnippet - The uv snippet.
* @param {?string} levelSnippet - The level snippet.
* @param {?string} biasSnippet - The bias snippet.
* @param {?string} depthSnippet - The depth snippet.
* @param {?string} compareSnippet - The compare snippet.
* @param {?Array<string>} gradSnippet - The grad snippet.
* @return {string} The generated code snippet.
*/
generateSnippet( builder, textureProperty, uvSnippet, levelSnippet, biasSnippet, depthSnippet, compareSnippet, gradSnippet ) {
const texture = this.value;
let snippet;
if ( levelSnippet ) {
snippet = builder.generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet, depthSnippet );
} else if ( biasSnippet ) {
snippet = builder.generateTextureBias( texture, textureProperty, uvSnippet, biasSnippet, depthSnippet );
} else if ( gradSnippet ) {
snippet = builder.generateTextureGrad( texture, textureProperty, uvSnippet, gradSnippet, depthSnippet );
} else if ( compareSnippet ) {
snippet = builder.generateTextureCompare( texture, textureProperty, uvSnippet, compareSnippet, depthSnippet );
} else if ( this.sampler === false ) {
snippet = builder.generateTextureLoad( texture, textureProperty, uvSnippet, depthSnippet );
} else {
snippet = builder.generateTexture( texture, textureProperty, uvSnippet, depthSnippet );
}
return snippet;
}
/**
* Generates the code snippet of the texture node.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {string} output - The current output.
* @return {string} The generated code snippet.
*/
generate( builder, output ) {
const texture = this.value;
const properties = builder.getNodeProperties( this );
const textureProperty = super.generate( builder, 'property' );
if ( /^sampler/.test( output ) ) {
return textureProperty + '_sampler';
} else if ( builder.isReference( output ) ) {
return textureProperty;
} else {
const nodeData = builder.getDataFromNode( this );
let propertyName = nodeData.propertyName;
if ( propertyName === undefined ) {
const { uvNode, levelNode, biasNode, compareNode, depthNode, gradNode } = properties;
const uvSnippet = this.generateUV( builder, uvNode );
const levelSnippet = levelNode ? levelNode.build( builder, 'float' ) : null;
const biasSnippet = biasNode ? biasNode.build( builder, 'float' ) : null;
const depthSnippet = depthNode ? depthNode.build( builder, 'int' ) : null;
const compareSnippet = compareNode ? compareNode.build( builder, 'float' ) : null;
const gradSnippet = gradNode ? [ gradNode[ 0 ].build( builder, 'vec2' ), gradNode[ 1 ].build( builder, 'vec2' ) ] : null;
const nodeVar = builder.getVarFromNode( this );
propertyName = builder.getPropertyName( nodeVar );
const snippet = this.generateSnippet( builder, textureProperty, uvSnippet, levelSnippet, biasSnippet, depthSnippet, compareSnippet, gradSnippet );
builder.addLineFlowCode( `${propertyName} = ${snippet}`, this );
nodeData.snippet = snippet;
nodeData.propertyName = propertyName;
}
let snippet = propertyName;
const nodeType = this.getNodeType( builder );
if ( builder.needsToWorkingColorSpace( texture ) ) {
snippet = colorSpaceToWorking( expression( snippet, nodeType ), texture.colorSpace ).setup( builder ).build( builder, nodeType );
}
return builder.format( snippet, nodeType, output );
}
}
/**
* Sets the sampler value.
*
* @param {boolean} value - The sampler value to set.
* @return {TextureNode} A reference to this texture node.
*/
setSampler( value ) {
this.sampler = value;
return this;
}
/**
* Returns the sampler value.
*
* @return {boolean} The sampler value.
*/
getSampler() {
return this.sampler;
}
// @TODO: Move to TSL
/**
* @function
* @deprecated since r172. Use {@link TextureNode#sample} instead.
*
* @param {Node} uvNode - The uv node.
* @return {TextureNode} A texture node representing the texture sample.
*/
uv( uvNode ) { // @deprecated, r172
console.warn( 'THREE.TextureNode: .uv() has been renamed. Use .sample() instead.' );
return this.sample( uvNode );
}
/**
* Samples the texture with the given uv node.
*
* @param {Node} uvNode - The uv node.
* @return {TextureNode} A texture node representing the texture sample.
*/
sample( uvNode ) {
const textureNode = this.clone();
textureNode.uvNode = nodeObject( uvNode );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
/**
* Samples a blurred version of the texture by defining an internal bias.
*
* @param {Node<float>} amountNode - How blurred the texture should be.
* @return {TextureNode} A texture node representing the texture sample.
*/
blur( amountNode ) {
const textureNode = this.clone();
textureNode.biasNode = nodeObject( amountNode ).mul( maxMipLevel( textureNode ) );
textureNode.referenceNode = this.getSelf();
const map = textureNode.value;
if ( textureNode.generateMipmaps === false && ( map && map.generateMipmaps === false || map.minFilter === NearestFilter || map.magFilter === NearestFilter ) ) {
console.warn( 'THREE.TSL: texture().blur() requires mipmaps and sampling. Use .generateMipmaps=true and .minFilter/.magFilter=THREE.LinearFilter in the Texture.' );
textureNode.biasNode = null;
}
return nodeObject( textureNode );
}
/**
* Samples a specific mip of the texture.
*
* @param {Node<int>} levelNode - The mip level to sample.
* @return {TextureNode} A texture node representing the texture sample.
*/
level( levelNode ) {
const textureNode = this.clone();
textureNode.levelNode = nodeObject( levelNode );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
/**
* Returns the texture size of the requested level.
*
* @param {Node<int>} levelNode - The level to compute the size for.
* @return {TextureSizeNode} The texture size.
*/
size( levelNode ) {
return textureSize( this, levelNode );
}
/**
* Samples the texture with the given bias.
*
* @param {Node<float>} biasNode - The bias node.
* @return {TextureNode} A texture node representing the texture sample.
*/
bias( biasNode ) {
const textureNode = this.clone();
textureNode.biasNode = nodeObject( biasNode );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
/**
* Samples the texture by executing a compare operation.
*
* @param {Node<float>} compareNode - The node that defines the compare value.
* @return {TextureNode} A texture node representing the texture sample.
*/
compare( compareNode ) {
const textureNode = this.clone();
textureNode.compareNode = nodeObject( compareNode );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
/**
* Samples the texture using an explicit gradient.
*
* @param {Node<vec2>} gradNodeX - The gradX node.
* @param {Node<vec2>} gradNodeY - The gradY node.
* @return {TextureNode} A texture node representing the texture sample.
*/
grad( gradNodeX, gradNodeY ) {
const textureNode = this.clone();
textureNode.gradNode = [ nodeObject( gradNodeX ), nodeObject( gradNodeY ) ];
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
/**
* Samples the texture by defining a depth node.
*
* @param {Node<int>} depthNode - The depth node.
* @return {TextureNode} A texture node representing the texture sample.
*/
depth( depthNode ) {
const textureNode = this.clone();
textureNode.depthNode = nodeObject( depthNode );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
// --
serialize( data ) {
super.serialize( data );
data.value = this.value.toJSON( data.meta ).uuid;
data.sampler = this.sampler;
data.updateMatrix = this.updateMatrix;
data.updateType = this.updateType;
}
deserialize( data ) {
super.deserialize( data );
this.value = data.meta.textures[ data.value ];
this.sampler = data.sampler;
this.updateMatrix = data.updateMatrix;
this.updateType = data.updateType;
}
/**
* The update is used to implement the update of the uv transformation matrix.
*/
update() {
const texture = this.value;
const matrixUniform = this._matrixUniform;
if ( matrixUniform !== null ) matrixUniform.value = texture.matrix;
if ( texture.matrixAutoUpdate === true ) {
texture.updateMatrix();
}
}
/**
* Clones the texture node.
*
* @return {TextureNode} The cloned texture node.
*/
clone() {
const newNode = new this.constructor( this.value, this.uvNode, this.levelNode, this.biasNode );
newNode.sampler = this.sampler;
newNode.depthNode = this.depthNode;
newNode.compareNode = this.compareNode;
newNode.gradNode = this.gradNode;
return newNode;
}
}
export default TextureNode;
/**
* TSL function for creating a texture node.
*
* @tsl
* @function
* @param {?Texture} value - The texture.
* @param {?Node<vec2|vec3>} [uvNode=null] - The uv node.
* @param {?Node<int>} [levelNode=null] - The level node.
* @param {?Node<float>} [biasNode=null] - The bias node.
* @returns {TextureNode}
*/
const textureBase = /*@__PURE__*/ nodeProxy( TextureNode ).setParameterLength( 1, 4 ).setName( 'texture' );
/**
* TSL function for creating a texture node or sample a texture node already existing.
*
* @tsl
* @function
* @param {?Texture|TextureNode} [value=EmptyTexture] - The texture.
* @param {?Node<vec2|vec3>} [uvNode=null] - The uv node.
* @param {?Node<int>} [levelNode=null] - The level node.
* @param {?Node<float>} [biasNode=null] - The bias node.
* @returns {TextureNode}
*/
export const texture = ( value = EmptyTexture, uvNode = null, levelNode = null, biasNode = null ) => {
let textureNode;
if ( value && value.isTextureNode === true ) {
textureNode = nodeObject( value.clone() );
textureNode.referenceNode = value.getSelf(); // Ensure the reference is set to the original node
if ( uvNode !== null ) textureNode.uvNode = nodeObject( uvNode );
if ( levelNode !== null ) textureNode.levelNode = nodeObject( levelNode );
if ( biasNode !== null ) textureNode.biasNode = nodeObject( biasNode );
} else {
textureNode = textureBase( value, uvNode, levelNode, biasNode );
}
return textureNode;
};
/**
* TSL function for creating a uniform texture node.
*
* @tsl
* @function
* @param {?Texture} value - The texture.
* @returns {TextureNode}
*/
export const uniformTexture = ( value = EmptyTexture ) => texture( value );
/**
* TSL function for creating a texture node that fetches/loads texels without interpolation.
*
* @tsl
* @function
* @param {?Texture|TextureNode} [value=EmptyTexture] - The texture.
* @param {?Node<vec2|vec3>} [uvNode=null] - The uv node.
* @param {?Node<int>} [levelNode=null] - The level node.
* @param {?Node<float>} [biasNode=null] - The bias node.
* @returns {TextureNode}
*/
export const textureLoad = ( ...params ) => texture( ...params ).setSampler( false );
//export const textureLevel = ( value, uv, level ) => texture( value, uv ).level( level );
/**
* Converts a texture or texture node to a sampler.
*
* @tsl
* @function
* @param {TextureNode|Texture} value - The texture or texture node to convert.
* @returns {Node}
*/
export const sampler = ( value ) => ( value.isNode === true ? value : texture( value ) ).convert( 'sampler' );
/**
* Converts a texture or texture node to a sampler comparison.
*
* @tsl
* @function
* @param {TextureNode|Texture} value - The texture or texture node to convert.
* @returns {Node}
*/
export const samplerComparison = ( value ) => ( value.isNode === true ? value : texture( value ) ).convert( 'samplerComparison' );

View File

@@ -0,0 +1,77 @@
import Node from '../core/Node.js';
import { nodeProxy } from '../tsl/TSLBase.js';
/**
* A node that represents the dimensions of a texture. The texture size is
* retrieved in the shader via built-in shader functions like `textureDimensions()`
* or `textureSize()`.
*
* @augments Node
*/
class TextureSizeNode extends Node {
static get type() {
return 'TextureSizeNode';
}
/**
* Constructs a new texture size node.
*
* @param {TextureNode} textureNode - A texture node which size should be retrieved.
* @param {?Node<int>} [levelNode=null] - A level node which defines the requested mip.
*/
constructor( textureNode, levelNode = null ) {
super( 'uvec2' );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isTextureSizeNode = true;
/**
* A texture node which size should be retrieved.
*
* @type {TextureNode}
*/
this.textureNode = textureNode;
/**
* A level node which defines the requested mip.
*
* @type {Node<int>}
* @default null
*/
this.levelNode = levelNode;
}
generate( builder, output ) {
const textureProperty = this.textureNode.build( builder, 'property' );
const level = this.levelNode === null ? '0' : this.levelNode.build( builder, 'int' );
return builder.format( `${ builder.getMethod( 'textureDimensions' ) }( ${ textureProperty }, ${ level } )`, this.getNodeType( builder ), output );
}
}
export default TextureSizeNode;
/**
* TSL function for creating a texture size node.
*
* @tsl
* @function
* @param {TextureNode} textureNode - A texture node which size should be retrieved.
* @param {?Node<int>} [levelNode=null] - A level node which defines the requested mip.
* @returns {TextureSizeNode}
*/
export const textureSize = /*@__PURE__*/ nodeProxy( TextureSizeNode ).setParameterLength( 1, 2 );

11
app/node_modules/three/src/nodes/accessors/UV.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
import { attribute } from '../core/AttributeNode.js';
/**
* TSL function for creating an uv attribute node with the given index.
*
* @tsl
* @function
* @param {number} [index=0] - The uv index.
* @return {AttributeNode<vec2>} The uv attribute node.
*/
export const uv = ( index = 0 ) => attribute( 'uv' + ( index > 0 ? index : '' ), 'vec2' );

View File

@@ -0,0 +1,348 @@
import { nodeObject } from '../tsl/TSLBase.js';
import { NodeUpdateType } from '../core/constants.js';
import { getValueType } from '../core/NodeUtils.js';
import ArrayElementNode from '../utils/ArrayElementNode.js';
import BufferNode from './BufferNode.js';
/**
* Represents the element access on uniform array nodes.
*
* @augments ArrayElementNode
*/
class UniformArrayElementNode extends ArrayElementNode {
static get type() {
return 'UniformArrayElementNode';
}
/**
* Constructs a new buffer node.
*
* @param {UniformArrayNode} uniformArrayNode - The uniform array node to access.
* @param {IndexNode} indexNode - The index data that define the position of the accessed element in the array.
*/
constructor( uniformArrayNode, indexNode ) {
super( uniformArrayNode, indexNode );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isArrayBufferElementNode = true;
}
generate( builder ) {
const snippet = super.generate( builder );
const type = this.getNodeType();
const paddedType = this.node.getPaddedType();
return builder.format( snippet, paddedType, type );
}
}
/**
* Similar to {@link BufferNode} this module represents array-like data as
* uniform buffers. Unlike {@link BufferNode}, it can handle more common
* data types in the array (e.g `three.js` primitives) and automatically
* manage buffer padding. It should be the first choice when working with
* uniforms buffers.
* ```js
* const tintColors = uniformArray( [
* new Color( 1, 0, 0 ),
* new Color( 0, 1, 0 ),
* new Color( 0, 0, 1 )
* ], 'color' );
*
* const redColor = tintColors.element( 0 );
*
* @augments BufferNode
*/
class UniformArrayNode extends BufferNode {
static get type() {
return 'UniformArrayNode';
}
/**
* Constructs a new uniform array node.
*
* @param {Array<any>} value - Array holding the buffer data.
* @param {?string} [elementType=null] - The data type of a buffer element.
*/
constructor( value, elementType = null ) {
super( null );
/**
* Array holding the buffer data. Unlike {@link BufferNode}, the array can
* hold number primitives as well as three.js objects like vectors, matrices
* or colors.
*
* @type {Array<any>}
*/
this.array = value;
/**
* The data type of an array element.
*
* @type {string}
*/
this.elementType = elementType === null ? getValueType( value[ 0 ] ) : elementType;
/**
* The padded type. Uniform buffers must conform to a certain buffer layout
* so a separate type is computed to ensure correct buffer size.
*
* @type {string}
*/
this.paddedType = this.getPaddedType();
/**
* Overwritten since uniform array nodes are updated per render.
*
* @type {string}
* @default 'render'
*/
this.updateType = NodeUpdateType.RENDER;
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isArrayBufferNode = true;
}
/**
* This method is overwritten since the node type is inferred from the
* {@link UniformArrayNode#paddedType}.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The node type.
*/
getNodeType( /*builder*/ ) {
return this.paddedType;
}
/**
* The data type of the array elements.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The element type.
*/
getElementType() {
return this.elementType;
}
/**
* Returns the padded type based on the element type.
*
* @return {string} The padded type.
*/
getPaddedType() {
const elementType = this.elementType;
let paddedType = 'vec4';
if ( elementType === 'mat2' ) {
paddedType = 'mat2';
} else if ( /mat/.test( elementType ) === true ) {
paddedType = 'mat4';
} else if ( elementType.charAt( 0 ) === 'i' ) {
paddedType = 'ivec4';
} else if ( elementType.charAt( 0 ) === 'u' ) {
paddedType = 'uvec4';
}
return paddedType;
}
/**
* The update makes sure to correctly transfer the data from the (complex) objects
* in the array to the internal, correctly padded value buffer.
*
* @param {NodeFrame} frame - A reference to the current node frame.
*/
update( /*frame*/ ) {
const { array, value } = this;
const elementType = this.elementType;
if ( elementType === 'float' || elementType === 'int' || elementType === 'uint' ) {
for ( let i = 0; i < array.length; i ++ ) {
const index = i * 4;
value[ index ] = array[ i ];
}
} else if ( elementType === 'color' ) {
for ( let i = 0; i < array.length; i ++ ) {
const index = i * 4;
const vector = array[ i ];
value[ index ] = vector.r;
value[ index + 1 ] = vector.g;
value[ index + 2 ] = vector.b || 0;
//value[ index + 3 ] = vector.a || 0;
}
} else if ( elementType === 'mat2' ) {
for ( let i = 0; i < array.length; i ++ ) {
const index = i * 4;
const matrix = array[ i ];
value[ index ] = matrix.elements[ 0 ];
value[ index + 1 ] = matrix.elements[ 1 ];
value[ index + 2 ] = matrix.elements[ 2 ];
value[ index + 3 ] = matrix.elements[ 3 ];
}
} else if ( elementType === 'mat3' ) {
for ( let i = 0; i < array.length; i ++ ) {
const index = i * 16;
const matrix = array[ i ];
value[ index ] = matrix.elements[ 0 ];
value[ index + 1 ] = matrix.elements[ 1 ];
value[ index + 2 ] = matrix.elements[ 2 ];
value[ index + 4 ] = matrix.elements[ 3 ];
value[ index + 5 ] = matrix.elements[ 4 ];
value[ index + 6 ] = matrix.elements[ 5 ];
value[ index + 8 ] = matrix.elements[ 6 ];
value[ index + 9 ] = matrix.elements[ 7 ];
value[ index + 10 ] = matrix.elements[ 8 ];
value[ index + 15 ] = 1;
}
} else if ( elementType === 'mat4' ) {
for ( let i = 0; i < array.length; i ++ ) {
const index = i * 16;
const matrix = array[ i ];
for ( let i = 0; i < matrix.elements.length; i ++ ) {
value[ index + i ] = matrix.elements[ i ];
}
}
} else {
for ( let i = 0; i < array.length; i ++ ) {
const index = i * 4;
const vector = array[ i ];
value[ index ] = vector.x;
value[ index + 1 ] = vector.y;
value[ index + 2 ] = vector.z || 0;
value[ index + 3 ] = vector.w || 0;
}
}
}
/**
* Implement the value buffer creation based on the array data.
*
* @param {NodeBuilder} builder - A reference to the current node builder.
* @return {null}
*/
setup( builder ) {
const length = this.array.length;
const elementType = this.elementType;
let arrayType = Float32Array;
const paddedType = this.paddedType;
const paddedElementLength = builder.getTypeLength( paddedType );
if ( elementType.charAt( 0 ) === 'i' ) arrayType = Int32Array;
if ( elementType.charAt( 0 ) === 'u' ) arrayType = Uint32Array;
this.value = new arrayType( length * paddedElementLength );
this.bufferCount = length;
this.bufferType = paddedType;
return super.setup( builder );
}
/**
* Overwrites the default `element()` method to provide element access
* based on {@link UniformArrayNode}.
*
* @param {IndexNode} indexNode - The index node.
* @return {UniformArrayElementNode}
*/
element( indexNode ) {
return nodeObject( new UniformArrayElementNode( this, nodeObject( indexNode ) ) );
}
}
export default UniformArrayNode;
/**
* TSL function for creating an uniform array node.
*
* @tsl
* @function
* @param {Array<any>} values - Array-like data.
* @param {?string} [nodeType] - The data type of the array elements.
* @returns {UniformArrayNode}
*/
export const uniformArray = ( values, nodeType ) => nodeObject( new UniformArrayNode( values, nodeType ) );

View File

@@ -0,0 +1,77 @@
import ReferenceNode from './ReferenceNode.js';
import { nodeObject } from '../tsl/TSLBase.js';
/**
* A special type of reference node that allows to link values in
* `userData` fields to node objects.
* ```js
* sprite.userData.rotation = 1; // stores individual rotation per sprite
*
* const material = new THREE.SpriteNodeMaterial();
* material.rotationNode = userData( 'rotation', 'float' );
* ```
* Since `UserDataNode` is extended from {@link ReferenceNode}, the node value
* will automatically be updated when the `rotation` user data field changes.
*
* @augments ReferenceNode
*/
class UserDataNode extends ReferenceNode {
static get type() {
return 'UserDataNode';
}
/**
* Constructs a new user data node.
*
* @param {string} property - The property name that should be referenced by the node.
* @param {string} inputType - The node data type of the reference.
* @param {?Object} [userData=null] - A reference to the `userData` object. If not provided, the `userData` property of the 3D object that uses the node material is evaluated.
*/
constructor( property, inputType, userData = null ) {
super( property, inputType, userData );
/**
* A reference to the `userData` object. If not provided, the `userData`
* property of the 3D object that uses the node material is evaluated.
*
* @type {?Object}
* @default null
*/
this.userData = userData;
}
/**
* Overwritten to make sure {@link ReferenceNode#reference} points to the correct
* `userData` field.
*
* @param {(NodeFrame|NodeBuilder)} state - The current state to evaluate.
* @return {Object} A reference to the `userData` field.
*/
updateReference( state ) {
this.reference = this.userData !== null ? this.userData : state.object.userData;
return this.reference;
}
}
export default UserDataNode;
/**
* TSL function for creating a user data node.
*
* @tsl
* @function
* @param {string} name - The property name that should be referenced by the node.
* @param {string} inputType - The node data type of the reference.
* @param {?Object} userData - A reference to the `userData` object. If not provided, the `userData` property of the 3D object that uses the node material is evaluated.
* @returns {UserDataNode}
*/
export const userData = ( name, inputType, userData ) => nodeObject( new UserDataNode( name, inputType, userData ) );

View File

@@ -0,0 +1,224 @@
import TempNode from '../core/TempNode.js';
import { modelViewMatrix } from './ModelNode.js';
import { positionLocal, positionPrevious } from './Position.js';
import { nodeImmutable } from '../tsl/TSLBase.js';
import { NodeUpdateType } from '../core/constants.js';
import { Matrix4 } from '../../math/Matrix4.js';
import { uniform } from '../core/UniformNode.js';
import { sub } from '../math/OperatorNode.js';
import { cameraProjectionMatrix } from './Camera.js';
import { renderGroup } from '../core/UniformGroupNode.js';
const _objectData = new WeakMap();
/**
* A node for representing motion or velocity vectors. Foundation
* for advanced post processing effects like motion blur or TRAA.
*
* The node keeps track of the model, view and projection matrices
* of the previous frame and uses them to compute offsets in NDC space.
* These offsets represent the final velocity.
*
* @augments TempNode
*/
class VelocityNode extends TempNode {
static get type() {
return 'VelocityNode';
}
/**
* Constructs a new vertex color node.
*/
constructor() {
super( 'vec2' );
/**
* The current projection matrix.
*
* @type {?Matrix4}
* @default null
*/
this.projectionMatrix = null;
/**
* Overwritten since velocity nodes are updated per object.
*
* @type {string}
* @default 'object'
*/
this.updateType = NodeUpdateType.OBJECT;
/**
* Overwritten since velocity nodes save data after the update.
*
* @type {string}
* @default 'object'
*/
this.updateAfterType = NodeUpdateType.OBJECT;
/**
* Uniform node representing the previous model matrix in world space.
*
* @type {UniformNode<mat4>}
* @default null
*/
this.previousModelWorldMatrix = uniform( new Matrix4() );
/**
* Uniform node representing the previous projection matrix.
*
* @type {UniformNode<mat4>}
* @default null
*/
this.previousProjectionMatrix = uniform( new Matrix4() ).setGroup( renderGroup );
/**
* Uniform node representing the previous view matrix.
*
* @type {UniformNode<mat4>}
* @default null
*/
this.previousCameraViewMatrix = uniform( new Matrix4() );
}
/**
* Sets the given projection matrix.
*
* @param {Matrix4} projectionMatrix - The projection matrix to set.
*/
setProjectionMatrix( projectionMatrix ) {
this.projectionMatrix = projectionMatrix;
}
/**
* Updates velocity specific uniforms.
*
* @param {NodeFrame} frame - A reference to the current node frame.
*/
update( { frameId, camera, object } ) {
const previousModelMatrix = getPreviousMatrix( object );
this.previousModelWorldMatrix.value.copy( previousModelMatrix );
//
const cameraData = getData( camera );
if ( cameraData.frameId !== frameId ) {
cameraData.frameId = frameId;
if ( cameraData.previousProjectionMatrix === undefined ) {
cameraData.previousProjectionMatrix = new Matrix4();
cameraData.previousCameraViewMatrix = new Matrix4();
cameraData.currentProjectionMatrix = new Matrix4();
cameraData.currentCameraViewMatrix = new Matrix4();
cameraData.previousProjectionMatrix.copy( this.projectionMatrix || camera.projectionMatrix );
cameraData.previousCameraViewMatrix.copy( camera.matrixWorldInverse );
} else {
cameraData.previousProjectionMatrix.copy( cameraData.currentProjectionMatrix );
cameraData.previousCameraViewMatrix.copy( cameraData.currentCameraViewMatrix );
}
cameraData.currentProjectionMatrix.copy( this.projectionMatrix || camera.projectionMatrix );
cameraData.currentCameraViewMatrix.copy( camera.matrixWorldInverse );
this.previousProjectionMatrix.value.copy( cameraData.previousProjectionMatrix );
this.previousCameraViewMatrix.value.copy( cameraData.previousCameraViewMatrix );
}
}
/**
* Overwritten to updated velocity specific uniforms.
*
* @param {NodeFrame} frame - A reference to the current node frame.
*/
updateAfter( { object } ) {
getPreviousMatrix( object ).copy( object.matrixWorld );
}
/**
* Implements the velocity computation based on the previous and current vertex data.
*
* @param {NodeBuilder} builder - A reference to the current node builder.
* @return {Node<vec2>} The motion vector.
*/
setup( /*builder*/ ) {
const projectionMatrix = ( this.projectionMatrix === null ) ? cameraProjectionMatrix : uniform( this.projectionMatrix );
const previousModelViewMatrix = this.previousCameraViewMatrix.mul( this.previousModelWorldMatrix );
const clipPositionCurrent = projectionMatrix.mul( modelViewMatrix ).mul( positionLocal );
const clipPositionPrevious = this.previousProjectionMatrix.mul( previousModelViewMatrix ).mul( positionPrevious );
const ndcPositionCurrent = clipPositionCurrent.xy.div( clipPositionCurrent.w );
const ndcPositionPrevious = clipPositionPrevious.xy.div( clipPositionPrevious.w );
const velocity = sub( ndcPositionCurrent, ndcPositionPrevious );
return velocity;
}
}
function getData( object ) {
let objectData = _objectData.get( object );
if ( objectData === undefined ) {
objectData = {};
_objectData.set( object, objectData );
}
return objectData;
}
function getPreviousMatrix( object, index = 0 ) {
const objectData = getData( object );
let matrix = objectData[ index ];
if ( matrix === undefined ) {
objectData[ index ] = matrix = new Matrix4();
objectData[ index ].copy( object.matrixWorld );
}
return matrix;
}
export default VelocityNode;
/**
* TSL object that represents the velocity of a render pass.
*
* @tsl
* @type {VelocityNode}
*/
export const velocity = /*@__PURE__*/ nodeImmutable( VelocityNode );

View File

@@ -0,0 +1,110 @@
import AttributeNode from '../core/AttributeNode.js';
import { nodeObject } from '../tsl/TSLBase.js';
import { Vector4 } from '../../math/Vector4.js';
/**
* An attribute node for representing vertex colors.
*
* @augments AttributeNode
*/
class VertexColorNode extends AttributeNode {
static get type() {
return 'VertexColorNode';
}
/**
* Constructs a new vertex color node.
*
* @param {number} index - The attribute index.
*/
constructor( index ) {
super( null, 'vec4' );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isVertexColorNode = true;
/**
* The attribute index to enable more than one sets of vertex colors.
*
* @type {number}
* @default 0
*/
this.index = index;
}
/**
* Overwrites the default implementation by honoring the attribute index.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The attribute name.
*/
getAttributeName( /*builder*/ ) {
const index = this.index;
return 'color' + ( index > 0 ? index : '' );
}
generate( builder ) {
const attributeName = this.getAttributeName( builder );
const geometryAttribute = builder.hasGeometryAttribute( attributeName );
let result;
if ( geometryAttribute === true ) {
result = super.generate( builder );
} else {
// Vertex color fallback should be white
result = builder.generateConst( this.nodeType, new Vector4( 1, 1, 1, 1 ) );
}
return result;
}
serialize( data ) {
super.serialize( data );
data.index = this.index;
}
deserialize( data ) {
super.deserialize( data );
this.index = data.index;
}
}
export default VertexColorNode;
/**
* TSL function for creating a reference node.
*
* @tsl
* @function
* @param {number} [index=0] - The attribute index.
* @returns {VertexColorNode}
*/
export const vertexColor = ( index = 0 ) => nodeObject( new VertexColorNode( index ) );