first commit

This commit is contained in:
rafaeldpsilva
2025-12-10 12:32:12 +00:00
commit adbbf6bf50
3442 changed files with 2725681 additions and 0 deletions

144
web-app/node_modules/three/src/nodes/Nodes.js generated vendored Normal file
View File

@@ -0,0 +1,144 @@
// constants
export * from './core/constants.js';
// core
export { default as AssignNode } from './core/AssignNode.js';
export { default as AttributeNode } from './core/AttributeNode.js';
export { default as BypassNode } from './core/BypassNode.js';
export { default as CacheNode } from './core/CacheNode.js';
export { default as ConstNode } from './core/ConstNode.js';
export { default as ContextNode } from './core/ContextNode.js';
export { default as IndexNode } from './core/IndexNode.js';
export { default as LightingModel } from './core/LightingModel.js';
export { default as Node } from './core/Node.js';
export { default as VarNode } from './core/VarNode.js';
export { default as NodeAttribute } from './core/NodeAttribute.js';
export { default as NodeBuilder } from './core/NodeBuilder.js';
export { default as NodeCache } from './core/NodeCache.js';
export { default as NodeCode } from './core/NodeCode.js';
export { default as NodeFrame } from './core/NodeFrame.js';
export { default as NodeFunctionInput } from './core/NodeFunctionInput.js';
export { default as NodeUniform } from './core/NodeUniform.js';
export { default as NodeVar } from './core/NodeVar.js';
export { default as NodeVarying } from './core/NodeVarying.js';
export { default as ParameterNode } from './core/ParameterNode.js';
export { default as PropertyNode } from './core/PropertyNode.js';
export { default as StackNode } from './core/StackNode.js';
export { default as TempNode } from './core/TempNode.js';
export { default as UniformGroupNode } from './core/UniformGroupNode.js';
export { default as UniformNode } from './core/UniformNode.js';
export { default as VaryingNode } from './core/VaryingNode.js';
export { default as OutputStructNode } from './core/OutputStructNode.js';
export { default as MRTNode } from './core/MRTNode.js';
import * as NodeUtils from './core/NodeUtils.js';
export { NodeUtils };
// utils
export { default as ArrayElementNode } from './utils/ArrayElementNode.js';
export { default as ConvertNode } from './utils/ConvertNode.js';
export { default as EquirectUVNode } from './utils/EquirectUVNode.js';
export { default as FunctionOverloadingNode } from './utils/FunctionOverloadingNode.js';
export { default as JoinNode } from './utils/JoinNode.js';
export { default as LoopNode } from './utils/LoopNode.js';
export { default as MatcapUVNode } from './utils/MatcapUVNode.js';
export { default as MaxMipLevelNode } from './utils/MaxMipLevelNode.js';
export { default as RemapNode } from './utils/RemapNode.js';
export { default as RotateNode } from './utils/RotateNode.js';
export { default as SetNode } from './utils/SetNode.js';
export { default as SplitNode } from './utils/SplitNode.js';
export { default as SpriteSheetUVNode } from './utils/SpriteSheetUVNode.js';
export { default as StorageArrayElementNode } from './utils/StorageArrayElementNode.js';
export { default as TriplanarTexturesNode } from './utils/TriplanarTexturesNode.js';
export { default as ReflectorNode } from './utils/ReflectorNode.js';
export { default as RTTNode } from './utils/RTTNode.js';
// accessors
export { default as UniformArrayNode } from './accessors/UniformArrayNode.js';
export { default as BufferAttributeNode } from './accessors/BufferAttributeNode.js';
export { default as BufferNode } from './accessors/BufferNode.js';
export { default as VertexColorNode } from './accessors/VertexColorNode.js';
export { default as CubeTextureNode } from './accessors/CubeTextureNode.js';
export { default as InstanceNode } from './accessors/InstanceNode.js';
export { default as BatchNode } from './accessors/BatchNode.js';
export { default as MaterialNode } from './accessors/MaterialNode.js';
export { default as MaterialReferenceNode } from './accessors/MaterialReferenceNode.js';
export { default as RendererReferenceNode } from './accessors/RendererReferenceNode.js';
export { default as MorphNode } from './accessors/MorphNode.js';
export { default as ModelNode } from './accessors/ModelNode.js';
export { default as ModelViewProjectionNode } from './accessors/ModelViewProjectionNode.js';
export { default as Object3DNode } from './accessors/Object3DNode.js';
export { default as PointUVNode } from './accessors/PointUVNode.js';
export { default as ReferenceNode } from './accessors/ReferenceNode.js';
export { default as SkinningNode } from './accessors/SkinningNode.js';
export { default as SceneNode } from './accessors/SceneNode.js';
export { default as StorageBufferNode } from './accessors/StorageBufferNode.js';
export { default as TextureNode } from './accessors/TextureNode.js';
export { default as TextureSizeNode } from './accessors/TextureSizeNode.js';
export { default as StorageTextureNode } from './accessors/StorageTextureNode.js';
export { default as Texture3DNode } from './accessors/Texture3DNode.js';
export { default as UserDataNode } from './accessors/UserDataNode.js';
// display
export { default as BumpMapNode } from './display/BumpMapNode.js';
export { default as ColorSpaceNode } from './display/ColorSpaceNode.js';
export { default as FrontFacingNode } from './display/FrontFacingNode.js';
export { default as NormalMapNode } from './display/NormalMapNode.js';
export { default as PosterizeNode } from './display/PosterizeNode.js';
export { default as ToneMappingNode } from './display/ToneMappingNode.js';
export { default as ScreenNode } from './display/ScreenNode.js';
export { default as ViewportTextureNode } from './display/ViewportTextureNode.js';
export { default as ViewportSharedTextureNode } from './display/ViewportSharedTextureNode.js';
export { default as ViewportDepthTextureNode } from './display/ViewportDepthTextureNode.js';
export { default as ViewportDepthNode } from './display/ViewportDepthNode.js';
export { default as RenderOutputNode } from './display/RenderOutputNode.js';
export { default as PassNode } from './display/PassNode.js';
export { default as ToonOutlinePassNode } from './display/ToonOutlinePassNode.js';
// code
export { default as ExpressionNode } from './code/ExpressionNode.js';
export { default as CodeNode } from './code/CodeNode.js';
export { default as FunctionCallNode } from './code/FunctionCallNode.js';
export { default as FunctionNode } from './code/FunctionNode.js';
export { default as ScriptableNode } from './code/ScriptableNode.js';
export { default as ScriptableValueNode } from './code/ScriptableValueNode.js';
// fog
export { default as FogNode } from './fog/FogNode.js';
export { default as FogRangeNode } from './fog/FogRangeNode.js';
export { default as FogExp2Node } from './fog/FogExp2Node.js';
// geometry
export { default as RangeNode } from './geometry/RangeNode.js';
// gpgpu
export { default as ComputeNode } from './gpgpu/ComputeNode.js';
// lighting
export { default as PointLightNode } from './lighting/PointLightNode.js';
export { default as DirectionalLightNode } from './lighting/DirectionalLightNode.js';
export { default as RectAreaLightNode } from './lighting/RectAreaLightNode.js';
export { default as SpotLightNode } from './lighting/SpotLightNode.js';
export { default as IESSpotLightNode } from './lighting/IESSpotLightNode.js';
export { default as AmbientLightNode } from './lighting/AmbientLightNode.js';
export { default as LightsNode } from './lighting/LightsNode.js';
export { default as LightingNode } from './lighting/LightingNode.js';
export { default as LightingContextNode } from './lighting/LightingContextNode.js';
export { default as HemisphereLightNode } from './lighting/HemisphereLightNode.js';
export { default as LightProbeNode } from './lighting/LightProbeNode.js';
export { default as EnvironmentNode } from './lighting/EnvironmentNode.js';
export { default as BasicEnvironmentNode } from './lighting/BasicEnvironmentNode.js';
export { default as IrradianceNode } from './lighting/IrradianceNode.js';
export { default as AONode } from './lighting/AONode.js';
export { default as AnalyticLightNode } from './lighting/AnalyticLightNode.js';
export { default as ShadowNode } from './lighting/ShadowNode.js';
// pmrem
export { default as PMREMNode } from './pmrem/PMREMNode.js';
// parsers
export { default as GLSLNodeParser } from './parsers/GLSLNodeParser.js'; // @TODO: Move to jsm/renderers/webgl.
// lighting models
export { default as PhongLightingModel } from './functions/PhongLightingModel.js';
export { default as PhysicalLightingModel } from './functions/PhysicalLightingModel.js';

161
web-app/node_modules/three/src/nodes/TSL.js generated vendored Normal file
View File

@@ -0,0 +1,161 @@
// constants
export * from './core/constants.js';
// core
export * from './core/AssignNode.js';
export * from './core/AttributeNode.js';
export * from './core/BypassNode.js';
export * from './core/CacheNode.js';
export * from './core/ContextNode.js';
export * from './core/IndexNode.js';
export * from './core/ParameterNode.js';
export * from './core/PropertyNode.js';
export * from './core/StackNode.js';
export * from './core/UniformGroupNode.js';
export * from './core/UniformNode.js';
export * from './core/VaryingNode.js';
export * from './core/OutputStructNode.js';
export * from './core/MRTNode.js';
// math
export * from './math/Hash.js';
export * from './math/MathUtils.js';
export * from './math/TriNoise3D.js';
// utils
export * from './utils/EquirectUVNode.js';
export * from './utils/FunctionOverloadingNode.js';
export * from './utils/LoopNode.js';
export * from './utils/MatcapUVNode.js';
export * from './utils/MaxMipLevelNode.js';
export * from './utils/Oscillators.js';
export * from './utils/Packing.js';
export * from './utils/RemapNode.js';
export * from './utils/UVUtils.js';
export * from './utils/SpriteUtils.js';
export * from './utils/ViewportUtils.js';
export * from './utils/RotateNode.js';
export * from './utils/SpriteSheetUVNode.js';
export * from './utils/Timer.js';
export * from './utils/TriplanarTexturesNode.js';
export * from './utils/ReflectorNode.js';
export * from './utils/RTTNode.js';
export * from './utils/PostProcessingUtils.js';
// three.js shading language
export * from './tsl/TSLBase.js';
// accessors
export * from './accessors/AccessorsUtils.js';
export * from './accessors/UniformArrayNode.js';
export * from './accessors/Bitangent.js';
export * from './accessors/BufferAttributeNode.js';
export * from './accessors/BufferNode.js';
export * from './accessors/Camera.js';
export * from './accessors/VertexColorNode.js';
export * from './accessors/CubeTextureNode.js';
export * from './accessors/InstanceNode.js';
export * from './accessors/BatchNode.js';
export * from './accessors/MaterialNode.js';
export * from './accessors/MaterialProperties.js';
export * from './accessors/MaterialReferenceNode.js';
export * from './accessors/RendererReferenceNode.js';
export * from './accessors/MorphNode.js';
export * from './accessors/TextureBicubic.js';
export * from './accessors/ModelNode.js';
export * from './accessors/ModelViewProjectionNode.js';
export * from './accessors/Normal.js';
export * from './accessors/Object3DNode.js';
export * from './accessors/PointUVNode.js';
export * from './accessors/Position.js';
export * from './accessors/ReferenceNode.js';
export * from './accessors/ReflectVector.js';
export * from './accessors/SkinningNode.js';
export * from './accessors/SceneNode.js';
export * from './accessors/StorageBufferNode.js';
export * from './accessors/Tangent.js';
export * from './accessors/TextureNode.js';
export * from './accessors/TextureSizeNode.js';
export * from './accessors/StorageTextureNode.js';
export * from './accessors/Texture3DNode.js';
export * from './accessors/UV.js';
export * from './accessors/UserDataNode.js';
export * from './accessors/VelocityNode.js';
// display
export * from './display/BlendMode.js';
export * from './display/BumpMapNode.js';
export * from './display/ColorAdjustment.js';
export * from './display/ColorSpaceNode.js';
export * from './display/FrontFacingNode.js';
export * from './display/NormalMapNode.js';
export * from './display/PosterizeNode.js';
export * from './display/ToneMappingNode.js';
export * from './display/ScreenNode.js';
export * from './display/ViewportTextureNode.js';
export * from './display/ViewportSharedTextureNode.js';
export * from './display/ViewportDepthTextureNode.js';
export * from './display/ViewportDepthNode.js';
export * from './display/RenderOutputNode.js';
export * from './display/ToonOutlinePassNode.js';
export * from './display/PassNode.js';
export * from './display/ColorSpaceFunctions.js';
export * from './display/ToneMappingFunctions.js';
// code
export * from './code/ExpressionNode.js';
export * from './code/CodeNode.js';
export * from './code/FunctionCallNode.js';
export * from './code/FunctionNode.js';
export * from './code/ScriptableNode.js';
export * from './code/ScriptableValueNode.js';
// fog
export * from './fog/FogNode.js';
export * from './fog/FogRangeNode.js';
export * from './fog/FogExp2Node.js';
// geometry
export * from './geometry/RangeNode.js';
// gpgpu
export * from './gpgpu/ComputeNode.js';
export * from './gpgpu/ComputeBuiltinNode.js';
export * from './gpgpu/BarrierNode.js';
export * from './gpgpu/WorkgroupInfoNode.js';
export * from './gpgpu/AtomicFunctionNode.js';
// lighting
export * from './accessors/Lights.js';
export * from './lighting/LightsNode.js';
export * from './lighting/LightingContextNode.js';
export * from './lighting/ShadowNode.js';
export * from './lighting/PointLightNode.js';
// pmrem
export * from './pmrem/PMREMNode.js';
export * from './pmrem/PMREMUtils.js';
// procedural
export * from './procedural/Checker.js';
// materialX
export * from './materialx/MaterialXNodes.js';
// functions
export { default as BRDF_GGX } from './functions/BSDF/BRDF_GGX.js';
export { default as BRDF_Lambert } from './functions/BSDF/BRDF_Lambert.js';
export { default as D_GGX } from './functions/BSDF/D_GGX.js';
export { default as DFGApprox } from './functions/BSDF/DFGApprox.js';
export { default as F_Schlick } from './functions/BSDF/F_Schlick.js';
export { default as Schlick_to_F0 } from './functions/BSDF/Schlick_to_F0.js';
export { default as V_GGX_SmithCorrelated } from './functions/BSDF/V_GGX_SmithCorrelated.js';
export * from './lighting/LightUtils.js';
export { default as getGeometryRoughness } from './functions/material/getGeometryRoughness.js';
export { default as getParallaxCorrectNormal } from './functions/material/getParallaxCorrectNormal.js';
export { default as getRoughness } from './functions/material/getRoughness.js';
export { default as getShIrradianceAt } from './functions/material/getShIrradianceAt.js';

View File

@@ -0,0 +1,25 @@
import { bitangentView } from './Bitangent.js';
import { normalView, transformedNormalView } from './Normal.js';
import { tangentView } from './Tangent.js';
import { mat3 } from '../tsl/TSLBase.js';
import { mix } from '../math/MathNode.js';
import { anisotropy, anisotropyB, roughness } from '../core/PropertyNode.js';
import { positionViewDirection } from './Position.js';
export const TBNViewMatrix = /*@__PURE__*/ mat3( tangentView, bitangentView, normalView );
export const parallaxDirection = /*@__PURE__*/ positionViewDirection.mul( TBNViewMatrix )/*.normalize()*/;
export const parallaxUV = ( uv, scale ) => uv.sub( parallaxDirection.mul( scale ) );
export const transformedBentNormalView = /*@__PURE__*/ ( () => {
// https://google.github.io/filament/Filament.md.html#lighting/imagebasedlights/anisotropy
let bentNormal = anisotropyB.cross( positionViewDirection );
bentNormal = bentNormal.cross( anisotropyB ).normalize();
bentNormal = mix( bentNormal, transformedNormalView, anisotropy.mul( roughness.oneMinus() ).oneMinus().pow2().pow2() ).normalize();
return bentNormal;
} )();

View File

@@ -0,0 +1,128 @@
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';
class BatchNode extends Node {
static get type() {
return 'BatchNode';
}
constructor( batchMesh ) {
super( 'void' );
this.batchMesh = batchMesh;
this.batchingIdNode = null;
}
setup( builder ) {
// POSITION
if ( this.batchingIdNode === null ) {
if ( builder.getDrawIndex() === null ) {
this.batchingIdNode = instanceIndex;
} else {
this.batchingIdNode = drawIndex;
}
}
const getIndirectIndex = Fn( ( [ id ] ) => {
const size = textureSize( textureLoad( this.batchMesh._indirectTexture ), 0 );
const x = int( id ).modInt( int( size ) );
const y = int( id ).div( int( 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 = textureSize( textureLoad( matricesTexture ), 0 );
const j = float( indirectId ).mul( 4 ).toInt().toVar();
const x = j.modInt( size );
const y = j.div( int( 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 = textureSize( textureLoad( colorsTexture ), 0 ).x;
const j = id;
const x = j.modInt( 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;
export const batch = /*@__PURE__*/ nodeProxy( BatchNode );

View File

@@ -0,0 +1,13 @@
import { varying } from '../core/VaryingNode.js';
import { cameraViewMatrix } from './Camera.js';
import { normalGeometry, normalLocal, normalView, normalWorld, transformedNormalView } from './Normal.js';
import { tangentGeometry, tangentLocal, tangentView, tangentWorld, transformedTangentView } from './Tangent.js';
const getBitangent = ( crossNormalTangent ) => crossNormalTangent.mul( tangentGeometry.w ).xyz;
export const bitangentGeometry = /*@__PURE__*/ varying( getBitangent( normalGeometry.cross( tangentGeometry ) ), 'v_bitangentGeometry' ).normalize().toVar( 'bitangentGeometry' );
export const bitangentLocal = /*@__PURE__*/ varying( getBitangent( normalLocal.cross( tangentLocal ) ), 'v_bitangentLocal' ).normalize().toVar( 'bitangentLocal' );
export const bitangentView = /*@__PURE__*/ varying( getBitangent( normalView.cross( tangentView ) ), 'v_bitangentView' ).normalize().toVar( 'bitangentView' );
export const bitangentWorld = /*@__PURE__*/ varying( getBitangent( normalWorld.cross( tangentWorld ) ), 'v_bitangentWorld' ).normalize().toVar( 'bitangentWorld' );
export const transformedBitangentView = /*@__PURE__*/ getBitangent( transformedNormalView.cross( transformedTangentView ) ).normalize().toVar( 'transformedBitangentView' );
export const transformedBitangentWorld = /*@__PURE__*/ transformedBitangentView.transformDirection( cameraViewMatrix ).normalize().toVar( 'transformedBitangentWorld' );

View File

@@ -0,0 +1,165 @@
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';
class BufferAttributeNode extends InputNode {
static get type() {
return 'BufferAttributeNode';
}
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;
this.global = true;
if ( value && value.isBufferAttribute === true ) {
this.attribute = value;
this.usage = value.usage;
this.instanced = value.isInstancedBufferAttribute;
}
}
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;
}
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 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;
}
getInputType( /*builder*/ ) {
return 'bufferAttribute';
}
setUsage( value ) {
this.usage = value;
if ( this.attribute && this.attribute.isBufferAttribute === true ) {
this.attribute.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 );
addMethodChaining( 'toAttribute', ( bufferNode ) => bufferAttribute( bufferNode.value ) );

View File

@@ -0,0 +1,39 @@
import UniformNode from '../core/UniformNode.js';
import { nodeObject } from '../tsl/TSLBase.js';
class BufferNode extends UniformNode {
static get type() {
return 'BufferNode';
}
constructor( value, bufferType, bufferCount = 0 ) {
super( value, bufferType );
this.isBufferNode = true;
this.bufferType = bufferType;
this.bufferCount = bufferCount;
}
getElementType( builder ) {
return this.getNodeType( builder );
}
getInputType( /*builder*/ ) {
return 'buffer';
}
}
export default BufferNode;
export const buffer = ( value, type, count ) => nodeObject( new BufferNode( value, type, count ) );

View File

@@ -0,0 +1,12 @@
import { uniform } from '../core/UniformNode.js';
import { renderGroup } from '../core/UniformGroupNode.js';
import { Vector3 } from '../../math/Vector3.js';
export const cameraNear = /*@__PURE__*/ uniform( 'float' ).label( 'cameraNear' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.near );
export const cameraFar = /*@__PURE__*/ uniform( 'float' ).label( 'cameraFar' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.far );
export const cameraProjectionMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix );
export const cameraProjectionMatrixInverse = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrixInverse' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrixInverse );
export const cameraViewMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraViewMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse );
export const cameraWorldMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraWorldMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld );
export const cameraNormalMatrix = /*@__PURE__*/ uniform( 'mat3' ).label( 'cameraNormalMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix );
export const cameraPosition = /*@__PURE__*/ uniform( new Vector3() ).label( 'cameraPosition' ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) );

View File

@@ -0,0 +1,151 @@
import Node from '../core/Node.js';
import { nodeObject } from '../tsl/TSLBase.js';
import { positionView } from './Position.js';
import { diffuseColor, property } from '../core/PropertyNode.js';
import { Fn } from '../tsl/TSLBase.js';
import { Loop } from '../utils/LoopNode.js';
import { smoothstep } from '../math/MathNode.js';
import { uniformArray } from './UniformArrayNode.js';
class ClippingNode extends Node {
static get type() {
return 'ClippingNode';
}
constructor( scope = ClippingNode.DEFAULT ) {
super();
this.scope = scope;
}
setup( builder ) {
super.setup( builder );
const clippingContext = builder.clippingContext;
const { localClipIntersection, localClippingCount, globalClippingCount } = clippingContext;
const numClippingPlanes = globalClippingCount + localClippingCount;
const numUnionClippingPlanes = localClipIntersection ? numClippingPlanes - localClippingCount : numClippingPlanes;
if ( this.scope === ClippingNode.ALPHA_TO_COVERAGE ) {
return this.setupAlphaToCoverage( clippingContext.planes, numClippingPlanes, numUnionClippingPlanes );
} else {
return this.setupDefault( clippingContext.planes, numClippingPlanes, numUnionClippingPlanes );
}
}
setupAlphaToCoverage( planes, numClippingPlanes, numUnionClippingPlanes ) {
return Fn( () => {
const clippingPlanes = uniformArray( planes );
const distanceToPlane = property( 'float', 'distanceToPlane' );
const distanceGradient = property( 'float', 'distanceToGradient' );
const clipOpacity = property( 'float', 'clipOpacity' );
clipOpacity.assign( 1 );
let plane;
Loop( numUnionClippingPlanes, ( { i } ) => {
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 ) );
clipOpacity.equal( 0.0 ).discard();
} );
if ( numUnionClippingPlanes < numClippingPlanes ) {
const unionClipOpacity = property( 'float', 'unionclipOpacity' );
unionClipOpacity.assign( 1 );
Loop( { start: numUnionClippingPlanes, end: numClippingPlanes }, ( { i } ) => {
plane = clippingPlanes.element( i );
distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );
unionClipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ).oneMinus() );
} );
clipOpacity.mulAssign( unionClipOpacity.oneMinus() );
}
diffuseColor.a.mulAssign( clipOpacity );
diffuseColor.a.equal( 0.0 ).discard();
} )();
}
setupDefault( planes, numClippingPlanes, numUnionClippingPlanes ) {
return Fn( () => {
const clippingPlanes = uniformArray( planes );
let plane;
Loop( numUnionClippingPlanes, ( { i } ) => {
plane = clippingPlanes.element( i );
positionView.dot( plane.xyz ).greaterThan( plane.w ).discard();
} );
if ( numUnionClippingPlanes < numClippingPlanes ) {
const clipped = property( 'bool', 'clipped' );
clipped.assign( true );
Loop( { start: numUnionClippingPlanes, end: numClippingPlanes }, ( { i } ) => {
plane = clippingPlanes.element( i );
clipped.assign( positionView.dot( plane.xyz ).greaterThan( plane.w ).and( clipped ) );
} );
clipped.discard();
}
} )();
}
}
ClippingNode.ALPHA_TO_COVERAGE = 'alphaToCoverage';
ClippingNode.DEFAULT = 'default';
export default ClippingNode;
export const clipping = () => nodeObject( new ClippingNode() );
export const clippingAlpha = () => nodeObject( new ClippingNode( ClippingNode.ALPHA_TO_COVERAGE ) );

View File

@@ -0,0 +1,79 @@
import TextureNode from './TextureNode.js';
import { reflectVector, refractVector } from './ReflectVector.js';
import { nodeProxy, vec3 } from '../tsl/TSLBase.js';
import { CubeReflectionMapping, CubeRefractionMapping, WebGPUCoordinateSystem } from '../../constants.js';
class CubeTextureNode extends TextureNode {
static get type() {
return 'CubeTextureNode';
}
constructor( value, uvNode = null, levelNode = null, biasNode = null ) {
super( value, uvNode, levelNode, biasNode );
this.isCubeTextureNode = true;
}
getInputType( /*builder*/ ) {
return 'cubeTexture';
}
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 );
}
}
setUpdateMatrix( /*updateMatrix*/ ) { } // Ignore .updateMatrix for CubeTextureNode
setupUV( builder, uvNode ) {
const texture = this.value;
if ( builder.renderer.coordinateSystem === WebGPUCoordinateSystem || ! texture.isRenderTargetTexture ) {
return vec3( uvNode.x.negate(), uvNode.yz );
} else {
return uvNode;
}
}
generateUV( builder, cubeUV ) {
return cubeUV.build( builder, 'vec3' );
}
}
export default CubeTextureNode;
export const cubeTexture = /*@__PURE__*/ nodeProxy( CubeTextureNode );

View File

@@ -0,0 +1,144 @@
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';
class InstanceNode extends Node {
static get type() {
return 'InstanceNode';
}
constructor( instanceMesh ) {
super( 'void' );
this.instanceMesh = instanceMesh;
this.instanceMatrixNode = null;
this.instanceColorNode = null;
this.updateType = NodeUpdateType.FRAME;
this.buffer = null;
this.bufferColor = null;
}
setup( builder ) {
let instanceMatrixNode = this.instanceMatrixNode;
let instanceColorNode = this.instanceColorNode;
const instanceMesh = this.instanceMesh;
if ( instanceMatrixNode === null ) {
const instanceAttribute = instanceMesh.instanceMatrix;
// 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 ( instanceMesh.count <= 1000 ) {
instanceMatrixNode = buffer( instanceAttribute.array, 'mat4', Math.max( instanceMesh.count, 1 ) ).element( instanceIndex );
} else {
const buffer = new InstancedInterleavedBuffer( instanceAttribute.array, 16, 1 );
this.buffer = buffer;
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;
}
const instanceColorAttribute = instanceMesh.instanceColor;
if ( instanceColorAttribute && instanceColorNode === null ) {
const buffer = new InstancedBufferAttribute( instanceColorAttribute.array, 3 );
const bufferFn = instanceColorAttribute.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 );
}
}
update( /*frame*/ ) {
if ( this.instanceMesh.instanceMatrix.usage !== DynamicDrawUsage && this.buffer != null && this.instanceMesh.instanceMatrix.version !== this.buffer.version ) {
this.buffer.version = this.instanceMesh.instanceMatrix.version;
}
if ( this.instanceMesh.instanceColor && this.instanceMesh.instanceColor.usage !== DynamicDrawUsage && this.bufferColor != null && this.instanceMesh.instanceColor.version !== this.bufferColor.version ) {
this.bufferColor.version = this.instanceMesh.instanceColor.version;
}
}
}
export default InstanceNode;
export const instance = /*@__PURE__*/ nodeProxy( InstanceNode );

View File

@@ -0,0 +1,24 @@
import MaterialNode from './MaterialNode.js';
import { nodeImmutable } from '../tsl/TSLBase.js';
class InstancedPointsMaterialNode extends MaterialNode {
static get type() {
return 'InstancedPointsMaterialNode';
}
setup( /*builder*/ ) {
return this.getFloat( this.scope );
}
}
InstancedPointsMaterialNode.POINT_WIDTH = 'pointWidth';
export default InstancedPointsMaterialNode;
export const materialPointWidth = /*@__PURE__*/ nodeImmutable( InstancedPointsMaterialNode, InstancedPointsMaterialNode.POINT_WIDTH );

View File

@@ -0,0 +1,51 @@
import { uniform } from '../core/UniformNode.js';
import { renderGroup } from '../core/UniformGroupNode.js';
import { Vector3 } from '../../math/Vector3.js';
import { cameraViewMatrix } from './Camera.js';
let uniformsLib;
function getLightData( light ) {
uniformsLib = uniformsLib || new WeakMap();
let uniforms = uniformsLib.get( light );
if ( uniforms === undefined ) uniformsLib.set( light, uniforms = {} );
return uniforms;
}
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 ) ) );
}
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 ) ) );
}
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 );
} ) );
}
export const lightTargetDirection = ( light ) => cameraViewMatrix.transformDirection( lightPosition( light ).sub( lightTargetPosition( light ) ) );

View File

@@ -0,0 +1,441 @@
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();
class MaterialNode extends Node {
static get type() {
return 'MaterialNode';
}
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 = 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 specularIntensity = this.getFloat( scope );
if ( material.specularMap ) {
node = specularIntensity.mul( this.getTexture( scope ).a );
} else {
node = specularIntensity;
}
} 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_MAP ) {
node = this.getTexture( scope ).r.sub( 1.0 ).mul( this.getFloat( 'aoMapIntensity' ) ).add( 1.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_WIDTH = 'pointWidth';
MaterialNode.DISPERSION = 'dispersion';
MaterialNode.LIGHT_MAP = 'light';
MaterialNode.AO_MAP = 'ao';
export default MaterialNode;
export const materialAlphaTest = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ALPHA_TEST );
export const materialColor = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.COLOR );
export const materialShininess = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SHININESS );
export const materialEmissive = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.EMISSIVE );
export const materialOpacity = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.OPACITY );
export const materialSpecular = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SPECULAR );
export const materialSpecularIntensity = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SPECULAR_INTENSITY );
export const materialSpecularColor = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SPECULAR_COLOR );
export const materialSpecularStrength = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SPECULAR_STRENGTH );
export const materialReflectivity = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.REFLECTIVITY );
export const materialRoughness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ROUGHNESS );
export const materialMetalness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.METALNESS );
export const materialNormal = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.NORMAL ).context( { getUV: null } );
export const materialClearcoat = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.CLEARCOAT );
export const materialClearcoatRoughness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.CLEARCOAT_ROUGHNESS );
export const materialClearcoatNormal = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.CLEARCOAT_NORMAL ).context( { getUV: null } );
export const materialRotation = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ROTATION );
export const materialSheen = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SHEEN );
export const materialSheenRoughness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.SHEEN_ROUGHNESS );
export const materialAnisotropy = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ANISOTROPY );
export const materialIridescence = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE );
export const materialIridescenceIOR = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_IOR );
export const materialIridescenceThickness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_THICKNESS );
export const materialTransmission = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.TRANSMISSION );
export const materialThickness = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.THICKNESS );
export const materialIOR = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.IOR );
export const materialAttenuationDistance = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ATTENUATION_DISTANCE );
export const materialAttenuationColor = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.ATTENUATION_COLOR );
export const materialLineScale = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_SCALE );
export const materialLineDashSize = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_DASH_SIZE );
export const materialLineGapSize = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_GAP_SIZE );
export const materialLineWidth = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_WIDTH );
export const materialLineDashOffset = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LINE_DASH_OFFSET );
export const materialPointWidth = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.POINT_WIDTH );
export const materialDispersion = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.DISPERSION );
export const materialLightMap = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.LIGHT_MAP );
export const materialAOMap = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.AO_MAP );
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,3 @@
import { uniform } from '../core/UniformNode.js';
export const materialRefractionRatio = /*@__PURE__*/ uniform( 0 ).onReference( ( { material } ) => material ).onRenderUpdate( ( { material } ) => material.refractionRatio );

View File

@@ -0,0 +1,46 @@
import ReferenceNode from './ReferenceNode.js';
//import { renderGroup } from '../core/UniformGroupNode.js';
//import { NodeUpdateType } from '../core/constants.js';
import { nodeObject } from '../tsl/TSLBase.js';
class MaterialReferenceNode extends ReferenceNode {
static get type() {
return 'MaterialReferenceNode';
}
constructor( property, inputType, material = null ) {
super( property, inputType, material );
this.material = material;
//this.updateType = NodeUpdateType.RENDER;
this.isMaterialReferenceNode = true;
}
/*setNodeType( node ) {
super.setNodeType( node );
this.node.groupNode = renderGroup;
}*/
updateReference( state ) {
this.reference = this.material !== null ? this.material : state.material;
return this.reference;
}
}
export default MaterialReferenceNode;
export const materialReference = ( name, type, material ) => nodeObject( new MaterialReferenceNode( name, type, material ) );

View File

@@ -0,0 +1,72 @@
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';
class ModelNode extends Object3DNode {
static get type() {
return 'ModelNode';
}
constructor( scope ) {
super( scope );
}
update( frame ) {
this.object3d = frame.object;
super.update( frame );
}
}
export default ModelNode;
export const modelDirection = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.DIRECTION );
export const modelWorldMatrix = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.WORLD_MATRIX );
export const modelPosition = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.POSITION );
export const modelScale = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.SCALE );
export const modelViewPosition = /*@__PURE__*/ nodeImmutable( ModelNode, ModelNode.VIEW_POSITION );
export const modelNormalMatrix = /*@__PURE__*/ uniform( new Matrix3() ).onObjectUpdate( ( { object }, self ) => self.value.getNormalMatrix( object.matrixWorld ) );
export const modelWorldMatrixInverse = /*@__PURE__*/ uniform( new Matrix4() ).onObjectUpdate( ( { object }, self ) => self.value.copy( object.matrixWorld ).invert() );
export const modelViewMatrix = /*@__PURE__*/ cameraViewMatrix.mul( modelWorldMatrix ).toVar( 'modelViewMatrix' );
export const highPrecisionModelViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => {
builder.context.isHighPrecisionModelViewMatrix = true;
return uniform( 'mat4' ).onObjectUpdate( ( { object, camera } ) => {
return object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
} );
} ).once() )().toVar( 'highPrecisionModelViewMatrix' );
export const highPrecisionModelNormalViewMatrix = /*@__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( 'highPrecisionModelNormalMatrix' );

View File

@@ -0,0 +1,42 @@
import TempNode from '../core/TempNode.js';
import { cameraProjectionMatrix } from './Camera.js';
import { positionLocal } from './Position.js';
import { nodeProxy, varying } from '../tsl/TSLBase.js';
import { modelViewMatrix } from './ModelNode.js';
class ModelViewProjectionNode extends TempNode {
static get type() {
return 'ModelViewProjectionNode';
}
constructor( positionNode = null ) {
super( 'vec4' );
this.positionNode = positionNode;
}
setup( builder ) {
if ( builder.shaderStage === 'fragment' ) {
return varying( builder.context.mvp );
}
const position = this.positionNode || positionLocal;
const viewMatrix = builder.renderer.nodes.modelViewMatrix || modelViewMatrix;
return cameraProjectionMatrix.mul( viewMatrix ).mul( position );
}
}
export default ModelViewProjectionNode;
export const modelViewProjection = /*@__PURE__*/ nodeProxy( ModelViewProjectionNode );

View File

@@ -0,0 +1,263 @@
import Node from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { float, nodeProxy, Fn } 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 { ivec2, int } from '../tsl/TSLBase.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 );
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;
}
class MorphNode extends Node {
static get type() {
return 'MorphNode';
}
constructor( mesh ) {
super( 'void' );
this.mesh = mesh;
this.morphBaseInfluence = uniform( 1 );
this.updateType = NodeUpdateType.OBJECT;
}
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 ( 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 )
} ) );
}
} );
}
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 morphReference = /*@__PURE__*/ nodeProxy( MorphNode );

View File

@@ -0,0 +1,88 @@
import { attribute } from '../core/AttributeNode.js';
import { cameraViewMatrix } from './Camera.js';
import { modelNormalMatrix, modelWorldMatrix } from './ModelNode.js';
import { mat3, vec3 } from '../tsl/TSLBase.js';
import { positionView } from './Position.js';
import { Fn, varying } from '../tsl/TSLBase.js';
import { faceDirection } from '../display/FrontFacingNode.js';
export const normalGeometry = /*@__PURE__*/ attribute( 'normal', 'vec3' );
export const normalLocal = /*@__PURE__*/ ( Fn( ( builder ) => {
if ( builder.geometry.hasAttribute( 'normal' ) === false ) {
console.warn( 'TSL.NormalNode: Vertex attribute "normal" not found on geometry.' );
return vec3( 0, 1, 0 );
}
return normalGeometry;
}, 'vec3' ).once() )().toVar( 'normalLocal' );
export const normalFlat = /*@__PURE__*/ positionView.dFdx().cross( positionView.dFdy() ).normalize().toVar( 'normalFlat' );
export const normalView = /*@__PURE__*/ ( Fn( ( builder ) => {
let node;
if ( builder.material.flatShading === true ) {
node = normalFlat;
} else {
node = varying( transformNormalToView( normalLocal ), 'v_normalView' ).normalize();
}
return node;
}, 'vec3' ).once() )().toVar( 'normalView' );
export const normalWorld = /*@__PURE__*/ varying( normalView.transformDirection( cameraViewMatrix ), 'v_normalWorld' ).normalize().toVar( 'normalWorld' );
export const transformedNormalView = /*@__PURE__*/ ( Fn( ( builder ) => {
return builder.context.setupNormal();
}, 'vec3' ).once() )().mul( faceDirection ).toVar( 'transformedNormalView' );
export const transformedNormalWorld = /*@__PURE__*/ transformedNormalView.transformDirection( cameraViewMatrix ).toVar( 'transformedNormalWorld' );
export const transformedClearcoatNormalView = /*@__PURE__*/ ( Fn( ( builder ) => {
return builder.context.setupClearcoatNormal();
}, 'vec3' ).once() )().mul( faceDirection ).toVar( 'transformedClearcoatNormalView' );
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;
} );
export const transformNormalToView = /*@__PURE__*/ Fn( ( [ normal ], builder ) => {
const modelNormalViewMatrix = builder.renderer.nodes.modelNormalViewMatrix;
if ( modelNormalViewMatrix !== null ) {
return modelNormalViewMatrix.transformDirection( normal );
}
//
const transformedNormal = modelNormalMatrix.mul( normal );
return cameraViewMatrix.transformDirection( transformedNormal );
} );

View File

@@ -0,0 +1,134 @@
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';
class Object3DNode extends Node {
static get type() {
return 'Object3DNode';
}
constructor( scope, 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 ) {
return 'mat4';
} 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.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 ) {
this._uniformNode.nodeType = 'mat4';
} 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.WORLD_MATRIX = 'worldMatrix';
Object3DNode.POSITION = 'position';
Object3DNode.SCALE = 'scale';
Object3DNode.VIEW_POSITION = 'viewPosition';
Object3DNode.DIRECTION = 'direction';
export default Object3DNode;
export const objectDirection = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.DIRECTION );
export const objectWorldMatrix = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.WORLD_MATRIX );
export const objectPosition = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.POSITION );
export const objectScale = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.SCALE );
export const objectViewPosition = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.VIEW_POSITION );

View File

@@ -0,0 +1,30 @@
import Node from '../core/Node.js';
import { nodeImmutable } from '../tsl/TSLBase.js';
class PointUVNode extends Node {
static get type() {
return 'PointUVNode';
}
constructor() {
super( 'vec2' );
this.isPointUVNode = true;
}
generate( /*builder*/ ) {
return 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
}
}
export default PointUVNode;
export const pointUV = /*@__PURE__*/ nodeImmutable( PointUVNode );

View File

@@ -0,0 +1,10 @@
import { attribute } from '../core/AttributeNode.js';
import { modelWorldMatrix, modelViewMatrix } from './ModelNode.js';
export const positionGeometry = /*@__PURE__*/ attribute( 'position', 'vec3' );
export const positionLocal = /*@__PURE__*/ positionGeometry.varying( 'positionLocal' );
export const positionPrevious = /*@__PURE__*/ positionGeometry.varying( 'positionPrevious' );
export const positionWorld = /*@__PURE__*/ modelWorldMatrix.mul( positionLocal ).xyz.varying( 'v_positionWorld' );
export const positionWorldDirection = /*@__PURE__*/ positionLocal.transformDirection( modelWorldMatrix ).varying( 'v_positionWorldDirection' ).normalize().toVar( 'positionWorldDirection' );
export const positionView = /*@__PURE__*/ modelViewMatrix.mul( positionLocal ).xyz.varying( 'v_positionView' );
export const positionViewDirection = /*@__PURE__*/ positionView.negate().varying( 'v_positionViewDirection' ).normalize().toVar( 'positionViewDirection' );

View File

@@ -0,0 +1,171 @@
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';
class ReferenceElementNode extends ArrayElementNode {
static get type() {
return 'ReferenceElementNode';
}
constructor( referenceNode, indexNode ) {
super( referenceNode, indexNode );
this.referenceNode = referenceNode;
this.isReferenceElementNode = true;
}
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 );
}
}
class ReferenceBaseNode extends Node {
static get type() {
return 'ReferenceBaseNode';
}
constructor( property, uniformType, object = null, count = null ) {
super();
this.property = property;
this.uniformType = uniformType;
this.object = object;
this.count = count;
this.properties = property.split( '.' );
this.reference = object;
this.node = null;
this.group = null;
this.updateType = NodeUpdateType.OBJECT;
}
setGroup( group ) {
this.group = group;
return this;
}
element( indexNode ) {
return nodeObject( new ReferenceElementNode( this, nodeObject( indexNode ) ) );
}
setNodeType( uniformType ) {
const node = uniform( null, uniformType ).getSelf();
if ( this.group !== null ) {
node.setGroup( this.group );
}
this.node = node;
}
getNodeType( builder ) {
if ( this.node === null ) {
this.updateReference( builder );
this.updateValue();
}
return this.node.getNodeType( builder );
}
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;
}
updateReference( state ) {
this.reference = this.object !== null ? this.object : state.object;
return this.reference;
}
setup() {
this.updateValue();
return this.node;
}
update( /*frame*/ ) {
this.updateValue();
}
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;
export const reference = ( name, type, object ) => nodeObject( new ReferenceBaseNode( name, type, object ) );
export const referenceBuffer = ( name, type, count, object ) => nodeObject( new ReferenceBaseNode( name, type, object, count ) );

View File

@@ -0,0 +1,209 @@
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';
class ReferenceElementNode extends ArrayElementNode {
static get type() {
return 'ReferenceElementNode';
}
constructor( referenceNode, indexNode ) {
super( referenceNode, indexNode );
this.referenceNode = referenceNode;
this.isReferenceElementNode = true;
}
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 );
}
}
// TODO: Extends this from ReferenceBaseNode
class ReferenceNode extends Node {
static get type() {
return 'ReferenceNode';
}
constructor( property, uniformType, object = null, count = null ) {
super();
this.property = property;
this.uniformType = uniformType;
this.object = object;
this.count = count;
this.properties = property.split( '.' );
this.reference = object;
this.node = null;
this.group = null;
this.name = null;
this.updateType = NodeUpdateType.OBJECT;
}
element( indexNode ) {
return nodeObject( new ReferenceElementNode( this, nodeObject( indexNode ) ) );
}
setGroup( group ) {
this.group = group;
return this;
}
label( name ) {
this.name = name;
return this;
}
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();
}
getNodeType( builder ) {
if ( this.node === null ) {
this.updateReference( builder );
this.updateValue();
}
return this.node.getNodeType( builder );
}
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;
}
updateReference( state ) {
this.reference = this.object !== null ? this.object : state.object;
return this.reference;
}
setup() {
this.updateValue();
return this.node;
}
update( /*frame*/ ) {
this.updateValue();
}
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;
export const reference = ( name, type, object ) => nodeObject( new ReferenceNode( name, type, object ) );
export const referenceBuffer = ( name, type, count, object ) => nodeObject( new ReferenceNode( name, type, object, count ) );

View File

@@ -0,0 +1,10 @@
import { cameraViewMatrix } from './Camera.js';
import { transformedNormalView } from './Normal.js';
import { positionViewDirection } from './Position.js';
import { materialRefractionRatio } from './MaterialProperties.js';
export const reflectView = /*@__PURE__*/ positionViewDirection.negate().reflect( transformedNormalView );
export const refractView = /*@__PURE__*/ positionViewDirection.negate().refract( transformedNormalView, materialRefractionRatio );
export const reflectVector = /*@__PURE__*/ reflectView.transformDirection( cameraViewMatrix ).toVar( 'reflectVector' );
export const refractVector = /*@__PURE__*/ refractView.transformDirection( cameraViewMatrix ).toVar( 'reflectVector' );

View File

@@ -0,0 +1,35 @@
import ReferenceBaseNode from './ReferenceBaseNode.js';
import { nodeObject } from '../tsl/TSLCore.js';
import { renderGroup } from '../core/UniformGroupNode.js';
class RendererReferenceNode extends ReferenceBaseNode {
static get type() {
return 'RendererReferenceNode';
}
constructor( property, inputType, renderer = null ) {
super( property, inputType, renderer );
this.renderer = renderer;
this.setGroup( renderGroup );
}
updateReference( state ) {
this.reference = this.renderer !== null ? this.renderer : state.renderer;
return this.reference;
}
}
export default RendererReferenceNode;
export const rendererReference = ( name, type, renderer ) => nodeObject( new RendererReferenceNode( name, type, renderer ) );

View File

@@ -0,0 +1,89 @@
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();
class SceneNode extends Node {
static get type() {
return 'SceneNode';
}
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 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;
export const backgroundBlurriness = /*@__PURE__*/ nodeImmutable( SceneNode, SceneNode.BACKGROUND_BLURRINESS );
export const backgroundIntensity = /*@__PURE__*/ nodeImmutable( SceneNode, SceneNode.BACKGROUND_INTENSITY );
export const backgroundRotation = /*@__PURE__*/ nodeImmutable( SceneNode, SceneNode.BACKGROUND_ROTATION );

View File

@@ -0,0 +1,191 @@
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';
const _frameId = new WeakMap();
class SkinningNode extends Node {
static get type() {
return 'SkinningNode';
}
constructor( skinnedMesh, useReference = false ) {
super( 'void' );
this.skinnedMesh = skinnedMesh;
this.useReference = useReference;
this.updateType = NodeUpdateType.OBJECT;
//
this.skinIndexNode = attribute( 'skinIndex', 'uvec4' );
this.skinWeightNode = attribute( 'skinWeight', 'vec4' );
let bindMatrixNode, bindMatrixInverseNode, boneMatricesNode;
if ( useReference ) {
bindMatrixNode = reference( 'bindMatrix', 'mat4' );
bindMatrixInverseNode = reference( 'bindMatrixInverse', 'mat4' );
boneMatricesNode = referenceBuffer( 'skeleton.boneMatrices', 'mat4', skinnedMesh.skeleton.bones.length );
} else {
bindMatrixNode = uniform( skinnedMesh.bindMatrix, 'mat4' );
bindMatrixInverseNode = uniform( skinnedMesh.bindMatrixInverse, 'mat4' );
boneMatricesNode = buffer( skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length );
}
this.bindMatrixNode = bindMatrixNode;
this.bindMatrixInverseNode = bindMatrixInverseNode;
this.boneMatricesNode = boneMatricesNode;
this.previousBoneMatricesNode = null;
}
getSkinnedPosition( boneMatrices = this.boneMatricesNode, position = positionLocal ) {
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;
}
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;
}
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 );
}
needsPreviousBoneMatrices( builder ) {
const mrt = builder.renderer.getMRT();
return mrt && mrt.has( 'velocity' );
}
setup( builder ) {
if ( this.needsPreviousBoneMatrices( builder ) ) {
positionPrevious.assign( this.getPreviousSkinnedPosition( builder ) );
}
const skinPosition = this.getSkinnedPosition();
positionLocal.assign( skinPosition );
if ( builder.hasGeometryAttribute( 'normal' ) ) {
const skinNormal = this.getSkinnedNormal();
normalLocal.assign( skinNormal );
if ( builder.hasGeometryAttribute( 'tangent' ) ) {
tangentLocal.assign( skinNormal );
}
}
}
generate( builder, output ) {
if ( output !== 'void' ) {
return positionLocal.build( builder, output );
}
}
update( frame ) {
const object = this.useReference ? frame.object : this.skinnedMesh;
const skeleton = object.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;
export const skinning = ( skinnedMesh ) => nodeObject( new SkinningNode( skinnedMesh ) );
export const skinningReference = ( skinnedMesh ) => nodeObject( new SkinningNode( skinnedMesh, true ) );

View File

@@ -0,0 +1,169 @@
import BufferNode from './BufferNode.js';
import { bufferAttribute } from './BufferAttributeNode.js';
import { nodeObject, varying } from '../tsl/TSLBase.js';
import { storageElement } from '../utils/StorageArrayElementNode.js';
import { GPUBufferBindingType } from '../../renderers/webgpu/utils/WebGPUConstants.js';
class StorageBufferNode extends BufferNode {
static get type() {
return 'StorageBufferNode';
}
constructor( value, bufferType, bufferCount = 0 ) {
super( value, bufferType, bufferCount );
this.isStorageBufferNode = true;
this.access = GPUBufferBindingType.Storage;
this.isAtomic = false;
this.bufferObject = false;
this.bufferCount = bufferCount;
this._attribute = null;
this._varying = null;
this.global = true;
if ( value.isStorageBufferAttribute !== true && value.isStorageInstancedBufferAttribute !== true ) {
// TOOD: 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;
}
}
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;
}
getInputType( /*builder*/ ) {
return this.value.isIndirectStorageBufferAttribute ? 'indirectStorageBuffer' : 'storageBuffer';
}
element( indexNode ) {
return storageElement( this, indexNode );
}
setBufferObject( value ) {
this.bufferObject = value;
return this;
}
setAccess( value ) {
this.access = value;
return this;
}
toReadOnly() {
return this.setAccess( GPUBufferBindingType.ReadOnlyStorage );
}
setAtomic( value ) {
this.isAtomic = value;
return this;
}
toAtomic() {
return this.setAtomic( true );
}
getAttributeData() {
if ( this._attribute === null ) {
this._attribute = bufferAttribute( this.value );
this._varying = varying( this._attribute );
}
return {
attribute: this._attribute,
varying: this._varying
};
}
getNodeType( builder ) {
if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) {
return super.getNodeType( builder );
}
const { attribute } = this.getAttributeData();
return attribute.getNodeType( builder );
}
generate( 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;
// Read-Write Storage
export const storage = ( value, type, count ) => nodeObject( new StorageBufferNode( value, type, count ) );
export const storageObject = ( value, type, count ) => nodeObject( new StorageBufferNode( value, type, count ).setBufferObject( true ) );

View File

@@ -0,0 +1,107 @@
import TextureNode from './TextureNode.js';
import { nodeProxy } from '../tsl/TSLBase.js';
import { GPUStorageTextureAccess } from '../../renderers/webgpu/utils/WebGPUConstants.js';
class StorageTextureNode extends TextureNode {
static get type() {
return 'StorageTextureNode';
}
constructor( value, uvNode, storeNode = null ) {
super( value, uvNode );
this.storeNode = storeNode;
this.isStorageTextureNode = true;
this.access = GPUStorageTextureAccess.WriteOnly;
}
getInputType( /*builder*/ ) {
return 'storageTexture';
}
setup( builder ) {
super.setup( builder );
const properties = builder.getNodeProperties( this );
properties.storeNode = this.storeNode;
}
setAccess( value ) {
this.access = value;
return this;
}
generate( builder, output ) {
let snippet;
if ( this.storeNode !== null ) {
snippet = this.generateStore( builder );
} else {
snippet = super.generate( builder, output );
}
return snippet;
}
toReadOnly() {
return this.setAccess( GPUStorageTextureAccess.ReadOnly );
}
toWriteOnly() {
return this.setAccess( GPUStorageTextureAccess.WriteOnly );
}
generateStore( builder ) {
const properties = builder.getNodeProperties( this );
const { uvNode, storeNode } = properties;
const textureProperty = super.generate( builder, 'property' );
const uvSnippet = uvNode.build( builder, 'uvec2' );
const storeSnippet = storeNode.build( builder, 'vec4' );
const snippet = builder.generateTextureStore( builder, textureProperty, uvSnippet, storeSnippet );
builder.addLineFlowCode( snippet, this );
}
}
export default StorageTextureNode;
export const storageTexture = /*@__PURE__*/ nodeProxy( StorageTextureNode );
export const textureStore = ( value, uvNode, storeNode ) => {
const node = storageTexture( value, uvNode, storeNode );
if ( storeNode !== null ) node.append();
return node;
};

View File

@@ -0,0 +1,22 @@
import { attribute } from '../core/AttributeNode.js';
import { cameraViewMatrix } from './Camera.js';
import { modelViewMatrix } from './ModelNode.js';
import { Fn, vec4 } from '../tsl/TSLBase.js';
export const tangentGeometry = /*@__PURE__*/ Fn( ( builder ) => {
if ( builder.geometry.hasAttribute( 'tangent' ) === false ) {
builder.geometry.computeTangents();
}
return attribute( 'tangent', 'vec4' );
} )();
export const tangentLocal = /*@__PURE__*/ tangentGeometry.xyz.toVar( 'tangentLocal' );
export const tangentView = /*@__PURE__*/ modelViewMatrix.mul( vec4( tangentLocal, 0 ) ).xyz.varying( 'v_tangentView' ).normalize().toVar( 'tangentView' );
export const tangentWorld = /*@__PURE__*/ tangentView.transformDirection( cameraViewMatrix ).varying( 'v_tangentWorld' ).normalize().toVar( 'tangentWorld' );
export const transformedTangentView = /*@__PURE__*/ tangentView.toVar( 'transformedTangentView' );
export const transformedTangentWorld = /*@__PURE__*/ transformedTangentView.transformDirection( cameraViewMatrix ).normalize().toVar( 'transformedTangentWorld' );

View File

@@ -0,0 +1,103 @@
import TextureNode from './TextureNode.js';
import { nodeProxy, vec3, Fn, If } from '../tsl/TSLBase.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.uv( uv.add( vec3( - step, 0.0, 0.0 ) ) ).r.sub( texture.uv( uv.add( vec3( step, 0.0, 0.0 ) ) ).r );
const y = texture.uv( uv.add( vec3( 0.0, - step, 0.0 ) ) ).r.sub( texture.uv( uv.add( vec3( 0.0, step, 0.0 ) ) ).r );
const z = texture.uv( uv.add( vec3( 0.0, 0.0, - step ) ) ).r.sub( texture.uv( uv.add( vec3( 0.0, 0.0, step ) ) ).r );
ret.assign( vec3( x, y, z ) );
} );
return ret.normalize();
} );
class Texture3DNode extends TextureNode {
static get type() {
return 'Texture3DNode';
}
constructor( value, uvNode = null, levelNode = null ) {
super( value, uvNode, levelNode );
this.isTexture3DNode = true;
}
getInputType( /*builder*/ ) {
return 'texture3D';
}
getDefaultUV() {
return vec3( 0.5, 0.5, 0.5 );
}
setUpdateMatrix( /*updateMatrix*/ ) { } // Ignore .updateMatrix for 3d TextureNode
setupUV( builder, uvNode ) {
return uvNode;
}
generateUV( builder, uvNode ) {
return uvNode.build( builder, 'vec3' );
}
normal( uvNode ) {
return normal( { texture: this, uv: uvNode } );
}
}
export default Texture3DNode;
export const texture3D = /*@__PURE__*/ nodeProxy( Texture3DNode );

View File

@@ -0,0 +1,65 @@
import { add, mul, div } from '../math/OperatorNode.js';
import { floor, ceil, fract, pow } from '../math/MathNode.js';
import { Fn, float, vec2, vec4, int } from '../tsl/TSLBase.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 );
};
export const textureBicubic = /*@__PURE__*/ Fn( ( [ textureNode, lodNode = float( 3 ) ] ) => {
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 );
} );

View File

@@ -0,0 +1,451 @@
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, UnsignedIntType } from '../../constants.js';
class TextureNode extends UniformNode {
static get type() {
return 'TextureNode';
}
constructor( value, uvNode = null, levelNode = null, biasNode = null ) {
super( value );
this.isTextureNode = true;
this.uvNode = uvNode;
this.levelNode = levelNode;
this.biasNode = biasNode;
this.compareNode = null;
this.depthNode = null;
this.gradNode = null;
this.sampler = true;
this.updateMatrix = false;
this.updateType = NodeUpdateType.NONE;
this.referenceNode = null;
this._value = value;
this._matrixUniform = null;
this.setUpdateMatrix( uvNode === null );
}
set value( value ) {
if ( this.referenceNode ) {
this.referenceNode.value = value;
} else {
this._value = value;
}
}
get value() {
return this.referenceNode ? this.referenceNode.value : this._value;
}
getUniformHash( /*builder*/ ) {
return this.value.uuid;
}
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';
}
getInputType( /*builder*/ ) {
return 'texture';
}
getDefaultUV() {
return uv( this.value.channel );
}
updateReference( /*state*/ ) {
return this.value;
}
getTransformedUV( uvNode ) {
if ( this._matrixUniform === null ) this._matrixUniform = uniform( this.value.matrix );
return this._matrixUniform.mul( vec3( uvNode, 1 ) ).xy;
}
setUpdateMatrix( value ) {
this.updateMatrix = value;
this.updateType = value ? NodeUpdateType.FRAME : NodeUpdateType.NONE;
return this;
}
setupUV( builder, uvNode ) {
const texture = this.value;
if ( builder.isFlipY() && ( 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;
}
setup( builder ) {
const properties = builder.getNodeProperties( this );
properties.referenceNode = this.referenceNode;
//
let uvNode = this.uvNode;
if ( ( uvNode === null || builder.context.forceUVContext === true ) && builder.context.getUV ) {
uvNode = builder.context.getUV( this );
}
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;
}
generateUV( builder, uvNode ) {
return uvNode.build( builder, this.sampler === true ? 'vec2' : 'ivec2' );
}
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;
}
generate( builder, output ) {
const properties = builder.getNodeProperties( this );
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 { 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 );
}
}
setSampler( value ) {
this.sampler = value;
return this;
}
getSampler() {
return this.sampler;
}
// @TODO: Move to TSL
uv( uvNode ) {
const textureNode = this.clone();
textureNode.uvNode = nodeObject( uvNode );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
blur( amountNode ) {
const textureNode = this.clone();
textureNode.biasNode = nodeObject( amountNode ).mul( maxMipLevel( textureNode ) );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
level( levelNode ) {
const textureNode = this.clone();
textureNode.levelNode = nodeObject( levelNode );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
size( levelNode ) {
return textureSize( this, levelNode );
}
bias( biasNode ) {
const textureNode = this.clone();
textureNode.biasNode = nodeObject( biasNode );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
compare( compareNode ) {
const textureNode = this.clone();
textureNode.compareNode = nodeObject( compareNode );
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
grad( gradNodeX, gradNodeY ) {
const textureNode = this.clone();
textureNode.gradNode = [ nodeObject( gradNodeX ), nodeObject( gradNodeY ) ];
textureNode.referenceNode = this.getSelf();
return nodeObject( textureNode );
}
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;
}
update() {
const texture = this.value;
const matrixUniform = this._matrixUniform;
if ( matrixUniform !== null ) matrixUniform.value = texture.matrix;
if ( texture.matrixAutoUpdate === true ) {
texture.updateMatrix();
}
}
clone() {
const newNode = new this.constructor( this.value, this.uvNode, this.levelNode, this.biasNode );
newNode.sampler = this.sampler;
return newNode;
}
}
export default TextureNode;
export const texture = /*@__PURE__*/ nodeProxy( TextureNode );
export const textureLoad = ( ...params ) => texture( ...params ).setSampler( false );
//export const textureLevel = ( value, uv, level ) => texture( value, uv ).level( level );
export const sampler = ( aTexture ) => ( aTexture.isNode === true ? aTexture : texture( aTexture ) ).convert( 'sampler' );

View File

@@ -0,0 +1,36 @@
import Node from '../core/Node.js';
import { nodeProxy } from '../tsl/TSLBase.js';
class TextureSizeNode extends Node {
static get type() {
return 'TextureSizeNode';
}
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 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;
export const textureSize = /*@__PURE__*/ nodeProxy( TextureSizeNode );

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

@@ -0,0 +1,3 @@
import { attribute } from '../core/AttributeNode.js';
export const uv = ( index ) => attribute( 'uv' + ( index > 0 ? index : '' ), 'vec2' );

View File

@@ -0,0 +1,158 @@
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';
class UniformArrayElementNode extends ArrayElementNode {
static get type() {
return 'UniformArrayElementNode';
}
constructor( arrayBuffer, indexNode ) {
super( arrayBuffer, indexNode );
this.isArrayBufferElementNode = true;
}
generate( builder ) {
const snippet = super.generate( builder );
const type = this.getNodeType();
return builder.format( snippet, 'vec4', type );
}
}
class UniformArrayNode extends BufferNode {
static get type() {
return 'UniformArrayNode';
}
constructor( value, elementType = null ) {
super( null, 'vec4' );
this.array = value;
this.elementType = elementType;
this._elementType = null;
this._elementLength = 0;
this.updateType = NodeUpdateType.RENDER;
this.isArrayBufferNode = true;
}
getElementType() {
return this.elementType || this._elementType;
}
getElementLength() {
return this._elementLength;
}
update( /*frame*/ ) {
const { array, value } = this;
const elementLength = this.getElementLength();
const elementType = this.getElementType();
if ( elementLength === 1 ) {
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 {
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;
}
}
}
setup( builder ) {
const length = this.array.length;
this._elementType = this.elementType === null ? getValueType( this.array[ 0 ] ) : this.elementType;
this._elementLength = builder.getTypeLength( this._elementType );
let arrayType = Float32Array;
if ( this._elementType.charAt( 0 ) === 'i' ) arrayType = Int32Array;
else if ( this._elementType.charAt( 0 ) === 'u' ) arrayType = Uint32Array;
this.value = new arrayType( length * 4 );
this.bufferCount = length;
this.bufferType = builder.changeComponentType( 'vec4', builder.getComponentType( this._elementType ) );
return super.setup( builder );
}
element( indexNode ) {
return nodeObject( new UniformArrayElementNode( this, nodeObject( indexNode ) ) );
}
}
export default UniformArrayNode;
export const uniformArray = ( values, nodeType ) => nodeObject( new UniformArrayNode( values, nodeType ) );
//
export const uniforms = ( values, nodeType ) => { // @deprecated, r168
console.warn( 'TSL.UniformArrayNode: uniforms() has been renamed to uniformArray().' );
return nodeObject( new UniformArrayNode( values, nodeType ) );
};

View File

@@ -0,0 +1,32 @@
import ReferenceNode from './ReferenceNode.js';
import { nodeObject } from '../tsl/TSLBase.js';
class UserDataNode extends ReferenceNode {
static get type() {
return 'UserDataNode';
}
constructor( property, inputType, userData = null ) {
super( property, inputType, userData );
this.userData = userData;
}
updateReference( state ) {
this.reference = this.userData !== null ? this.userData : state.object.userData;
return this.reference;
}
}
export default UserDataNode;
export const userData = ( name, inputType, userData ) => nodeObject( new UserDataNode( name, inputType, userData ) );

View File

@@ -0,0 +1,144 @@
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();
class VelocityNode extends TempNode {
static get type() {
return 'VelocityNode';
}
constructor() {
super( 'vec2' );
this.projectionMatrix = null;
this.updateType = NodeUpdateType.OBJECT;
this.updateAfterType = NodeUpdateType.OBJECT;
this.previousModelWorldMatrix = uniform( new Matrix4() );
this.previousProjectionMatrix = uniform( new Matrix4() ).setGroup( renderGroup );
this.previousCameraViewMatrix = uniform( new Matrix4() );
}
setProjectionMatrix( projectionMatrix ) {
this.projectionMatrix = projectionMatrix;
}
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 );
}
}
updateAfter( { object } ) {
getPreviousMatrix( object ).copy( object.matrixWorld );
}
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();
}
return matrix;
}
export default VelocityNode;
export const velocity = /*@__PURE__*/ nodeImmutable( VelocityNode );

View File

@@ -0,0 +1,74 @@
import AttributeNode from '../core/AttributeNode.js';
import { nodeObject } from '../tsl/TSLBase.js';
import { Vector4 } from '../../math/Vector4.js';
class VertexColorNode extends AttributeNode {
static get type() {
return 'VertexColorNode';
}
constructor( index = 0 ) {
super( null, 'vec4' );
this.isVertexColorNode = true;
this.index = index;
}
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;
export const vertexColor = ( ...params ) => nodeObject( new VertexColorNode( ...params ) );

88
web-app/node_modules/three/src/nodes/code/CodeNode.js generated vendored Normal file
View File

@@ -0,0 +1,88 @@
import Node from '../core/Node.js';
import { nodeProxy } from '../tsl/TSLBase.js';
class CodeNode extends Node {
static get type() {
return 'CodeNode';
}
constructor( code = '', includes = [], language = '' ) {
super( 'code' );
this.isCodeNode = true;
this.code = code;
this.language = language;
this.includes = includes;
}
isGlobal() {
return true;
}
setIncludes( includes ) {
this.includes = includes;
return this;
}
getIncludes( /*builder*/ ) {
return this.includes;
}
generate( builder ) {
const includes = this.getIncludes( builder );
for ( const include of includes ) {
include.build( builder );
}
const nodeCode = builder.getCodeFromNode( this, this.getNodeType( builder ) );
nodeCode.code = this.code;
return nodeCode.code;
}
serialize( data ) {
super.serialize( data );
data.code = this.code;
data.language = this.language;
}
deserialize( data ) {
super.deserialize( data );
this.code = data.code;
this.language = data.language;
}
}
export default CodeNode;
export const code = /*@__PURE__*/ nodeProxy( CodeNode );
export const js = ( src, includes ) => code( src, includes, 'js' );
export const wgsl = ( src, includes ) => code( src, includes, 'wgsl' );
export const glsl = ( src, includes ) => code( src, includes, 'glsl' );

View File

@@ -0,0 +1,41 @@
import Node from '../core/Node.js';
import { nodeProxy } from '../tsl/TSLCore.js';
class ExpressionNode extends Node {
static get type() {
return 'ExpressionNode';
}
constructor( snippet = '', nodeType = 'void' ) {
super( nodeType );
this.snippet = snippet;
}
generate( builder, output ) {
const type = this.getNodeType( builder );
const snippet = this.snippet;
if ( type === 'void' ) {
builder.addLineFlowCode( snippet, this );
} else {
return builder.format( `( ${ snippet } )`, type, output );
}
}
}
export default ExpressionNode;
export const expression = /*@__PURE__*/ nodeProxy( ExpressionNode );

View File

@@ -0,0 +1,110 @@
import TempNode from '../core/TempNode.js';
import { addMethodChaining, nodeArray, nodeObject, nodeObjects } from '../tsl/TSLCore.js';
class FunctionCallNode extends TempNode {
static get type() {
return 'FunctionCallNode';
}
constructor( functionNode = null, parameters = {} ) {
super();
this.functionNode = functionNode;
this.parameters = parameters;
}
setParameters( parameters ) {
this.parameters = parameters;
return this;
}
getParameters() {
return this.parameters;
}
getNodeType( builder ) {
return this.functionNode.getNodeType( builder );
}
generate( builder ) {
const params = [];
const functionNode = this.functionNode;
const inputs = functionNode.getInputs( builder );
const parameters = this.parameters;
const generateInput = ( node, inputNode ) => {
const type = inputNode.type;
const pointer = type === 'pointer';
let output;
if ( pointer ) output = '&' + node.build( builder );
else output = node.build( builder, type );
return output;
};
if ( Array.isArray( parameters ) ) {
for ( let i = 0; i < parameters.length; i ++ ) {
params.push( generateInput( parameters[ i ], inputs[ i ] ) );
}
} else {
for ( const inputNode of inputs ) {
const node = parameters[ inputNode.name ];
if ( node !== undefined ) {
params.push( generateInput( node, inputNode ) );
} else {
throw new Error( `FunctionCallNode: Input '${inputNode.name}' not found in FunctionNode.` );
}
}
}
const functionName = functionNode.build( builder, 'property' );
return `${functionName}( ${params.join( ', ' )} )`;
}
}
export default FunctionCallNode;
export const call = ( func, ...params ) => {
params = params.length > 1 || ( params[ 0 ] && params[ 0 ].isNode === true ) ? nodeArray( params ) : nodeObjects( params[ 0 ] );
return nodeObject( new FunctionCallNode( nodeObject( func ), params ) );
};
addMethodChaining( 'call', call );

View File

@@ -0,0 +1,115 @@
import CodeNode from './CodeNode.js';
import { nodeObject } from '../tsl/TSLBase.js';
class FunctionNode extends CodeNode {
static get type() {
return 'FunctionNode';
}
constructor( code = '', includes = [], language = '' ) {
super( code, includes, language );
}
getNodeType( builder ) {
return this.getNodeFunction( builder ).type;
}
getInputs( builder ) {
return this.getNodeFunction( builder ).inputs;
}
getNodeFunction( builder ) {
const nodeData = builder.getDataFromNode( this );
let nodeFunction = nodeData.nodeFunction;
if ( nodeFunction === undefined ) {
nodeFunction = builder.parser.parseFunction( this.code );
nodeData.nodeFunction = nodeFunction;
}
return nodeFunction;
}
generate( builder, output ) {
super.generate( builder );
const nodeFunction = this.getNodeFunction( builder );
const name = nodeFunction.name;
const type = nodeFunction.type;
const nodeCode = builder.getCodeFromNode( this, type );
if ( name !== '' ) {
// use a custom property name
nodeCode.name = name;
}
const propertyName = builder.getPropertyName( nodeCode );
const code = this.getNodeFunction( builder ).getCode( propertyName );
nodeCode.code = code + '\n';
if ( output === 'property' ) {
return propertyName;
} else {
return builder.format( `${ propertyName }()`, type, output );
}
}
}
export default FunctionNode;
const nativeFn = ( code, includes = [], language = '' ) => {
for ( let i = 0; i < includes.length; i ++ ) {
const include = includes[ i ];
// TSL Function: glslFn, wgslFn
if ( typeof include === 'function' ) {
includes[ i ] = include.functionNode;
}
}
const functionNode = nodeObject( new FunctionNode( code, includes, language ) );
const fn = ( ...params ) => functionNode.call( ...params );
fn.functionNode = functionNode;
return fn;
};
export const glslFn = ( code, includes ) => nativeFn( code, includes, 'glsl' );
export const wgslFn = ( code, includes ) => nativeFn( code, includes, 'wgsl' );

View File

@@ -0,0 +1,505 @@
import Node from '../core/Node.js';
import { scriptableValue } from './ScriptableValueNode.js';
import { nodeProxy, float } from '../tsl/TSLBase.js';
import { hashArray, hashString } from '../core/NodeUtils.js';
class Resources extends Map {
get( key, callback = null, ...params ) {
if ( this.has( key ) ) return super.get( key );
if ( callback !== null ) {
const value = callback( ...params );
this.set( key, value );
return value;
}
}
}
class Parameters {
constructor( scriptableNode ) {
this.scriptableNode = scriptableNode;
}
get parameters() {
return this.scriptableNode.parameters;
}
get layout() {
return this.scriptableNode.getLayout();
}
getInputLayout( id ) {
return this.scriptableNode.getInputLayout( id );
}
get( name ) {
const param = this.parameters[ name ];
const value = param ? param.getValue() : null;
return value;
}
}
export const ScriptableNodeResources = new Resources();
class ScriptableNode extends Node {
static get type() {
return 'ScriptableNode';
}
constructor( codeNode = null, parameters = {} ) {
super();
this.codeNode = codeNode;
this.parameters = parameters;
this._local = new Resources();
this._output = scriptableValue();
this._outputs = {};
this._source = this.source;
this._method = null;
this._object = null;
this._value = null;
this._needsOutputUpdate = true;
this.onRefresh = this.onRefresh.bind( this );
this.isScriptableNode = true;
}
get source() {
return this.codeNode ? this.codeNode.code : '';
}
setLocal( name, value ) {
return this._local.set( name, value );
}
getLocal( name ) {
return this._local.get( name );
}
onRefresh() {
this._refresh();
}
getInputLayout( id ) {
for ( const element of this.getLayout() ) {
if ( element.inputType && ( element.id === id || element.name === id ) ) {
return element;
}
}
}
getOutputLayout( id ) {
for ( const element of this.getLayout() ) {
if ( element.outputType && ( element.id === id || element.name === id ) ) {
return element;
}
}
}
setOutput( name, value ) {
const outputs = this._outputs;
if ( outputs[ name ] === undefined ) {
outputs[ name ] = scriptableValue( value );
} else {
outputs[ name ].value = value;
}
return this;
}
getOutput( name ) {
return this._outputs[ name ];
}
getParameter( name ) {
return this.parameters[ name ];
}
setParameter( name, value ) {
const parameters = this.parameters;
if ( value && value.isScriptableNode ) {
this.deleteParameter( name );
parameters[ name ] = value;
parameters[ name ].getDefaultOutput().events.addEventListener( 'refresh', this.onRefresh );
} else if ( value && value.isScriptableValueNode ) {
this.deleteParameter( name );
parameters[ name ] = value;
parameters[ name ].events.addEventListener( 'refresh', this.onRefresh );
} else if ( parameters[ name ] === undefined ) {
parameters[ name ] = scriptableValue( value );
parameters[ name ].events.addEventListener( 'refresh', this.onRefresh );
} else {
parameters[ name ].value = value;
}
return this;
}
getValue() {
return this.getDefaultOutput().getValue();
}
deleteParameter( name ) {
let valueNode = this.parameters[ name ];
if ( valueNode ) {
if ( valueNode.isScriptableNode ) valueNode = valueNode.getDefaultOutput();
valueNode.events.removeEventListener( 'refresh', this.onRefresh );
}
return this;
}
clearParameters() {
for ( const name of Object.keys( this.parameters ) ) {
this.deleteParameter( name );
}
this.needsUpdate = true;
return this;
}
call( name, ...params ) {
const object = this.getObject();
const method = object[ name ];
if ( typeof method === 'function' ) {
return method( ...params );
}
}
async callAsync( name, ...params ) {
const object = this.getObject();
const method = object[ name ];
if ( typeof method === 'function' ) {
return method.constructor.name === 'AsyncFunction' ? await method( ...params ) : method( ...params );
}
}
getNodeType( builder ) {
return this.getDefaultOutputNode().getNodeType( builder );
}
refresh( output = null ) {
if ( output !== null ) {
this.getOutput( output ).refresh();
} else {
this._refresh();
}
}
getObject() {
if ( this.needsUpdate ) this.dispose();
if ( this._object !== null ) return this._object;
//
const refresh = () => this.refresh();
const setOutput = ( id, value ) => this.setOutput( id, value );
const parameters = new Parameters( this );
const THREE = ScriptableNodeResources.get( 'THREE' );
const TSL = ScriptableNodeResources.get( 'TSL' );
const method = this.getMethod( this.codeNode );
const params = [ parameters, this._local, ScriptableNodeResources, refresh, setOutput, THREE, TSL ];
this._object = method( ...params );
const layout = this._object.layout;
if ( layout ) {
if ( layout.cache === false ) {
this._local.clear();
}
// default output
this._output.outputType = layout.outputType || null;
if ( Array.isArray( layout.elements ) ) {
for ( const element of layout.elements ) {
const id = element.id || element.name;
if ( element.inputType ) {
if ( this.getParameter( id ) === undefined ) this.setParameter( id, null );
this.getParameter( id ).inputType = element.inputType;
}
if ( element.outputType ) {
if ( this.getOutput( id ) === undefined ) this.setOutput( id, null );
this.getOutput( id ).outputType = element.outputType;
}
}
}
}
return this._object;
}
deserialize( data ) {
super.deserialize( data );
for ( const name in this.parameters ) {
let valueNode = this.parameters[ name ];
if ( valueNode.isScriptableNode ) valueNode = valueNode.getDefaultOutput();
valueNode.events.addEventListener( 'refresh', this.onRefresh );
}
}
getLayout() {
return this.getObject().layout;
}
getDefaultOutputNode() {
const output = this.getDefaultOutput().value;
if ( output && output.isNode ) {
return output;
}
return float();
}
getDefaultOutput() {
return this._exec()._output;
}
getMethod() {
if ( this.needsUpdate ) this.dispose();
if ( this._method !== null ) return this._method;
//
const parametersProps = [ 'parameters', 'local', 'global', 'refresh', 'setOutput', 'THREE', 'TSL' ];
const interfaceProps = [ 'layout', 'init', 'main', 'dispose' ];
const properties = interfaceProps.join( ', ' );
const declarations = 'var ' + properties + '; var output = {};\n';
const returns = '\nreturn { ...output, ' + properties + ' };';
const code = declarations + this.codeNode.code + returns;
//
this._method = new Function( ...parametersProps, code );
return this._method;
}
dispose() {
if ( this._method === null ) return;
if ( this._object && typeof this._object.dispose === 'function' ) {
this._object.dispose();
}
this._method = null;
this._object = null;
this._source = null;
this._value = null;
this._needsOutputUpdate = true;
this._output.value = null;
this._outputs = {};
}
setup() {
return this.getDefaultOutputNode();
}
getCacheKey( force ) {
const values = [ hashString( this.source ), this.getDefaultOutputNode().getCacheKey( force ) ];
for ( const param in this.parameters ) {
values.push( this.parameters[ param ].getCacheKey( force ) );
}
return hashArray( values );
}
set needsUpdate( value ) {
if ( value === true ) this.dispose();
}
get needsUpdate() {
return this.source !== this._source;
}
_exec() {
if ( this.codeNode === null ) return this;
if ( this._needsOutputUpdate === true ) {
this._value = this.call( 'main' );
this._needsOutputUpdate = false;
}
this._output.value = this._value;
return this;
}
_refresh() {
this.needsUpdate = true;
this._exec();
this._output.refresh();
}
}
export default ScriptableNode;
export const scriptable = /*@__PURE__*/ nodeProxy( ScriptableNode );

View File

@@ -0,0 +1,170 @@
import Node from '../core/Node.js';
import { arrayBufferToBase64, base64ToArrayBuffer } from '../core/NodeUtils.js';
import { nodeProxy, float } from '../tsl/TSLBase.js';
import { EventDispatcher } from '../../core/EventDispatcher.js';
class ScriptableValueNode extends Node {
static get type() {
return 'ScriptableValueNode';
}
constructor( value = null ) {
super();
this._value = value;
this._cache = null;
this.inputType = null;
this.outpuType = null;
this.events = new EventDispatcher();
this.isScriptableValueNode = true;
}
get isScriptableOutputNode() {
return this.outputType !== null;
}
set value( val ) {
if ( this._value === val ) return;
if ( this._cache && this.inputType === 'URL' && this.value.value instanceof ArrayBuffer ) {
URL.revokeObjectURL( this._cache );
this._cache = null;
}
this._value = val;
this.events.dispatchEvent( { type: 'change' } );
this.refresh();
}
get value() {
return this._value;
}
refresh() {
this.events.dispatchEvent( { type: 'refresh' } );
}
getValue() {
const value = this.value;
if ( value && this._cache === null && this.inputType === 'URL' && value.value instanceof ArrayBuffer ) {
this._cache = URL.createObjectURL( new Blob( [ value.value ] ) );
} else if ( value && value.value !== null && value.value !== undefined && (
( ( this.inputType === 'URL' || this.inputType === 'String' ) && typeof value.value === 'string' ) ||
( this.inputType === 'Number' && typeof value.value === 'number' ) ||
( this.inputType === 'Vector2' && value.value.isVector2 ) ||
( this.inputType === 'Vector3' && value.value.isVector3 ) ||
( this.inputType === 'Vector4' && value.value.isVector4 ) ||
( this.inputType === 'Color' && value.value.isColor ) ||
( this.inputType === 'Matrix3' && value.value.isMatrix3 ) ||
( this.inputType === 'Matrix4' && value.value.isMatrix4 )
) ) {
return value.value;
}
return this._cache || value;
}
getNodeType( builder ) {
return this.value && this.value.isNode ? this.value.getNodeType( builder ) : 'float';
}
setup() {
return this.value && this.value.isNode ? this.value : float();
}
serialize( data ) {
super.serialize( data );
if ( this.value !== null ) {
if ( this.inputType === 'ArrayBuffer' ) {
data.value = arrayBufferToBase64( this.value );
} else {
data.value = this.value ? this.value.toJSON( data.meta ).uuid : null;
}
} else {
data.value = null;
}
data.inputType = this.inputType;
data.outputType = this.outputType;
}
deserialize( data ) {
super.deserialize( data );
let value = null;
if ( data.value !== null ) {
if ( data.inputType === 'ArrayBuffer' ) {
value = base64ToArrayBuffer( data.value );
} else if ( data.inputType === 'Texture' ) {
value = data.meta.textures[ data.value ];
} else {
value = data.meta.nodes[ data.value ] || null;
}
}
this.value = value;
this.inputType = data.inputType;
this.outputType = data.outputType;
}
}
export default ScriptableValueNode;
export const scriptableValue = /*@__PURE__*/ nodeProxy( ScriptableValueNode );

131
web-app/node_modules/three/src/nodes/core/AssignNode.js generated vendored Normal file
View File

@@ -0,0 +1,131 @@
import TempNode from '../core/TempNode.js';
import { addMethodChaining, nodeProxy } from '../tsl/TSLCore.js';
import { vectorComponents } from '../core/constants.js';
class AssignNode extends TempNode {
static get type() {
return 'AssignNode';
}
constructor( targetNode, sourceNode ) {
super();
this.targetNode = targetNode;
this.sourceNode = sourceNode;
}
hasDependencies() {
return false;
}
getNodeType( builder, output ) {
return output !== 'void' ? this.targetNode.getNodeType( builder ) : 'void';
}
needsSplitAssign( builder ) {
const { targetNode } = this;
if ( builder.isAvailable( 'swizzleAssign' ) === false && targetNode.isSplitNode && targetNode.components.length > 1 ) {
const targetLength = builder.getTypeLength( targetNode.node.getNodeType( builder ) );
const assignDiferentVector = vectorComponents.join( '' ).slice( 0, targetLength ) !== targetNode.components;
return assignDiferentVector;
}
return false;
}
generate( builder, output ) {
const { targetNode, sourceNode } = this;
const needsSplitAssign = this.needsSplitAssign( builder );
const targetType = targetNode.getNodeType( builder );
const target = targetNode.context( { assign: true } ).build( builder );
const source = sourceNode.build( builder, targetType );
const sourceType = sourceNode.getNodeType( builder );
const nodeData = builder.getDataFromNode( this );
//
let snippet;
if ( nodeData.initialized === true ) {
if ( output !== 'void' ) {
snippet = target;
}
} else if ( needsSplitAssign ) {
const sourceVar = builder.getVarFromNode( this, null, targetType );
const sourceProperty = builder.getPropertyName( sourceVar );
builder.addLineFlowCode( `${ sourceProperty } = ${ source }`, this );
const targetRoot = targetNode.node.context( { assign: true } ).build( builder );
for ( let i = 0; i < targetNode.components.length; i ++ ) {
const component = targetNode.components[ i ];
builder.addLineFlowCode( `${ targetRoot }.${ component } = ${ sourceProperty }[ ${ i } ]`, this );
}
if ( output !== 'void' ) {
snippet = target;
}
} else {
snippet = `${ target } = ${ source }`;
if ( output === 'void' || sourceType === 'void' ) {
builder.addLineFlowCode( snippet, this );
if ( output !== 'void' ) {
snippet = target;
}
}
}
nodeData.initialized = true;
return builder.format( snippet, targetType, output );
}
}
export default AssignNode;
export const assign = /*@__PURE__*/ nodeProxy( AssignNode );
addMethodChaining( 'assign', assign );

View File

@@ -0,0 +1,125 @@
import Node from './Node.js';
import { nodeObject, varying } from '../tsl/TSLBase.js';
class AttributeNode extends Node {
static get type() {
return 'AttributeNode';
}
constructor( attributeName, nodeType = null ) {
super( nodeType );
this.global = true;
this._attributeName = attributeName;
}
getHash( builder ) {
return this.getAttributeName( builder );
}
getNodeType( builder ) {
let nodeType = this.nodeType;
if ( nodeType === null ) {
const attributeName = this.getAttributeName( builder );
if ( builder.hasGeometryAttribute( attributeName ) ) {
const attribute = builder.geometry.getAttribute( attributeName );
nodeType = builder.getTypeFromAttribute( attribute );
} else {
nodeType = 'float';
}
}
return nodeType;
}
setAttributeName( attributeName ) {
this._attributeName = attributeName;
return this;
}
getAttributeName( /*builder*/ ) {
return this._attributeName;
}
generate( builder ) {
const attributeName = this.getAttributeName( builder );
const nodeType = this.getNodeType( builder );
const geometryAttribute = builder.hasGeometryAttribute( attributeName );
if ( geometryAttribute === true ) {
const attribute = builder.geometry.getAttribute( attributeName );
const attributeType = builder.getTypeFromAttribute( attribute );
const nodeAttribute = builder.getAttribute( attributeName, attributeType );
if ( builder.shaderStage === 'vertex' ) {
return builder.format( nodeAttribute.name, attributeType, nodeType );
} else {
const nodeVarying = varying( this );
return nodeVarying.build( builder, nodeType );
}
} else {
console.warn( `AttributeNode: Vertex attribute "${ attributeName }" not found on geometry.` );
return builder.generateConst( nodeType );
}
}
serialize( data ) {
super.serialize( data );
data.global = this.global;
data._attributeName = this._attributeName;
}
deserialize( data ) {
super.deserialize( data );
this.global = data.global;
this._attributeName = data._attributeName;
}
}
export default AttributeNode;
export const attribute = ( name, nodeType ) => nodeObject( new AttributeNode( name, nodeType ) );

View File

@@ -0,0 +1,49 @@
import Node from './Node.js';
import { addMethodChaining, nodeProxy } from '../tsl/TSLCore.js';
class BypassNode extends Node {
static get type() {
return 'BypassNode';
}
constructor( returnNode, callNode ) {
super();
this.isBypassNode = true;
this.outputNode = returnNode;
this.callNode = callNode;
}
getNodeType( builder ) {
return this.outputNode.getNodeType( builder );
}
generate( builder ) {
const snippet = this.callNode.build( builder, 'void' );
if ( snippet !== '' ) {
builder.addLineFlowCode( snippet, this );
}
return this.outputNode.build( builder );
}
}
export default BypassNode;
export const bypass = /*@__PURE__*/ nodeProxy( BypassNode );
addMethodChaining( 'bypass', bypass );

50
web-app/node_modules/three/src/nodes/core/CacheNode.js generated vendored Normal file
View File

@@ -0,0 +1,50 @@
import Node from './Node.js';
import { addMethodChaining, nodeObject } from '../tsl/TSLCore.js';
class CacheNode extends Node {
static get type() {
return 'CacheNode';
}
constructor( node, parent = true ) {
super();
this.node = node;
this.parent = parent;
this.isCacheNode = true;
}
getNodeType( builder ) {
return this.node.getNodeType( builder );
}
build( builder, ...params ) {
const previousCache = builder.getCache();
const cache = builder.getCacheFromNode( this, this.parent );
builder.setCache( cache );
const data = this.node.build( builder, ...params );
builder.setCache( previousCache );
return data;
}
}
export default CacheNode;
export const cache = ( node, ...params ) => nodeObject( new CacheNode( nodeObject( node ), ...params ) );
addMethodChaining( 'cache', cache );

35
web-app/node_modules/three/src/nodes/core/ConstNode.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
import InputNode from './InputNode.js';
class ConstNode extends InputNode {
static get type() {
return 'ConstNode';
}
constructor( value, nodeType = null ) {
super( value, nodeType );
this.isConstNode = true;
}
generateConst( builder ) {
return builder.generateConst( this.getNodeType( builder ), this.value );
}
generate( builder, output ) {
const type = this.getNodeType( builder );
return builder.format( this.generateConst( builder ), type, output );
}
}
export default ConstNode;

View File

@@ -0,0 +1,77 @@
import Node from './Node.js';
import { addMethodChaining, nodeProxy } from '../tsl/TSLCore.js';
class ContextNode extends Node {
static get type() {
return 'ContextNode';
}
constructor( node, value = {} ) {
super();
this.isContextNode = true;
this.node = node;
this.value = value;
}
getScope() {
return this.node.getScope();
}
getNodeType( builder ) {
return this.node.getNodeType( builder );
}
analyze( builder ) {
this.node.build( builder );
}
setup( builder ) {
const previousContext = builder.getContext();
builder.setContext( { ...builder.context, ...this.value } );
const node = this.node.build( builder );
builder.setContext( previousContext );
return node;
}
generate( builder, output ) {
const previousContext = builder.getContext();
builder.setContext( { ...builder.context, ...this.value } );
const snippet = this.node.build( builder, output );
builder.setContext( previousContext );
return snippet;
}
}
export default ContextNode;
export const context = /*@__PURE__*/ nodeProxy( ContextNode );
export const label = ( node, name ) => context( node, { label: name } );
addMethodChaining( 'context', context );
addMethodChaining( 'label', label );

99
web-app/node_modules/three/src/nodes/core/IndexNode.js generated vendored Normal file
View File

@@ -0,0 +1,99 @@
import Node from './Node.js';
import { nodeImmutable, varying } from '../tsl/TSLBase.js';
class IndexNode extends Node {
static get type() {
return 'IndexNode';
}
constructor( scope ) {
super( 'uint' );
this.scope = scope;
this.isInstanceIndexNode = true;
}
generate( builder ) {
const nodeType = this.getNodeType( builder );
const scope = this.scope;
let propertyName;
if ( scope === IndexNode.VERTEX ) {
// The index of a vertex within a mesh.
propertyName = builder.getVertexIndex();
} else if ( scope === IndexNode.INSTANCE ) {
// The index of either a mesh instance or an invocation of a compute shader.
propertyName = builder.getInstanceIndex();
} else if ( scope === IndexNode.DRAW ) {
// The index of a draw call.
propertyName = builder.getDrawIndex();
} else if ( scope === IndexNode.INVOCATION_LOCAL ) {
// The index of a compute invocation within the scope of a workgroup load.
propertyName = builder.getInvocationLocalIndex();
} else if ( scope === IndexNode.INVOCATION_SUBGROUP ) {
// The index of a compute invocation within the scope of a subgroup.
propertyName = builder.getInvocationSubgroupIndex();
} else if ( scope === IndexNode.SUBGROUP ) {
// The index of the subgroup the current compute invocation belongs to.
propertyName = builder.getSubgroupIndex();
} else {
throw new Error( 'THREE.IndexNode: Unknown scope: ' + scope );
}
let output;
if ( builder.shaderStage === 'vertex' || builder.shaderStage === 'compute' ) {
output = propertyName;
} else {
const nodeVarying = varying( this );
output = nodeVarying.build( builder, nodeType );
}
return output;
}
}
IndexNode.VERTEX = 'vertex';
IndexNode.INSTANCE = 'instance';
IndexNode.SUBGROUP = 'subgroup';
IndexNode.INVOCATION_LOCAL = 'invocationLocal';
IndexNode.INVOCATION_SUBGROUP = 'invocationSubgroup';
IndexNode.DRAW = 'draw';
export default IndexNode;
export const vertexIndex = /*@__PURE__*/ nodeImmutable( IndexNode, IndexNode.VERTEX );
export const instanceIndex = /*@__PURE__*/ nodeImmutable( IndexNode, IndexNode.INSTANCE );
export const subgroupIndex = /*@__PURE__*/ nodeImmutable( IndexNode, IndexNode.SUBGROUP );
export const invocationSubgroupIndex = /*@__PURE__*/ nodeImmutable( IndexNode, IndexNode.INVOCATION_SUBGROUP );
export const invocationLocalIndex = /*@__PURE__*/ nodeImmutable( IndexNode, IndexNode.INVOCATION_LOCAL );
export const drawIndex = /*@__PURE__*/ nodeImmutable( IndexNode, IndexNode.DRAW );

87
web-app/node_modules/three/src/nodes/core/InputNode.js generated vendored Normal file
View File

@@ -0,0 +1,87 @@
import Node from './Node.js';
import { getValueType, getValueFromType, arrayBufferToBase64 } from './NodeUtils.js';
class InputNode extends Node {
static get type() {
return 'InputNode';
}
constructor( value, nodeType = null ) {
super( nodeType );
this.isInputNode = true;
this.value = value;
this.precision = null;
}
getNodeType( /*builder*/ ) {
if ( this.nodeType === null ) {
return getValueType( this.value );
}
return this.nodeType;
}
getInputType( builder ) {
return this.getNodeType( builder );
}
setPrecision( precision ) {
this.precision = precision;
return this;
}
serialize( data ) {
super.serialize( data );
data.value = this.value;
if ( this.value && this.value.toArray ) data.value = this.value.toArray();
data.valueType = getValueType( this.value );
data.nodeType = this.nodeType;
if ( data.valueType === 'ArrayBuffer' ) data.value = arrayBufferToBase64( data.value );
data.precision = this.precision;
}
deserialize( data ) {
super.deserialize( data );
this.nodeType = data.nodeType;
this.value = Array.isArray( data.value ) ? getValueFromType( data.valueType, ...data.value ) : data.value;
this.precision = data.precision || null;
if ( this.value && this.value.fromArray ) this.value = this.value.fromArray( data.value );
}
generate( /*builder, output*/ ) {
console.warn( 'Abstract function.' );
}
}
export default InputNode;

View File

@@ -0,0 +1,17 @@
class LightingModel {
start( /*input, stack, builder*/ ) { }
finish( /*input, stack, builder*/ ) { }
direct( /*input, stack, builder*/ ) { }
directRectArea( /*input, stack, builder*/ ) {}
indirect( /*input, stack, builder*/ ) { }
ambientOcclusion( /*input, stack, builder*/ ) { }
}
export default LightingModel;

85
web-app/node_modules/three/src/nodes/core/MRTNode.js generated vendored Normal file
View File

@@ -0,0 +1,85 @@
import OutputStructNode from './OutputStructNode.js';
import { nodeProxy, vec4 } from '../tsl/TSLBase.js';
export function getTextureIndex( textures, name ) {
for ( let i = 0; i < textures.length; i ++ ) {
if ( textures[ i ].name === name ) {
return i;
}
}
return - 1;
}
class MRTNode extends OutputStructNode {
static get type() {
return 'MRTNode';
}
constructor( outputNodes ) {
super();
this.outputNodes = outputNodes;
this.isMRTNode = true;
}
has( name ) {
return this.outputNodes[ name ] !== undefined;
}
get( name ) {
return this.outputNodes[ name ];
}
merge( mrtNode ) {
const outputs = { ...this.outputNodes, ...mrtNode.outputNodes };
return mrt( outputs );
}
setup( builder ) {
const outputNodes = this.outputNodes;
const mrt = builder.renderer.getRenderTarget();
const members = [];
const textures = mrt.textures;
for ( const name in outputNodes ) {
const index = getTextureIndex( textures, name );
members[ index ] = vec4( outputNodes[ name ] );
}
this.members = members;
return super.setup( builder );
}
}
export default MRTNode;
export const mrt = /*@__PURE__*/ nodeProxy( MRTNode );

551
web-app/node_modules/three/src/nodes/core/Node.js generated vendored Normal file
View File

@@ -0,0 +1,551 @@
import { NodeUpdateType } from './constants.js';
import { getNodeChildren, getCacheKey } from './NodeUtils.js';
import { EventDispatcher } from '../../core/EventDispatcher.js';
import { MathUtils } from '../../math/MathUtils.js';
let _nodeId = 0;
class Node extends EventDispatcher {
static get type() {
return 'Node';
}
constructor( nodeType = null ) {
super();
this.nodeType = nodeType;
this.updateType = NodeUpdateType.NONE;
this.updateBeforeType = NodeUpdateType.NONE;
this.updateAfterType = NodeUpdateType.NONE;
this.uuid = MathUtils.generateUUID();
this.version = 0;
this._cacheKey = null;
this._cacheKeyVersion = 0;
this.global = false;
this.isNode = true;
Object.defineProperty( this, 'id', { value: _nodeId ++ } );
}
set needsUpdate( value ) {
if ( value === true ) {
this.version ++;
}
}
get type() {
return this.constructor.type;
}
onUpdate( callback, updateType ) {
this.updateType = updateType;
this.update = callback.bind( this.getSelf() );
return this;
}
onFrameUpdate( callback ) {
return this.onUpdate( callback, NodeUpdateType.FRAME );
}
onRenderUpdate( callback ) {
return this.onUpdate( callback, NodeUpdateType.RENDER );
}
onObjectUpdate( callback ) {
return this.onUpdate( callback, NodeUpdateType.OBJECT );
}
onReference( callback ) {
this.updateReference = callback.bind( this.getSelf() );
return this;
}
getSelf() {
// Returns non-node object.
return this.self || this;
}
updateReference( /*state*/ ) {
return this;
}
isGlobal( /*builder*/ ) {
return this.global;
}
* getChildren() {
for ( const { childNode } of getNodeChildren( this ) ) {
yield childNode;
}
}
dispose() {
this.dispatchEvent( { type: 'dispose' } );
}
traverse( callback ) {
callback( this );
for ( const childNode of this.getChildren() ) {
childNode.traverse( callback );
}
}
getCacheKey( force = false ) {
force = force || this.version !== this._cacheKeyVersion;
if ( force === true || this._cacheKey === null ) {
this._cacheKey = getCacheKey( this, force );
this._cacheKeyVersion = this.version;
}
return this._cacheKey;
}
getScope() {
return this;
}
getHash( /*builder*/ ) {
return this.uuid;
}
getUpdateType() {
return this.updateType;
}
getUpdateBeforeType() {
return this.updateBeforeType;
}
getUpdateAfterType() {
return this.updateAfterType;
}
getElementType( builder ) {
const type = this.getNodeType( builder );
const elementType = builder.getElementType( type );
return elementType;
}
getNodeType( builder ) {
const nodeProperties = builder.getNodeProperties( this );
if ( nodeProperties.outputNode ) {
return nodeProperties.outputNode.getNodeType( builder );
}
return this.nodeType;
}
getShared( builder ) {
const hash = this.getHash( builder );
const nodeFromHash = builder.getNodeFromHash( hash );
return nodeFromHash || this;
}
setup( builder ) {
const nodeProperties = builder.getNodeProperties( this );
let index = 0;
for ( const childNode of this.getChildren() ) {
nodeProperties[ 'node' + index ++ ] = childNode;
}
// return a outputNode if exists
return null;
}
analyze( builder ) {
const usageCount = builder.increaseUsage( this );
if ( usageCount === 1 ) {
// node flow children
const nodeProperties = builder.getNodeProperties( this );
for ( const childNode of Object.values( nodeProperties ) ) {
if ( childNode && childNode.isNode === true ) {
childNode.build( builder );
}
}
}
}
generate( builder, output ) {
const { outputNode } = builder.getNodeProperties( this );
if ( outputNode && outputNode.isNode === true ) {
return outputNode.build( builder, output );
}
}
updateBefore( /*frame*/ ) {
console.warn( 'Abstract function.' );
}
updateAfter( /*frame*/ ) {
console.warn( 'Abstract function.' );
}
update( /*frame*/ ) {
console.warn( 'Abstract function.' );
}
build( builder, output = null ) {
const refNode = this.getShared( builder );
if ( this !== refNode ) {
return refNode.build( builder, output );
}
builder.addNode( this );
builder.addChain( this );
/* Build stages expected results:
- "setup" -> Node
- "analyze" -> null
- "generate" -> String
*/
let result = null;
const buildStage = builder.getBuildStage();
if ( buildStage === 'setup' ) {
this.updateReference( builder );
const properties = builder.getNodeProperties( this );
if ( properties.initialized !== true ) {
const stackNodesBeforeSetup = builder.stack.nodes.length;
properties.initialized = true;
properties.outputNode = this.setup( builder );
if ( properties.outputNode !== null && builder.stack.nodes.length !== stackNodesBeforeSetup ) {
// !! no outputNode !!
//properties.outputNode = builder.stack;
}
for ( const childNode of Object.values( properties ) ) {
if ( childNode && childNode.isNode === true ) {
childNode.build( builder );
}
}
}
} else if ( buildStage === 'analyze' ) {
this.analyze( builder );
} else if ( buildStage === 'generate' ) {
const isGenerateOnce = this.generate.length === 1;
if ( isGenerateOnce ) {
const type = this.getNodeType( builder );
const nodeData = builder.getDataFromNode( this );
result = nodeData.snippet;
if ( result === undefined ) {
result = this.generate( builder ) || '';
nodeData.snippet = result;
} else if ( nodeData.flowCodes !== undefined && builder.context.nodeBlock !== undefined ) {
builder.addFlowCodeHierarchy( this, builder.context.nodeBlock );
}
result = builder.format( result, type, output );
} else {
result = this.generate( builder, output ) || '';
}
}
builder.removeChain( this );
builder.addSequentialNode( this );
return result;
}
getSerializeChildren() {
return getNodeChildren( this );
}
serialize( json ) {
const nodeChildren = this.getSerializeChildren();
const inputNodes = {};
for ( const { property, index, childNode } of nodeChildren ) {
if ( index !== undefined ) {
if ( inputNodes[ property ] === undefined ) {
inputNodes[ property ] = Number.isInteger( index ) ? [] : {};
}
inputNodes[ property ][ index ] = childNode.toJSON( json.meta ).uuid;
} else {
inputNodes[ property ] = childNode.toJSON( json.meta ).uuid;
}
}
if ( Object.keys( inputNodes ).length > 0 ) {
json.inputNodes = inputNodes;
}
}
deserialize( json ) {
if ( json.inputNodes !== undefined ) {
const nodes = json.meta.nodes;
for ( const property in json.inputNodes ) {
if ( Array.isArray( json.inputNodes[ property ] ) ) {
const inputArray = [];
for ( const uuid of json.inputNodes[ property ] ) {
inputArray.push( nodes[ uuid ] );
}
this[ property ] = inputArray;
} else if ( typeof json.inputNodes[ property ] === 'object' ) {
const inputObject = {};
for ( const subProperty in json.inputNodes[ property ] ) {
const uuid = json.inputNodes[ property ][ subProperty ];
inputObject[ subProperty ] = nodes[ uuid ];
}
this[ property ] = inputObject;
} else {
const uuid = json.inputNodes[ property ];
this[ property ] = nodes[ uuid ];
}
}
}
}
toJSON( meta ) {
const { uuid, type } = this;
const isRoot = ( meta === undefined || typeof meta === 'string' );
if ( isRoot ) {
meta = {
textures: {},
images: {},
nodes: {}
};
}
// serialize
let data = meta.nodes[ uuid ];
if ( data === undefined ) {
data = {
uuid,
type,
meta,
metadata: {
version: 4.6,
type: 'Node',
generator: 'Node.toJSON'
}
};
if ( isRoot !== true ) meta.nodes[ data.uuid ] = data;
this.serialize( data );
delete data.meta;
}
// TODO: Copied from Object3D.toJSON
function extractFromCache( cache ) {
const values = [];
for ( const key in cache ) {
const data = cache[ key ];
delete data.metadata;
values.push( data );
}
return values;
}
if ( isRoot ) {
const textures = extractFromCache( meta.textures );
const images = extractFromCache( meta.images );
const nodes = extractFromCache( meta.nodes );
if ( textures.length > 0 ) data.textures = textures;
if ( images.length > 0 ) data.images = images;
if ( nodes.length > 0 ) data.nodes = nodes;
}
return data;
}
}
export default Node;

View File

@@ -0,0 +1,15 @@
class NodeAttribute {
constructor( name, type, node = null ) {
this.isNodeAttribute = true;
this.name = name;
this.type = type;
this.node = node;
}
}
export default NodeAttribute;

1503
web-app/node_modules/three/src/nodes/core/NodeBuilder.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

36
web-app/node_modules/three/src/nodes/core/NodeCache.js generated vendored Normal file
View File

@@ -0,0 +1,36 @@
let id = 0;
class NodeCache {
constructor( parent = null ) {
this.id = id ++;
this.nodesData = new WeakMap();
this.parent = parent;
}
getData( node ) {
let data = this.nodesData.get( node );
if ( data === undefined && this.parent !== null ) {
data = this.parent.getData( node );
}
return data;
}
setData( node, data ) {
this.nodesData.set( node, data );
}
}
export default NodeCache;

15
web-app/node_modules/three/src/nodes/core/NodeCode.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
class NodeCode {
constructor( name, type, code = '' ) {
this.name = name;
this.type = type;
this.code = code;
Object.defineProperty( this, 'isNodeCode', { value: true } );
}
}
export default NodeCode;

185
web-app/node_modules/three/src/nodes/core/NodeFrame.js generated vendored Normal file
View File

@@ -0,0 +1,185 @@
import { NodeUpdateType } from './constants.js';
class NodeFrame {
constructor() {
this.time = 0;
this.deltaTime = 0;
this.frameId = 0;
this.renderId = 0;
this.startTime = null;
this.updateMap = new WeakMap();
this.updateBeforeMap = new WeakMap();
this.updateAfterMap = new WeakMap();
this.renderer = null;
this.material = null;
this.camera = null;
this.object = null;
this.scene = null;
}
_getMaps( referenceMap, nodeRef ) {
let maps = referenceMap.get( nodeRef );
if ( maps === undefined ) {
maps = {
renderMap: new WeakMap(),
frameMap: new WeakMap()
};
referenceMap.set( nodeRef, maps );
}
return maps;
}
updateBeforeNode( node ) {
const updateType = node.getUpdateBeforeType();
const reference = node.updateReference( this );
if ( updateType === NodeUpdateType.FRAME ) {
const { frameMap } = this._getMaps( this.updateBeforeMap, reference );
if ( frameMap.get( reference ) !== this.frameId ) {
if ( node.updateBefore( this ) !== false ) {
frameMap.set( reference, this.frameId );
}
}
} else if ( updateType === NodeUpdateType.RENDER ) {
const { renderMap } = this._getMaps( this.updateBeforeMap, reference );
if ( renderMap.get( reference ) !== this.renderId ) {
if ( node.updateBefore( this ) !== false ) {
renderMap.set( reference, this.renderId );
}
}
} else if ( updateType === NodeUpdateType.OBJECT ) {
node.updateBefore( this );
}
}
updateAfterNode( node ) {
const updateType = node.getUpdateAfterType();
const reference = node.updateReference( this );
if ( updateType === NodeUpdateType.FRAME ) {
const { frameMap } = this._getMaps( this.updateAfterMap, reference );
if ( frameMap.get( reference ) !== this.frameId ) {
if ( node.updateAfter( this ) !== false ) {
frameMap.set( reference, this.frameId );
}
}
} else if ( updateType === NodeUpdateType.RENDER ) {
const { renderMap } = this._getMaps( this.updateAfterMap, reference );
if ( renderMap.get( reference ) !== this.renderId ) {
if ( node.updateAfter( this ) !== false ) {
renderMap.set( reference, this.renderId );
}
}
} else if ( updateType === NodeUpdateType.OBJECT ) {
node.updateAfter( this );
}
}
updateNode( node ) {
const updateType = node.getUpdateType();
const reference = node.updateReference( this );
if ( updateType === NodeUpdateType.FRAME ) {
const { frameMap } = this._getMaps( this.updateMap, reference );
if ( frameMap.get( reference ) !== this.frameId ) {
if ( node.update( this ) !== false ) {
frameMap.set( reference, this.frameId );
}
}
} else if ( updateType === NodeUpdateType.RENDER ) {
const { renderMap } = this._getMaps( this.updateMap, reference );
if ( renderMap.get( reference ) !== this.renderId ) {
if ( node.update( this ) !== false ) {
renderMap.set( reference, this.renderId );
}
}
} else if ( updateType === NodeUpdateType.OBJECT ) {
node.update( this );
}
}
update() {
this.frameId ++;
if ( this.lastTime === undefined ) this.lastTime = performance.now();
this.deltaTime = ( performance.now() - this.lastTime ) / 1000;
this.lastTime = performance.now();
this.time += this.deltaTime;
}
}
export default NodeFrame;

View File

@@ -0,0 +1,22 @@
class NodeFunction {
constructor( type, inputs, name = '', precision = '' ) {
this.type = type;
this.inputs = inputs;
this.name = name;
this.precision = precision;
}
getCode( /*name = this.name*/ ) {
console.warn( 'Abstract function.' );
}
}
NodeFunction.isNodeFunction = true;
export default NodeFunction;

View File

@@ -0,0 +1,17 @@
class NodeFunctionInput {
constructor( type, name, count = null, qualifier = '', isConst = false ) {
this.type = type;
this.name = name;
this.count = count;
this.qualifier = qualifier;
this.isConst = isConst;
}
}
NodeFunctionInput.isNodeFunctionInput = true;
export default NodeFunctionInput;

View File

@@ -0,0 +1,11 @@
class NodeParser {
parseFunction( /*source*/ ) {
console.warn( 'Abstract function.' );
}
}
export default NodeParser;

View File

@@ -0,0 +1,39 @@
class NodeUniform {
constructor( name, type, node ) {
this.isNodeUniform = true;
this.name = name;
this.type = type;
this.node = node.getSelf();
}
get value() {
return this.node.value;
}
set value( val ) {
this.node.value = val;
}
get id() {
return this.node.id;
}
get groupNode() {
return this.node.groupNode;
}
}
export default NodeUniform;

258
web-app/node_modules/three/src/nodes/core/NodeUtils.js generated vendored Normal file
View File

@@ -0,0 +1,258 @@
import { Color } from '../../math/Color.js';
import { Matrix3 } from '../../math/Matrix3.js';
import { Matrix4 } from '../../math/Matrix4.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector3 } from '../../math/Vector3.js';
import { Vector4 } from '../../math/Vector4.js';
// cyrb53 (c) 2018 bryc (github.com/bryc). License: Public domain. Attribution appreciated.
// A fast and simple 64-bit (or 53-bit) string hash function with decent collision resistance.
// Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity.
// See https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript/52171480#52171480
// https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
function cyrb53( value, seed = 0 ) {
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
if ( value instanceof Array ) {
for ( let i = 0, val; i < value.length; i ++ ) {
val = value[ i ];
h1 = Math.imul( h1 ^ val, 2654435761 );
h2 = Math.imul( h2 ^ val, 1597334677 );
}
} else {
for ( let i = 0, ch; i < value.length; i ++ ) {
ch = value.charCodeAt( i );
h1 = Math.imul( h1 ^ ch, 2654435761 );
h2 = Math.imul( h2 ^ ch, 1597334677 );
}
}
h1 = Math.imul( h1 ^ ( h1 >>> 16 ), 2246822507 );
h1 ^= Math.imul( h2 ^ ( h2 >>> 13 ), 3266489909 );
h2 = Math.imul( h2 ^ ( h2 >>> 16 ), 2246822507 );
h2 ^= Math.imul( h1 ^ ( h1 >>> 13 ), 3266489909 );
return 4294967296 * ( 2097151 & h2 ) + ( h1 >>> 0 );
}
export const hashString = ( str ) => cyrb53( str );
export const hashArray = ( array ) => cyrb53( array );
export const hash = ( ...params ) => cyrb53( params );
export function getCacheKey( object, force = false ) {
const values = [];
if ( object.isNode === true ) {
values.push( object.id );
object = object.getSelf();
}
for ( const { property, childNode } of getNodeChildren( object ) ) {
values.push( values, cyrb53( property.slice( 0, - 4 ) ), childNode.getCacheKey( force ) );
}
return cyrb53( values );
}
export function* getNodeChildren( node, toJSON = false ) {
for ( const property in node ) {
// Ignore private properties.
if ( property.startsWith( '_' ) === true ) continue;
const object = node[ property ];
if ( Array.isArray( object ) === true ) {
for ( let i = 0; i < object.length; i ++ ) {
const child = object[ i ];
if ( child && ( child.isNode === true || toJSON && typeof child.toJSON === 'function' ) ) {
yield { property, index: i, childNode: child };
}
}
} else if ( object && object.isNode === true ) {
yield { property, childNode: object };
} else if ( typeof object === 'object' ) {
for ( const subProperty in object ) {
const child = object[ subProperty ];
if ( child && ( child.isNode === true || toJSON && typeof child.toJSON === 'function' ) ) {
yield { property, index: subProperty, childNode: child };
}
}
}
}
}
export function getValueType( value ) {
if ( value === undefined || value === null ) return null;
const typeOf = typeof value;
if ( value.isNode === true ) {
return 'node';
} else if ( typeOf === 'number' ) {
return 'float';
} else if ( typeOf === 'boolean' ) {
return 'bool';
} else if ( typeOf === 'string' ) {
return 'string';
} else if ( typeOf === 'function' ) {
return 'shader';
} else if ( value.isVector2 === true ) {
return 'vec2';
} else if ( value.isVector3 === true ) {
return 'vec3';
} else if ( value.isVector4 === true ) {
return 'vec4';
} else if ( value.isMatrix3 === true ) {
return 'mat3';
} else if ( value.isMatrix4 === true ) {
return 'mat4';
} else if ( value.isColor === true ) {
return 'color';
} else if ( value instanceof ArrayBuffer ) {
return 'ArrayBuffer';
}
return null;
}
export function getValueFromType( type, ...params ) {
const last4 = type ? type.slice( - 4 ) : undefined;
if ( params.length === 1 ) { // ensure same behaviour as in NodeBuilder.format()
if ( last4 === 'vec2' ) params = [ params[ 0 ], params[ 0 ] ];
else if ( last4 === 'vec3' ) params = [ params[ 0 ], params[ 0 ], params[ 0 ] ];
else if ( last4 === 'vec4' ) params = [ params[ 0 ], params[ 0 ], params[ 0 ], params[ 0 ] ];
}
if ( type === 'color' ) {
return new Color( ...params );
} else if ( last4 === 'vec2' ) {
return new Vector2( ...params );
} else if ( last4 === 'vec3' ) {
return new Vector3( ...params );
} else if ( last4 === 'vec4' ) {
return new Vector4( ...params );
} else if ( last4 === 'mat3' ) {
return new Matrix3( ...params );
} else if ( last4 === 'mat4' ) {
return new Matrix4( ...params );
} else if ( type === 'bool' ) {
return params[ 0 ] || false;
} else if ( ( type === 'float' ) || ( type === 'int' ) || ( type === 'uint' ) ) {
return params[ 0 ] || 0;
} else if ( type === 'string' ) {
return params[ 0 ] || '';
} else if ( type === 'ArrayBuffer' ) {
return base64ToArrayBuffer( params[ 0 ] );
}
return null;
}
export function arrayBufferToBase64( arrayBuffer ) {
let chars = '';
const array = new Uint8Array( arrayBuffer );
for ( let i = 0; i < array.length; i ++ ) {
chars += String.fromCharCode( array[ i ] );
}
return btoa( chars );
}
export function base64ToArrayBuffer( base64 ) {
return Uint8Array.from( atob( base64 ), c => c.charCodeAt( 0 ) ).buffer;
}

14
web-app/node_modules/three/src/nodes/core/NodeVar.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
class NodeVar {
constructor( name, type ) {
this.isNodeVar = true;
this.name = name;
this.type = type;
}
}
export default NodeVar;

View File

@@ -0,0 +1,17 @@
import NodeVar from './NodeVar.js';
class NodeVarying extends NodeVar {
constructor( name, type ) {
super( name, type );
this.needsInterpolation = false;
this.isNodeVarying = true;
}
}
export default NodeVarying;

View File

@@ -0,0 +1,63 @@
import Node from './Node.js';
import StructTypeNode from './StructTypeNode.js';
import { nodeProxy } from '../tsl/TSLBase.js';
class OutputStructNode extends Node {
static get type() {
return 'OutputStructNode';
}
constructor( ...members ) {
super();
this.members = members;
this.isOutputStructNode = true;
}
setup( builder ) {
super.setup( builder );
const members = this.members;
const types = [];
for ( let i = 0; i < members.length; i ++ ) {
types.push( members[ i ].getNodeType( builder ) );
}
this.nodeType = builder.getStructTypeFromNode( new StructTypeNode( types ) ).name;
}
generate( builder, output ) {
const propertyName = builder.getOutputStructName();
const members = this.members;
const structPrefix = propertyName !== '' ? propertyName + '.' : '';
for ( let i = 0; i < members.length; i ++ ) {
const snippet = members[ i ].build( builder, output );
builder.addLineFlowCode( `${ structPrefix }m${ i } = ${ snippet }`, this );
}
return propertyName;
}
}
export default OutputStructNode;
export const outputStruct = /*@__PURE__*/ nodeProxy( OutputStructNode );

View File

@@ -0,0 +1,36 @@
import { nodeObject } from '../tsl/TSLBase.js';
import PropertyNode from './PropertyNode.js';
class ParameterNode extends PropertyNode {
static get type() {
return 'ParameterNode';
}
constructor( nodeType, name = null ) {
super( nodeType, name );
this.isParameterNode = true;
}
getHash() {
return this.uuid;
}
generate() {
return this.name;
}
}
export default ParameterNode;
export const parameter = ( type, name ) => nodeObject( new ParameterNode( type, name ) );

View File

@@ -0,0 +1,88 @@
import Node from './Node.js';
import { nodeImmutable, nodeObject } from '../tsl/TSLCore.js';
class PropertyNode extends Node {
static get type() {
return 'PropertyNode';
}
constructor( nodeType, name = null, varying = false ) {
super( nodeType );
this.name = name;
this.varying = varying;
this.isPropertyNode = true;
}
getHash( builder ) {
return this.name || super.getHash( builder );
}
isGlobal( /*builder*/ ) {
return true;
}
generate( builder ) {
let nodeVar;
if ( this.varying === true ) {
nodeVar = builder.getVaryingFromNode( this, this.name );
nodeVar.needsInterpolation = true;
} else {
nodeVar = builder.getVarFromNode( this, this.name );
}
return builder.getPropertyName( nodeVar );
}
}
export default PropertyNode;
export const property = ( type, name ) => nodeObject( new PropertyNode( type, name ) );
export const varyingProperty = ( type, name ) => nodeObject( new PropertyNode( type, name, true ) );
export const diffuseColor = /*@__PURE__*/ nodeImmutable( PropertyNode, 'vec4', 'DiffuseColor' );
export const emissive = /*@__PURE__*/ nodeImmutable( PropertyNode, 'vec3', 'EmissiveColor' );
export const roughness = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Roughness' );
export const metalness = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Metalness' );
export const clearcoat = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Clearcoat' );
export const clearcoatRoughness = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'ClearcoatRoughness' );
export const sheen = /*@__PURE__*/ nodeImmutable( PropertyNode, 'vec3', 'Sheen' );
export const sheenRoughness = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'SheenRoughness' );
export const iridescence = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Iridescence' );
export const iridescenceIOR = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'IridescenceIOR' );
export const iridescenceThickness = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'IridescenceThickness' );
export const alphaT = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'AlphaT' );
export const anisotropy = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Anisotropy' );
export const anisotropyT = /*@__PURE__*/ nodeImmutable( PropertyNode, 'vec3', 'AnisotropyT' );
export const anisotropyB = /*@__PURE__*/ nodeImmutable( PropertyNode, 'vec3', 'AnisotropyB' );
export const specularColor = /*@__PURE__*/ nodeImmutable( PropertyNode, 'color', 'SpecularColor' );
export const specularF90 = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'SpecularF90' );
export const shininess = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Shininess' );
export const output = /*@__PURE__*/ nodeImmutable( PropertyNode, 'vec4', 'Output' );
export const dashSize = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'dashSize' );
export const gapSize = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'gapSize' );
export const pointWidth = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'pointWidth' );
export const ior = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'IOR' );
export const transmission = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Transmission' );
export const thickness = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Thickness' );
export const attenuationDistance = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'AttenuationDistance' );
export const attenuationColor = /*@__PURE__*/ nodeImmutable( PropertyNode, 'color', 'AttenuationColor' );
export const dispersion = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Dispersion' );

109
web-app/node_modules/three/src/nodes/core/StackNode.js generated vendored Normal file
View File

@@ -0,0 +1,109 @@
import Node from './Node.js';
import { select } from '../math/ConditionalNode.js';
import { ShaderNode, nodeProxy, getCurrentStack, setCurrentStack } from '../tsl/TSLBase.js';
class StackNode extends Node {
static get type() {
return 'StackNode';
}
constructor( parent = null ) {
super();
this.nodes = [];
this.outputNode = null;
this.parent = parent;
this._currentCond = null;
this.isStackNode = true;
}
getNodeType( builder ) {
return this.outputNode ? this.outputNode.getNodeType( builder ) : 'void';
}
add( node ) {
this.nodes.push( node );
return this;
}
If( boolNode, method ) {
const methodNode = new ShaderNode( method );
this._currentCond = select( boolNode, methodNode );
return this.add( this._currentCond );
}
ElseIf( boolNode, method ) {
const methodNode = new ShaderNode( method );
const ifNode = select( boolNode, methodNode );
this._currentCond.elseNode = ifNode;
this._currentCond = ifNode;
return this;
}
Else( method ) {
this._currentCond.elseNode = new ShaderNode( method );
return this;
}
build( builder, ...params ) {
const previousStack = getCurrentStack();
setCurrentStack( this );
for ( const node of this.nodes ) {
node.build( builder, 'void' );
}
setCurrentStack( previousStack );
return this.outputNode ? this.outputNode.build( builder, ...params ) : super.build( builder, ...params );
}
//
else( ...params ) { // @deprecated, r168
console.warn( 'TSL.StackNode: .else() has been renamed to .Else().' );
return this.Else( ...params );
}
elseif( ...params ) { // @deprecated, r168
console.warn( 'TSL.StackNode: .elseif() has been renamed to .ElseIf().' );
return this.ElseIf( ...params );
}
}
export default StackNode;
export const stack = /*@__PURE__*/ nodeProxy( StackNode );

View File

@@ -0,0 +1,28 @@
import Node from './Node.js';
class StructTypeNode extends Node {
static get type() {
return 'StructTypeNode';
}
constructor( types ) {
super();
this.types = types;
this.isStructTypeNode = true;
}
getMemberTypes() {
return this.types;
}
}
export default StructTypeNode;

62
web-app/node_modules/three/src/nodes/core/TempNode.js generated vendored Normal file
View File

@@ -0,0 +1,62 @@
import Node from './Node.js';
class TempNode extends Node {
static get type() {
return 'TempNode';
}
constructor( type ) {
super( type );
this.isTempNode = true;
}
hasDependencies( builder ) {
return builder.getDataFromNode( this ).usageCount > 1;
}
build( builder, output ) {
const buildStage = builder.getBuildStage();
if ( buildStage === 'generate' ) {
const type = builder.getVectorType( this.getNodeType( builder, output ) );
const nodeData = builder.getDataFromNode( this );
if ( nodeData.propertyName !== undefined ) {
return builder.format( nodeData.propertyName, type, output );
} else if ( type !== 'void' && output !== 'void' && this.hasDependencies( builder ) ) {
const snippet = super.build( builder, type );
const nodeVar = builder.getVarFromNode( this, null, type );
const propertyName = builder.getPropertyName( nodeVar );
builder.addLineFlowCode( `${propertyName} = ${snippet}`, this );
nodeData.snippet = snippet;
nodeData.propertyName = propertyName;
return builder.format( nodeData.propertyName, type, output );
}
}
return super.build( builder, output );
}
}
export default TempNode;

View File

@@ -0,0 +1,13 @@
class UniformGroup {
constructor( name ) {
this.name = name;
this.isUniformGroup = true;
}
}
export default UniformGroup;

View File

@@ -0,0 +1,59 @@
import Node from './Node.js';
class UniformGroupNode extends Node {
static get type() {
return 'UniformGroupNode';
}
constructor( name, shared = false, order = 1 ) {
super( 'string' );
this.name = name;
this.version = 0;
this.shared = shared;
this.order = order;
this.isUniformGroup = true;
}
set needsUpdate( value ) {
if ( value === true ) this.version ++;
}
serialize( data ) {
super.serialize( data );
data.name = this.name;
data.version = this.version;
data.shared = this.shared;
}
deserialize( data ) {
super.deserialize( data );
this.name = data.name;
this.version = data.version;
this.shared = data.shared;
}
}
export default UniformGroupNode;
export const uniformGroup = ( name ) => new UniformGroupNode( name );
export const sharedUniformGroup = ( name, order = 0 ) => new UniformGroupNode( name, true, order );
export const frameGroup = /*@__PURE__*/ sharedUniformGroup( 'frame' );
export const renderGroup = /*@__PURE__*/ sharedUniformGroup( 'render' );
export const objectGroup = /*@__PURE__*/ uniformGroup( 'object' );

View File

@@ -0,0 +1,112 @@
import InputNode from './InputNode.js';
import { objectGroup } from './UniformGroupNode.js';
import { nodeObject, getConstNodeType } from '../tsl/TSLCore.js';
class UniformNode extends InputNode {
static get type() {
return 'UniformNode';
}
constructor( value, nodeType = null ) {
super( value, nodeType );
this.isUniformNode = true;
this.name = '';
this.groupNode = objectGroup;
}
label( name ) {
this.name = name;
return this;
}
setGroup( group ) {
this.groupNode = group;
return this;
}
getGroup() {
return this.groupNode;
}
getUniformHash( builder ) {
return this.getHash( builder );
}
onUpdate( callback, updateType ) {
const self = this.getSelf();
callback = callback.bind( self );
return super.onUpdate( ( frame ) => {
const value = callback( frame, self );
if ( value !== undefined ) {
this.value = value;
}
}, updateType );
}
generate( builder, output ) {
const type = this.getNodeType( builder );
const hash = this.getUniformHash( builder );
let sharedNode = builder.getNodeFromHash( hash );
if ( sharedNode === undefined ) {
builder.setHashNode( this, hash );
sharedNode = this;
}
const sharedNodeType = sharedNode.getInputType( builder );
const nodeUniform = builder.getUniformFromNode( sharedNode, sharedNodeType, builder.shaderStage, this.name || builder.context.label );
const propertyName = builder.getPropertyName( nodeUniform );
if ( builder.context.label !== undefined ) delete builder.context.label;
return builder.format( propertyName, type, output );
}
}
export default UniformNode;
export const uniform = ( arg1, arg2 ) => {
const nodeType = getConstNodeType( arg2 || arg1 );
// @TODO: get ConstNode from .traverse() in the future
const value = ( arg1 && arg1.isNode === true ) ? ( arg1.node && arg1.node.value ) || arg1.value : arg1;
return nodeObject( new UniformNode( value, nodeType ) );
};

72
web-app/node_modules/three/src/nodes/core/VarNode.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
import Node from './Node.js';
import { addMethodChaining, nodeProxy } from '../tsl/TSLCore.js';
class VarNode extends Node {
static get type() {
return 'VarNode';
}
constructor( node, name = null ) {
super();
this.node = node;
this.name = name;
this.global = true;
this.isVarNode = true;
}
getHash( builder ) {
return this.name || super.getHash( builder );
}
getNodeType( builder ) {
return this.node.getNodeType( builder );
}
generate( builder ) {
const { node, name } = this;
const nodeVar = builder.getVarFromNode( this, name, builder.getVectorType( this.getNodeType( builder ) ) );
const propertyName = builder.getPropertyName( nodeVar );
const snippet = node.build( builder, nodeVar.type );
builder.addLineFlowCode( `${propertyName} = ${snippet}`, this );
return propertyName;
}
}
export default VarNode;
const createVar = /*@__PURE__*/ nodeProxy( VarNode );
addMethodChaining( 'toVar', ( ...params ) => createVar( ...params ).append() );
// Deprecated
export const temp = ( node ) => { // @deprecated, r170
console.warn( 'TSL: "temp" is deprecated. Use ".toVar()" instead.' );
return createVar( node );
};
addMethodChaining( 'temp', temp );

View File

@@ -0,0 +1,108 @@
import Node from './Node.js';
import { NodeShaderStage } from './constants.js';
import { addMethodChaining, nodeProxy } from '../tsl/TSLCore.js';
class VaryingNode extends Node {
static get type() {
return 'VaryingNode';
}
constructor( node, name = null ) {
super();
this.node = node;
this.name = name;
this.isVaryingNode = true;
}
isGlobal() {
return true;
}
getHash( builder ) {
return this.name || super.getHash( builder );
}
getNodeType( builder ) {
// VaryingNode is auto type
return this.node.getNodeType( builder );
}
setupVarying( builder ) {
const properties = builder.getNodeProperties( this );
let varying = properties.varying;
if ( varying === undefined ) {
const name = this.name;
const type = this.getNodeType( builder );
properties.varying = varying = builder.getVaryingFromNode( this, name, type );
properties.node = this.node;
}
// this property can be used to check if the varying can be optimized for a variable
varying.needsInterpolation || ( varying.needsInterpolation = ( builder.shaderStage === 'fragment' ) );
return varying;
}
setup( builder ) {
this.setupVarying( builder );
}
analyze( builder ) {
this.setupVarying( builder );
return this.node.analyze( builder );
}
generate( builder ) {
const properties = builder.getNodeProperties( this );
const varying = this.setupVarying( builder );
if ( properties.propertyName === undefined ) {
const type = this.getNodeType( builder );
const propertyName = builder.getPropertyName( varying, NodeShaderStage.VERTEX );
// force node run in vertex stage
builder.flowNodeFromShaderStage( NodeShaderStage.VERTEX, this.node, type, propertyName );
properties.propertyName = propertyName;
}
return builder.getPropertyName( varying );
}
}
export default VaryingNode;
export const varying = /*@__PURE__*/ nodeProxy( VaryingNode );
addMethodChaining( 'varying', varying );

28
web-app/node_modules/three/src/nodes/core/constants.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
export const NodeShaderStage = {
VERTEX: 'vertex',
FRAGMENT: 'fragment'
};
export const NodeUpdateType = {
NONE: 'none',
FRAME: 'frame',
RENDER: 'render',
OBJECT: 'object'
};
export const NodeType = {
BOOLEAN: 'bool',
INTEGER: 'int',
FLOAT: 'float',
VECTOR2: 'vec2',
VECTOR3: 'vec3',
VECTOR4: 'vec4',
MATRIX2: 'mat2',
MATRIX3: 'mat3',
MATRIX4: 'mat4'
};
export const defaultShaderStages = [ 'fragment', 'vertex' ];
export const defaultBuildStages = [ 'setup', 'analyze', 'generate' ];
export const shaderStages = [ ...defaultShaderStages, 'compute' ];
export const vectorComponents = [ 'x', 'y', 'z', 'w' ];

View File

@@ -0,0 +1,54 @@
import { Fn } from '../tsl/TSLBase.js';
import { mix, min, step } from '../math/MathNode.js';
export const burn = /*@__PURE__*/ Fn( ( [ base, blend ] ) => {
return min( 1.0, base.oneMinus().div( blend ) ).oneMinus();
} ).setLayout( {
name: 'burnBlend',
type: 'vec3',
inputs: [
{ name: 'base', type: 'vec3' },
{ name: 'blend', type: 'vec3' }
]
} );
export const dodge = /*@__PURE__*/ Fn( ( [ base, blend ] ) => {
return min( base.div( blend.oneMinus() ), 1.0 );
} ).setLayout( {
name: 'dodgeBlend',
type: 'vec3',
inputs: [
{ name: 'base', type: 'vec3' },
{ name: 'blend', type: 'vec3' }
]
} );
export const screen = /*@__PURE__*/ Fn( ( [ base, blend ] ) => {
return base.oneMinus().mul( blend.oneMinus() ).oneMinus();
} ).setLayout( {
name: 'screenBlend',
type: 'vec3',
inputs: [
{ name: 'base', type: 'vec3' },
{ name: 'blend', type: 'vec3' }
]
} );
export const overlay = /*@__PURE__*/ Fn( ( [ base, blend ] ) => {
return mix( base.mul( 2.0 ).mul( blend ), base.oneMinus().mul( 2.0 ).mul( blend.oneMinus() ).oneMinus(), step( 0.5, base ) );
} ).setLayout( {
name: 'overlayBlend',
type: 'vec3',
inputs: [
{ name: 'base', type: 'vec3' },
{ name: 'blend', type: 'vec3' }
]
} );

View File

@@ -0,0 +1,81 @@
import TempNode from '../core/TempNode.js';
import { uv } from '../accessors/UV.js';
import { normalView } from '../accessors/Normal.js';
import { positionView } from '../accessors/Position.js';
import { faceDirection } from './FrontFacingNode.js';
import { Fn, nodeProxy, float, vec2 } from '../tsl/TSLBase.js';
// Bump Mapping Unparametrized Surfaces on the GPU by Morten S. Mikkelsen
// https://mmikk.github.io/papers3d/mm_sfgrad_bump.pdf
const dHdxy_fwd = Fn( ( { textureNode, bumpScale } ) => {
// It's used to preserve the same TextureNode instance
const sampleTexture = ( callback ) => textureNode.cache().context( { getUV: ( texNode ) => callback( texNode.uvNode || uv() ), forceUVContext: true } );
const Hll = float( sampleTexture( ( uvNode ) => uvNode ) );
return vec2(
float( sampleTexture( ( uvNode ) => uvNode.add( uvNode.dFdx() ) ) ).sub( Hll ),
float( sampleTexture( ( uvNode ) => uvNode.add( uvNode.dFdy() ) ) ).sub( Hll )
).mul( bumpScale );
} );
// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)
const perturbNormalArb = Fn( ( inputs ) => {
const { surf_pos, surf_norm, dHdxy } = inputs;
// normalize is done to ensure that the bump map looks the same regardless of the texture's scale
const vSigmaX = surf_pos.dFdx().normalize();
const vSigmaY = surf_pos.dFdy().normalize();
const vN = surf_norm; // normalized
const R1 = vSigmaY.cross( vN );
const R2 = vN.cross( vSigmaX );
const fDet = vSigmaX.dot( R1 ).mul( faceDirection );
const vGrad = fDet.sign().mul( dHdxy.x.mul( R1 ).add( dHdxy.y.mul( R2 ) ) );
return fDet.abs().mul( surf_norm ).sub( vGrad ).normalize();
} );
class BumpMapNode extends TempNode {
static get type() {
return 'BumpMapNode';
}
constructor( textureNode, scaleNode = null ) {
super( 'vec3' );
this.textureNode = textureNode;
this.scaleNode = scaleNode;
}
setup() {
const bumpScale = this.scaleNode !== null ? this.scaleNode : 1;
const dHdxy = dHdxy_fwd( { textureNode: this.textureNode, bumpScale } );
return perturbNormalArb( {
surf_pos: positionView,
surf_norm: normalView,
dHdxy
} );
}
}
export default BumpMapNode;
export const bumpMap = /*@__PURE__*/ nodeProxy( BumpMapNode );

View File

@@ -0,0 +1,95 @@
import { dot, max, mix } from '../math/MathNode.js';
import { add } from '../math/OperatorNode.js';
import { Fn, If, float, vec3, vec4 } from '../tsl/TSLBase.js';
import { ColorManagement } from '../../math/ColorManagement.js';
import { Vector3 } from '../../math/Vector3.js';
import { LinearSRGBColorSpace } from '../../constants.js';
export const grayscale = /*@__PURE__*/ Fn( ( [ color ] ) => {
return luminance( color.rgb );
} );
export const saturation = /*@__PURE__*/ Fn( ( [ color, adjustment = float( 1 ) ] ) => {
return adjustment.mix( luminance( color.rgb ), color.rgb );
} );
export const vibrance = /*@__PURE__*/ Fn( ( [ color, adjustment = float( 1 ) ] ) => {
const average = add( color.r, color.g, color.b ).div( 3.0 );
const mx = color.r.max( color.g.max( color.b ) );
const amt = mx.sub( average ).mul( adjustment ).mul( - 3.0 );
return mix( color.rgb, mx, amt );
} );
export const hue = /*@__PURE__*/ Fn( ( [ color, adjustment = float( 1 ) ] ) => {
const k = vec3( 0.57735, 0.57735, 0.57735 );
const cosAngle = adjustment.cos();
return vec3( color.rgb.mul( cosAngle ).add( k.cross( color.rgb ).mul( adjustment.sin() ).add( k.mul( dot( k, color.rgb ).mul( cosAngle.oneMinus() ) ) ) ) );
} );
export const luminance = (
color,
luminanceCoefficients = vec3( ColorManagement.getLuminanceCoefficients( new Vector3() ) )
) => dot( color, luminanceCoefficients );
export const threshold = ( color, threshold ) => mix( vec3( 0.0 ), color, luminance( color ).sub( threshold ).max( 0 ) );
/**
* Color Decision List (CDL) v1.2
*
* Compact representation of color grading information, defined by slope, offset, power, and
* saturation. The CDL should be typically be given input in a log space (such as LogC, ACEScc,
* or AgX Log), and will return output in the same space. Output may require clamping >=0.
*
* @param {vec4} color Input (-Infinity < input < +Infinity)
* @param {number | vec3} slope Slope (0 ≤ slope < +Infinity)
* @param {number | vec3} offset Offset (-Infinity < offset < +Infinity; typically -1 < offset < 1)
* @param {number | vec3} power Power (0 < power < +Infinity)
* @param {number} saturation Saturation (0 ≤ saturation < +Infinity; typically 0 ≤ saturation < 4)
* @param {vec3} luminanceCoefficients Luminance coefficients for saturation term, typically Rec. 709
* @return Output, -Infinity < output < +Infinity
*
* References:
* - ASC CDL v1.2
* - https://blender.stackexchange.com/a/55239/43930
* - https://docs.acescentral.com/specifications/acescc/
*/
export const cdl = /*@__PURE__*/ Fn( ( [
color,
slope = vec3( 1 ),
offset = vec3( 0 ),
power = vec3( 1 ),
saturation = float( 1 ),
// ASC CDL v1.2 explicitly requires Rec. 709 luminance coefficients.
luminanceCoefficients = vec3( ColorManagement.getLuminanceCoefficients( new Vector3(), LinearSRGBColorSpace ) )
] ) => {
// NOTE: The ASC CDL v1.2 defines a [0, 1] clamp on the slope+offset term, and another on the
// saturation term. Per the ACEScc specification and Filament, limits may be omitted to support
// values outside [0, 1], requiring a workaround for negative values in the power expression.
const luma = color.rgb.dot( vec3( luminanceCoefficients ) );
const v = max( color.rgb.mul( slope ).add( offset ), 0.0 ).toVar();
const pv = v.pow( power ).toVar();
If( v.r.greaterThan( 0.0 ), () => { v.r.assign( pv.r ); } ); // eslint-disable-line
If( v.g.greaterThan( 0.0 ), () => { v.g.assign( pv.g ); } ); // eslint-disable-line
If( v.b.greaterThan( 0.0 ), () => { v.b.assign( pv.b ); } ); // eslint-disable-line
v.assign( luma.add( v.sub( luma ).mul( saturation ) ) );
return vec4( v.rgb, color.a );
} );

View File

@@ -0,0 +1,38 @@
import { mix } from '../math/MathNode.js';
import { Fn } from '../tsl/TSLCore.js';
export const sRGBTransferEOTF = /*@__PURE__*/ Fn( ( [ color ] ) => {
const a = color.mul( 0.9478672986 ).add( 0.0521327014 ).pow( 2.4 );
const b = color.mul( 0.0773993808 );
const factor = color.lessThanEqual( 0.04045 );
const rgbResult = mix( a, b, factor );
return rgbResult;
} ).setLayout( {
name: 'sRGBTransferEOTF',
type: 'vec3',
inputs: [
{ name: 'color', type: 'vec3' }
]
} );
export const sRGBTransferOETF = /*@__PURE__*/ Fn( ( [ color ] ) => {
const a = color.pow( 0.41666 ).mul( 1.055 ).sub( 0.055 );
const b = color.mul( 12.92 );
const factor = color.lessThanEqual( 0.0031308 );
const rgbResult = mix( a, b, factor );
return rgbResult;
} ).setLayout( {
name: 'sRGBTransferOETF',
type: 'vec3',
inputs: [
{ name: 'color', type: 'vec3' }
]
} );

View File

@@ -0,0 +1,102 @@
import TempNode from '../core/TempNode.js';
import { addMethodChaining, mat3, nodeObject, vec4 } from '../tsl/TSLCore.js';
import { SRGBTransfer } from '../../constants.js';
import { ColorManagement } from '../../math/ColorManagement.js';
import { sRGBTransferEOTF, sRGBTransferOETF } from './ColorSpaceFunctions.js';
import { Matrix3 } from '../../math/Matrix3.js';
const WORKING_COLOR_SPACE = 'WorkingColorSpace';
const OUTPUT_COLOR_SPACE = 'OutputColorSpace';
class ColorSpaceNode extends TempNode {
static get type() {
return 'ColorSpaceNode';
}
constructor( colorNode, source, target ) {
super( 'vec4' );
this.colorNode = colorNode;
this.source = source;
this.target = target;
}
resolveColorSpace( builder, colorSpace ) {
if ( colorSpace === WORKING_COLOR_SPACE ) {
return ColorManagement.workingColorSpace;
} else if ( colorSpace === OUTPUT_COLOR_SPACE ) {
return builder.context.outputColorSpace || builder.renderer.outputColorSpace;
}
return colorSpace;
}
setup( builder ) {
const { colorNode } = this;
const source = this.resolveColorSpace( builder, this.source );
const target = this.resolveColorSpace( builder, this.target );
let outputNode = colorNode;
if ( ColorManagement.enabled === false || source === target || ! source || ! target ) {
return outputNode;
}
if ( ColorManagement.getTransfer( source ) === SRGBTransfer ) {
outputNode = vec4( sRGBTransferEOTF( outputNode.rgb ), outputNode.a );
}
if ( ColorManagement.getPrimaries( source ) !== ColorManagement.getPrimaries( target ) ) {
outputNode = vec4(
mat3( ColorManagement._getMatrix( new Matrix3(), source, target ) ).mul( outputNode.rgb ),
outputNode.a
);
}
if ( ColorManagement.getTransfer( target ) === SRGBTransfer ) {
outputNode = vec4( sRGBTransferOETF( outputNode.rgb ), outputNode.a );
}
return outputNode;
}
}
export default ColorSpaceNode;
export const toOutputColorSpace = ( node ) => nodeObject( new ColorSpaceNode( nodeObject( node ), WORKING_COLOR_SPACE, OUTPUT_COLOR_SPACE ) );
export const toWorkingColorSpace = ( node ) => nodeObject( new ColorSpaceNode( nodeObject( node ), OUTPUT_COLOR_SPACE, WORKING_COLOR_SPACE ) );
export const workingToColorSpace = ( node, colorSpace ) => nodeObject( new ColorSpaceNode( nodeObject( node ), WORKING_COLOR_SPACE, colorSpace ) );
export const colorSpaceToWorking = ( node, colorSpace ) => nodeObject( new ColorSpaceNode( nodeObject( node ), colorSpace, WORKING_COLOR_SPACE ) );
export const convertColorSpace = ( node, sourceColorSpace, targetColorSpace ) => nodeObject( new ColorSpaceNode( nodeObject( node ), sourceColorSpace, targetColorSpace ) );
addMethodChaining( 'toOutputColorSpace', toOutputColorSpace );
addMethodChaining( 'toWorkingColorSpace', toWorkingColorSpace );
addMethodChaining( 'workingToColorSpace', workingToColorSpace );
addMethodChaining( 'colorSpaceToWorking', colorSpaceToWorking );

View File

@@ -0,0 +1,45 @@
import Node from '../core/Node.js';
import { nodeImmutable, float } from '../tsl/TSLBase.js';
import { BackSide, WebGLCoordinateSystem } from '../../constants.js';
class FrontFacingNode extends Node {
static get type() {
return 'FrontFacingNode';
}
constructor() {
super( 'bool' );
this.isFrontFacingNode = true;
}
generate( builder ) {
const { renderer, material } = builder;
if ( renderer.coordinateSystem === WebGLCoordinateSystem ) {
if ( material.side === BackSide ) {
return 'false';
}
}
return builder.getFrontFacing();
}
}
export default FrontFacingNode;
export const frontFacing = /*@__PURE__*/ nodeImmutable( FrontFacingNode );
export const faceDirection = /*@__PURE__*/ float( frontFacing ).mul( 2.0 ).sub( 1.0 );

View File

@@ -0,0 +1,106 @@
import TempNode from '../core/TempNode.js';
import { add } from '../math/OperatorNode.js';
import { normalView, transformNormalToView } from '../accessors/Normal.js';
import { positionView } from '../accessors/Position.js';
import { TBNViewMatrix } from '../accessors/AccessorsUtils.js';
import { uv } from '../accessors/UV.js';
import { faceDirection } from './FrontFacingNode.js';
import { Fn, nodeProxy, vec3 } from '../tsl/TSLBase.js';
import { TangentSpaceNormalMap, ObjectSpaceNormalMap } from '../../constants.js';
// Normal Mapping Without Precomputed Tangents
// http://www.thetenthplanet.de/archives/1180
const perturbNormal2Arb = /*@__PURE__*/ Fn( ( inputs ) => {
const { eye_pos, surf_norm, mapN, uv } = inputs;
const q0 = eye_pos.dFdx();
const q1 = eye_pos.dFdy();
const st0 = uv.dFdx();
const st1 = uv.dFdy();
const N = surf_norm; // normalized
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 = faceDirection.mul( det.inverseSqrt() );
return add( T.mul( mapN.x, scale ), B.mul( mapN.y, scale ), N.mul( mapN.z ) ).normalize();
} );
class NormalMapNode extends TempNode {
static get type() {
return 'NormalMapNode';
}
constructor( node, scaleNode = null ) {
super( 'vec3' );
this.node = node;
this.scaleNode = scaleNode;
this.normalMapType = TangentSpaceNormalMap;
}
setup( builder ) {
const { normalMapType, scaleNode } = this;
let normalMap = this.node.mul( 2.0 ).sub( 1.0 );
if ( scaleNode !== null ) {
normalMap = vec3( normalMap.xy.mul( scaleNode ), normalMap.z );
}
let outputNode = null;
if ( normalMapType === ObjectSpaceNormalMap ) {
outputNode = transformNormalToView( normalMap );
} else if ( normalMapType === TangentSpaceNormalMap ) {
const tangent = builder.hasGeometryAttribute( 'tangent' );
if ( tangent === true ) {
outputNode = TBNViewMatrix.mul( normalMap ).normalize();
} else {
outputNode = perturbNormal2Arb( {
eye_pos: positionView,
surf_norm: normalView,
mapN: normalMap,
uv: uv()
} );
}
}
return outputNode;
}
}
export default NormalMapNode;
export const normalMap = /*@__PURE__*/ nodeProxy( NormalMapNode );

View File

@@ -0,0 +1,380 @@
import TempNode from '../core/TempNode.js';
import { default as TextureNode/*, texture*/ } from '../accessors/TextureNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { nodeObject } from '../tsl/TSLBase.js';
import { uniform } from '../core/UniformNode.js';
import { viewZToOrthographicDepth, perspectiveDepthToViewZ } from './ViewportDepthNode.js';
import { HalfFloatType/*, FloatType*/ } from '../../constants.js';
import { Vector2 } from '../../math/Vector2.js';
import { DepthTexture } from '../../textures/DepthTexture.js';
import { RenderTarget } from '../../core/RenderTarget.js';
const _size = /*@__PURE__*/ new Vector2();
class PassTextureNode extends TextureNode {
static get type() {
return 'PassTextureNode';
}
constructor( passNode, texture ) {
super( texture );
this.passNode = passNode;
this.setUpdateMatrix( false );
}
setup( builder ) {
if ( builder.object.isQuadMesh ) this.passNode.build( builder );
return super.setup( builder );
}
clone() {
return new this.constructor( this.passNode, this.value );
}
}
class PassMultipleTextureNode extends PassTextureNode {
static get type() {
return 'PassMultipleTextureNode';
}
constructor( passNode, textureName, previousTexture = false ) {
super( passNode, null );
this.textureName = textureName;
this.previousTexture = previousTexture;
}
updateTexture() {
this.value = this.previousTexture ? this.passNode.getPreviousTexture( this.textureName ) : this.passNode.getTexture( this.textureName );
}
setup( builder ) {
this.updateTexture();
return super.setup( builder );
}
clone() {
return new this.constructor( this.passNode, this.textureName, this.previousTexture );
}
}
class PassNode extends TempNode {
static get type() {
return 'PassNode';
}
constructor( scope, scene, camera, options = {} ) {
super( 'vec4' );
this.scope = scope;
this.scene = scene;
this.camera = camera;
this.options = options;
this._pixelRatio = 1;
this._width = 1;
this._height = 1;
const depthTexture = new DepthTexture();
depthTexture.isRenderTargetTexture = true;
//depthTexture.type = FloatType;
depthTexture.name = 'depth';
const renderTarget = new RenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, { type: HalfFloatType, ...options, } );
renderTarget.texture.name = 'output';
renderTarget.depthTexture = depthTexture;
this.renderTarget = renderTarget;
this.updateBeforeType = NodeUpdateType.FRAME;
this._textures = {
output: renderTarget.texture,
depth: depthTexture
};
this._textureNodes = {};
this._linearDepthNodes = {};
this._viewZNodes = {};
this._previousTextures = {};
this._previousTextureNodes = {};
this._cameraNear = uniform( 0 );
this._cameraFar = uniform( 0 );
this._mrt = null;
this.isPassNode = true;
}
setMRT( mrt ) {
this._mrt = mrt;
return this;
}
getMRT() {
return this._mrt;
}
isGlobal() {
return true;
}
getTexture( name ) {
let texture = this._textures[ name ];
if ( texture === undefined ) {
const refTexture = this.renderTarget.texture;
texture = refTexture.clone();
texture.isRenderTargetTexture = true;
texture.name = name;
this._textures[ name ] = texture;
this.renderTarget.textures.push( texture );
}
return texture;
}
getPreviousTexture( name ) {
let texture = this._previousTextures[ name ];
if ( texture === undefined ) {
texture = this.getTexture( name ).clone();
texture.isRenderTargetTexture = true;
this._previousTextures[ name ] = texture;
}
return texture;
}
toggleTexture( name ) {
const prevTexture = this._previousTextures[ name ];
if ( prevTexture !== undefined ) {
const texture = this._textures[ name ];
const index = this.renderTarget.textures.indexOf( texture );
this.renderTarget.textures[ index ] = prevTexture;
this._textures[ name ] = prevTexture;
this._previousTextures[ name ] = texture;
this._textureNodes[ name ].updateTexture();
this._previousTextureNodes[ name ].updateTexture();
}
}
getTextureNode( name = 'output' ) {
let textureNode = this._textureNodes[ name ];
if ( textureNode === undefined ) {
textureNode = nodeObject( new PassMultipleTextureNode( this, name ) );
textureNode.updateTexture();
this._textureNodes[ name ] = textureNode;
}
return textureNode;
}
getPreviousTextureNode( name = 'output' ) {
let textureNode = this._previousTextureNodes[ name ];
if ( textureNode === undefined ) {
if ( this._textureNodes[ name ] === undefined ) this.getTextureNode( name );
textureNode = nodeObject( new PassMultipleTextureNode( this, name, true ) );
textureNode.updateTexture();
this._previousTextureNodes[ name ] = textureNode;
}
return textureNode;
}
getViewZNode( name = 'depth' ) {
let viewZNode = this._viewZNodes[ name ];
if ( viewZNode === undefined ) {
const cameraNear = this._cameraNear;
const cameraFar = this._cameraFar;
this._viewZNodes[ name ] = viewZNode = perspectiveDepthToViewZ( this.getTextureNode( name ), cameraNear, cameraFar );
}
return viewZNode;
}
getLinearDepthNode( name = 'depth' ) {
let linearDepthNode = this._linearDepthNodes[ name ];
if ( linearDepthNode === undefined ) {
const cameraNear = this._cameraNear;
const cameraFar = this._cameraFar;
const viewZNode = this.getViewZNode( name );
// TODO: just if ( builder.camera.isPerspectiveCamera )
this._linearDepthNodes[ name ] = linearDepthNode = viewZToOrthographicDepth( viewZNode, cameraNear, cameraFar );
}
return linearDepthNode;
}
setup( { renderer } ) {
this.renderTarget.samples = this.options.samples === undefined ? renderer.samples : this.options.samples;
// Disable MSAA for WebGL backend for now
if ( renderer.backend.isWebGLBackend === true ) {
this.renderTarget.samples = 0;
}
this.renderTarget.depthTexture.isMultisampleRenderTargetTexture = this.renderTarget.samples > 1;
return this.scope === PassNode.COLOR ? this.getTextureNode() : this.getLinearDepthNode();
}
updateBefore( frame ) {
const { renderer } = frame;
const { scene, camera } = this;
this._pixelRatio = renderer.getPixelRatio();
const size = renderer.getSize( _size );
this.setSize( size.width, size.height );
const currentRenderTarget = renderer.getRenderTarget();
const currentMRT = renderer.getMRT();
this._cameraNear.value = camera.near;
this._cameraFar.value = camera.far;
for ( const name in this._previousTextures ) {
this.toggleTexture( name );
}
renderer.setRenderTarget( this.renderTarget );
renderer.setMRT( this._mrt );
renderer.render( scene, camera );
renderer.setRenderTarget( currentRenderTarget );
renderer.setMRT( currentMRT );
}
setSize( width, height ) {
this._width = width;
this._height = height;
const effectiveWidth = this._width * this._pixelRatio;
const effectiveHeight = this._height * this._pixelRatio;
this.renderTarget.setSize( effectiveWidth, effectiveHeight );
}
setPixelRatio( pixelRatio ) {
this._pixelRatio = pixelRatio;
this.setSize( this._width, this._height );
}
dispose() {
this.renderTarget.dispose();
}
}
PassNode.COLOR = 'color';
PassNode.DEPTH = 'depth';
export default PassNode;
export const pass = ( scene, camera, options ) => nodeObject( new PassNode( PassNode.COLOR, scene, camera, options ) );
export const passTexture = ( pass, texture ) => nodeObject( new PassTextureNode( pass, texture ) );
export const depthPass = ( scene, camera ) => nodeObject( new PassNode( PassNode.DEPTH, scene, camera ) );

View File

@@ -0,0 +1,33 @@
import TempNode from '../core/TempNode.js';
import { nodeProxy } from '../tsl/TSLBase.js';
class PosterizeNode extends TempNode {
static get type() {
return 'PosterizeNode';
}
constructor( sourceNode, stepsNode ) {
super();
this.sourceNode = sourceNode;
this.stepsNode = stepsNode;
}
setup() {
const { sourceNode, stepsNode } = this;
return sourceNode.mul( stepsNode ).floor().div( stepsNode );
}
}
export default PosterizeNode;
export const posterize = /*@__PURE__*/ nodeProxy( PosterizeNode );

View File

@@ -0,0 +1,60 @@
import TempNode from '../core/TempNode.js';
import { addMethodChaining, nodeObject } from '../tsl/TSLCore.js';
import { NoColorSpace, NoToneMapping } from '../../constants.js';
import { ColorManagement } from '../../math/ColorManagement.js';
class RenderOutputNode extends TempNode {
static get type() {
return 'RenderOutputNode';
}
constructor( colorNode, toneMapping, outputColorSpace ) {
super( 'vec4' );
this.colorNode = colorNode;
this.toneMapping = toneMapping;
this.outputColorSpace = outputColorSpace;
this.isRenderOutput = true;
}
setup( { context } ) {
let outputNode = this.colorNode || context.color;
// tone mapping
const toneMapping = ( this.toneMapping !== null ? this.toneMapping : context.toneMapping ) || NoToneMapping;
const outputColorSpace = ( this.outputColorSpace !== null ? this.outputColorSpace : context.outputColorSpace ) || NoColorSpace;
if ( toneMapping !== NoToneMapping ) {
outputNode = outputNode.toneMapping( toneMapping );
}
// working to output color space
if ( outputColorSpace !== NoColorSpace && outputColorSpace !== ColorManagement.workingColorSpace ) {
outputNode = outputNode.workingToColorSpace( outputColorSpace );
}
return outputNode;
}
}
export default RenderOutputNode;
export const renderOutput = ( color, toneMapping = null, outputColorSpace = null ) => nodeObject( new RenderOutputNode( nodeObject( color ), toneMapping, outputColorSpace ) );
addMethodChaining( 'renderOutput', renderOutput );

View File

@@ -0,0 +1,181 @@
import Node from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { uniform } from '../core/UniformNode.js';
import { Fn, nodeImmutable, vec2 } from '../tsl/TSLBase.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector4 } from '../../math/Vector4.js';
let screenSizeVec, viewportVec;
class ScreenNode extends Node {
static get type() {
return 'ScreenNode';
}
constructor( scope ) {
super();
this.scope = scope;
this.isViewportNode = true;
}
getNodeType() {
if ( this.scope === ScreenNode.VIEWPORT ) return 'vec4';
else return 'vec2';
}
getUpdateType() {
let updateType = NodeUpdateType.NONE;
if ( this.scope === ScreenNode.SIZE || this.scope === ScreenNode.VIEWPORT ) {
updateType = NodeUpdateType.RENDER;
}
this.updateType = updateType;
return updateType;
}
update( { renderer } ) {
const renderTarget = renderer.getRenderTarget();
if ( this.scope === ScreenNode.VIEWPORT ) {
if ( renderTarget !== null ) {
viewportVec.copy( renderTarget.viewport );
} else {
renderer.getViewport( viewportVec );
viewportVec.multiplyScalar( renderer.getPixelRatio() );
}
} else {
if ( renderTarget !== null ) {
screenSizeVec.width = renderTarget.width;
screenSizeVec.height = renderTarget.height;
} else {
renderer.getDrawingBufferSize( screenSizeVec );
}
}
}
setup( /*builder*/ ) {
const scope = this.scope;
let output = null;
if ( scope === ScreenNode.SIZE ) {
output = uniform( screenSizeVec || ( screenSizeVec = new Vector2() ) );
} else if ( scope === ScreenNode.VIEWPORT ) {
output = uniform( viewportVec || ( viewportVec = new Vector4() ) );
} else {
output = vec2( screenCoordinate.div( screenSize ) );
}
return output;
}
generate( builder ) {
if ( this.scope === ScreenNode.COORDINATE ) {
let coord = builder.getFragCoord();
if ( builder.isFlipY() ) {
// follow webgpu standards
const size = builder.getNodeProperties( screenSize ).outputNode.build( builder );
coord = `${ builder.getType( 'vec2' ) }( ${ coord }.x, ${ size }.y - ${ coord }.y )`;
}
return coord;
}
return super.generate( builder );
}
}
ScreenNode.COORDINATE = 'coordinate';
ScreenNode.VIEWPORT = 'viewport';
ScreenNode.SIZE = 'size';
ScreenNode.UV = 'uv';
export default ScreenNode;
// Screen
export const screenUV = /*@__PURE__*/ nodeImmutable( ScreenNode, ScreenNode.UV );
export const screenSize = /*@__PURE__*/ nodeImmutable( ScreenNode, ScreenNode.SIZE );
export const screenCoordinate = /*@__PURE__*/ nodeImmutable( ScreenNode, ScreenNode.COORDINATE );
// Viewport
export const viewport = /*@__PURE__*/ nodeImmutable( ScreenNode, ScreenNode.VIEWPORT );
export const viewportSize = viewport.zw;
export const viewportCoordinate = /*@__PURE__*/ screenCoordinate.sub( viewport.xy );
export const viewportUV = /*@__PURE__*/ viewportCoordinate.div( viewportSize );
// Deprecated
export const viewportResolution = /*@__PURE__*/ ( Fn( () => { // @deprecated, r169
console.warn( 'TSL.ViewportNode: "viewportResolution" is deprecated. Use "screenSize" instead.' );
return screenSize;
}, 'vec2' ).once() )();
export const viewportTopLeft = /*@__PURE__*/ ( Fn( () => { // @deprecated, r168
console.warn( 'TSL.ViewportNode: "viewportTopLeft" is deprecated. Use "screenUV" instead.' );
return screenUV;
}, 'vec2' ).once() )();
export const viewportBottomLeft = /*@__PURE__*/ ( Fn( () => { // @deprecated, r168
console.warn( 'TSL.ViewportNode: "viewportBottomLeft" is deprecated. Use "screenUV.flipY()" instead.' );
return screenUV.flipY();
}, 'vec2' ).once() )();

View File

@@ -0,0 +1,190 @@
import { Fn, float, mat3, vec3, If } from '../tsl/TSLBase.js';
import { select } from '../math/ConditionalNode.js';
import { clamp, log2, max, min, pow, mix } from '../math/MathNode.js';
import { mul, sub, div } from '../math/OperatorNode.js';
// exposure only
export const linearToneMapping = /*@__PURE__*/ Fn( ( [ color, exposure ] ) => {
return color.mul( exposure ).clamp();
} ).setLayout( {
name: 'linearToneMapping',
type: 'vec3',
inputs: [
{ name: 'color', type: 'vec3' },
{ name: 'exposure', type: 'float' }
]
} );
// source: https://www.cs.utah.edu/docs/techreports/2002/pdf/UUCS-02-001.pdf
export const reinhardToneMapping = /*@__PURE__*/ Fn( ( [ color, exposure ] ) => {
color = color.mul( exposure );
return color.div( color.add( 1.0 ) ).clamp();
} ).setLayout( {
name: 'reinhardToneMapping',
type: 'vec3',
inputs: [
{ name: 'color', type: 'vec3' },
{ name: 'exposure', type: 'float' }
]
} );
// source: http://filmicworlds.com/blog/filmic-tonemapping-operators/
export const cineonToneMapping = /*@__PURE__*/ Fn( ( [ color, exposure ] ) => {
// filmic operator by Jim Hejl and Richard Burgess-Dawson
color = color.mul( exposure );
color = color.sub( 0.004 ).max( 0.0 );
const a = color.mul( color.mul( 6.2 ).add( 0.5 ) );
const b = color.mul( color.mul( 6.2 ).add( 1.7 ) ).add( 0.06 );
return a.div( b ).pow( 2.2 );
} ).setLayout( {
name: 'cineonToneMapping',
type: 'vec3',
inputs: [
{ name: 'color', type: 'vec3' },
{ name: 'exposure', type: 'float' }
]
} );
// source: https://github.com/selfshadow/ltc_code/blob/master/webgl/shaders/ltc/ltc_blit.fs
const RRTAndODTFit = /*@__PURE__*/ Fn( ( [ color ] ) => {
const a = color.mul( color.add( 0.0245786 ) ).sub( 0.000090537 );
const b = color.mul( color.add( 0.4329510 ).mul( 0.983729 ) ).add( 0.238081 );
return a.div( b );
} );
// source: https://github.com/selfshadow/ltc_code/blob/master/webgl/shaders/ltc/ltc_blit.fs
export const acesFilmicToneMapping = /*@__PURE__*/ Fn( ( [ color, exposure ] ) => {
// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
const ACESInputMat = mat3(
0.59719, 0.35458, 0.04823,
0.07600, 0.90834, 0.01566,
0.02840, 0.13383, 0.83777
);
// ODT_SAT => XYZ => D60_2_D65 => sRGB
const ACESOutputMat = mat3(
1.60475, - 0.53108, - 0.07367,
- 0.10208, 1.10813, - 0.00605,
- 0.00327, - 0.07276, 1.07602
);
color = color.mul( exposure ).div( 0.6 );
color = ACESInputMat.mul( color );
// Apply RRT and ODT
color = RRTAndODTFit( color );
color = ACESOutputMat.mul( color );
// Clamp to [0, 1]
return color.clamp();
} ).setLayout( {
name: 'acesFilmicToneMapping',
type: 'vec3',
inputs: [
{ name: 'color', type: 'vec3' },
{ name: 'exposure', type: 'float' }
]
} );
const LINEAR_REC2020_TO_LINEAR_SRGB = /*@__PURE__*/ mat3( vec3( 1.6605, - 0.1246, - 0.0182 ), vec3( - 0.5876, 1.1329, - 0.1006 ), vec3( - 0.0728, - 0.0083, 1.1187 ) );
const LINEAR_SRGB_TO_LINEAR_REC2020 = /*@__PURE__*/ mat3( vec3( 0.6274, 0.0691, 0.0164 ), vec3( 0.3293, 0.9195, 0.0880 ), vec3( 0.0433, 0.0113, 0.8956 ) );
const agxDefaultContrastApprox = /*@__PURE__*/ Fn( ( [ x_immutable ] ) => {
const x = vec3( x_immutable ).toVar();
const x2 = vec3( x.mul( x ) ).toVar();
const x4 = vec3( x2.mul( x2 ) ).toVar();
return float( 15.5 ).mul( x4.mul( x2 ) ).sub( mul( 40.14, x4.mul( x ) ) ).add( mul( 31.96, x4 ).sub( mul( 6.868, x2.mul( x ) ) ).add( mul( 0.4298, x2 ).add( mul( 0.1191, x ).sub( 0.00232 ) ) ) );
} );
export const agxToneMapping = /*@__PURE__*/ Fn( ( [ color, exposure ] ) => {
const colortone = vec3( color ).toVar();
const AgXInsetMatrix = mat3( vec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ), vec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ), vec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 ) );
const AgXOutsetMatrix = mat3( vec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ), vec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ), vec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 ) );
const AgxMinEv = float( - 12.47393 );
const AgxMaxEv = float( 4.026069 );
colortone.mulAssign( exposure );
colortone.assign( LINEAR_SRGB_TO_LINEAR_REC2020.mul( colortone ) );
colortone.assign( AgXInsetMatrix.mul( colortone ) );
colortone.assign( max( colortone, 1e-10 ) );
colortone.assign( log2( colortone ) );
colortone.assign( colortone.sub( AgxMinEv ).div( AgxMaxEv.sub( AgxMinEv ) ) );
colortone.assign( clamp( colortone, 0.0, 1.0 ) );
colortone.assign( agxDefaultContrastApprox( colortone ) );
colortone.assign( AgXOutsetMatrix.mul( colortone ) );
colortone.assign( pow( max( vec3( 0.0 ), colortone ), vec3( 2.2 ) ) );
colortone.assign( LINEAR_REC2020_TO_LINEAR_SRGB.mul( colortone ) );
colortone.assign( clamp( colortone, 0.0, 1.0 ) );
return colortone;
} ).setLayout( {
name: 'agxToneMapping',
type: 'vec3',
inputs: [
{ name: 'color', type: 'vec3' },
{ name: 'exposure', type: 'float' }
]
} );
// https://modelviewer.dev/examples/tone-mapping
export const neutralToneMapping = /*@__PURE__*/ Fn( ( [ color, exposure ] ) => {
const StartCompression = float( 0.8 - 0.04 );
const Desaturation = float( 0.15 );
color = color.mul( exposure );
const x = min( color.r, min( color.g, color.b ) );
const offset = select( x.lessThan( 0.08 ), x.sub( mul( 6.25, x.mul( x ) ) ), 0.04 );
color.subAssign( offset );
const peak = max( color.r, max( color.g, color.b ) );
If( peak.lessThan( StartCompression ), () => {
return color;
} );
const d = sub( 1, StartCompression );
const newPeak = sub( 1, d.mul( d ).div( peak.add( d.sub( StartCompression ) ) ) );
color.mulAssign( newPeak.div( peak ) );
const g = sub( 1, div( 1, Desaturation.mul( peak.sub( newPeak ) ).add( 1 ) ) );
return mix( color, vec3( newPeak ), g );
} ).setLayout( {
name: 'neutralToneMapping',
type: 'vec3',
inputs: [
{ name: 'color', type: 'vec3' },
{ name: 'exposure', type: 'float' }
]
} );

View File

@@ -0,0 +1,67 @@
import TempNode from '../core/TempNode.js';
import { addMethodChaining, nodeObject, vec4 } from '../tsl/TSLCore.js';
import { rendererReference } from '../accessors/RendererReferenceNode.js';
import { NoToneMapping } from '../../constants.js';
import { hash } from '../core/NodeUtils.js';
class ToneMappingNode extends TempNode {
static get type() {
return 'ToneMappingNode';
}
constructor( toneMapping, exposureNode = toneMappingExposure, colorNode = null ) {
super( 'vec3' );
this.toneMapping = toneMapping;
this.exposureNode = exposureNode;
this.colorNode = colorNode;
}
getCacheKey() {
return hash( super.getCacheKey(), this.toneMapping );
}
setup( builder ) {
const colorNode = this.colorNode || builder.context.color;
const toneMapping = this.toneMapping;
if ( toneMapping === NoToneMapping ) return colorNode;
let outputNode = null;
const toneMappingFn = builder.renderer.library.getToneMappingFunction( toneMapping );
if ( toneMappingFn !== null ) {
outputNode = vec4( toneMappingFn( colorNode.rgb, this.exposureNode ), colorNode.a );
} else {
console.error( 'ToneMappingNode: Unsupported Tone Mapping configuration.', toneMapping );
outputNode = colorNode;
}
return outputNode;
}
}
export default ToneMappingNode;
export const toneMapping = ( mapping, exposure, color ) => nodeObject( new ToneMappingNode( mapping, nodeObject( exposure ), nodeObject( color ) ) );
export const toneMappingExposure = /*@__PURE__*/ rendererReference( 'toneMappingExposure', 'float' );
addMethodChaining( 'toneMapping', ( color, mapping, exposure ) => toneMapping( mapping, exposure, color ) );

View File

@@ -0,0 +1,111 @@
import { float, nodeObject, normalize, vec4 } from '../tsl/TSLBase.js';
import { Color } from '../../math/Color.js';
import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
import { cameraProjectionMatrix } from '../../nodes/accessors/Camera.js';
import { modelViewMatrix } from '../../nodes/accessors/ModelNode.js';
import { positionLocal } from '../../nodes/accessors/Position.js';
import { normalLocal } from '../../nodes/accessors/Normal.js';
import { BackSide } from '../../constants.js';
import PassNode from './PassNode.js';
class ToonOutlinePassNode extends PassNode {
static get type() {
return 'ToonOutlinePassNode';
}
constructor( scene, camera, colorNode, thicknessNode, alphaNode ) {
super( PassNode.COLOR, scene, camera );
this.colorNode = colorNode;
this.thicknessNode = thicknessNode;
this.alphaNode = alphaNode;
this._materialCache = new WeakMap();
}
updateBefore( frame ) {
const { renderer } = frame;
const currentRenderObjectFunction = renderer.getRenderObjectFunction();
renderer.setRenderObjectFunction( ( object, scene, camera, geometry, material, group, lightsNode ) => {
// only render outline for supported materials
if ( material.isMeshToonMaterial || material.isMeshToonNodeMaterial ) {
if ( material.wireframe === false ) {
const outlineMaterial = this._getOutlineMaterial( material );
renderer.renderObject( object, scene, camera, geometry, outlineMaterial, group, lightsNode );
}
}
// default
renderer.renderObject( object, scene, camera, geometry, material, group, lightsNode );
} );
super.updateBefore( frame );
renderer.setRenderObjectFunction( currentRenderObjectFunction );
}
_createMaterial() {
const material = new NodeMaterial();
material.isMeshToonOutlineMaterial = true;
material.name = 'Toon_Outline';
material.side = BackSide;
// vertex node
const outlineNormal = normalLocal.negate();
const mvp = cameraProjectionMatrix.mul( modelViewMatrix );
const ratio = float( 1.0 ); // TODO: support outline thickness ratio for each vertex
const pos = mvp.mul( vec4( positionLocal, 1.0 ) );
const pos2 = mvp.mul( vec4( positionLocal.add( outlineNormal ), 1.0 ) );
const norm = normalize( pos.sub( pos2 ) ); // NOTE: subtract pos2 from pos because BackSide objectNormal is negative
material.vertexNode = pos.add( norm.mul( this.thicknessNode ).mul( pos.w ).mul( ratio ) );
// color node
material.colorNode = vec4( this.colorNode, this.alphaNode );
return material;
}
_getOutlineMaterial( originalMaterial ) {
let outlineMaterial = this._materialCache.get( originalMaterial );
if ( outlineMaterial === undefined ) {
outlineMaterial = this._createMaterial();
this._materialCache.set( originalMaterial, outlineMaterial );
}
return outlineMaterial;
}
}
export default ToonOutlinePassNode;
export const toonOutlinePass = ( scene, camera, color = new Color( 0, 0, 0 ), thickness = 0.003, alpha = 1 ) => nodeObject( new ToonOutlinePassNode( scene, camera, nodeObject( color ), nodeObject( thickness ), nodeObject( alpha ) ) );

View File

@@ -0,0 +1,157 @@
import Node from '../core/Node.js';
import { log2, nodeImmutable, nodeProxy } from '../tsl/TSLBase.js';
import { cameraNear, cameraFar } from '../accessors/Camera.js';
import { positionView } from '../accessors/Position.js';
import { viewportDepthTexture } from './ViewportDepthTextureNode.js';
class ViewportDepthNode extends Node {
static get type() {
return 'ViewportDepthNode';
}
constructor( scope, valueNode = null ) {
super( 'float' );
this.scope = scope;
this.valueNode = valueNode;
this.isViewportDepthNode = true;
}
generate( builder ) {
const { scope } = this;
if ( scope === ViewportDepthNode.DEPTH_BASE ) {
return builder.getFragDepth();
}
return super.generate( builder );
}
setup( { camera } ) {
const { scope } = this;
const value = this.valueNode;
let node = null;
if ( scope === ViewportDepthNode.DEPTH_BASE ) {
if ( value !== null ) {
node = depthBase().assign( value );
}
} else if ( scope === ViewportDepthNode.DEPTH ) {
if ( camera.isPerspectiveCamera ) {
node = viewZToPerspectiveDepth( positionView.z, cameraNear, cameraFar );
} else {
node = viewZToOrthographicDepth( positionView.z, cameraNear, cameraFar );
}
} else if ( scope === ViewportDepthNode.LINEAR_DEPTH ) {
if ( value !== null ) {
if ( camera.isPerspectiveCamera ) {
const viewZ = perspectiveDepthToViewZ( value, cameraNear, cameraFar );
node = viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
} else {
node = value;
}
} else {
node = viewZToOrthographicDepth( positionView.z, cameraNear, cameraFar );
}
}
return node;
}
}
ViewportDepthNode.DEPTH_BASE = 'depthBase';
ViewportDepthNode.DEPTH = 'depth';
ViewportDepthNode.LINEAR_DEPTH = 'linearDepth';
export default ViewportDepthNode;
// NOTE: viewZ, the z-coordinate in camera space, is negative for points in front of the camera
// -near maps to 0; -far maps to 1
export const viewZToOrthographicDepth = ( viewZ, near, far ) => viewZ.add( near ).div( near.sub( far ) );
// maps orthographic depth in [ 0, 1 ] to viewZ
export const orthographicDepthToViewZ = ( depth, near, far ) => near.sub( far ).mul( depth ).sub( near );
// NOTE: https://twitter.com/gonnavis/status/1377183786949959682
// -near maps to 0; -far maps to 1
export const viewZToPerspectiveDepth = ( viewZ, near, far ) => near.add( viewZ ).mul( far ).div( far.sub( near ).mul( viewZ ) );
// maps perspective depth in [ 0, 1 ] to viewZ
export const perspectiveDepthToViewZ = ( depth, near, far ) => near.mul( far ).div( far.sub( near ).mul( depth ).sub( far ) );
export const perspectiveDepthToLogarithmicDepth = ( perspectiveW, near, far ) => {
// The final logarithmic depth formula used here is adapted from one described in an
// article by Thatcher Ulrich (see http://tulrich.com/geekstuff/log_depth_buffer.txt),
// which was an improvement upon an earlier formula one described in an
// Outerra article (https://outerra.blogspot.com/2009/08/logarithmic-z-buffer.html).
// Ulrich's formula is the following:
// z = K * log( w / cameraNear ) / log( cameraFar / cameraNear )
// where K = 2^k - 1, and k is the number of bits in the depth buffer.
// The Outerra variant ignored the camera near plane (it assumed it was 0) and instead
// opted for a "C-constant" for resolution adjustment of objects near the camera.
// Outerra states: "Notice that the 'C' variant doesnt use a near plane distance, it has it
// set at 0" (quote from https://outerra.blogspot.com/2012/11/maximizing-depth-buffer-range-and.html).
// Ulrich's variant has the benefit of constant relative precision over the whole near-far range.
// It was debated here whether Outerra's "C-constant" or Ulrich's "near plane" variant should
// be used, and ultimately Ulrich's "near plane" version was chosen.
// Outerra eventually made another improvement to their original "C-constant" variant,
// but it still does not incorporate the camera near plane (for this version,
// see https://outerra.blogspot.com/2013/07/logarithmic-depth-buffer-optimizations.html).
// Here we make 4 changes to Ulrich's formula:
// 1. Clamp the camera near plane so we don't divide by 0.
// 2. Use log2 instead of log to avoid an extra multiply (shaders implement log using log2).
// 3. Assume K is 1 (K = maximum value in depth buffer; see Ulrich's formula above).
// 4. Add 1 to each division by cameraNear to ensure the depth curve is shifted to the left as cameraNear increases.
// For visual representation of this depth curve, see https://www.desmos.com/calculator/lz5rqfysih
near = near.max( 1e-6 ).toVar();
const numerator = log2( perspectiveW.div( near ).add( 1 ) );
const denominator = log2( far.div( near ).add( 1 ) );
return numerator.div( denominator );
};
const depthBase = /*@__PURE__*/ nodeProxy( ViewportDepthNode, ViewportDepthNode.DEPTH_BASE );
export const depth = /*@__PURE__*/ nodeImmutable( ViewportDepthNode, ViewportDepthNode.DEPTH );
export const linearDepth = /*@__PURE__*/ nodeProxy( ViewportDepthNode, ViewportDepthNode.LINEAR_DEPTH );
export const viewportLinearDepth = /*@__PURE__*/ linearDepth( viewportDepthTexture() );
depth.assign = ( value ) => depthBase( value );

View File

@@ -0,0 +1,33 @@
import ViewportTextureNode from './ViewportTextureNode.js';
import { nodeProxy } from '../tsl/TSLBase.js';
import { screenUV } from './ScreenNode.js';
import { DepthTexture } from '../../textures/DepthTexture.js';
let sharedDepthbuffer = null;
class ViewportDepthTextureNode extends ViewportTextureNode {
static get type() {
return 'ViewportDepthTextureNode';
}
constructor( uvNode = screenUV, levelNode = null ) {
if ( sharedDepthbuffer === null ) {
sharedDepthbuffer = new DepthTexture();
}
super( uvNode, levelNode, sharedDepthbuffer );
}
}
export default ViewportDepthTextureNode;
export const viewportDepthTexture = /*@__PURE__*/ nodeProxy( ViewportDepthTextureNode );

View File

@@ -0,0 +1,39 @@
import ViewportTextureNode from './ViewportTextureNode.js';
import { nodeProxy } from '../tsl/TSLBase.js';
import { screenUV } from './ScreenNode.js';
import { FramebufferTexture } from '../../textures/FramebufferTexture.js';
let _sharedFramebuffer = null;
class ViewportSharedTextureNode extends ViewportTextureNode {
static get type() {
return 'ViewportSharedTextureNode';
}
constructor( uvNode = screenUV, levelNode = null ) {
if ( _sharedFramebuffer === null ) {
_sharedFramebuffer = new FramebufferTexture();
}
super( uvNode, levelNode, _sharedFramebuffer );
}
updateReference() {
return this;
}
}
export default ViewportSharedTextureNode;
export const viewportSharedTexture = /*@__PURE__*/ nodeProxy( ViewportSharedTextureNode );

View File

@@ -0,0 +1,81 @@
import TextureNode from '../accessors/TextureNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { nodeProxy } from '../tsl/TSLBase.js';
import { screenUV } from './ScreenNode.js';
import { Vector2 } from '../../math/Vector2.js';
import { FramebufferTexture } from '../../textures/FramebufferTexture.js';
import { LinearMipmapLinearFilter } from '../../constants.js';
const _size = /*@__PURE__*/ new Vector2();
class ViewportTextureNode extends TextureNode {
static get type() {
return 'ViewportTextureNode';
}
constructor( uvNode = screenUV, levelNode = null, framebufferTexture = null ) {
if ( framebufferTexture === null ) {
framebufferTexture = new FramebufferTexture();
framebufferTexture.minFilter = LinearMipmapLinearFilter;
}
super( framebufferTexture, uvNode, levelNode );
this.generateMipmaps = false;
this.isOutputTextureNode = true;
this.updateBeforeType = NodeUpdateType.FRAME;
}
updateBefore( frame ) {
const renderer = frame.renderer;
renderer.getDrawingBufferSize( _size );
//
const framebufferTexture = this.value;
if ( framebufferTexture.image.width !== _size.width || framebufferTexture.image.height !== _size.height ) {
framebufferTexture.image.width = _size.width;
framebufferTexture.image.height = _size.height;
framebufferTexture.needsUpdate = true;
}
//
const currentGenerateMipmaps = framebufferTexture.generateMipmaps;
framebufferTexture.generateMipmaps = this.generateMipmaps;
renderer.copyFramebufferToTexture( framebufferTexture );
framebufferTexture.generateMipmaps = currentGenerateMipmaps;
}
clone() {
const viewportTextureNode = new this.constructor( this.uvNode, this.levelNode, this.value );
viewportTextureNode.generateMipmaps = this.generateMipmaps;
return viewportTextureNode;
}
}
export default ViewportTextureNode;
export const viewportTexture = /*@__PURE__*/ nodeProxy( ViewportTextureNode );
export const viewportMipTexture = /*@__PURE__*/ nodeProxy( ViewportTextureNode, null, null, { generateMipmaps: true } );

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