Initial commit

This commit is contained in:
ChKendel
2026-01-31 21:50:25 +01:00
commit c65f983229
1088 changed files with 452910 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
import Node, { addNodeClass } from '../core/Node.js';
import { varying } from '../core/VaryingNode.js';
import { normalize } from '../math/MathNode.js';
import { cameraViewMatrix } from './CameraNode.js';
import { normalGeometry, normalLocal, normalView, normalWorld, transformedNormalView } from './NormalNode.js';
import { tangentGeometry, tangentLocal, tangentView, tangentWorld, transformedTangentView } from './TangentNode.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class BitangentNode extends Node {
constructor( scope = BitangentNode.LOCAL ) {
super( 'vec3' );
this.scope = scope;
}
getHash( /*builder*/ ) {
return `bitangent-${this.scope}`;
}
generate( builder ) {
const scope = this.scope;
let crossNormalTangent;
if ( scope === BitangentNode.GEOMETRY ) {
crossNormalTangent = normalGeometry.cross( tangentGeometry );
} else if ( scope === BitangentNode.LOCAL ) {
crossNormalTangent = normalLocal.cross( tangentLocal );
} else if ( scope === BitangentNode.VIEW ) {
crossNormalTangent = normalView.cross( tangentView );
} else if ( scope === BitangentNode.WORLD ) {
crossNormalTangent = normalWorld.cross( tangentWorld );
}
const vertexNode = crossNormalTangent.mul( tangentGeometry.w ).xyz;
const outputNode = normalize( varying( vertexNode ) );
return outputNode.build( builder, this.getNodeType( builder ) );
}
serialize( data ) {
super.serialize( data );
data.scope = this.scope;
}
deserialize( data ) {
super.deserialize( data );
this.scope = data.scope;
}
}
BitangentNode.GEOMETRY = 'geometry';
BitangentNode.LOCAL = 'local';
BitangentNode.VIEW = 'view';
BitangentNode.WORLD = 'world';
export default BitangentNode;
export const bitangentGeometry = nodeImmutable( BitangentNode, BitangentNode.GEOMETRY );
export const bitangentLocal = nodeImmutable( BitangentNode, BitangentNode.LOCAL );
export const bitangentView = nodeImmutable( BitangentNode, BitangentNode.VIEW );
export const bitangentWorld = nodeImmutable( BitangentNode, BitangentNode.WORLD );
export const transformedBitangentView = normalize( transformedNormalView.cross( transformedTangentView ).mul( tangentGeometry.w ) );
export const transformedBitangentWorld = normalize( transformedBitangentView.transformDirection( cameraViewMatrix ) );
addNodeClass( 'BitangentNode', BitangentNode );

View File

@@ -0,0 +1,125 @@
import InputNode from '../core/InputNode.js';
import { addNodeClass } from '../core/Node.js';
import { varying } from '../core/VaryingNode.js';
import { nodeObject, addNodeElement } from '../shadernode/ShaderNode.js';
import { InterleavedBufferAttribute, InterleavedBuffer, StaticDrawUsage, DynamicDrawUsage } from 'three';
class BufferAttributeNode extends InputNode {
constructor( value, bufferType = null, bufferStride = 0, bufferOffset = 0 ) {
super( value, bufferType );
this.isBufferNode = true;
this.bufferType = bufferType;
this.bufferStride = bufferStride;
this.bufferOffset = bufferOffset;
this.usage = StaticDrawUsage;
this.instanced = false;
this.attribute = null;
if ( value && value.isBufferAttribute === true ) {
this.attribute = value;
this.usage = value.usage;
this.instanced = value.isInstancedBufferAttribute;
}
}
getNodeType( builder ) {
if ( this.bufferType === null ) {
this.bufferType = builder.getTypeFromAttribute( this.attribute );
}
return this.bufferType;
}
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
}
generate( builder ) {
const nodeType = this.getNodeType( builder );
const nodeUniform = builder.getBufferAttributeFromNode( this, nodeType );
const propertyName = builder.getPropertyName( nodeUniform );
let output = null;
if ( builder.shaderStage === 'vertex' ) {
output = propertyName;
} else {
const nodeVarying = varying( this );
output = nodeVarying.build( builder, nodeType );
}
return output;
}
getInputType( /*builder*/ ) {
return 'bufferAttribute';
}
setUsage( value ) {
this.usage = value;
return this;
}
setInstanced( value ) {
this.instanced = value;
return this;
}
}
export default BufferAttributeNode;
export const bufferAttribute = ( array, type, stride, offset ) => nodeObject( new BufferAttributeNode( array, type, stride, offset ) );
export const dynamicBufferAttribute = ( array, type, stride, offset ) => bufferAttribute( array, type, stride, offset ).setUsage( DynamicDrawUsage );
export const instancedBufferAttribute = ( array, type, stride, offset ) => bufferAttribute( array, type, stride, offset ).setInstanced( true );
export const instancedDynamicBufferAttribute = ( array, type, stride, offset ) => dynamicBufferAttribute( array, type, stride, offset ).setInstanced( true );
addNodeElement( 'toAttribute', ( bufferNode ) => bufferAttribute( bufferNode.value ) );
addNodeClass( 'BufferAttributeNode', BufferAttributeNode );

View File

@@ -0,0 +1,30 @@
import UniformNode from '../core/UniformNode.js';
import { addNodeClass } from '../core/Node.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
class BufferNode extends UniformNode {
constructor( value, bufferType, bufferCount = 0 ) {
super( value, bufferType );
this.isBufferNode = true;
this.bufferType = bufferType;
this.bufferCount = bufferCount;
}
getInputType( /*builder*/ ) {
return 'buffer';
}
}
export default BufferNode;
export const buffer = ( value, type, count ) => nodeObject( new BufferNode( value, type, count ) );
addNodeClass( 'BufferNode', BufferNode );

View File

@@ -0,0 +1,98 @@
import Object3DNode from './Object3DNode.js';
import { addNodeClass } from '../core/Node.js';
import { label } from '../core/ContextNode.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class CameraNode extends Object3DNode {
constructor( scope = CameraNode.POSITION ) {
super( scope );
}
getNodeType( builder ) {
const scope = this.scope;
if ( scope === CameraNode.PROJECTION_MATRIX ) {
return 'mat4';
} else if ( scope === CameraNode.NEAR || scope === CameraNode.FAR ) {
return 'float';
}
return super.getNodeType( builder );
}
update( frame ) {
const camera = frame.camera;
const uniformNode = this._uniformNode;
const scope = this.scope;
if ( scope === CameraNode.VIEW_MATRIX ) {
uniformNode.value = camera.matrixWorldInverse;
} else if ( scope === CameraNode.PROJECTION_MATRIX ) {
uniformNode.value = camera.projectionMatrix;
} else if ( scope === CameraNode.NEAR ) {
uniformNode.value = camera.near;
} else if ( scope === CameraNode.FAR ) {
uniformNode.value = camera.far;
} else {
this.object3d = camera;
super.update( frame );
}
}
generate( builder ) {
const scope = this.scope;
if ( scope === CameraNode.PROJECTION_MATRIX ) {
this._uniformNode.nodeType = 'mat4';
} else if ( scope === CameraNode.NEAR || scope === CameraNode.FAR ) {
this._uniformNode.nodeType = 'float';
}
return super.generate( builder );
}
}
CameraNode.PROJECTION_MATRIX = 'projectionMatrix';
CameraNode.NEAR = 'near';
CameraNode.FAR = 'far';
export default CameraNode;
export const cameraProjectionMatrix = label( nodeImmutable( CameraNode, CameraNode.PROJECTION_MATRIX ), 'projectionMatrix' );
export const cameraNear = nodeImmutable( CameraNode, CameraNode.NEAR );
export const cameraFar = nodeImmutable( CameraNode, CameraNode.FAR );
export const cameraViewMatrix = nodeImmutable( CameraNode, CameraNode.VIEW_MATRIX );
export const cameraNormalMatrix = nodeImmutable( CameraNode, CameraNode.NORMAL_MATRIX );
export const cameraWorldMatrix = nodeImmutable( CameraNode, CameraNode.WORLD_MATRIX );
export const cameraPosition = nodeImmutable( CameraNode, CameraNode.POSITION );
addNodeClass( 'CameraNode', CameraNode );

View File

@@ -0,0 +1,118 @@
import TextureNode from './TextureNode.js';
import UniformNode from '../core/UniformNode.js';
import { reflectVector } from './ReflectVectorNode.js';
import { addNodeClass } from '../core/Node.js';
import { colorSpaceToLinear } from '../display/ColorSpaceNode.js';
import { expression } from '../code/ExpressionNode.js';
import { addNodeElement, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
class CubeTextureNode extends TextureNode {
constructor( value, uvNode = null, levelNode = null ) {
super( value, uvNode, levelNode );
this.isCubeTextureNode = true;
}
getInputType( /*builder*/ ) {
return 'cubeTexture';
}
getDefaultUV() {
return reflectVector;
}
setUpdateMatrix( /*updateMatrix*/ ) { } // Ignore .updateMatrix for CubeTextureNode
generate( builder, output ) {
const { uvNode, levelNode } = builder.getNodeProperties( this );
const texture = this.value;
if ( ! texture || texture.isCubeTexture !== true ) {
throw new Error( 'CubeTextureNode: Need a three.js cube texture.' );
}
const textureProperty = UniformNode.prototype.generate.call( this, builder, 'cubeTexture' );
if ( output === 'sampler' ) {
return textureProperty + '_sampler';
} else if ( builder.isReference( output ) ) {
return textureProperty;
} else {
const nodeData = builder.getDataFromNode( this );
let propertyName = nodeData.propertyName;
if ( propertyName === undefined ) {
const cubeUV = vec3( uvNode.x.negate(), uvNode.yz );
const uvSnippet = cubeUV.build( builder, 'vec3' );
const nodeVar = builder.getVarFromNode( this );
propertyName = builder.getPropertyName( nodeVar );
let snippet = null;
if ( levelNode && levelNode.isNode === true ) {
const levelSnippet = levelNode.build( builder, 'float' );
snippet = builder.getTextureLevel( this, textureProperty, uvSnippet, levelSnippet );
} else {
snippet = builder.getTexture( this, textureProperty, uvSnippet );
}
builder.addLineFlowCode( `${propertyName} = ${snippet}` );
if ( builder.context.tempWrite !== false ) {
nodeData.snippet = snippet;
nodeData.propertyName = propertyName;
}
}
let snippet = propertyName;
const nodeType = this.getNodeType( builder );
if ( builder.needsColorSpaceToLinear( this.value ) ) {
snippet = colorSpaceToLinear( expression( snippet, nodeType ), this.value.colorSpace ).setup( builder ).build( builder, nodeType );
}
return builder.format( snippet, nodeType, output );
}
}
}
export default CubeTextureNode;
export const cubeTexture = nodeProxy( CubeTextureNode );
addNodeElement( 'cubeTexture', cubeTexture );
addNodeClass( 'CubeTextureNode', CubeTextureNode );

View File

@@ -0,0 +1,71 @@
import Node, { addNodeClass } from '../core/Node.js';
import { instancedBufferAttribute, instancedDynamicBufferAttribute } from './BufferAttributeNode.js';
import { normalLocal } from './NormalNode.js';
import { positionLocal } from './PositionNode.js';
import { nodeProxy, vec3, mat3, mat4 } from '../shadernode/ShaderNode.js';
import { DynamicDrawUsage, InstancedInterleavedBuffer } from 'three';
class InstanceNode extends Node {
constructor( instanceMesh ) {
super( 'void' );
this.instanceMesh = instanceMesh;
this.instanceMatrixNode = null;
}
setup( /*builder*/ ) {
let instanceMatrixNode = this.instanceMatrixNode;
if ( instanceMatrixNode === null ) {
const instanceMesh = this.instanceMesh;
const instanceAttribute = instanceMesh.instanceMatrix;
const buffer = new InstancedInterleavedBuffer( instanceAttribute.array, 16, 1 );
const bufferFn = instanceAttribute.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;
}
// POSITION
const instancePosition = instanceMatrixNode.mul( positionLocal ).xyz;
// NORMAL
const m = mat3( instanceMatrixNode[ 0 ].xyz, instanceMatrixNode[ 1 ].xyz, instanceMatrixNode[ 2 ].xyz );
const transformedNormal = normalLocal.div( vec3( m[ 0 ].dot( m[ 0 ] ), m[ 1 ].dot( m[ 1 ] ), m[ 2 ].dot( m[ 2 ] ) ) );
const instanceNormal = m.mul( transformedNormal ).xyz;
// ASSIGNS
positionLocal.assign( instancePosition );
normalLocal.assign( instanceNormal );
}
}
export default InstanceNode;
export const instance = nodeProxy( InstanceNode );
addNodeClass( 'InstanceNode', InstanceNode );

View File

@@ -0,0 +1,21 @@
import MaterialNode from './MaterialNode.js';
import { addNodeClass } from '../core/Node.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class InstancedPointsMaterialNode extends MaterialNode {
setup( /*builder*/ ) {
return this.getFloat( this.scope );
}
}
InstancedPointsMaterialNode.POINT_WIDTH = 'pointWidth';
export default InstancedPointsMaterialNode;
export const materialPointWidth = nodeImmutable( InstancedPointsMaterialNode, InstancedPointsMaterialNode.POINT_WIDTH );
addNodeClass( 'InstancedPointsMaterialNode', InstancedPointsMaterialNode );

View File

@@ -0,0 +1,314 @@
import Node, { addNodeClass } from '../core/Node.js';
import { reference } from './ReferenceNode.js';
import { materialReference } from './MaterialReferenceNode.js';
import { normalView } from './NormalNode.js';
import { nodeImmutable, float } from '../shadernode/ShaderNode.js';
const _propertyCache = new Map();
class MaterialNode extends Node {
constructor( scope ) {
super();
this.scope = scope;
}
getCache( property, type ) {
let node = _propertyCache.get( property );
if ( node === undefined ) {
node = materialReference( property, type );
_propertyCache.set( property, node );
}
return node;
}
getFloat( property ) {
return this.getCache( property, 'float' );
}
getColor( property ) {
return this.getCache( property, 'color' );
}
getTexture( property ) {
return this.getCache( property === 'map' ? 'map' : property + 'Map', 'texture' );
}
setup( builder ) {
const material = builder.context.material;
const scope = this.scope;
let node = null;
if ( scope === MaterialNode.COLOR ) {
const colorNode = this.getColor( scope );
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( scope ).r;
} else {
node = float( 1 );
}
} 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 emissiveNode = this.getColor( scope );
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 = this.getTexture( 'normal' ).normalMap( this.getCache( 'normalScale', 'vec2' ) );
} else if ( material.bumpMap ) {
node = this.getTexture( 'bump' ).r.bumpMap( 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 = this.getTexture( scope ).normalMap( 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.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 {
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_COLOR = 'specular';
MaterialNode.SPECULAR_STRENGTH = 'specularStrength';
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.IRIDESCENCE = 'iridescence';
MaterialNode.IRIDESCENCE_IOR = 'iridescenceIOR';
MaterialNode.IRIDESCENCE_THICKNESS = 'iridescenceThickness';
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_WIDTH = 'pointWidth';
export default MaterialNode;
export const materialAlphaTest = nodeImmutable( MaterialNode, MaterialNode.ALPHA_TEST );
export const materialColor = nodeImmutable( MaterialNode, MaterialNode.COLOR );
export const materialShininess = nodeImmutable( MaterialNode, MaterialNode.SHININESS );
export const materialEmissive = nodeImmutable( MaterialNode, MaterialNode.EMISSIVE );
export const materialOpacity = nodeImmutable( MaterialNode, MaterialNode.OPACITY );
export const materialSpecularColor = nodeImmutable( MaterialNode, MaterialNode.SPECULAR_COLOR );
export const materialSpecularStrength = nodeImmutable( MaterialNode, MaterialNode.SPECULAR_STRENGTH );
export const materialReflectivity = nodeImmutable( MaterialNode, MaterialNode.REFLECTIVITY );
export const materialRoughness = nodeImmutable( MaterialNode, MaterialNode.ROUGHNESS );
export const materialMetalness = nodeImmutable( MaterialNode, MaterialNode.METALNESS );
export const materialNormal = nodeImmutable( MaterialNode, MaterialNode.NORMAL );
export const materialClearcoat = nodeImmutable( MaterialNode, MaterialNode.CLEARCOAT );
export const materialClearcoatRoughness = nodeImmutable( MaterialNode, MaterialNode.CLEARCOAT_ROUGHNESS );
export const materialClearcoatNormal = nodeImmutable( MaterialNode, MaterialNode.CLEARCOAT_NORMAL );
export const materialRotation = nodeImmutable( MaterialNode, MaterialNode.ROTATION );
export const materialSheen = nodeImmutable( MaterialNode, MaterialNode.SHEEN );
export const materialSheenRoughness = nodeImmutable( MaterialNode, MaterialNode.SHEEN_ROUGHNESS );
export const materialIridescence = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE );
export const materialIridescenceIOR = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_IOR );
export const materialIridescenceThickness = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_THICKNESS );
export const materialLineScale = nodeImmutable( MaterialNode, MaterialNode.LINE_SCALE );
export const materialLineDashSize = nodeImmutable( MaterialNode, MaterialNode.LINE_DASH_SIZE );
export const materialLineGapSize = nodeImmutable( MaterialNode, MaterialNode.LINE_GAP_SIZE );
export const materialLineWidth = nodeImmutable( MaterialNode, MaterialNode.LINE_WIDTH );
export const materialLineDashOffset = nodeImmutable( MaterialNode, MaterialNode.LINE_DASH_OFFSET );
export const materialPointWidth = nodeImmutable( MaterialNode, MaterialNode.POINT_WIDTH );
addNodeClass( 'MaterialNode', MaterialNode );

View File

@@ -0,0 +1,42 @@
import ReferenceNode from './ReferenceNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { addNodeClass } from '../core/Node.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
class MaterialReferenceNode extends ReferenceNode {
constructor( property, inputType, material = null ) {
super( property, inputType, material );
this.material = material;
this.updateType = NodeUpdateType.RENDER;
}
updateReference( frame ) {
this.reference = this.material !== null ? this.material : frame.material;
return this.reference;
}
setup( builder ) {
const material = this.material !== null ? this.material : builder.material;
this.node.value = material[ this.property ];
return super.setup( builder );
}
}
export default MaterialReferenceNode;
export const materialReference = ( name, type, material ) => nodeObject( new MaterialReferenceNode( name, type, material ) );
addNodeClass( 'MaterialReferenceNode', MaterialReferenceNode );

View File

@@ -0,0 +1,33 @@
import Object3DNode from './Object3DNode.js';
import { addNodeClass } from '../core/Node.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class ModelNode extends Object3DNode {
constructor( scope = ModelNode.VIEW_MATRIX ) {
super( scope );
}
update( frame ) {
this.object3d = frame.object;
super.update( frame );
}
}
export default ModelNode;
export const modelDirection = nodeImmutable( ModelNode, ModelNode.DIRECTION );
export const modelViewMatrix = nodeImmutable( ModelNode, ModelNode.VIEW_MATRIX ).temp( 'ModelViewMatrix' );
export const modelNormalMatrix = nodeImmutable( ModelNode, ModelNode.NORMAL_MATRIX );
export const modelWorldMatrix = nodeImmutable( ModelNode, ModelNode.WORLD_MATRIX );
export const modelPosition = nodeImmutable( ModelNode, ModelNode.POSITION );
export const modelScale = nodeImmutable( ModelNode, ModelNode.SCALE );
export const modelViewPosition = nodeImmutable( ModelNode, ModelNode.VIEW_POSITION );
addNodeClass( 'ModelNode', ModelNode );

View File

@@ -0,0 +1,30 @@
import { addNodeClass } from '../core/Node.js';
import TempNode from '../core/TempNode.js';
import { cameraProjectionMatrix } from './CameraNode.js';
import { modelViewMatrix } from './ModelNode.js';
import { positionLocal } from './PositionNode.js';
import { nodeProxy } from '../shadernode/ShaderNode.js';
class ModelViewProjectionNode extends TempNode {
constructor( positionNode = positionLocal ) {
super( 'vec4' );
this.positionNode = positionNode;
}
setup() {
return cameraProjectionMatrix.mul( modelViewMatrix ).mul( this.positionNode );
}
}
export default ModelViewProjectionNode;
export const modelViewProjection = nodeProxy( ModelViewProjectionNode );
addNodeClass( 'ModelViewProjectionNode', ModelViewProjectionNode );

View File

@@ -0,0 +1,70 @@
import Node, { addNodeClass } from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { nodeProxy } from '../shadernode/ShaderNode.js';
import { uniform } from '../core/UniformNode.js';
import { reference } from './ReferenceNode.js';
import { bufferAttribute } from './BufferAttributeNode.js';
import { positionLocal } from './PositionNode.js';
class MorphNode extends Node {
constructor( mesh ) {
super( 'void' );
this.mesh = mesh;
this.morphBaseInfluence = uniform( 1 );
this.updateType = NodeUpdateType.OBJECT;
}
setupAttribute( name, assignNode = positionLocal ) {
const mesh = this.mesh;
const attributes = mesh.geometry.morphAttributes[ name ];
assignNode.mulAssign( this.morphBaseInfluence );
for ( let i = 0; i < attributes.length; i ++ ) {
const attribute = attributes[ i ];
const bufferAttrib = bufferAttribute( attribute.array, 'vec3' );
const influence = reference( i, 'float', mesh.morphTargetInfluences );
assignNode.addAssign( bufferAttrib.mul( influence ) );
}
}
setup( /*builder*/ ) {
this.setupAttribute( 'position' );
}
update() {
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;
export const morph = nodeProxy( MorphNode );
addNodeClass( 'MorphNode', MorphNode );

View File

@@ -0,0 +1,96 @@
import Node, { addNodeClass } from '../core/Node.js';
import { attribute } from '../core/AttributeNode.js';
import { varying } from '../core/VaryingNode.js';
import { property } from '../core/PropertyNode.js';
import { normalize } from '../math/MathNode.js';
import { cameraViewMatrix } from './CameraNode.js';
import { modelNormalMatrix } from './ModelNode.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class NormalNode extends Node {
constructor( scope = NormalNode.LOCAL ) {
super( 'vec3' );
this.scope = scope;
}
isGlobal() {
return true;
}
getHash( /*builder*/ ) {
return `normal-${this.scope}`;
}
generate( builder ) {
const scope = this.scope;
let outputNode = null;
if ( scope === NormalNode.GEOMETRY ) {
outputNode = attribute( 'normal', 'vec3' );
} else if ( scope === NormalNode.LOCAL ) {
outputNode = varying( normalGeometry );
} else if ( scope === NormalNode.VIEW ) {
const vertexNode = modelNormalMatrix.mul( normalLocal );
outputNode = normalize( varying( vertexNode ) );
} else if ( scope === NormalNode.WORLD ) {
// To use inverseTransformDirection only inverse the param order like this: cameraViewMatrix.transformDirection( normalView )
const vertexNode = normalView.transformDirection( cameraViewMatrix );
outputNode = normalize( varying( vertexNode ) );
}
return outputNode.build( builder, this.getNodeType( builder ) );
}
serialize( data ) {
super.serialize( data );
data.scope = this.scope;
}
deserialize( data ) {
super.deserialize( data );
this.scope = data.scope;
}
}
NormalNode.GEOMETRY = 'geometry';
NormalNode.LOCAL = 'local';
NormalNode.VIEW = 'view';
NormalNode.WORLD = 'world';
export default NormalNode;
export const normalGeometry = nodeImmutable( NormalNode, NormalNode.GEOMETRY );
export const normalLocal = nodeImmutable( NormalNode, NormalNode.LOCAL ).temp( 'Normal' );
export const normalView = nodeImmutable( NormalNode, NormalNode.VIEW );
export const normalWorld = nodeImmutable( NormalNode, NormalNode.WORLD );
export const transformedNormalView = property( 'vec3', 'TransformedNormalView' );
export const transformedNormalWorld = transformedNormalView.transformDirection( cameraViewMatrix ).normalize();
export const transformedClearcoatNormalView = property( 'vec3', 'TransformedClearcoatNormalView' );
addNodeClass( 'NormalNode', NormalNode );

View File

@@ -0,0 +1,150 @@
import Node, { addNodeClass } from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import UniformNode from '../core/UniformNode.js';
import { nodeProxy } from '../shadernode/ShaderNode.js';
import { Vector3 } from 'three';
class Object3DNode extends Node {
constructor( scope = Object3DNode.VIEW_MATRIX, object3d = null ) {
super();
this.scope = scope;
this.object3d = object3d;
this.updateType = NodeUpdateType.OBJECT;
this._uniformNode = new UniformNode( null );
}
getNodeType() {
const scope = this.scope;
if ( scope === Object3DNode.WORLD_MATRIX || scope === Object3DNode.VIEW_MATRIX ) {
return 'mat4';
} else if ( scope === Object3DNode.NORMAL_MATRIX ) {
return 'mat3';
} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION || scope === Object3DNode.SCALE ) {
return 'vec3';
}
}
update( frame ) {
const object = this.object3d;
const uniformNode = this._uniformNode;
const scope = this.scope;
if ( scope === Object3DNode.VIEW_MATRIX ) {
uniformNode.value = object.modelViewMatrix;
} else if ( scope === Object3DNode.NORMAL_MATRIX ) {
uniformNode.value = object.normalMatrix;
} else 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 );
}
}
generate( builder ) {
const scope = this.scope;
if ( scope === Object3DNode.WORLD_MATRIX || scope === Object3DNode.VIEW_MATRIX ) {
this._uniformNode.nodeType = 'mat4';
} else if ( scope === Object3DNode.NORMAL_MATRIX ) {
this._uniformNode.nodeType = 'mat3';
} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION || scope === Object3DNode.SCALE ) {
this._uniformNode.nodeType = 'vec3';
}
return this._uniformNode.build( builder );
}
serialize( data ) {
super.serialize( data );
data.scope = this.scope;
}
deserialize( data ) {
super.deserialize( data );
this.scope = data.scope;
}
}
Object3DNode.VIEW_MATRIX = 'viewMatrix';
Object3DNode.NORMAL_MATRIX = 'normalMatrix';
Object3DNode.WORLD_MATRIX = 'worldMatrix';
Object3DNode.POSITION = 'position';
Object3DNode.SCALE = 'scale';
Object3DNode.VIEW_POSITION = 'viewPosition';
Object3DNode.DIRECTION = 'direction';
export default Object3DNode;
export const objectDirection = nodeProxy( Object3DNode, Object3DNode.DIRECTION );
export const objectViewMatrix = nodeProxy( Object3DNode, Object3DNode.VIEW_MATRIX );
export const objectNormalMatrix = nodeProxy( Object3DNode, Object3DNode.NORMAL_MATRIX );
export const objectWorldMatrix = nodeProxy( Object3DNode, Object3DNode.WORLD_MATRIX );
export const objectPosition = nodeProxy( Object3DNode, Object3DNode.POSITION );
export const objectScale = nodeProxy( Object3DNode, Object3DNode.SCALE );
export const objectViewPosition = nodeProxy( Object3DNode, Object3DNode.VIEW_POSITION );
addNodeClass( 'Object3DNode', Object3DNode );

View File

@@ -0,0 +1,26 @@
import Node, { addNodeClass } from '../core/Node.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class PointUVNode extends Node {
constructor() {
super( 'vec2' );
this.isPointUVNode = true;
}
generate( /*builder*/ ) {
return 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
}
}
export default PointUVNode;
export const pointUV = nodeImmutable( PointUVNode );
addNodeClass( 'PointUVNode', PointUVNode );

View File

@@ -0,0 +1,104 @@
import Node, { addNodeClass } from '../core/Node.js';
import { attribute } from '../core/AttributeNode.js';
import { varying } from '../core/VaryingNode.js';
import { normalize } from '../math/MathNode.js';
import { modelWorldMatrix, modelViewMatrix } from './ModelNode.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class PositionNode extends Node {
constructor( scope = PositionNode.LOCAL ) {
super( 'vec3' );
this.scope = scope;
}
isGlobal() {
return true;
}
getHash( /*builder*/ ) {
return `position-${this.scope}`;
}
generate( builder ) {
const scope = this.scope;
let outputNode = null;
if ( scope === PositionNode.GEOMETRY ) {
outputNode = attribute( 'position', 'vec3' );
} else if ( scope === PositionNode.LOCAL ) {
outputNode = varying( positionGeometry );
} else if ( scope === PositionNode.WORLD ) {
const vertexPositionNode = modelWorldMatrix.mul( positionLocal );
outputNode = varying( vertexPositionNode );
} else if ( scope === PositionNode.VIEW ) {
const vertexPositionNode = modelViewMatrix.mul( positionLocal );
outputNode = varying( vertexPositionNode );
} else if ( scope === PositionNode.VIEW_DIRECTION ) {
const vertexPositionNode = positionView.negate();
outputNode = normalize( varying( vertexPositionNode ) );
} else if ( scope === PositionNode.WORLD_DIRECTION ) {
const vertexPositionNode = positionLocal.transformDirection( modelWorldMatrix );
outputNode = normalize( varying( vertexPositionNode ) );
}
return outputNode.build( builder, this.getNodeType( builder ) );
}
serialize( data ) {
super.serialize( data );
data.scope = this.scope;
}
deserialize( data ) {
super.deserialize( data );
this.scope = data.scope;
}
}
PositionNode.GEOMETRY = 'geometry';
PositionNode.LOCAL = 'local';
PositionNode.WORLD = 'world';
PositionNode.WORLD_DIRECTION = 'worldDirection';
PositionNode.VIEW = 'view';
PositionNode.VIEW_DIRECTION = 'viewDirection';
export default PositionNode;
export const positionGeometry = nodeImmutable( PositionNode, PositionNode.GEOMETRY );
export const positionLocal = nodeImmutable( PositionNode, PositionNode.LOCAL ).temp( 'Position' );
export const positionWorld = nodeImmutable( PositionNode, PositionNode.WORLD );
export const positionWorldDirection = nodeImmutable( PositionNode, PositionNode.WORLD_DIRECTION );
export const positionView = nodeImmutable( PositionNode, PositionNode.VIEW );
export const positionViewDirection = nodeImmutable( PositionNode, PositionNode.VIEW_DIRECTION );
addNodeClass( 'PositionNode', PositionNode );

View File

@@ -0,0 +1,78 @@
import Node, { addNodeClass } from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { uniform } from '../core/UniformNode.js';
import { texture } from './TextureNode.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
class ReferenceNode extends Node {
constructor( property, uniformType, object = null ) {
super();
this.property = property;
this.uniformType = uniformType;
this.object = object;
this.reference = null;
this.node = null;
this.updateType = NodeUpdateType.OBJECT;
this.setNodeType( uniformType );
}
updateReference( frame ) {
this.reference = this.object !== null ? this.object : frame.object;
return this.reference;
}
setNodeType( uniformType ) {
let node = null;
if ( uniformType === 'texture' ) {
node = texture( null );
} else {
node = uniform( uniformType );
}
this.node = node;
}
getNodeType( builder ) {
return this.node.getNodeType( builder );
}
update( /*frame*/ ) {
this.node.value = this.reference[ this.property ];
}
setup( /*builder*/ ) {
return this.node;
}
}
export default ReferenceNode;
export const reference = ( name, type, object ) => nodeObject( new ReferenceNode( name, type, object ) );
addNodeClass( 'ReferenceNode', ReferenceNode );

View File

@@ -0,0 +1,35 @@
import Node, { addNodeClass } from '../core/Node.js';
import { cameraViewMatrix } from './CameraNode.js';
import { transformedNormalView } from './NormalNode.js';
import { positionViewDirection } from './PositionNode.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class ReflectVectorNode extends Node {
constructor() {
super( 'vec3' );
}
getHash( /*builder*/ ) {
return 'reflectVector';
}
setup() {
const reflectView = positionViewDirection.negate().reflect( transformedNormalView );
return reflectView.transformDirection( cameraViewMatrix );
}
}
export default ReflectVectorNode;
export const reflectVector = nodeImmutable( ReflectVectorNode );
addNodeClass( 'ReflectVectorNode', ReflectVectorNode );

View File

@@ -0,0 +1,52 @@
import Node from '../core/Node.js';
import { addNodeClass } from '../core/Node.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
import { reference } from './ReferenceNode.js';
class SceneNode extends Node {
constructor( scope = SceneNode.BACKGROUND_BLURRINESS, scene = null ) {
super();
this.scope = scope;
this.scene = scene;
}
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 {
console.error( 'THREE.SceneNode: Unknown scope:', scope );
}
return output;
}
}
SceneNode.BACKGROUND_BLURRINESS = 'backgroundBlurriness';
SceneNode.BACKGROUND_INTENSITY = 'backgroundIntensity';
export default SceneNode;
export const backgroundBlurriness = nodeImmutable( SceneNode, SceneNode.BACKGROUND_BLURRINESS );
export const backgroundIntensity = nodeImmutable( SceneNode, SceneNode.BACKGROUND_INTENSITY );
addNodeClass( 'SceneNode', SceneNode );

View File

@@ -0,0 +1,103 @@
import Node, { addNodeClass } from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { nodeProxy } from '../shadernode/ShaderNode.js';
import { attribute } from '../core/AttributeNode.js';
import { uniform } from '../core/UniformNode.js';
import { add } from '../math/OperatorNode.js';
import { buffer } from './BufferNode.js';
import { normalLocal } from './NormalNode.js';
import { positionLocal } from './PositionNode.js';
import { tangentLocal } from './TangentNode.js';
class SkinningNode extends Node {
constructor( skinnedMesh ) {
super( 'void' );
this.skinnedMesh = skinnedMesh;
this.updateType = NodeUpdateType.OBJECT;
//
this.skinIndexNode = attribute( 'skinIndex', 'uvec4' );
this.skinWeightNode = attribute( 'skinWeight', 'vec4' );
this.bindMatrixNode = uniform( skinnedMesh.bindMatrix, 'mat4' );
this.bindMatrixInverseNode = uniform( skinnedMesh.bindMatrixInverse, 'mat4' );
this.boneMatricesNode = buffer( skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length );
}
setup( builder ) {
const { skinIndexNode, skinWeightNode, bindMatrixNode, bindMatrixInverseNode, boneMatricesNode } = this;
const boneMatX = boneMatricesNode.element( skinIndexNode.x );
const boneMatY = boneMatricesNode.element( skinIndexNode.y );
const boneMatZ = boneMatricesNode.element( skinIndexNode.z );
const boneMatW = boneMatricesNode.element( skinIndexNode.w );
// POSITION
const skinVertex = bindMatrixNode.mul( positionLocal );
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 )
);
const skinPosition = bindMatrixInverseNode.mul( skinned ).xyz;
// 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 );
const skinNormal = skinMatrix.transformDirection( normalLocal ).xyz;
// ASSIGNS
positionLocal.assign( skinPosition );
normalLocal.assign( skinNormal );
if ( builder.hasGeometryAttribute( 'tangent' ) ) {
tangentLocal.assign( skinNormal );
}
}
generate( builder, output ) {
if ( output !== 'void' ) {
return positionLocal.build( builder, output );
}
}
update() {
this.skinnedMesh.skeleton.update();
}
}
export default SkinningNode;
export const skinning = nodeProxy( SkinningNode );
addNodeClass( 'SkinningNode', SkinningNode );

View File

@@ -0,0 +1,27 @@
import BufferNode from './BufferNode.js';
import { addNodeClass } from '../core/Node.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
class StorageBufferNode extends BufferNode {
constructor( value, bufferType, bufferCount = 0 ) {
super( value, bufferType, bufferCount );
this.isStorageBufferNode = true;
}
getInputType( /*builder*/ ) {
return 'storageBuffer';
}
}
export default StorageBufferNode;
export const storage = ( value, type, count ) => nodeObject( new StorageBufferNode( value, type, count ) );
addNodeClass( 'StorageBufferNode', StorageBufferNode );

View File

@@ -0,0 +1,103 @@
import Node, { addNodeClass } from '../core/Node.js';
import { attribute } from '../core/AttributeNode.js';
import { temp } from '../core/VarNode.js';
import { varying } from '../core/VaryingNode.js';
import { normalize } from '../math/MathNode.js';
import { cameraViewMatrix } from './CameraNode.js';
import { modelViewMatrix } from './ModelNode.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class TangentNode extends Node {
constructor( scope = TangentNode.LOCAL ) {
super();
this.scope = scope;
}
getHash( /*builder*/ ) {
return `tangent-${this.scope}`;
}
getNodeType() {
const scope = this.scope;
if ( scope === TangentNode.GEOMETRY ) {
return 'vec4';
}
return 'vec3';
}
generate( builder ) {
const scope = this.scope;
let outputNode = null;
if ( scope === TangentNode.GEOMETRY ) {
outputNode = attribute( 'tangent', 'vec4' );
} else if ( scope === TangentNode.LOCAL ) {
outputNode = varying( tangentGeometry.xyz );
} else if ( scope === TangentNode.VIEW ) {
const vertexNode = modelViewMatrix.mul( tangentLocal ).xyz;
outputNode = normalize( varying( vertexNode ) );
} else if ( scope === TangentNode.WORLD ) {
const vertexNode = tangentView.transformDirection( cameraViewMatrix );
outputNode = normalize( varying( vertexNode ) );
}
return outputNode.build( builder, this.getNodeType( builder ) );
}
serialize( data ) {
super.serialize( data );
data.scope = this.scope;
}
deserialize( data ) {
super.deserialize( data );
this.scope = data.scope;
}
}
TangentNode.GEOMETRY = 'geometry';
TangentNode.LOCAL = 'local';
TangentNode.VIEW = 'view';
TangentNode.WORLD = 'world';
export default TangentNode;
export const tangentGeometry = nodeImmutable( TangentNode, TangentNode.GEOMETRY );
export const tangentLocal = nodeImmutable( TangentNode, TangentNode.LOCAL );
export const tangentView = nodeImmutable( TangentNode, TangentNode.VIEW );
export const tangentWorld = nodeImmutable( TangentNode, TangentNode.WORLD );
export const transformedTangentView = temp( tangentView, 'TransformedTangentView' );
export const transformedTangentWorld = normalize( transformedTangentView.transformDirection( cameraViewMatrix ) );
addNodeClass( 'TangentNode', TangentNode );

View File

@@ -0,0 +1,94 @@
import TempNode from '../core/TempNode.js';
import { addNodeClass } from '../core/Node.js';
import { add, mul, div } from '../math/OperatorNode.js';
import { floor, ceil, fract, pow } from '../math/MathNode.js';
import { nodeProxy, addNodeElement, float, vec2, vec4, int } from '../shadernode/ShaderNode.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.uv( p0 ).level( lod ) ), g1x.mul( textureNode.uv( p1 ).level( lod ) ) ) );
const b = g1( fuv.y ).mul( add( g0x.mul( textureNode.uv( p2 ).level( lod ) ), g1x.mul( textureNode.uv( p3 ).level( lod ) ) ) );
return a.add( b );
};
const textureBicubicMethod = ( 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 );
};
class TextureBicubicNode extends TempNode {
constructor( textureNode, blurNode = float( 3 ) ) {
super( 'vec4' );
this.textureNode = textureNode;
this.blurNode = blurNode;
}
setup() {
return textureBicubicMethod( this.textureNode, this.blurNode );
}
}
export default TextureBicubicNode;
export const textureBicubic = nodeProxy( TextureBicubicNode );
addNodeElement( 'bicubic', textureBicubic );
addNodeClass( 'TextureBicubicNode', TextureBicubicNode );

View File

@@ -0,0 +1,281 @@
import UniformNode, { uniform } from '../core/UniformNode.js';
import { uv } from './UVNode.js';
import { textureSize } from './TextureSizeNode.js';
import { colorSpaceToLinear } from '../display/ColorSpaceNode.js';
import { context } from '../core/ContextNode.js';
import { expression } from '../code/ExpressionNode.js';
import { addNodeClass } from '../core/Node.js';
import { addNodeElement, nodeProxy, vec3, nodeObject } from '../shadernode/ShaderNode.js';
import { NodeUpdateType } from '../core/constants.js';
class TextureNode extends UniformNode {
constructor( value, uvNode = null, levelNode = null, compareNode = null ) {
super( value );
this.isTextureNode = true;
this.uvNode = uvNode;
this.levelNode = levelNode;
this.compareNode = compareNode;
this.updateMatrix = false;
this.updateType = NodeUpdateType.NONE;
this.setUpdateMatrix( uvNode === null );
}
getUniformHash( /*builder*/ ) {
return this.value.uuid;
}
getNodeType( /*builder*/ ) {
if ( this.value.isDepthTexture === true ) return 'float';
return 'vec4';
}
getInputType( /*builder*/ ) {
return 'texture';
}
getDefaultUV() {
return uv( this.value.channel );
}
updateReference( /*frame*/ ) {
return this.value;
}
getTransformedUV( uvNode ) {
const texture = this.value;
return uniform( texture.matrix ).mul( vec3( uvNode, 1 ) ).xy;
}
setUpdateMatrix( value ) {
this.updateMatrix = value;
this.updateType = value ? NodeUpdateType.FRAME : NodeUpdateType.NONE;
return this;
}
setup( builder ) {
const properties = builder.getNodeProperties( this );
//
let uvNode = this.uvNode;
if ( ( uvNode === null || builder.context.forceUVContext === true ) && builder.context.getUVNode ) {
uvNode = builder.context.getUVNode( this );
}
if ( ! uvNode ) uvNode = this.getDefaultUV();
if ( this.updateMatrix === true ) {
uvNode = this.getTransformedUV( uvNode );
}
//
let levelNode = this.levelNode;
if ( levelNode === null && builder.context.getSamplerLevelNode ) {
levelNode = builder.context.getSamplerLevelNode( this );
}
//
properties.uvNode = uvNode;
properties.levelNode = levelNode ? builder.context.getMIPLevelAlgorithmNode( this, levelNode ) : null;
}
generate( builder, output ) {
const { uvNode, levelNode } = builder.getNodeProperties( this );
const compareNode = this.compareNode;
const texture = this.value;
if ( ! texture || texture.isTexture !== true ) {
throw new Error( 'TextureNode: Need a three.js texture.' );
}
const textureProperty = super.generate( builder, 'property' );
if ( output === 'sampler' ) {
return textureProperty + '_sampler';
} else if ( builder.isReference( output ) ) {
return textureProperty;
} else {
const nodeData = builder.getDataFromNode( this );
let propertyName = nodeData.propertyName;
if ( propertyName === undefined ) {
const uvSnippet = uvNode.build( builder, 'vec2' );
const nodeVar = builder.getVarFromNode( this );
propertyName = builder.getPropertyName( nodeVar );
let snippet = null;
if ( levelNode && levelNode.isNode === true ) {
const levelSnippet = levelNode.build( builder, 'float' );
snippet = builder.getTextureLevel( texture, textureProperty, uvSnippet, levelSnippet );
} else if ( compareNode !== null ) {
const compareSnippet = compareNode.build( builder, 'float' );
snippet = builder.getTextureCompare( texture, textureProperty, uvSnippet, compareSnippet );
} else {
snippet = builder.getTexture( texture, textureProperty, uvSnippet );
}
builder.addLineFlowCode( `${propertyName} = ${snippet}` );
if ( builder.context.tempWrite !== false ) {
nodeData.snippet = snippet;
nodeData.propertyName = propertyName;
}
}
let snippet = propertyName;
const nodeType = this.getNodeType( builder );
if ( builder.needsColorSpaceToLinear( this.value ) ) {
snippet = colorSpaceToLinear( expression( snippet, nodeType ), this.value.colorSpace ).setup( builder ).build( builder, nodeType );
}
return builder.format( snippet, nodeType, output );
}
}
uv( uvNode ) {
const textureNode = this.clone();
textureNode.uvNode = uvNode;
return nodeObject( textureNode );
}
level( levelNode ) {
const textureNode = this.clone();
textureNode.levelNode = levelNode;
return context( textureNode, {
getMIPLevelAlgorithmNode: ( textureNode, levelNode ) => levelNode
} );
}
size( levelNode ) {
return textureSize( this, levelNode );
}
compare( compareNode ) {
const textureNode = this.clone();
textureNode.compareNode = nodeObject( compareNode );
return nodeObject( textureNode );
}
serialize( data ) {
super.serialize( data );
data.value = this.value.toJSON( data.meta ).uuid;
}
deserialize( data ) {
super.deserialize( data );
this.value = data.meta.textures[ data.value ];
}
update() {
const texture = this.value;
if ( texture.matrixAutoUpdate === true ) {
texture.updateMatrix();
}
}
clone() {
return new this.constructor( this.value, this.uvNode, this.levelNode, this.compareNode );
}
}
export default TextureNode;
export const texture = nodeProxy( TextureNode );
//export const textureLevel = ( value, uv, level ) => texture( value, uv ).level( level );
export const sampler = ( aTexture ) => ( aTexture.isNode === true ? aTexture : texture( aTexture ) ).convert( 'sampler' );
addNodeElement( 'texture', texture );
//addNodeElement( 'textureLevel', textureLevel );
addNodeClass( 'TextureNode', TextureNode );

View File

@@ -0,0 +1,35 @@
import Node from '../core/Node.js';
import { addNodeClass } from '../core/Node.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
class TextureSizeNode extends Node {
constructor( textureNode, levelNode = null ) {
super( 'uvec2' );
this.isTextureSizeNode = true;
this.textureNode = textureNode;
this.levelNode = levelNode;
}
generate( builder, output ) {
const textureProperty = this.textureNode.build( builder, 'property' );
const levelNode = this.levelNode.build( builder, 'int' );
return builder.format( `${builder.getMethod( 'textureDimensions' )}( ${textureProperty}, ${levelNode} )`, this.getNodeType( builder ), output );
}
}
export default TextureSizeNode;
export const textureSize = nodeProxy( TextureSizeNode );
addNodeElement( 'textureSize', textureSize );
addNodeClass( 'TextureSizeNode', TextureSizeNode );

View File

@@ -0,0 +1,29 @@
import { addNodeClass } from '../core/Node.js';
import TextureNode from './TextureNode.js';
import { nodeProxy } from '../shadernode/ShaderNode.js';
class TextureStoreNode extends TextureNode {
constructor( value, uvNode, storeNode = null ) {
super( value, uvNode );
this.storeNode = storeNode;
this.isStoreTextureNode = true;
}
getNodeType( /*builder*/ ) {
return 'void';
}
}
export default TextureStoreNode;
export const textureStore = nodeProxy( TextureStoreNode );
addNodeClass( 'TextureStoreNode', TextureStoreNode );

View File

@@ -0,0 +1,47 @@
import { addNodeClass } from '../core/Node.js';
import AttributeNode from '../core/AttributeNode.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
class UVNode extends AttributeNode {
constructor( index = 0 ) {
super( null, 'vec2' );
this.isUVNode = true;
this.index = index;
}
getAttributeName( /*builder*/ ) {
const index = this.index;
return 'uv' + ( index > 0 ? index : '' );
}
serialize( data ) {
super.serialize( data );
data.index = this.index;
}
deserialize( data ) {
super.deserialize( data );
this.index = data.index;
}
}
export default UVNode;
export const uv = ( ...params ) => nodeObject( new UVNode( ...params ) );
addNodeClass( 'UVNode', UVNode );

View File

@@ -0,0 +1,29 @@
import ReferenceNode from './ReferenceNode.js';
import { addNodeClass } from '../core/Node.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
class UserDataNode extends ReferenceNode {
constructor( property, inputType, userData = null ) {
super( property, inputType, userData );
this.userData = userData;
}
update( frame ) {
this.reference = this.userData !== null ? this.userData : frame.object.userData;
super.update( frame );
}
}
export default UserDataNode;
export const userData = ( name, inputType, userData ) => nodeObject( new UserDataNode( name, inputType, userData ) );
addNodeClass( 'UserDataNode', UserDataNode );