From 202a4956863207d0c3253268b3d12f1e3576ce0f Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Thu, 25 Jun 2026 10:09:25 +0200 Subject: [PATCH 1/9] LineSegment2: Don't raycast without a valid resolution. (#33872) --- examples/jsm/lines/LineMaterial.js | 2 +- examples/jsm/lines/LineSegments2.js | 8 ++++++++ examples/jsm/lines/webgpu/LineSegments2.js | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/examples/jsm/lines/LineMaterial.js b/examples/jsm/lines/LineMaterial.js index c7e1684e11bf09..274d0938bc80dc 100644 --- a/examples/jsm/lines/LineMaterial.js +++ b/examples/jsm/lines/LineMaterial.js @@ -10,7 +10,7 @@ UniformsLib.line = { worldUnits: { value: 1 }, linewidth: { value: 1 }, - resolution: { value: new Vector2( 1, 1 ) }, + resolution: { value: new Vector2() }, dashOffset: { value: 0 }, dashScale: { value: 1 }, dashSize: { value: 1 }, diff --git a/examples/jsm/lines/LineSegments2.js b/examples/jsm/lines/LineSegments2.js index 6fce6f25a135b4..659b7eb5bcfc85 100644 --- a/examples/jsm/lines/LineSegments2.js +++ b/examples/jsm/lines/LineSegments2.js @@ -326,6 +326,14 @@ class LineSegments2 extends Mesh { } + // early out if no resolution has been set (line was not rendered yet) + + if ( worldUnits === false && ( this.material.resolution.x === 0 || this.material.resolution.y === 0 ) ) { + + return; + + } + const threshold = ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold || 0 : 0; _ray = raycaster.ray; diff --git a/examples/jsm/lines/webgpu/LineSegments2.js b/examples/jsm/lines/webgpu/LineSegments2.js index 3d9f75f79f20b8..fd41f521a91a8b 100644 --- a/examples/jsm/lines/webgpu/LineSegments2.js +++ b/examples/jsm/lines/webgpu/LineSegments2.js @@ -324,6 +324,14 @@ class LineSegments2 extends Mesh { } + // early out if no resolution has been set (line was not rendered yet) + + if ( worldUnits === false && ( this._resolution.x === 0 || this._resolution.y === 0 ) ) { + + return; + + } + const threshold = ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold || 0 : 0; _ray = raycaster.ray; From 64d604ba00dc3b56ce10deded9cfabfad28fcf51 Mon Sep 17 00:00:00 2001 From: 0beqz <000beqz@gmail.com> Date: Thu, 25 Jun 2026 11:11:23 +0200 Subject: [PATCH 2/9] Spatiotemporal Denoiser for SSR (#33843) --- examples/files.json | 1 + .../display/ImportanceSampledEnvironment.js | 560 +++++++++ .../jsm/tsl/display/RecurrentDenoiseNode.js | 912 +++++++++++++++ examples/jsm/tsl/display/SSRNode.js | 959 ++++++++++++--- .../jsm/tsl/display/TemporalReprojectNode.js | 1023 +++++++++++++++++ examples/jsm/tsl/utils/RNoise.js | 51 + examples/jsm/tsl/utils/SpecularHelpers.js | 325 ++++++ .../webgpu_postprocessing_ssr_denoise.jpg | Bin 0 -> 93899 bytes examples/webgpu_postprocessing_ssr.html | 21 +- .../webgpu_postprocessing_ssr_denoise.html | 574 +++++++++ test/e2e/puppeteer.js | 1 + 11 files changed, 4290 insertions(+), 137 deletions(-) create mode 100644 examples/jsm/tsl/display/ImportanceSampledEnvironment.js create mode 100644 examples/jsm/tsl/display/RecurrentDenoiseNode.js create mode 100644 examples/jsm/tsl/display/TemporalReprojectNode.js create mode 100644 examples/jsm/tsl/utils/RNoise.js create mode 100644 examples/jsm/tsl/utils/SpecularHelpers.js create mode 100644 examples/screenshots/webgpu_postprocessing_ssr_denoise.jpg create mode 100644 examples/webgpu_postprocessing_ssr_denoise.html diff --git a/examples/files.json b/examples/files.json index 0a244fc23f73eb..caba6a3209fb89 100644 --- a/examples/files.json +++ b/examples/files.json @@ -449,6 +449,7 @@ "webgpu_postprocessing_ssgi", "webgpu_postprocessing_ssgi_ballpool", "webgpu_postprocessing_ssr", + "webgpu_postprocessing_ssr_denoise", "webgpu_postprocessing_sss", "webgpu_postprocessing_traa", "webgpu_postprocessing_transition", diff --git a/examples/jsm/tsl/display/ImportanceSampledEnvironment.js b/examples/jsm/tsl/display/ImportanceSampledEnvironment.js new file mode 100644 index 00000000000000..188d3de2ef60e0 --- /dev/null +++ b/examples/jsm/tsl/display/ImportanceSampledEnvironment.js @@ -0,0 +1,560 @@ +/** + * HDR environment importance sampling (CDF tables + MIS) for screen-space effects. + * + * CDF precomputation and the MIS env-miss estimator are adapted from + * [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer). + * + * @see {@link https://github.com/gkjohnson/three-gpu-pathtracer} + */ + +import { If, dot, equirectUV, float, luminance, max, normalize, texture, uniform, vec2, vec4 } from 'three/tsl'; +import { ClampToEdgeWrapping, DataTexture, DataUtils, FloatType, HalfFloatType, LinearFilter, RedFormat, RepeatWrapping, Source, Vector2 } from 'three/webgpu'; +import { D_GTR, F_Schlick, GeometryTerm, SmithG, equirectDirPdf, misPowerHeuristic } from '../utils/SpecularHelpers.js'; + +function colorToLuminance( r, g, b ) { + + return 0.2126 * r + 0.7152 * g + 0.0722 * b; + +} + +function binarySearchFindClosestIndexOf( array, targetValue, offset = 0, count = array.length ) { + + let lower = offset; + let upper = offset + count - 1; + + while ( lower < upper ) { + + const mid = ( lower + upper ) >> 1; + + if ( array[ mid ] < targetValue ) { + + lower = mid + 1; + + } else { + + upper = mid; + + } + + } + + return lower - offset; + +} + +function preprocessEnvMap( envMap ) { + + const map = envMap.clone(); + map.source = new Source( { ...map.image } ); + const { width, height, data } = map.image; + + let newData = data; + + if ( map.type !== HalfFloatType ) { + + newData = new Uint16Array( data.length ); + + let maxIntValue; + if ( data instanceof Int8Array || data instanceof Int16Array || data instanceof Int32Array ) { + + maxIntValue = 2 ** ( 8 * data.BYTES_PER_ELEMENT - 1 ) - 1; + + } else { + + maxIntValue = 2 ** ( 8 * data.BYTES_PER_ELEMENT ) - 1; + + } + + for ( let i = 0, l = data.length; i < l; i ++ ) { + + let v = data[ i ]; + + if ( map.type === HalfFloatType ) { + + v = DataUtils.fromHalfFloat( data[ i ] ); + + } + + if ( map.type !== FloatType && map.type !== HalfFloatType ) { + + v /= maxIntValue; + + } + + newData[ i ] = DataUtils.toHalfFloat( v ); + + } + + map.image.data = newData; + map.type = HalfFloatType; + + } + + if ( map.flipY ) { + + const ogData = newData; + newData = newData.slice(); + + for ( let y = 0; y < height; y ++ ) { + + for ( let x = 0; x < width; x ++ ) { + + const newY = height - y - 1; + const ogIndex = 4 * ( y * width + x ); + const newIndex = 4 * ( newY * width + x ); + + newData[ newIndex + 0 ] = ogData[ ogIndex + 0 ]; + newData[ newIndex + 1 ] = ogData[ ogIndex + 1 ]; + newData[ newIndex + 2 ] = ogData[ ogIndex + 2 ]; + newData[ newIndex + 3 ] = ogData[ ogIndex + 3 ]; + + } + + } + + map.flipY = false; + map.image.data = newData; + + } + + return map; + +} + +/** + * Precomputes marginal and conditional CDF textures from an equirectangular HDR environment map + * for luminance importance sampling. + */ +class EnvMapCDFGenerator { + + constructor() { + + this.map = null; + this.marginalWeights = null; + this.conditionalWeights = null; + this.totalSum = 0; + + } + + updateFrom( hdr ) { + + this.updateMapOnly( hdr ); + + const { width, height, data } = this.map.image; + + const pdfConditional = new Float32Array( width * height ); + const cdfConditional = new Float32Array( width * height ); + const pdfMarginal = new Float32Array( height ); + const cdfMarginal = new Float32Array( height ); + + let totalSumValue = 0.0; + let cumulativeWeightMarginal = 0.0; + + for ( let y = 0; y < height; y ++ ) { + + let cumulativeRowWeight = 0.0; + + for ( let x = 0; x < width; x ++ ) { + + const i = y * width + x; + const r = DataUtils.fromHalfFloat( data[ 4 * i + 0 ] ); + const g = DataUtils.fromHalfFloat( data[ 4 * i + 1 ] ); + const b = DataUtils.fromHalfFloat( data[ 4 * i + 2 ] ); + + const weight = colorToLuminance( r, g, b ); + cumulativeRowWeight += weight; + totalSumValue += weight; + + pdfConditional[ i ] = weight; + cdfConditional[ i ] = cumulativeRowWeight; + + } + + if ( cumulativeRowWeight !== 0 ) { + + for ( let i = y * width, l = y * width + width; i < l; i ++ ) { + + pdfConditional[ i ] /= cumulativeRowWeight; + cdfConditional[ i ] /= cumulativeRowWeight; + + } + + } + + cumulativeWeightMarginal += cumulativeRowWeight; + pdfMarginal[ y ] = cumulativeRowWeight; + cdfMarginal[ y ] = cumulativeWeightMarginal; + + } + + if ( cumulativeWeightMarginal !== 0 ) { + + for ( let i = 0, l = pdfMarginal.length; i < l; i ++ ) { + + pdfMarginal[ i ] /= cumulativeWeightMarginal; + cdfMarginal[ i ] /= cumulativeWeightMarginal; + + } + + } + + const marginalDataArray = new Uint16Array( height ); + const conditionalDataArray = new Uint16Array( width * height ); + + for ( let i = 0; i < height; i ++ ) { + + const dist = ( i + 1 ) / height; + const row = binarySearchFindClosestIndexOf( cdfMarginal, dist ); + marginalDataArray[ i ] = DataUtils.toHalfFloat( ( row + 0.5 ) / height ); + + } + + for ( let y = 0; y < height; y ++ ) { + + for ( let x = 0; x < width; x ++ ) { + + const i = y * width + x; + const dist = ( x + 1 ) / width; + const col = binarySearchFindClosestIndexOf( cdfConditional, dist, y * width, width ); + conditionalDataArray[ i ] = DataUtils.toHalfFloat( ( col + 0.5 ) / width ); + + } + + } + + if ( this.marginalWeights ) { + + this.marginalWeights.dispose(); + + } + + if ( this.conditionalWeights ) { + + this.conditionalWeights.dispose(); + + } + + this.marginalWeights = new DataTexture( marginalDataArray, height, 1 ); + this.marginalWeights.type = HalfFloatType; + this.marginalWeights.format = RedFormat; + this.marginalWeights.minFilter = LinearFilter; + this.marginalWeights.magFilter = LinearFilter; + this.marginalWeights.wrapS = ClampToEdgeWrapping; + this.marginalWeights.wrapT = ClampToEdgeWrapping; + this.marginalWeights.generateMipmaps = false; + this.marginalWeights.needsUpdate = true; + + this.conditionalWeights = new DataTexture( conditionalDataArray, width, height ); + this.conditionalWeights.type = HalfFloatType; + this.conditionalWeights.format = RedFormat; + this.conditionalWeights.minFilter = LinearFilter; + this.conditionalWeights.magFilter = LinearFilter; + this.conditionalWeights.wrapS = ClampToEdgeWrapping; + this.conditionalWeights.wrapT = ClampToEdgeWrapping; + this.conditionalWeights.generateMipmaps = false; + this.conditionalWeights.needsUpdate = true; + + this.totalSum = totalSumValue; + + } + + updateMapOnly( hdr ) { + + if ( this.map ) { + + this.map.dispose(); + + } + + const map = preprocessEnvMap( hdr ); + map.wrapS = RepeatWrapping; + map.wrapT = ClampToEdgeWrapping; + + this.map = map; + this.totalSum = 0; + + } + + dispose() { + + if ( this.marginalWeights ) { + + this.marginalWeights.dispose(); + this.marginalWeights = null; + + } + + if ( this.conditionalWeights ) { + + this.conditionalWeights.dispose(); + this.conditionalWeights = null; + + } + + if ( this.map ) { + + this.map.dispose(); + this.map = null; + + } + + } + +} + +/** + * Manages a preprocessed HDR environment map (CDF textures, uniforms) and exposes + * TSL helpers for BRDF-direction lookups and MIS importance sampling. + * + * @see {@link https://github.com/gkjohnson/three-gpu-pathtracer} + */ +class ImportanceSampledEnvironment { + + /** + * @param {boolean} [importanceSampling=false] - When `true`, builds luminance CDF tables and enables MIS env sampling. + */ + constructor( importanceSampling = false ) { + + this._importanceSampling = importanceSampling; + this._cdf = new EnvMapCDFGenerator(); + + this._totalSum = uniform( 0.0, 'float' ); + this._size = uniform( new Vector2( 1, 1 ) ); + this.intensity = uniform( 1.0, 'float' ); + + this._mapNode = null; + this._marginalNode = null; + this._conditionalNode = null; + + } + + /** + * @param {import('three').Texture} hdr - Equirectangular HDR environment map. + */ + updateFrom( hdr ) { + + if ( this._importanceSampling ) { + + this._cdf.updateFrom( hdr ); + this._totalSum.value = this._cdf.totalSum; + + } else { + + this._cdf.updateMapOnly( hdr ); + + } + + this._size.value.set( this._cdf.map.image.width, this._cdf.map.image.height ); + + if ( this._mapNode === null ) { + + this._mapNode = texture( this._cdf.map ); + + if ( this._importanceSampling ) { + + this._marginalNode = texture( this._cdf.marginalWeights ); + this._conditionalNode = texture( this._cdf.conditionalWeights ); + + } + + } else { + + this._mapNode.value = this._cdf.map; + + if ( this._importanceSampling ) { + + this._marginalNode.value = this._cdf.marginalWeights; + this._conditionalNode.value = this._cdf.conditionalWeights; + + } + + } + + } + + clear() { + + this.dispose(); + this._cdf = new EnvMapCDFGenerator(); + this._mapNode = null; + this._marginalNode = null; + this._conditionalNode = null; + this._totalSum.value = 0; + this._size.value.set( 1, 1 ); + + } + + /** + * Simple environment lookup along the reflected direction (no MIS). + * + * @param {Object} params + * @param {import('three/tsl').UniformNode} params.cameraWorldMatrix + * @param {import('three/tsl').Node} params.viewReflectDir + * @param {import('three/tsl').Node} [params.sampleWeight] - Optional radiance scale (defaults to 1). + * @return {import('three/tsl').Node} + */ + sampleReflect( { cameraWorldMatrix, viewReflectDir, sampleWeight = float( 1 ) } ) { + + const worldReflectDir = cameraWorldMatrix.mul( vec4( viewReflectDir, float( 0 ) ) ).xyz.normalize(); + const envUV = equirectUV( worldReflectDir ); + + // Explicit LOD 0: the per-pixel reflected direction is discontinuous at the equirect pole/seam + // (atan is undefined at the poles), so derivative-driven mip selection collapses to the coarsest + // (near-average) mip there and produces a bright streak. Roughness is handled via direction sampling. + return texture( this._mapNode, envUV ).level( 0 ).rgb.mul( this.intensity ).mul( sampleWeight ); + + } + + /** + * Environment reflection for a screen-space miss using only the BRDF / reflected-ray direction. + * + * @param {Object} params + * @param {import('three/tsl').UniformNode} params.cameraWorldMatrix + * @param {import('three/tsl').Node} params.viewReflectDir - View-space GGX-sampled reflected ray. + * @param {import('three/tsl').Node} params.N - View-space shading normal. + * @param {import('three/tsl').Node} params.V - View-space direction to camera. + * @param {import('three/tsl').Node} params.alpha - GGX roughness (alpha). + * @param {import('three/tsl').Node} params.f0 + * @return {import('three/tsl').Node} + */ + sampleEnvironmentBRDF( { + cameraWorldMatrix, + viewReflectDir, + N, + V, + alpha, + f0 + } ) { + + const worldNormal = cameraWorldMatrix.mul( vec4( N, 0 ) ).xyz.normalize().toVar(); + const worldV = cameraWorldMatrix.mul( vec4( V, 0 ) ).xyz.normalize().toVar(); + const NdotV = max( float( 0 ), dot( worldNormal, worldV ) ).toVar(); + + const L1 = cameraWorldMatrix.mul( vec4( viewReflectDir, float( 0 ) ) ).xyz.normalize().toVar(); + // Explicit LOD 0: the equirect mapping is singular at the poles (atan undefined when the reflected + // ray points straight up/down, e.g. a flat floor under a top-down camera), so derivative-driven mip + // selection picks the coarsest, near-average mip and yields a bright streak. Sample full-res instead. + const brdfEnvColor = texture( this._mapNode, equirectUV( L1 ) ).level( 0 ).rgb; + + const H1 = normalize( worldV.add( L1 ) ).toVar(); + const NdotL1 = max( float( 0 ), dot( worldNormal, L1 ) ).toVar(); + const VdotH1 = max( float( 0 ), dot( worldV, H1 ) ).toVar(); + + const W1 = F_Schlick( f0, VdotH1 ).mul( GeometryTerm( NdotL1, NdotV, alpha ) ).div( SmithG( NdotV, alpha ).max( float( 1e-4 ) ) ); + + return brdfEnvColor.mul( W1 ).mul( this.intensity ); + + } + + /** + * Environment reflection for a screen-space miss, estimated with multiple importance + * sampling (MIS) between the BRDF / reflected-ray direction and the env-luminance CDF + * direction. Both techniques use consistent solid-angle PDFs (`D·G1(N·V)/(4·N·V)`), so + * the power heuristic is unbiased. Adapted from three-gpu-pathtracer. + * + * @see {@link https://github.com/gkjohnson/three-gpu-pathtracer} + * + * @param {Object} params + * @param {import('three/tsl').UniformNode} params.cameraWorldMatrix + * @param {import('three/tsl').Node} params.viewReflectDir - View-space GGX-sampled reflected ray. + * @param {import('three/tsl').Node} params.N - View-space shading normal. + * @param {import('three/tsl').Node} params.V - View-space direction to camera. + * @param {import('three/tsl').Node} params.alpha - GGX roughness (alpha). + * @param {import('three/tsl').Node} params.f0 + * @param {import('three/tsl').Node} params.Xi2 - Second blue-noise sample (zw used for the CDF). + * @return {import('three/tsl').Node} + */ + sampleEnvironmentMIS( { + cameraWorldMatrix, + viewReflectDir, + N, + V, + alpha, + f0, + Xi2 + } ) { + + const mapNode = this._mapNode; + const marginalNode = this._marginalNode; + const conditionalNode = this._conditionalNode; + const totalSum = this._totalSum; + const envW = this._size.x; + const envH = this._size.y; + const envMapIntensity = this.intensity; + + const worldNormal = cameraWorldMatrix.mul( vec4( N, 0 ) ).xyz.normalize().toVar(); + const worldV = cameraWorldMatrix.mul( vec4( V, 0 ) ).xyz.normalize().toVar(); + const NdotV = max( float( 0 ), dot( worldNormal, worldV ) ).toVar(); + + // MIS sample 1: the BRDF / reflected-ray direction + const L1 = cameraWorldMatrix.mul( vec4( viewReflectDir, float( 0 ) ) ).xyz.normalize().toVar(); + const brdfEnvColor = texture( mapNode, equirectUV( L1 ) ).level( 0 ).rgb; + + const H1 = normalize( worldV.add( L1 ) ).toVar(); + const NdotL1 = max( float( 0 ), dot( worldNormal, L1 ) ).toVar(); + const NdotH1 = max( float( 0 ), dot( worldNormal, H1 ) ).toVar(); + const VdotH1 = max( float( 0 ), dot( worldV, H1 ) ).toVar(); + + // Solid-angle PDF of the reflected ray for the BRDF technique: D(H)·G1(N·V)/(4·N·V). + const pdfBrdf1 = D_GTR( alpha, NdotH1, float( 2 ) ).mul( SmithG( NdotV, alpha ) ).div( max( float( 1e-6 ), float( 4 ).mul( NdotV ) ) ).max( float( 1e-8 ) ); + // Env-luminance CDF PDF evaluated at the same direction. + const pdfEnv1 = envW.mul( envH ).mul( luminance( brdfEnvColor ).div( totalSum ) ).mul( equirectDirPdf( L1 ) ).max( float( 1e-8 ) ); + const w1 = misPowerHeuristic( pdfBrdf1, pdfEnv1 ); + + // Monte-Carlo weight f·cosθ/pdfBrdf1 = F·G1(N·L) (GGX D cancels analytically — stable at low + // roughness). G2 and the pdf's G1 must use the same alpha for the cancellation to hold. + const W1 = F_Schlick( f0, VdotH1 ).mul( GeometryTerm( NdotL1, NdotV, alpha ) ).div( SmithG( NdotV, alpha ).max( float( 1e-4 ) ) ); + const result = brdfEnvColor.mul( W1 ).mul( w1 ).toVar(); + + // MIS sample 2: the env-luminance CDF direction + // Mitigates noise on high-dynamic-range environments (the CDF lands samples on bright regions + // the BRDF lobe rarely hits). Skipped for near-mirror lobes (alpha ≲ 0.01, i.e. roughness ≲ 0.1): + // a global CDF direction almost never lands inside such a tight specular lobe. + If( alpha.greaterThan( 0.01 ), () => { + + const r_env = vec2( Xi2.z, Xi2.w ); + const v_cdf = texture( marginalNode, vec2( r_env.x, float( 0 ) ) ).r; + const u_cdf = texture( conditionalNode, vec2( r_env.y, v_cdf ) ).r; + const isEnvUV = vec2( u_cdf, v_cdf ); + const envDirWS = equirectUV( isEnvUV ); + + const envHalf = normalize( worldV.add( envDirWS ) ); + const envNdotL = max( float( 0 ), dot( worldNormal, envDirWS ) ); + const envNdotH = max( float( 0 ), dot( worldNormal, envHalf ) ); + const envVdotH = max( float( 0 ), dot( worldV, envHalf ) ); + + If( envNdotL.greaterThan( 0.001 ), () => { + + // GGX normal-distribution term, shared by the BRDF pdf and the specular BRDF + // (both evaluate D(envNdotH)) so the pow is computed once. + const D = D_GTR( alpha, envNdotH, float( 2 ) ).toVar(); + + const sampledColor = texture( mapNode, isEnvUV ).level( 0 ).rgb; + const pdfEnv2 = envW.mul( envH ).mul( luminance( sampledColor ).div( totalSum ) ).mul( equirectDirPdf( envDirWS ) ).max( float( 1e-8 ) ); + // BRDF technique pdf at the env direction — same solid-angle form as pdfBrdf1 (no V·H). + const pdfBrdf2 = D.mul( SmithG( NdotV, alpha ) ).div( max( float( 1e-6 ), float( 4 ).mul( NdotV ) ) ).max( float( 1e-8 ) ); + const w2 = misPowerHeuristic( pdfEnv2, pdfBrdf2 ); + + // Specular BRDF (without Fresnel): D·G2 / (4·N·L·N·V), reusing D. Same GGX alpha as the pdf. + const envBrdfSpec = D.mul( GeometryTerm( envNdotL, NdotV, alpha ) ).div( max( float( 1e-6 ), float( 4 ).mul( envNdotL ).mul( NdotV ) ) ); + const envFresnelWeight = F_Schlick( f0, envVdotH ); // vec3 — chromatic metal tint + + result.addAssign( sampledColor.mul( envBrdfSpec ).mul( envFresnelWeight ).mul( envNdotL ).div( pdfEnv2 ).mul( w2 ) ); + + } ); + + } ); + + return result.mul( envMapIntensity ); + + } + + dispose() { + + this._cdf.dispose(); + + } + +} + +export default ImportanceSampledEnvironment; diff --git a/examples/jsm/tsl/display/RecurrentDenoiseNode.js b/examples/jsm/tsl/display/RecurrentDenoiseNode.js new file mode 100644 index 00000000000000..1dd8a2e430488f --- /dev/null +++ b/examples/jsm/tsl/display/RecurrentDenoiseNode.js @@ -0,0 +1,912 @@ +import { abs, atan, bool, convertToTexture, cos, cross, Discard, dot, EPSILON, exp, float, Fn, getScreenPosition, getViewPosition, If, int, log, Loop, luminance, mat2, max, mix, nodeObject, NodeUpdateType, normalize, passTexture, PI, property, reflect, sin, smoothstep, sqrt, tan, texture, uniform, unpackRGBToNormal, uv, vec2, vec3, vec4 } from 'three/tsl'; +import { HalfFloatType, MathUtils, Matrix4, NodeMaterial, QuadMesh, RendererUtils, RenderTarget, TempNode, Vector2 } from 'three/webgpu'; +import { bindAnalyticNoise } from '../utils/RNoise.js'; +import { ENV_RAY_LENGTH_THRESHOLD } from '../utils/SpecularHelpers.js'; + +const _quadMesh = /*@__PURE__*/ new QuadMesh(); +const _size = /*@__PURE__*/ new Vector2(); + +let _rendererState; + +const KERNEL_SAMPLES = 8; +const NOISE_ROTATION_SEED = 83; +const WORLD_RADIUS_SCALE = 0.1; + +const AO_EDGE_STOPPING_BIAS = 0.05; +const AGGRESSIVITY_RADIUS_MIN = 0.001; +const DIFFUSE_CHROMA_WEIGHT = 2.0; +// Neighborhood luma coefficient-of-variation thresholds for gating the temporal inverse-luminance +// (firefly) suppression: below MIN the region is treated as flicker-free, above MAX as noisy. +const FLICKER_COV_GATE_MIN = 0.1; +const FLICKER_COV_GATE_MAX = 2; + +/** + * Golden-angle Vogel disk offset. + * + * @tsl + */ +const vogelDisk = Fn( ( [ i, radius ] ) => { + + const sampleCount = 8; + const theta = i.add( 0.5 ).mul( 2.399827721492203 ); + const r = radius.mul( sqrt( i.add( 0.5 ).div( sampleCount ) ) ); + return vec2( cos( theta ), sin( theta ) ).mul( r ); + +} ).setLayout( { + name: 'vogelDisk', + type: 'vec2', + inputs: [ + { name: 'i', type: 'float' }, + { name: 'radius', type: 'float' } + ] +} ); + +/** + * Chromatic color-similarity distance between two linear base colors (albedo). + * + * @tsl + */ +const diffuseColorDistance = Fn( ( [ a, b, compressLuma ] ) => { + + const toYCoCg = ( c ) => vec3( + dot( c, vec3( 0.25, 0.5, 0.25 ) ), + c.r.sub( c.b ), + c.g.sub( c.r.add( c.b ).mul( 0.5 ) ) + ); + + const ya = toYCoCg( a ); + const yb = toYCoCg( b ); + + // `compressLuma` (0/1) range-compresses the luma term with log(1+L) so a fixed lumaPhi gives + // scale-invariant differences across the HDR range. 0 leaves luma linear (used for LDR albedo). + const compress = ( L ) => mix( L, log( L.add( 1 ) ), compressLuma ); + + const dLuma = abs( compress( ya.x ).sub( compress( yb.x ) ) ); + const dChroma = vec2( ya.y.sub( yb.y ), ya.z.sub( yb.z ) ).length(); + + return dLuma.add( dChroma.mul( DIFFUSE_CHROMA_WEIGHT ) ); + +} ).setLayout( { + name: 'diffuseColorDistance', + type: 'float', + inputs: [ + { name: 'a', type: 'vec3' }, + { name: 'b', type: 'vec3' }, + { name: 'compressLuma', type: 'float' } + ] +} ); + +const _temporalWeight = Fn( ( [ x, strength ] ) => float( 1 ).div( x.pow( strength ) ) ).setLayout( { + name: 'temporalWeight', + type: 'float', + inputs: [ + { name: 'x', type: 'float' }, + { name: 'strength', type: 'float' } + ] +} ); + +/** + * Temporal accumulation variance factor in `[0, 1]`. Higher values mean more history confidence. + * + * @tsl + */ +const getTemporalVarianceFactor = Fn( ( [ frameNum, strength ] ) => { + + return _temporalWeight( frameNum, strength ).max( 0.05 ); + +} ).setLayout( { + name: 'getTemporalVarianceFactor', + type: 'float', + inputs: [ + { name: 'frameNum', type: 'float' }, + { name: 'strength', type: 'float' } + ] +} ); + +/** + * World-space frustum height at `viewZ`. Algorithm originally from REBLUR (NRD). + * `tanHalfFovY` is `tan( verticalFov / 2 )`, hoisted by the caller since it is loop-invariant. + * + * @tsl + */ +const computeFrustumSize = Fn( ( [ viewZ, tanHalfFovY ] ) => { + + return float( 2 ).mul( viewZ ).mul( tanHalfFovY ); + +} ).setLayout( { + name: 'computeFrustumSize', + type: 'float', + inputs: [ + { name: 'viewZ', type: 'float' }, + { name: 'tanHalfFovY', type: 'float' } + ] +} ); + +/** + * Maps world-space SSR ray length to `[0, 1]`. Environment rays (`worldRayLength == 0`) map to `1`. + * Algorithm originally from REBLUR (NRD). + * + * @tsl + */ +const computeHitDistFactor = Fn( ( [ worldRayLength, viewZ, tanHalfFovY ] ) => { + + const frustumSize = computeFrustumSize( viewZ, tanHalfFovY ); + const factor = worldRayLength.div( frustumSize.max( 1e-6 ) ).clamp( 0, 1 ); + + return factor; + +} ).setLayout( { + name: 'computeHitDistFactor', + type: 'float', + inputs: [ + { name: 'worldRayLength', type: 'float' }, + { name: 'viewZ', type: 'float' }, + { name: 'tanHalfFovY', type: 'float' } + ] +} ); + +/** + * Maps an AO factor for edge-stopping comparisons. + * + * @tsl + */ +const mapAo = Fn( ( [ aoVal ] ) => aoVal.pow( 0.1 ) ); + +/** + * Specular dominant direction — smooth surfaces lean toward reflection, rough toward normal. + * + * @tsl + */ +const getSpecularDominantDirection = Fn( ( [ N, V, roughness ] ) => { + + return normalize( mix( N, reflect( V.negate(), N ), roughness.oneMinus() ) ); + +} ).setLayout( { + name: 'getSpecularDominantDirection', + type: 'vec3', + inputs: [ + { name: 'N', type: 'vec3' }, + { name: 'V', type: 'vec3' }, + { name: 'roughness', type: 'float' } + ] +} ); + +/** + * GGX inverse-CDF: half-angle tangent enclosing `percent` of the specular lobe volume. + * `roughness` is perceptual (alpha = roughness²). + * + * @tsl + */ +const specularLobeTanHalfAngle = Fn( ( [ roughness, percent ] ) => { + + const alpha = roughness.mul( roughness ); + return alpha.mul( sqrt( percent.div( float( 1 ).sub( percent ).max( 1e-6 ) ) ) ); + +} ).setLayout( { + name: 'specularLobeTanHalfAngle', + type: 'float', + inputs: [ + { name: 'roughness', type: 'float' }, + { name: 'percent', type: 'float' } + ] +} ); + +const EXP_WEIGHT_SCALE = 4; +const NORMAL_ENCODING_ERROR = 1.5 / 255; + +/** + * Loop-invariant part of the adaptive normal edge-stopping weight: the Gaussian falloff + * constant `2·EXP_WEIGHT_SCALE / lobeHalfAngle²`. `roughness`/`aggressivity`/`invNormalPhi` + * are constant across the kernel, so this is hoisted out of the tap loop and evaluated once + * per pixel. Lobe half-angle from REBLUR (NRD). + * + * @tsl + */ +const lobeNormalFalloff = Fn( ( [ roughness, aggressivity, invNormalPhi ] ) => { + + const percent = mix( invNormalPhi.pow2(), float( 0 ), aggressivity.sqrt() ).clamp( 0.1, 0.99 ); + const tanHalfAngle = specularLobeTanHalfAngle( roughness, percent ); + const lobeHalfAngle = max( atan( tanHalfAngle ), float( NORMAL_ENCODING_ERROR ) ); + + const invHalfAngle = float( 1 ).div( lobeHalfAngle ); + return invHalfAngle.mul( invHalfAngle ).mul( 2 * EXP_WEIGHT_SCALE ); + +} ).setLayout( { + name: 'lobeNormalFalloff', + type: 'float', + inputs: [ + { name: 'roughness', type: 'float' }, + { name: 'aggressivity', type: 'float' }, + { name: 'invNormalPhi', type: 'float' } + ] +} ); + +/** + * Adaptive lobe normal edge-stopping weight + * + * Evaluated entirely in cosine space: with `angle² ≈ 2(1 − cosθ)`, the original + * `exp( −SCALE·angle/halfAngle )` becomes a Gaussian `exp( falloff·(cosθ − 1) )`, so a + * single `exp` replaces the per-tap `acos`. Matches the original at the half-angle for + * narrow lobes and is slightly more permissive for wide (diffuse) ones. + * + * @tsl + */ +const lobeNormalWeight = Fn( ( [ viewNormal, nNormalV, lobeFalloff ] ) => { + + const cosA = dot( viewNormal, nNormalV ); + + return exp( cosA.sub( 1 ).mul( lobeFalloff ) ); + +} ).setLayout( { + name: 'lobeNormalWeight', + type: 'float', + inputs: [ + { name: 'viewNormal', type: 'vec3' }, + { name: 'nNormalV', type: 'vec3' }, + { name: 'lobeFalloff', type: 'float' } + ] +} ); + +/** + * View-space plane distance between two surface points (edge-stopping geometry term). + * + * @tsl + */ +const planeDistance = Fn( ( [ position, nPosition, normal ] ) => { + + return abs( dot( position.sub( nPosition ), normal ) ); + +} ).setLayout( { + name: 'planeDistance', + type: 'float', + inputs: [ + { name: 'position', type: 'vec3' }, + { name: 'nPosition', type: 'vec3' }, + { name: 'normal', type: 'vec3' }, + ] +} ); + +/** + * Inverse-luminance temporal blend with optional adaptive trust (Karis-style). + * + * @tsl + */ +const karisTemporalBlend = Fn( ( [ denoisedRgb, denoisedRaw, a, flickerSuppression, adaptiveTrust, nbhdMeanLuma, nbhdStddevLuma ] ) => { + + const localCoV = nbhdStddevLuma.div( nbhdMeanLuma.max( 1e-4 ) ); + const trustSuppress = localCoV.mul( adaptiveTrust ).mul( a.oneMinus() ).clamp( 0, 0.9 ); + const aTrust = a.mul( trustSuppress.oneMinus() ); + + // In flicker-free neighborhoods, back off the inverse-luminance weighting so valid bright highlights + // keep their energy. Scaled by adaptiveTrust so the default (0) path is unchanged. + const noisy = smoothstep( FLICKER_COV_GATE_MIN, FLICKER_COV_GATE_MAX, localCoV ); + const effFlicker = flickerSuppression.mul( mix( adaptiveTrust.oneMinus(), float( 1 ), noisy ) ); + + const wHist = float( 1 ).sub( aTrust ).div( luminance( denoisedRgb ).mul( effFlicker ).mul( 10 ).add( 1 ) ); + const wRaw = aTrust.div( luminance( denoisedRaw ).mul( effFlicker ).mul( 10 ).add( 1 ) ); + return denoisedRgb.mul( wHist ).add( denoisedRaw.mul( wRaw ) ).div( wHist.add( wRaw ).max( EPSILON ) ); + +} ).setLayout( { + name: 'karisTemporalBlend', + type: 'vec3', + inputs: [ + { name: 'denoisedRgb', type: 'vec3' }, + { name: 'denoisedRaw', type: 'vec3' }, + { name: 'a', type: 'float' }, + { name: 'flickerSuppression', type: 'float' }, + { name: 'adaptiveTrust', type: 'float' }, + { name: 'nbhdMeanLuma', type: 'float' }, + { name: 'nbhdStddevLuma', type: 'float' } + ] +} ); + +const toTextureNode = ( value ) => { + + if ( value === null ) return null; + + if ( value.isTexture === true ) return texture( value ); + + return convertToTexture( value.getTextureNode?.() ?? value ); + +}; + +/** + * @typedef {'diffuse'|'specular'} DenoiseMode + */ + +/** + * @typedef {'raylength'|'ao'|'none'} DenoiseAlphaSource + */ + +/** + * @typedef {Object} RecurrentDenoiseNodeOptions + * @property {?Node} [depth=null] - Scene depth buffer for view-space edge stopping. + * @property {?Node} [normal=null] - View-space normals for geometric edge stopping. + * @property {?Node} [metalRoughness=null] - Roughness/metalness G-buffer for specular edge stopping. + * @property {?Node} [diffuse=null] - Scene base color (albedo) G-buffer for chromatic edge stopping. + * @property {?Node} [raw=null] - Unfiltered input (e.g. raw SSR/SSGI) for secondary sampling and temporal blend. + * @property {DenoiseMode} [mode='diffuse'] - Denoising kernel type. + * @property {boolean} [accumulate=true] - When `true`, temporally blend the spatially-denoised result + * (Karis-style) and write frame weight to alpha for feedback loops. When `false`, only spatial filtering is applied. + */ + +/** + * Post processing node for denoising temporally-accumulated screen-space effects + * such as SSGI (ambient occlusion / indirect diffuse) and SSR (specular reflections). + * + * The denoising kernel is selected at construction time via `mode`: + * `'diffuse'` (SSGI) or `'specular'` (SSR). The kernel uses a fixed 8-sample Vogel disk. + * + * @augments TempNode + * @three_import import { recurrentDenoise } from 'three/addons/tsl/display/RecurrentDenoiseNode.js'; + */ +class RecurrentDenoiseNode extends TempNode { + + static get type() { + + return 'RecurrentDenoiseNode'; + + } + + /** + * @param {TextureNode} inputTexture - Temporally filtered input to denoise (e.g. TRAA output). + * @param {Camera} camera + * @param {RecurrentDenoiseNodeOptions} [options={}] + */ + constructor( inputTexture, camera, options = {} ) { + + super( 'vec4' ); + + const { + depth = null, + normal = null, + metalRoughness = null, + diffuse = null, + raw = null, + mode = 'diffuse', + accumulate = true, + } = options; + + this.isRecurrentDenoiseNode = true; + this.camera = camera; + + /** + * Denoising kernel type. + * + * @type {DenoiseMode} + */ + this.mode = mode; + + /** + * When `true`, apply temporal blending after spatial denoising. When `false`, output spatially + * filtered colour only (alpha is passed through from the input temporal pass). + * + * @type {boolean} + */ + this.accumulate = accumulate; + + this.textureNode = inputTexture; + this.depthNode = depth !== null ? nodeObject( depth ) : null; + this.normalNode = normal !== null ? nodeObject( normal ) : null; + this.rawNode = toTextureNode( raw ); + this.roughnessMetalnessNode = metalRoughness !== null ? nodeObject( metalRoughness ) : null; + this.diffuseNode = diffuse !== null ? nodeObject( diffuse ) : null; + + this._noiseIndex = uniform( 0 ); + + this.lumaPhi = uniform( 5 ); + this.depthPhi = uniform( 5 ); + this.normalPhi = uniform( 5 ); + this.radius = uniform( 5 ); + this.alphaPhi = uniform( 1 ); + this.roughnessPhi = uniform( 100 ); + this.diffusePhi = uniform( 100 ); + this.adapt = uniform( 0.5 ); + this.smoothDisocclusions = uniform( true, 'bool' ); + this.strength = uniform( 0.25 ); + this.maxFrames = uniform( 32 ); + + /** + * Which channel of the raw texture drives alpha-based edge stopping. + * `'raylength'` — alpha encodes SSR ray length; `'ao'` — alpha encodes AO factor; + * `'none'` — skip alpha-based edge stopping. + * + * @type {DenoiseAlphaSource} + * @default 'raylength' + */ + this.alphaSource = 'raylength'; + + this.flickerSuppression = uniform( 1 ); + this.adaptiveTrust = uniform( 0 ); + + this.updateBeforeType = NodeUpdateType.FRAME; + + this._resolution = uniform( new Vector2() ); + this._fovY = uniform( MathUtils.degToRad( camera.fov ) ); + this._cameraProjectionMatrixInverse = uniform( new Matrix4().copy( camera.projectionMatrixInverse ) ); + this._cameraProjectionMatrix = uniform( new Matrix4().copy( camera.projectionMatrix ) ); + this._viewMatrix = uniform( new Matrix4().copy( camera.matrixWorldInverse ) ); + + this._renderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } ); + this._renderTarget.texture.name = 'RecurrentDenoiseNode.output'; + + this._material = new NodeMaterial(); + this._material.name = 'RecurrentDenoise'; + + this._textureNode = passTexture( this, this._renderTarget.texture ); + + } + + setSize( width, height ) { + + if ( width === null || height === null ) return; + + this._renderTarget.setSize( width, height ); + this._resolution.value.set( width, height ); + + } + + getTextureNode() { + + return this._textureNode; + + } + + /** + * Returns the internal output render target (e.g. for temporal reprojection/SSGI temporal feedback loops). + * + * @returns {RenderTarget} + */ + getRenderTarget() { + + return this._renderTarget; + + } + + updateBefore( frame ) { + + const { renderer } = frame; + + const drawingBufferSize = renderer.getDrawingBufferSize( _size ); + const width = drawingBufferSize.width; + const height = drawingBufferSize.height; + + const needsRestart = this._renderTarget.width !== width || this._renderTarget.height !== height; + this.setSize( width, height ); + + this._cameraProjectionMatrix.value.copy( this.camera.projectionMatrix ); + this._cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse ); + this._viewMatrix.value.copy( this.camera.matrixWorldInverse ); + + if ( this.camera.isPerspectiveCamera ) { + + this._fovY.value = MathUtils.degToRad( this.camera.fov ); + + } + + if ( frame.frameId !== undefined ) this._noiseIndex.value = frame.frameId; + + // Denoise renders via an internal _quadMesh, not through the RenderPipeline output graph. + // Upstream passes (e.g. TemporalReprojectNode) referenced by a PassTextureNode input are + // otherwise never scheduled, their updateBefore() would not run and this pass would sample + // a stale/empty render target. + if ( this.textureNode.isPassTextureNode === true ) frame.updateBeforeNode( this.textureNode.passNode ); + + _rendererState = RendererUtils.resetRendererState( renderer, _rendererState ); + + if ( needsRestart === true ) { + + renderer.initRenderTarget( this._renderTarget ); + renderer.setRenderTarget( this._renderTarget ); + renderer.clear(); + renderer.setRenderTarget( null ); + + } + + renderer.setRenderTarget( this._renderTarget ); + _quadMesh.material = this._material; + _quadMesh.name = 'RecurrentDenoise'; + _quadMesh.render( renderer ); + renderer.setRenderTarget( null ); + + RendererUtils.restoreRendererState( renderer, _rendererState ); + + } + + setup( builder ) { + + const sampleAnalyticNoise = bindAnalyticNoise( this._resolution, NOISE_ROTATION_SEED ); + + const noiseRotationMatrix = Fn( ( [ r ] ) => { + + const angle = r.mul( 2 ).mul( PI ); + return mat2( cos( angle ), sin( angle ).negate(), sin( angle ), cos( angle ) ); + + } ); + + const sampleTexture = ( uvCoord ) => texture( this.textureNode, uvCoord ).max( 0 ); + const sampleRaw = ( uvCoord ) => this.rawNode?.sample( uvCoord )?.max( 0 ) ?? vec3( 0 ).max( 0 ); + const sampleDepth = ( uvCoord ) => this.depthNode?.sample( uvCoord )?.x ?? float( 0.5 ); + const sampleNormal = ( uvCoord ) => unpackRGBToNormal( this.normalNode?.sample( uvCoord )?.rgb ?? vec3( 0, 0, 1 ) ); + const sampleRoughnessMetalness = ( uvCoord ) => this.roughnessMetalnessNode?.sample( uvCoord )?.rg ?? vec2( 0, 1 ); + const sampleDiffuse = ( uvCoord ) => this.diffuseNode?.sample( uvCoord )?.rgb ?? vec3( 0 ); + + // Neighborhood luma moments for the adaptive-trust (firefly) gating of the temporal blend. + const getNeighborhoodStats = Fn( ( [ uvCoord, centerSample ] ) => { + + const rlSum = float( 0 ).toVar(); + const rlSumW = float( 0 ).toVar(); + const meanLuma = float( 0 ).toVar(); + const m2Luma = float( 0 ).toVar(); + const lumaCount = float( 0 ).toVar(); + const hasEnvRay = bool( false ).toVar(); + + // 4-tap cross (pre-sampled center + 4 axis neighbors) instead of a full 3×3 — about half the fetches. + // The center tap reuses the caller's already-sampled raw texel. + const accumulate = ( dx, dy, sample ) => { + + const neighbor = sample !== undefined + ? sample + : texture( this.rawNode, uvCoord.add( vec2( dx, dy ).div( this._resolution ) ) ).max( 0 ).toConst(); + + if ( this.alphaSource === 'raylength' ) { + + const sampleRl = neighbor.a.toVar(); + If( sampleRl.greaterThan( ENV_RAY_LENGTH_THRESHOLD ), () => { + + sampleRl.assign( 0.25 ); + hasEnvRay.assign( true ); + + } ); + const w = float( 1 ).div( sampleRl.add( 0.001 ) ); + rlSum.addAssign( sampleRl.mul( w ) ); + rlSumW.addAssign( w ); + + } + + If( this.adaptiveTrust.greaterThan( 0 ), () => { + + const nLuma = luminance( neighbor.rgb ); + lumaCount.addAssign( 1 ); + const delta = nLuma.sub( meanLuma ).toConst(); + meanLuma.addAssign( delta.div( lumaCount ) ); + m2Luma.addAssign( delta.mul( nLuma.sub( meanLuma ) ) ); + + } ); + + }; + + accumulate( 0, 0, centerSample ); + accumulate( - 1, 0 ); + accumulate( 1, 0 ); + accumulate( 0, - 1 ); + accumulate( 0, 1 ); + + const avgRayLength = this.alphaSource === 'raylength' ? rlSum.div( rlSumW ) : float( 1 ); + const stddevLuma = sqrt( m2Luma.div( lumaCount.max( 1 ) ) ); + + // vec3( avgRayLength, meanLuma, stddevLuma ) + return vec4( avgRayLength, meanLuma, stddevLuma, hasEnvRay.toFloat() ); + + } ).setLayout( { + name: 'getNeighborhoodStats', + type: 'vec4', + inputs: [ + { name: 'uvCoord', type: 'vec2' }, + { name: 'centerSample', type: 'vec4' } + ] + } ); + + const denoiseFn = Fn( ( [ uvCoord ] ) => { + + const result = property( 'vec4' ); + + const depth = sampleDepth( uvCoord ).toConst(); + + const runDenoise = () => { + + const viewNormal = sampleNormal( uvCoord ).toConst(); + const worldNormal = viewNormal.transformDirection( this._viewMatrix ).toConst(); + const texel = sampleTexture( uvCoord ).max( 0 ).toConst(); + + const viewPosition = getViewPosition( uvCoord, depth, this._cameraProjectionMatrixInverse ).toConst(); + const roughnessMetalness = sampleRoughnessMetalness( uvCoord ).toConst(); + const roughness = roughnessMetalness.g; + const metalness = roughnessMetalness.r; + + const noiseTexel = sampleAnalyticNoise( uvCoord, this._noiseIndex ); + const rotationMatrix = noiseRotationMatrix( noiseTexel.r ); + + const frameNum = float( 1 ).div( texel.a ); + const varianceFactor = getTemporalVarianceFactor( frameNum, this.strength.oneMinus() ); + const aggressivity = varianceFactor.oneMinus(); + + const raw = sampleRaw( uvCoord ).toConst(); + + const viewZ = abs( viewPosition.z ); + const rl = float( 1 ).toVar(); + const nbhdMeanLuma = float( 0 ).toVar(); + const nbhdStddevLuma = float( 0 ).toVar(); + const hasEnvRay = bool( false ).toVar(); + + if ( this.alphaSource === 'raylength' ) { + + const stats = getNeighborhoodStats( uvCoord, raw ); + rl.assign( stats.x ); + nbhdMeanLuma.assign( stats.y ); + nbhdStddevLuma.assign( stats.z ); + hasEnvRay.assign( stats.w.greaterThan( 0.5 ) ); + + } else { + + If( this.adaptiveTrust.greaterThan( 0 ), () => { + + const stats = getNeighborhoodStats( uvCoord, raw ); + nbhdMeanLuma.assign( stats.y ); + nbhdStddevLuma.assign( stats.z ); + + } ); + + } + + const tanHalfFovY = this.alphaSource === 'raylength' ? tan( this._fovY.mul( 0.5 ) ).toConst() : null; + const hitDistFactor = this.alphaSource === 'raylength' + ? computeHitDistFactor( rl, viewZ, tanHalfFovY ).toConst() + : float( 1 ); + + const denoised = texel.rgb.toVar(); + const totalWeight = float( 1 ).toVar(); + const denoisedFrame = frameNum.toVar(); + const totalFrameWeight = float( 1 ).toVar(); + + const denoisedRaw = raw.rgb.toVar(); + const totalWeightRaw = float( 1 ).toVar(); + + If( raw.rgb.length().lessThan( 0.0001 ), () => { + + denoisedRaw.assign( vec3( 0 ) ); + totalWeightRaw.assign( 0 ); + + } ); + + const avgAo = this.alphaSource === 'ao' ? raw.a.toConst() : float( 1 ); + const mappedAvgAo = this.alphaSource === 'ao' ? mapAo( avgAo ) : float( 0 ); + + const worldRadius = this.radius.mul( WORLD_RADIUS_SCALE ).toVar(); + + if ( this.mode === 'specular' ) { + + worldRadius.mulAssign( rl.mul( viewPosition.z.abs() ) ); + worldRadius.mulAssign( roughness.sqrt().max( 0.01 ) ); + + } else { + + worldRadius.mulAssign( avgAo.pow( 2 ).mul( viewPosition.z.abs() ) ); + + } + + worldRadius.mulAssign( mix( 1, AGGRESSIVITY_RADIUS_MIN, aggressivity ) ); + + const T = vec3( 0 ).toVar(); + const B = vec3( 0 ).toVar(); + + if ( this.mode === 'specular' ) { + + const V = normalize( viewPosition ).negate(); + const D = getSpecularDominantDirection( viewNormal, V, roughness ); + const R = reflect( D.negate(), viewNormal ); + const Tv = normalize( cross( viewNormal, R ) ); + const Bv = cross( R, Tv ); + const viewAngle = abs( viewNormal.z ).acos().div( float( Math.PI * 0.5 ) ).clamp( 0, 1 ); + const skewFactor = mix( 1.0, roughness, viewAngle ); + T.assign( Tv.mul( skewFactor ) ); + B.assign( Bv ); + + } else { + + const up = vec3( 0, 0, 1 ); + const Tv = cross( up, viewNormal ).normalize().toVar(); + If( Tv.length().lessThan( EPSILON ), () => { + + Tv.assign( cross( vec3( 0, 1, 0 ), viewNormal ).normalize() ); + + } ); + T.assign( Tv ); + B.assign( cross( viewNormal, Tv ).normalize() ); + + } + + T.mulAssign( worldRadius ); + B.mulAssign( worldRadius ); + + const centerDiffuse = sampleDiffuse( uvCoord ).toConst(); + const radiusShrink = float( 1 ).toVar(); + + // Directional analog of radiusShrink: an accumulated tangent-space shift that skews + // subsequent taps toward directions that yielded high weight (related geometry). + const polarBias = vec2( 0 ).toVar(); + + const depthWeightScale = this.depthPhi.mul( 500 ).mul( viewNormal.z.abs() ).div( viewPosition.z.abs() ); + + // Lobe geometry depends only on per-pixel terms, so compute its falloff constant once here. + const lobeFalloff = lobeNormalFalloff( roughness, aggressivity, this.normalPhi.oneMinus() ).toConst(); + + Loop( { start: int( 0 ), end: int( KERNEL_SAMPLES ), type: 'int', condition: '<', name: 'i' }, ( { i } ) => { + + const baseOffset = vogelDisk( float( i ), 1 ).toVar(); + const sampleDir = baseOffset.normalize().toConst(); + + // Blend the tap direction toward the polar bias, then restore the Vogel radius and shrink. + const skewedDir = mix( sampleDir, polarBias.max( EPSILON ).normalize(), this.adapt.mul( aggressivity ) + .mul( polarBias.dot( polarBias ).greaterThan( 0.001 ).select( 1, 0 ) ) ); + const offset = rotationMatrix.mul( skewedDir.mul( baseOffset.length().mul( radiusShrink ) ) ).toVar(); + + // Exact per-sample view-space projection (both paths) + const sampleViewPos = viewPosition.add( B.mul( offset.x ).add( T.mul( offset.y ) ) ); + const sampleUv = getScreenPosition( sampleViewPos, this._cameraProjectionMatrix ).toVar(); + sampleUv.assign( sampleUv.abs().oneMinus().abs().oneMinus().clamp() ); + + const neighborColor = sampleTexture( sampleUv ).max( 0 ).toConst(); + + // When no raw texture is bound, sampleRaw falls back to the filtered texture at the same UV. + const rawNeighborColor = sampleRaw( sampleUv ).max( 0 ).toVar(); + // if ( this.mode === 'diffuse' ) rawNeighborColor.rgb.assign( mix( neighborColor.rgb, rawNeighborColor.rgb, neighborColor.a ) ); + + const nDepth = sampleDepth( sampleUv ); + const nViewPosition = getViewPosition( sampleUv, nDepth, this._cameraProjectionMatrixInverse ).toConst(); + const nViewZ = abs( nViewPosition.z ).toConst(); + + const kernelDiff = float( 0 ).toVar(); + + // Luma edge stopping + kernelDiff.addAssign( luminance( rawNeighborColor.rgb ).sub( luminance( raw.rgb ) ).abs().mul( this.lumaPhi ).mul( 10 ) ); + + // Diffuse edge stopping (only relevant for specular mode) + if ( this.diffuseNode !== null ) { + + kernelDiff.addAssign( ( diffuseColorDistance( centerDiffuse, sampleDiffuse( sampleUv ), float( 0 ) ).mul( this.diffusePhi ).mul( metalness ) ) ); + + } + + // AO edge stopping + if ( this.alphaSource === 'ao' ) { + + const neighborMappedAo = mapAo( rawNeighborColor.a ); + // We multiply here with aggressivity as well, since early application of aoW yields noise + const aoW = mappedAvgAo.div( mappedAvgAo.add( neighborMappedAo ).add( AO_EDGE_STOPPING_BIAS ) ).mul( this.alphaPhi ).mul( aggressivity ); + + kernelDiff.addAssign( ( aoW ) ); + + + } else if ( this.alphaSource === 'raylength' ) { + + // Ray length edge stopping + + const neighborHitDistFactor = computeHitDistFactor( rawNeighborColor.a, nViewZ, tanHalfFovY ); + const hdfDiff = hitDistFactor.sub( neighborHitDistFactor ).abs(); + + const rayLengthFactor = hdfDiff.mul( this.alphaPhi ).div( viewPosition.z.abs() ); + + // Env rays are harder to compare so we accept if this sample is an env ray and there is an env ray in the neighborhood + kernelDiff.addAssign( rawNeighborColor.a.greaterThan( ENV_RAY_LENGTH_THRESHOLD ).and( hasEnvRay ).select( 1, rayLengthFactor ) ); + + } + + // Roughness edge stopping + if ( this.mode === 'specular' ) kernelDiff.addAssign( ( abs( roughness.sub( sampleRoughnessMetalness( sampleUv ).g ) ).mul( this.roughnessPhi ) ) ); + + const nViewNormal = sampleNormal( sampleUv ); + const nWorldNormal = nViewNormal.transformDirection( this._viewMatrix ); + const distToPlane = planeDistance( viewPosition, nViewPosition, viewNormal ); + + // Geometric edge stopping (depth and normal) + const depthDiff = distToPlane.mul( depthWeightScale ); + const normalW = lobeNormalWeight( worldNormal, nWorldNormal, lobeFalloff ); + + // Sum every negative-exponent edge-stopping term (kernel + depth/plane, plus the SSR hit-distance term) + const w = exp( kernelDiff.mul( aggressivity ).add( depthDiff ).negate() ).mul( normalW ).toVar(); + + // Feedback to shrink radius based on the weight + radiusShrink.assign( mix( radiusShrink, w, this.adapt ) ); + + // Polar feedback: skew subsequent taps toward high-weight directions (related geometry) + polarBias.assign( mix( polarBias, sampleDir.mul( w.sub( 0.5 ) ), 0.5 ) ); + + // to mitigate the effect of fireflies and high variance in recently disoccluded regions, we weigh by the inverse luminance for the first 5 frames + w.mulAssign( mix( float( 1 ).div( luminance( rawNeighborColor.rgb ).pow( 2 ).add( 0.01 ) ), 1, frameNum.div( 5 ).min( 1 ) ) ); + + denoisedRaw.addAssign( rawNeighborColor.rgb.mul( w ) ); + totalWeightRaw.addAssign( w ); + + denoised.addAssign( neighborColor.rgb.mul( w ) ); + totalWeight.addAssign( w ); + + // Denoising the alpha (accumulation speed), to get smoother disocclusion transitions + If( this.smoothDisocclusions, () => { + + const neighborAWeight = neighborColor.a.greaterThan( texel.a ).select( w.mul( 0.33 ), 0 ); + denoisedFrame.addAssign( float( 1 ).div( neighborColor.a ).mul( neighborAWeight ) ); + totalFrameWeight.addAssign( neighborAWeight ); + + } ); + + } ); + + denoised.divAssign( totalWeight.max( EPSILON ) ); + denoised.assign( denoised.max( EPSILON ) ); + denoisedRaw.divAssign( totalWeightRaw.max( EPSILON ) ); + + if ( this.accumulate ) { + + const computedFrame = denoisedFrame.div( totalFrameWeight.max( EPSILON ) ); + const a = float( 1 ).div( computedFrame.max( EPSILON ) ).toConst(); + + if ( this.rawNode !== null ) { + + const blended = karisTemporalBlend( + denoised, + denoisedRaw, + a, + this.flickerSuppression, + this.adaptiveTrust, + nbhdMeanLuma, + nbhdStddevLuma + ); + + result.assign( vec4( blended, a ) ); + + } else { + + const finalDenoised = mix( denoised, denoisedRaw, a ); + result.assign( vec4( finalDenoised, a ) ); + + } + + } else { + + result.assign( vec4( denoised, texel.a ) ); + + } + + }; + + If( depth.greaterThanEqual( 1.0 ), () => { + + Discard(); + + } ).Else( runDenoise ); + + return result; + + } ); + + this._material.fragmentNode = denoiseFn( uv() ).context( builder.getSharedContext() ); + this._material.needsUpdate = true; + + return this._textureNode; + + } + + dispose() { + + this._renderTarget.dispose(); + this._material.dispose(); + + } + +} + +export default RecurrentDenoiseNode; + +/** + * @tsl + * @param {Node} inputTexture - Temporally filtered input to denoise (e.g. TRAA output). + * @param {Camera} camera + * @param {RecurrentDenoiseNodeOptions} [options={}] + * @returns {RecurrentDenoiseNode} + */ +export const recurrentDenoise = ( inputTexture, camera, options = {} ) => nodeObject( new RecurrentDenoiseNode( + toTextureNode( inputTexture ), + camera, + options +) ); diff --git a/examples/jsm/tsl/display/SSRNode.js b/examples/jsm/tsl/display/SSRNode.js index 11d052708a4f5b..e7905c59434ca3 100644 --- a/examples/jsm/tsl/display/SSRNode.js +++ b/examples/jsm/tsl/display/SSRNode.js @@ -1,11 +1,30 @@ -import { HalfFloatType, RenderTarget, Vector2, RendererUtils, QuadMesh, TempNode, NodeMaterial, NodeUpdateType, LinearFilter, LinearMipmapLinearFilter } from 'three/webgpu'; -import { texture, reference, viewZToPerspectiveDepth, logarithmicDepthToViewZ, getScreenPosition, getViewPosition, mul, div, cross, float, Continue, Break, Loop, int, max, abs, sub, If, dot, reflect, normalize, screenCoordinate, nodeObject, Fn, passTexture, uv, uniform, perspectiveDepthToViewZ, orthographicDepthToViewZ, vec2, vec3, vec4 } from 'three/tsl'; +import { Break, Continue, Fn, If, Loop, abs, bool, cross, distance, div, dot, float, getScreenPosition, getViewPosition, int, logarithmicDepthToViewZ, luminance, max, min, mix, mul, nodeObject, normalize, orthographicDepthToViewZ, passTexture, perspectiveDepthToViewZ, reference, reflect, sub, texture, trunc, uniform, uv, vec2, vec3, vec4, viewZToPerspectiveDepth } from 'three/tsl'; +import { HalfFloatType, LinearFilter, LinearMipmapLinearFilter, Matrix4, NodeMaterial, NodeUpdateType, QuadMesh, RenderTarget, RendererUtils, TempNode, Vector2, Vector3 } from 'three/webgpu'; +import { bindAnalyticNoise } from '../utils/RNoise.js'; +import { ENV_RAY_LENGTH, getSpecularDominantFactor, ggxReflectionSample } from '../utils/SpecularHelpers.js'; import { boxBlur } from './boxBlur.js'; +import ImportanceSampledEnvironment from './ImportanceSampledEnvironment.js'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); let _rendererState; +// Maximum ray-march step count; `quality` (0..1) scales it to a fixed per-ray count. +const MAX_STEPS = 64; + +/** + * @typedef {Object} SSRNodeOptions + * @property {boolean} [stochastic=false] - When `false`, traces a single mirror reflection and softens roughness with a blur pass (first-generation SSR). When `true`, varies the reflection direction per pixel with stochastic GGX rays (second-generation SSR); higher quality on rough/glossy surfaces but noisier, so it expects a temporal/spatial denoiser downstream. + * @property {Node} [metalnessNode=null] - Per-pixel metalness. Drives GGX reflection sampling and, with `reflectNonMetals=false`, the non-metal early-out. + * @property {Node} [roughnessNode=null] - Per-pixel roughness. Drives GGX sampling and the blur mip selection. + * @property {boolean} [reflectNonMetals=false] - Only used when `stochastic=false`. When `false`, non-metallic surfaces are discarded for a noticeable performance gain; set `true` to also reflect dielectrics (e.g. marble, polished wood, plastic). + * @property {Texture} [environmentNode=null] - Equirectangular HDR environment map with CPU-side `image.data` (e.g. from RGBELoader). Not compatible with PMREM / `scene.environment` cubemaps. + * @property {boolean} [envImportanceSampling=false] - When `true`, precomputes env-luminance CDF tables and uses MIS for environment misses. Build-time only. + * @property {Node} [diffuseNode=null] - Scene diffuse / base color. Defaults to `vec3(1)` in the shader when omitted. + * @property {boolean} [binaryRefine=false] - Sub-step binary-search refinement of detected hits. Compile-time constant (baked into the shader at construction). + * @property {Camera} [camera=null] - Camera the scene is rendered with. Inferred from the color pass when omitted. + */ + /** * Post processing node for computing screen space reflections (SSR). * @@ -28,14 +47,42 @@ class SSRNode extends TempNode { * @param {Node} colorNode - The node that represents the beauty pass. * @param {Node} depthNode - A node that represents the beauty pass's depth. * @param {Node} normalNode - A node that represents the beauty pass's normals. - * @param {Node} metalnessNode - A node that represents the beauty pass's metalness. - * @param {?Node} [roughnessNode=null] - A node that represents the beauty pass's roughness. - * @param {?Camera} [camera=null] - The camera the scene is rendered with. + * @param {SSRNodeOptions} [options] - Optional inputs for material and environment data. */ - constructor( colorNode, depthNode, normalNode, metalnessNode, roughnessNode = null, camera = null ) { + constructor( colorNode, depthNode, normalNode, options = {} ) { super( 'vec4' ); + const { + stochastic = false, + metalnessNode = null, + roughnessNode = null, + reflectNonMetals = false, + environmentNode = null, + envImportanceSampling = false, + diffuseNode = null, + binaryRefine = false + } = options; + + let camera = options.camera ?? null; + + /** + * When `true`, the reflection direction is varied per pixel with stochastic GGX rays + * (second-generation SSR). When `false`, a single mirror reflection is traced and + * roughness is softened with a blur pass (first-generation SSR). + * + * @type {boolean} + */ + this.stochastic = stochastic; + + /** + * When `true`, env-luminance CDF tables are built and MIS is used for environment misses. + * Fixed at construction time. + * + * @type {boolean} + */ + this.envImportanceSampling = envImportanceSampling; + /** * The node that represents the beauty pass. * @@ -43,6 +90,14 @@ class SSRNode extends TempNode { */ this.colorNode = colorNode; + /** + * A node that represents the scene's diffuse color (typically the MRT `diffuseColor` attachment). + * When `null`, the shader uses `vec3(1)`. + * + * @type {?Node} + */ + this.diffuseNode = diffuseNode !== null ? nodeObject( diffuseNode ) : null; + /** * A node that represents the beauty pass's depth. * @@ -58,23 +113,32 @@ class SSRNode extends TempNode { this.normalNode = normalNode; /** - * A node that represents the beauty pass's metalness. + * Per-pixel metalness, used to drive the GGX reflection sampling and the non-metal + * early-out. When `null`, the shader treats surfaces as non-metallic. * - * @type {Node} + * @type {?Node} */ this.metalnessNode = metalnessNode; /** - * Whether the SSR reflections should be blurred or not. Blurring is a costly - * operation so turn it off if you encounter performance issues on certain - * devices. + * Per-pixel roughness, used to drive the GGX reflection sampling and the blur mip + * selection. When `null`, the shader treats surfaces as fully smooth. * - * @private - * @type {Node} - * @default false + * @type {?Node} */ this.roughnessNode = roughnessNode; + /** + * Only used when {@link SSRNode#stochastic} is `false`. When `false`, non-metallic + * surfaces are discarded for a noticeable performance gain; set `true` to also + * reflect dielectrics. Baked into the shader as a compile-time constant; assigning a + * new value recompiles the SSR material. + * + * @type {boolean} + * @default false + */ + this._reflectNonMetals = reflectNonMetals; + /** * The resolution scale. Valid values are in the range * `[0,1]`. `1` means best quality but also results in @@ -111,11 +175,45 @@ class SSRNode extends TempNode { this.thickness = uniform( 0.1 ); /** - * Controls how the SSR reflections are blended with the beauty pass. + * A multiplier for the overall reflection intensity. `1` leaves the + * reflections unchanged, lower values dim them and higher values boost them. + * + * @type {UniformNode} + * @default 1 + */ + this.intensity = uniform( 1 ); + + /** + * Screen-edge fade width, in UV units. As a screen-space hit approaches a screen + * border, the reflection is faded over this distance — either toward the environment + * reflection ({@link SSRNode#screenEdgeFadeBlack} `false`) or to zero intensity + * (`true`). `0` disables it. + * + * @type {UniformNode} + * @default 0.2 + */ + this.screenEdgeFade = uniform( 0.2 ); + + /** + * When `true`, SSR fades to zero near screen borders instead of blending toward + * the environment map. Hits are faded by the reflection sample UV; misses are + * faded by the surface pixel UV. + * + * Baked into the shader as a compile-time constant so the unused fade branch is + * eliminated; assigning a new value recompiles the SSR material. + * + * @type {boolean} + * @default false + */ + this._screenEdgeFadeBlack = false; + + /** + * Absolute env luminance cap. HDR env samples above this are scaled down (hue preserved). * * @type {UniformNode} + * @default 10 */ - this.opacity = uniform( 1 ); + this.maxLuminance = uniform( 10 ); /** * This parameter controls how detailed the raymarching process works. @@ -130,12 +228,72 @@ class SSRNode extends TempNode { */ this.quality = uniform( 0.5 ); + /** + * Mirror bias for the stochastic GGX sampling. Concentrates the reflected rays toward + * the lobe's narrow (near-mirror) core, trading a small amount of bias for less noise. + * `0` samples the full VNDF lobe; values toward `1` tighten the cone. Range `[0,1]`. + * + * @type {UniformNode} + * @default 0.5 + */ + this.mirrorBias = uniform( 0.5 ); + /** * The quality of the blur. Must be an integer in the range `[1,3]`. * - * @type {UniformNode} + * Baked into the blur shader as a compile-time constant so the `(size*2+1)²` + * sample loop unrolls; assigning a new value recompiles the blur material. + * + * @type {number} + * @default 2 + */ + this._blurQuality = 2; + + /** + * Enables sub-step binary-search refinement of a detected hit. When on, a coarse + * crossing is bisected toward the exact intersection (sharper hits, less step + * aliasing) at the cost of extra depth samples. Baked into the shader as a + * compile-time constant; assigning a new value rebuilds the SSR material. + * + * @type {boolean} + * @default false */ - this.blurQuality = uniform( 2 ); + this._binaryRefine = binaryRefine; + + /** + * Non-linear step distribution exponent. `1` = uniform steps; `> 1` concentrates + * samples near the ray origin — where most short-range reflections are missed — and + * spaces them out toward maxDistance, as `s = (i / steps) ^ stepExponent`. + * + * Baked into the shader as a compile-time constant so `pow()` folds to a few + * multiplies; assigning a new value recompiles the SSR material. Only used by the + * stochastic reflection path. + * + * @type {number} + * @default 2 + */ + this._stepExponent = 2; + + /** + * HDR environment map for screen-space misses. + * + * @type {?Texture} + */ + this.environmentNode = environmentNode; + + /** + * A node that represents the history texture for multi-bounce reflections. + * + * @type {?Texture} + */ + this.historyTexture = null; + + /** + * A node that represents the velocity texture for reprojection. + * + * @type {?Node} + */ + this.velocityTexture = null; // @@ -200,21 +358,38 @@ class SSRNode extends TempNode { */ this._cameraFar = reference( 'far', 'float', camera ); + this._cameraWorldMatrix = uniform( new Matrix4().copy( camera.matrixWorld ) ); + this._cameraWorldPosition = uniform( new Vector3().copy( camera.position ) ); + + this._cameraViewMatrix = uniform( new Matrix4().copy( camera.matrixWorld ) ); + this._cameraViewMatrixInverse = uniform( new Matrix4().copy( camera.matrixWorldInverse ) ); + /** - * Whether the scene's camera is perspective or orthographic. + * The resolution of the pass. * * @private - * @type {UniformNode} + * @type {UniformNode} */ - this._isPerspectiveCamera = uniform( camera.isPerspectiveCamera === true ); + this._resolution = uniform( new Vector2() ); + + this._noiseIndex = uniform( 0 ); /** - * The resolution of the pass. + * CDF-backed environment sampler. Created when {@link setEnvMap} is called. * * @private - * @type {UniformNode} + * @type {?ImportanceSampledEnvironment} */ - this._resolution = uniform( new Vector2() ); + this._importanceEnvironment = null; + + /** + * Intensity multiplier applied to environment-map reflections on screen-space + * misses and at screen edges. Defaults to π to match the former hardcoded multiplier. + * + * @type {UniformNode} + * @default Math.PI + */ + this.environmentIntensity = uniform( Math.PI ); /** * The render target the SSR is rendered into. @@ -244,6 +419,16 @@ class SSRNode extends TempNode { this._ssrMaterial = new NodeMaterial(); this._ssrMaterial.name = 'SSRNode.SSR'; + /** + * The SSR fragment `Fn` and its shared context, captured in {@link SSRNode#setup}. + * Re-invoking the `Fn` produces a fresh node graph, which is how the baked + * compile-time constants are re-applied when they change (see {@link SSRNode#_buildSSRMaterial}). + * + * @private + */ + this._ssrFn = null; + this._sharedContext = null; + /** * The blur material. * @@ -272,10 +457,10 @@ class SSRNode extends TempNode { let blurredTextureNode = null; - if ( this.roughnessNode !== null ) { + if ( this.stochastic === false && this.roughnessNode !== null ) { const mips = this._blurRenderTarget.texture.mipmaps.length - 1; - const r = float( this.roughnessNode ); + const r = this.roughnessNode; const lod = r.mul( r ).mul( mips ).clamp( 0, mips ); blurredTextureNode = passTexture( this, this._blurRenderTarget.texture ).level( lod ); @@ -290,6 +475,159 @@ class SSRNode extends TempNode { */ this._blurredTextureNode = blurredTextureNode; + if ( environmentNode !== null && environmentNode.isTexture === true ) { + + this.setEnvMap( environmentNode ); + + } + + } + + /** + * Non-linear step distribution exponent (compile-time constant). See the backing + * field for details. Assigning a new value recompiles the SSR material. + * + * @type {number} + */ + get stepExponent() { + + return this._stepExponent; + + } + + set stepExponent( value ) { + + if ( value !== this._stepExponent ) { + + this._stepExponent = value; + this._buildSSRMaterial(); + + } + + } + + /** + * Blur kernel size (compile-time constant). Assigning a new value recompiles the + * blur material. + * + * @type {number} + */ + get blurQuality() { + + return this._blurQuality; + + } + + set blurQuality( value ) { + + if ( value !== this._blurQuality ) { + + this._blurQuality = value; + + // The size is baked into the boxBlur node tree, so rebuild it (recompiles the material). + if ( this.stochastic === false ) this._buildBlurMaterial(); + + } + + } + + /** + * Builds (or rebuilds) the blur material's node graph, baking the current + * {@link SSRNode#blurQuality} as the kernel size so the sample loop unrolls. + * + * @private + */ + _buildBlurMaterial() { + + this._blurMaterial.fragmentNode = boxBlur( texture( this._ssrRenderTarget.texture ), { size: this._blurQuality, separation: this._blurSpread } ); + this._blurMaterial.needsUpdate = true; + + } + + /** + * Whether SSR fades to black near screen borders (compile-time constant). Assigning + * a new value recompiles the SSR material. + * + * @type {boolean} + */ + get screenEdgeFadeBlack() { + + return this._screenEdgeFadeBlack; + + } + + set screenEdgeFadeBlack( value ) { + + if ( value !== this._screenEdgeFadeBlack ) { + + this._screenEdgeFadeBlack = value; + this._buildSSRMaterial(); + + } + + } + + /** + * Whether sub-step binary-search hit refinement is enabled (compile-time constant). + * Assigning a new value rebuilds the SSR material. + * + * @type {boolean} + */ + get binaryRefine() { + + return this._binaryRefine; + + } + + set binaryRefine( value ) { + + if ( value !== this._binaryRefine ) { + + this._binaryRefine = value; + this._buildSSRMaterial(); + + } + + } + + /** + * Whether dielectrics are reflected in the non-stochastic path (compile-time constant). + * Assigning a new value rebuilds the SSR material. + * + * @type {boolean} + */ + get reflectNonMetals() { + + return this._reflectNonMetals; + + } + + set reflectNonMetals( value ) { + + if ( value !== this._reflectNonMetals ) { + + this._reflectNonMetals = value; + this._buildSSRMaterial(); + + } + + } + + /** + * Rebuilds the SSR material's node graph by re-invoking the fragment `Fn`, which + * re-bakes the compile-time constants ({@link SSRNode#binaryRefine}, + * {@link SSRNode#stepExponent}, {@link SSRNode#screenEdgeFadeBlack}) at their current + * values. A no-op until {@link SSRNode#setup} has captured the `Fn`. + * + * @private + */ + _buildSSRMaterial() { + + if ( this._ssrFn === null ) return; + + this._ssrMaterial.fragmentNode = this._ssrFn().context( this._sharedContext ); + this._ssrMaterial.needsUpdate = true; + } /** @@ -299,7 +637,7 @@ class SSRNode extends TempNode { */ getTextureNode() { - return this.roughnessNode !== null ? this._blurredTextureNode : this._textureNode; + return ( this.stochastic === false && this.roughnessNode !== null ) ? this._blurredTextureNode : this._textureNode; } @@ -320,6 +658,81 @@ class SSRNode extends TempNode { } + /** + * Wires the feedback inputs for multi-bounce reflections: the previous frame's + * denoised result (`history`) and the velocity buffer used to reproject it + * (`velocity`). `history` accepts the producing node (e.g. a + * {@link RecurrentDenoiseNode}) — its output render target is used — or a raw + * texture. Pass `null` for both to disable multi-bounce. + * + * @param {Texture} history + * @param {Node} velocity + */ + setHistory( history, velocity ) { + + this.historyTexture = ( history && typeof history.getRenderTarget === 'function' ) + ? history.getRenderTarget().texture + : history; + this.velocityTexture = velocity; + + } + + /** + * Sets the environment map for importance-sampled env lighting when + * screen-space rays miss. Call this whenever the scene's env map changes. + * + * Uses {@link ImportanceSampledEnvironment} (CDF + MIS adapted from + * [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer)). + * + * @param {Texture|null} hdr - The equirectangular HDR environment map, or null to disable. + * @see {@link https://github.com/gkjohnson/three-gpu-pathtracer} + */ + setEnvMap( hdr ) { + + if ( hdr === null ) { + + if ( this._importanceEnvironment !== null ) { + + this._importanceEnvironment.clear(); + this._importanceEnvironment = null; + + } + + this._buildSSRMaterial(); + return; + + } + + if ( hdr.image === undefined || hdr.image.data === undefined ) { + + console.warn( 'SSRNode: `environmentNode` / `setEnvMap()` expects an equirectangular HDR texture with CPU-side image data (e.g. RGBELoader). PMREM cubemaps and `scene.environment` are not supported.' ); + return; + + } + + if ( this._importanceEnvironment === null ) { + + this._importanceEnvironment = new ImportanceSampledEnvironment( this.envImportanceSampling ); + + } + + this._importanceEnvironment.updateFrom( hdr ); + this._buildSSRMaterial(); + + } + + /** + * Intensity multiplier for the importance-sampled env contribution. + * Only available after {@link setEnvMap} has been called. + * + * @type {?UniformNode} + */ + get envMapIntensity() { + + return this._importanceEnvironment !== null ? this._importanceEnvironment.intensity : null; + + } + /** * This method is used to render the effect once per frame. * @@ -329,6 +742,9 @@ class SSRNode extends TempNode { const { renderer } = frame; + this._cameraWorldMatrix.value.copy( this.camera.matrixWorld ); + this._cameraWorldPosition.value.copy( this.camera.position ); + _rendererState = RendererUtils.resetRendererState( renderer, _rendererState ); const ssrRenderTarget = this._ssrRenderTarget; @@ -340,6 +756,9 @@ class SSRNode extends TempNode { this.setSize( size.width, size.height ); + // Advance the noise index once per frame (matches SSGI / Denoise). + this._noiseIndex.value = ( this._noiseIndex.value + 1 ) % 0x7fffffff; + // clear renderer.setMRT( null ); @@ -353,7 +772,7 @@ class SSRNode extends TempNode { // blur (optional) - if ( this.roughnessNode !== null ) { + if ( this.stochastic === false && this.roughnessNode !== null ) { // blur mips but leave the base mip unblurred @@ -386,7 +805,7 @@ class SSRNode extends TempNode { const uvNode = uv(); - const pointToLineDistance = Fn( ( [ point, linePointA, linePointB ] )=> { + const pointToLineDistance = Fn( ( [ point, linePointA, linePointB ] ) => { // https://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html @@ -394,13 +813,13 @@ class SSRNode extends TempNode { } ); - const pointPlaneDistance = Fn( ( [ point, planePoint, planeNormal ] )=> { + const pointPlaneDistance = Fn( ( [ point, planePoint, planeNormal ] ) => { // https://mathworld.wolfram.com/Point-PlaneDistance.html // https://en.wikipedia.org/wiki/Plane_(geometry) // http://paulbourke.net/geometry/pointlineplane/ - // planeNormal is already normalized, so denominator is 1 + // planeNormal is already normalized, so the denominator is 1. const d = mul( planeNormal.x, planePoint.x ).add( mul( planeNormal.y, planePoint.y ) ).add( mul( planeNormal.z, planePoint.z ) ).negate().toVar(); const distance = mul( planeNormal.x, point.x ).add( mul( planeNormal.y, point.y ) ).add( mul( planeNormal.z, point.z ) ).add( d ); return distance; @@ -441,176 +860,438 @@ class SSRNode extends TempNode { }; + const sampleMarchNoise = this.stochastic === true ? bindAnalyticNoise( this._resolution, 47 ) : null; + + const computeScreenBorderFactor = Fn( ( [ uvCoord, borderWidth ] ) => { + + const border = borderWidth.max( 1e-4 ); + + // Distance to the nearest screen edge — uniform falloff at corners. + const edgeDist = min( + min( uvCoord.x, float( 1 ).sub( uvCoord.x ) ), + min( uvCoord.y, float( 1 ).sub( uvCoord.y ) ) + ); + + // Two smoothsteps for a softer ease-in-out than a single ramp. + const t = edgeDist.smoothstep( 0, border ); + + return t.smoothstep( 0, 1 ).pow( 0.125 ); + + } ).setLayout( { + name: 'computeScreenBorderFactor', + type: 'float', + inputs: [ + { name: 'uvCoord', type: 'vec2' }, + { name: 'borderWidth', type: 'float' } + ] + } ); + const ssr = Fn( () => { - const metalness = float( this.metalnessNode ); + const noise = this.stochastic === true ? sampleMarchNoise( uvNode, this._noiseIndex ) : null; + const uvPos = uvNode.toVar(); + + const depth = sampleDepth( uvPos ).toVar(); - // fragments with no metalness do not reflect their environment - metalness.equal( 0.0 ).discard(); + // Skip background pixels (cleared far-plane depth); the target is cleared each frame. + depth.greaterThanEqual( 1.0 ).discard(); - // compute some standard FX entities - const depth = sampleDepth( uvNode ).toVar(); - const viewPosition = getViewPosition( uvNode, depth, this._cameraProjectionMatrixInverse ).toVar(); + const viewPosition = getViewPosition( uvPos, depth, this._cameraProjectionMatrixInverse ).toVar(); + const worldPosition = this._cameraWorldMatrix.mul( vec4( viewPosition, 1.0 ) ).xyz.toVar(); const viewNormal = this.normalNode.rgb.normalize().toVar(); - // compute the direction from the position in view space to the camera const viewIncidentDir = ( ( this.camera.isPerspectiveCamera ) ? normalize( viewPosition ) : vec3( 0, 0, - 1 ) ).toVar(); - // compute the direction in which the light is reflected on the surface - const viewReflectDir = reflect( viewIncidentDir, viewNormal ).toVar(); + // The node system samples the metalness/roughness textures at the current uv, + // so no explicit sample() is needed here. + const metalness = float( this.metalnessNode ); + + if ( this.stochastic === false && this._reflectNonMetals === false ) { + + metalness.lessThanEqual( 0.0 ).discard(); + + } + + const roughness = float( this.roughnessNode ); + const glossiness = min( roughness.div( 0.25 ), 1 ).oneMinus(); + // Only the fade-to-black miss path reads this, and that path is baked out otherwise. + const surfaceBorderFactor = this.screenEdgeFadeBlack ? computeScreenBorderFactor( uvPos, this.screenEdgeFade ) : null; + const hitBorderWidth = this.screenEdgeFade.mul( glossiness ); + + const V = viewIncidentDir.negate().normalize().toVar(); + + let viewReflectDir, finalSampleWeight, specDominantFactor; + const albedo = vec3( 1 ).toVar(); + let sampleEnvReflection = null; + + if ( this.stochastic === false ) { + + viewReflectDir = reflect( viewIncidentDir, viewNormal ).normalize().toVar(); + finalSampleWeight = vec3( metalness ); + specDominantFactor = float( 1 ); + + } else { + + const Xi = noise.toVar(); + // Mirror-bias: pull `Xi.y` toward the cap top to tighten the GGX lobe and cut mid-roughness + // noise. Unbiased — bounded VNDF keeps brdf·cos/pdf ~constant (EA, "Stochastic SSR"). + Xi.y.assign( mix( Xi.y, 0.0, this.mirrorBias.mul( Xi.w.sqrt() ) ) ); + + albedo.assign( ( this.diffuseNode !== null ? this.diffuseNode.sample( uvPos ).rgb : vec3( 1 ) ) ); + const ggxSample = ggxReflectionSample( viewNormal, V, roughness, metalness, albedo, Xi ).toVar(); + + // Sometimes the GGX sample is facing away from the surface, so we need to re-sample. + If( ggxSample.get( 'reflectDir' ).dot( viewNormal ).lessThan( 0 ), () => { + + ggxSample.assign( ggxReflectionSample( viewNormal, V, roughness, metalness, albedo, Xi.add( Xi.mul( 7 ) ).fract() ) ); + + } ); + + viewReflectDir = ggxSample.get( 'reflectDir' ).toVar(); + finalSampleWeight = ggxSample.get( 'sampleWeight' ).toVar(); + specDominantFactor = getSpecularDominantFactor( ggxSample.get( 'NdotV' ), roughness ).toVar(); + + sampleEnvReflection = () => { + + const envColor = vec3( 0 ).toVar(); + + if ( this.envImportanceSampling ) { + + const Xi2 = bindAnalyticNoise( this._resolution, 59 )( uvPos, this._noiseIndex ); + + envColor.assign( this._importanceEnvironment.sampleEnvironmentMIS( { + cameraWorldMatrix: this._cameraWorldMatrix, + viewReflectDir, + N: viewNormal, + V, + alpha: ggxSample.get( 'alpha' ), + f0: ggxSample.get( 'f0' ), + Xi2 + } ) ); + + } else { + + envColor.assign( this._importanceEnvironment.sampleEnvironmentBRDF( { + cameraWorldMatrix: this._cameraWorldMatrix, + viewReflectDir, + N: viewNormal, + V, + alpha: ggxSample.get( 'alpha' ), + f0: ggxSample.get( 'f0' ) + } ) ); + + } + + return envColor; + + }; + + } + + // Multi-bounce: fold in the previous frame's reflection at the hit point, reprojected by its + // own motion. The (1 - history.a) decay damps the feedback. No-op until both textures are set. + const reprojectHitPointHistory = ( uvHit, color ) => { + + if ( ! ( this.historyTexture && this.velocityTexture ) ) return color; + + const velocity = this.velocityTexture.sample( uvHit ).xy; + const historyUV = uvHit.sub( velocity ); + const historyBounce = texture( this.historyTexture, historyUV ).toVar(); + const sampleDecay = historyBounce.a.oneMinus(); + + return color.add( historyBounce.rgb.mul( sampleDecay ) ); + + }; + + // Fades a screen-space hit near the screen borders, using the hit sample UV (where the + // screen-space data was read). `screenEdgeFadeBlack` is baked, so the two modes branch in + // JS: fade the reflection to black, or blend it toward the environment reflection. + const applyHitEdgeFade = ( reflectColor, uvS, hitBorderWidth ) => { + + if ( this.screenEdgeFadeBlack ) { + + const hitBorderFactor = computeScreenBorderFactor( uvS, this.screenEdgeFade ); + reflectColor.rgb.mulAssign( hitBorderFactor ); + + } else { + + const hitBorderFactor = computeScreenBorderFactor( uvS, hitBorderWidth ); + + If( hitBorderFactor.lessThan( 1 ), () => { + + reflectColor.rgb.assign( mix( sampleEnvReflection().mul( this.environmentIntensity ), reflectColor.rgb, hitBorderFactor ) ); + + } ); + + } + + }; - // adapt maximum distance to the local geometry (see https://www.mathsisfun.com/algebra/vectors-dot-product.html) const maxReflectRayLen = this.maxDistance.div( dot( viewIncidentDir.negate(), viewNormal ) ).toVar(); - // compute the maximum point of the reflection ray in view space const d1viewPosition = viewPosition.add( viewReflectDir.mul( maxReflectRayLen ) ).toVar(); - // check if d1viewPosition lies behind the camera near plane - If( this._isPerspectiveCamera.and( d1viewPosition.z.greaterThan( this._cameraNear.negate() ) ), () => { + // Camera type is fixed at build time, so guard the near-plane clamp with a JS branch + // rather than a runtime uniform (the orthographic case compiles it out entirely). + if ( this.camera.isPerspectiveCamera ) { - // if so, ensure d1viewPosition is clamped on the near plane. - // this prevents artifacts during the ray marching process - const t = sub( this._cameraNear.negate(), viewPosition.z ).div( viewReflectDir.z ); - d1viewPosition.assign( viewPosition.add( viewReflectDir.mul( t ) ) ); + If( d1viewPosition.z.greaterThan( this._cameraNear.negate() ), () => { - } ); + const t = sub( this._cameraNear.negate(), viewPosition.z ).div( viewReflectDir.z ); + d1viewPosition.assign( viewPosition.add( viewReflectDir.mul( t ) ) ); - // d0 and d1 are the start and maximum points of the reflection ray in screen space - const d0 = screenCoordinate.xy.toVar(); - const d1 = getScreenPosition( d1viewPosition, this._cameraProjectionMatrix ).mul( this._resolution ).toVar(); + } ); - // below variables are used to control the raymarching process + } - // total length of the ray - const totalLen = d1.sub( d0 ).length().toVar(); + const d0 = uvPos.mul( this._resolution ).xy.toVar(); + const d1 = getScreenPosition( d1viewPosition, this._cameraProjectionMatrix ).mul( this._resolution ).toVar(); - // offset in x and y direction const xLen = d1.x.sub( d0.x ).toVar(); const yLen = d1.y.sub( d0.y ).toVar(); - // determine the larger delta - // The larger difference will help to determine how much to travel in the X and Y direction each iteration and - // how many iterations are needed to travel the entire ray - const totalStep = int( max( abs( xLen ), abs( yLen ) ).mul( this.quality.clamp() ) ).toConst(); + // dominant-axis ray length in texels (used for the per-step floor below) + const rayLen = max( xLen.abs(), yLen.abs() ).max( 1 ).toVar(); + + // Blur traces a single mirror ray, so spend steps in proportion to the ray's screen-space + // length (cheap for the short rays that dominate). Scatter needs a fixed, bounded count for + // coherent stochastic sampling; each step then spans the whole ray as rayVec / totalStep. + const totalStep = int( this.stochastic === false + ? trunc( max( abs( xLen ), abs( yLen ) ).mul( this.quality.clamp() ) ).max( int( 1 ) ).toConst() + : this.quality.clamp().mul( MAX_STEPS ).max( float( 1 ) ) ).toConst(); - // step sizes in the x and y directions const xSpan = xLen.div( totalStep ).toVar(); const ySpan = yLen.div( totalStep ).toVar(); - const output = vec4( 0 ).toVar(); + const stepVec = vec2( xSpan, ySpan ).toVar(); + const invResolution = vec2( float( 1 ), float( 1 ) ).div( this._resolution ).toVar(); + const uvPixelStepX = vec2( invResolution.x, float( 0 ) ).toVar(); - // the actual ray marching loop - // starting from d0, the code gradually travels along the ray and looks for an intersection with the geometry. - // it does not exceed d1 (the maximum ray extend) - Loop( totalStep, ( { i } ) => { - - // advance on the ray by computing a new position in screen coordinates - const xy = vec2( d0.x.add( xSpan.mul( float( i ) ) ), d0.y.add( ySpan.mul( float( i ) ) ) ).toVar(); + const output = vec4( 0 ).toVar(); + const hit = float( 0 ).toVar(); + + // Reflected-ray view-space Z at ray parameter s ∈ [0,1] (linear in 1/z for perspective), + // hoisted so the march and refinement evaluate it identically. + const recipVPZ = float( 1 ).div( viewPosition.z ).toConst(); + const recipD1VPZ = float( 1 ).div( d1viewPosition.z ).toConst(); + + // Camera type is known at build time, so branch at compile time rather than via a runtime select. + const reflectRayZAt = this.camera.isPerspectiveCamera + ? ( sVal ) => float( 1 ).div( recipVPZ.add( sVal.mul( recipD1VPZ.sub( recipVPZ ) ) ) ) + : ( sVal ) => viewPosition.z.add( sVal.mul( d1viewPosition.z.sub( viewPosition.z ) ) ); + + // Screen-space position along the ray for a given s ∈ [0,1]. + const screenPosAt = ( sVal ) => d0.add( stepVec.mul( sVal.mul( totalStep ) ) ); + + // Ray parameter s ∈ [0,1] for step `idx`. Blur marches uniformly (matching the original loop: + // one ~texel step per iteration). Scatter uses an exponential remap `(idx/steps)^stepExponent` + // that concentrates samples near the origin, floored to ≥1 texel/step; `jitter` dissolves banding. + const sampleFraction = this.stochastic === false + ? ( idx ) => idx.div( totalStep ) + : ( idx ) => max( + idx.add( noise.z.sub( 0.5 ) ).div( totalStep ).pow( this.stepExponent ), + idx.div( rayLen ) + ); + + // Carry the hit out of the loop so refinement runs after the march, not nested inside it (a + // loop-inside-a-loop tripped shader-compiler bugs on some drivers). hitSLo/hitSHi bracket s. + const foundHit = bool( false ).toVar(); + const hitSLo = float( 0 ).toVar(); + const hitSHi = float( 0 ).toVar(); + // Carry the coarse hit's UV/depth to skip a redundant fetch when refinement is off. + const hitUvS = vec2( 0 ).toVar(); + const hitD = float( 0 ).toVar(); + + // March from d0 toward d1, looking for an intersection with the depth buffer. + Loop( { start: int( 1 ), end: totalStep }, ( { i } ) => { + + // Exponentially-distributed ray parameter, shared by the sample position and ray depth. + const s = sampleFraction( float( i ) ).toVar(); + + const xy = screenPosAt( s ).toVar(); - // stop processing if the new position lies outside of the screen If( xy.x.lessThan( 0 ).or( xy.x.greaterThan( this._resolution.x ) ).or( xy.y.lessThan( 0 ) ).or( xy.y.greaterThan( this._resolution.y ) ), () => { Break(); } ); - // compute new uv, depth and viewZ for the next fragment - const uvNode = xy.div( this._resolution ); - const d = sampleDepth( uvNode ).toVar(); + const uvS = xy.mul( invResolution ).toVar(); + const d = sampleDepth( uvS ).toVar(); const vZ = getViewZ( d ).toVar(); - const viewReflectRayZ = float( 0 ).toVar(); + const viewReflectRayZ = reflectRayZAt( s ).toVar(); - // normalized distance between the current position xy and the starting point d0 - const s = xy.sub( d0 ).length().div( totalLen ); + If( viewReflectRayZ.lessThanEqual( vZ ), () => { + + // Depth crossing: ray went behind the depth buffer. Gate by thickness before stopping + // so an occluder gap doesn't end the march prematurely. + const vP = getViewPosition( uvS, d, this._cameraProjectionMatrixInverse ).toVar(); + const away = pointToLineDistance( vP, viewPosition, d1viewPosition ).toVar(); + + const uvNeighbor = uvS.add( uvPixelStepX ).toVar(); + const vPNeighbor = getViewPosition( uvNeighbor, d, this._cameraProjectionMatrixInverse ).toVar(); + const minThickness = vPNeighbor.x.sub( vP.x ).mul( 3 ).toVar(); + const tk = max( minThickness, this.thickness ).toVar(); + + If( away.lessThanEqual( tk ), () => { + + const vN = this.normalNode.sample( uvS ).rgb.normalize().toVar(); + + // the reflected ray is pointing towards the same side as the fragment's normal (current ray position), + // which means it wouldn't reflect off the surface. The loop continues to the next step for the next ray sample. + if ( this.stochastic === false ) { + + If( dot( viewReflectDir, vN ).greaterThanEqual( 0 ), () => { + + Continue(); - // depending on the camera type, we now compute the z-coordinate of the reflected ray at the current step in view space - If( this._isPerspectiveCamera, () => { + } ); - const recipVPZ = float( 1 ).div( viewPosition.z ).toVar(); - viewReflectRayZ.assign( float( 1 ).div( recipVPZ.add( s.mul( float( 1 ).div( d1viewPosition.z ).sub( recipVPZ ) ) ) ) ); + // this distance represents the depth of the intersection point between the reflected ray and the scene. + const distance = pointPlaneDistance( vP, viewPosition, viewNormal ).toVar(); - } ).Else( () => { + // Distance exceeding limit: The reflection is potentially too far away and + // might not contribute significantly to the final color + If( distance.greaterThan( this.maxDistance ), () => { + + Break(); + + } ); + + } + + foundHit.assign( true ); + hitUvS.assign( uvS ); + hitD.assign( d ); + + if ( this.binaryRefine ) { + + hitSLo.assign( sampleFraction( float( i ).sub( 1 ) ) ); + hitSHi.assign( s ); + + } + + Break(); - viewReflectRayZ.assign( viewPosition.z.add( s.mul( d1viewPosition.z.sub( viewPosition.z ) ) ) ); + + } ); } ); - // if viewReflectRayZ is less or equal than the real z-coordinate at this place, it potentially intersects the geometry - If( viewReflectRayZ.lessThanEqual( vZ ), () => { + } ); - // compute the distance of the new location to the ray in view space - // to clarify vP is the fragment's view position which is not an exact point on the ray - const vP = getViewPosition( uvNode, d, this._cameraProjectionMatrixInverse ).toVar(); - const away = pointToLineDistance( vP, viewPosition, d1viewPosition ).toVar(); + If( foundHit, () => { - // compute the minimum thickness between the current fragment and its neighbor in the x-direction. - const xyNeighbor = vec2( xy.x.add( 1 ), xy.y ).toVar(); // move one pixel - const uvNeighbor = xyNeighbor.div( this._resolution ); - const vPNeighbor = getViewPosition( uvNeighbor, d, this._cameraProjectionMatrixInverse ).toVar(); - const minThickness = vPNeighbor.x.sub( vP.x ).toVar(); - minThickness.mulAssign( 3 ); // expand a bit to avoid errors + // Bisect the bracketed crossing toward the exact intersection. Run after the march, not + // nested (a loop-inside-a-loop tripped shader-compiler bugs on some drivers). + if ( this.binaryRefine ) { - const tk = max( minThickness, this.thickness ).toVar(); + Loop( { start: int( 0 ), end: int( 8 ), type: 'int', condition: '<' }, () => { + + const sMid = hitSLo.add( hitSHi ).mul( 0.5 ).toVar(); + const sceneZMid = getViewZ( sampleDepth( screenPosAt( sMid ).mul( invResolution ) ) ); - If( away.lessThanEqual( tk ), () => { // hit + If( reflectRayZAt( sMid ).lessThanEqual( sceneZMid ), () => { - const vN = this.normalNode.sample( uvNode ).rgb.normalize().toVar(); + hitSHi.assign( sMid ); - If( dot( viewReflectDir, vN ).greaterThanEqual( 0 ), () => { + } ).Else( () => { - // the reflected ray is pointing towards the same side as the fragment's normal (current ray position), - // which means it wouldn't reflect off the surface. The loop continues to the next step for the next ray sample. - Continue(); + hitSLo.assign( sMid ); } ); - // this distance represents the depth of the intersection point between the reflected ray and the scene. - const distance = pointPlaneDistance( vP, viewPosition, viewNormal ).toVar(); + } ); - If( distance.greaterThan( this.maxDistance ), () => { + // Refinement moved the crossing, so re-fetch UV/depth at the refined `s`. + hitUvS.assign( screenPosAt( hitSHi ).mul( invResolution ) ); + hitD.assign( sampleDepth( hitUvS ) ); - // Distance exceeding limit: The reflection is potentially too far away and - // might not contribute significantly to the final color - Break(); + } - } ); + // Shade the hit, reusing the depth fetched during the march (or refinement). + const uvS = hitUvS; + const vP = getViewPosition( uvS, hitD, this._cameraProjectionMatrixInverse ).toVar(); - const op = this.opacity.mul( metalness ).toVar(); + // In blur mode the ratio² falloff re-grows past maxDistance, so over-range hits fall back + // to env. The scatter path bounds reach via ray length, so every hit shades. + const distancePointPlane = this.stochastic === false ? pointPlaneDistance( vP, viewPosition, viewNormal ).toVar() : float( 0 ); + const withinRange = distancePointPlane.lessThanEqual( this.maxDistance ); - // distance attenuation (the reflection should fade out the farther it is away from the surface) - const ratio = float( 1 ).sub( distance.div( this.maxDistance ) ).toVar(); - const attenuation = ratio.mul( ratio ); - op.mulAssign( attenuation ); + If( withinRange, () => { - // fresnel (reflect more light on surfaces that are viewed at grazing angles) - const fresnelCoe = div( dot( viewIncidentDir, viewReflectDir ).add( 1 ), 2 ); - op.mulAssign( fresnelCoe ); + const hitWorldPosition = this._cameraWorldMatrix.mul( vec4( vP, 1.0 ) ).xyz.toVar(); + const worldDistance = distance( worldPosition, hitWorldPosition ).mul( specDominantFactor ).toVar(); - // output - const reflectColor = this.colorNode.sample( uvNode ); - output.assign( vec4( reflectColor.rgb.mul( op ), 1 ) ); - Break(); + const reflectColor = this.colorNode.sample( uvS ).toVar(); - } ); + // Multi-bounce: add the reprojected previous-frame reflection at the hit point. + reflectColor.rgb.assign( reprojectHitPointHistory( uvS, reflectColor.rgb ) ); + + if ( this.stochastic === true ) applyHitEdgeFade( reflectColor, uvS, hitBorderWidth ); + + // The scatter (GGX) path bakes distance/grazing response into finalSampleWeight. + // The mirror/blur path is a plain reflection, so reapply upstream's squared + // distance attenuation and grazing Fresnel here to match its falloff. + let weightedColor = reflectColor.rgb.mul( finalSampleWeight ); + + if ( this.stochastic === false ) { + + const ratio = float( 1 ).sub( distancePointPlane.div( this.maxDistance ) ).toVar(); + const attenuation = ratio.mul( ratio ).toVar(); + const fresnelCoe = div( dot( viewIncidentDir, viewReflectDir ).add( 1 ), 2 ).toVar(); + weightedColor = weightedColor.mul( attenuation.mul( fresnelCoe ) ); + + } + + hit.assign( 1 ); + output.assign( vec4( weightedColor, worldDistance ) ); } ); } ); - return output; + // Screen-space ray missed: environment fallback (MIS when CDF env is set up). + If( hit.equal( 0 ), () => { + + if ( this.stochastic === true ) { + + output.assign( vec4( sampleEnvReflection().mul( this.environmentIntensity ), float( ENV_RAY_LENGTH ) ) ); + + // Misses fade by the surface pixel UV (where the reflection is being shaded). + if ( this.screenEdgeFadeBlack ) { + + output.rgb.mulAssign( surfaceBorderFactor ); + + } + + } + + } ); + + const lum = luminance( output.rgb ).max( 1e-4 ).toVar(); + output.rgb.mulAssign( this.maxLuminance.div( lum ).min( 1 ) ); + + // scale the reflection color by the user-controlled intensity + output.rgb.mulAssign( this.intensity ); + + return output.max( 0 ); } ); - this._ssrMaterial.fragmentNode = ssr().context( builder.getSharedContext() ); - this._ssrMaterial.needsUpdate = true; - // below materials are used for blurring + this._ssrFn = ssr; + this._sharedContext = builder.getSharedContext(); + this._buildSSRMaterial(); const reflectionBuffer = texture( this._ssrRenderTarget.texture ); - this._blurMaterial.fragmentNode = boxBlur( reflectionBuffer, { size: this.blurQuality, separation: this._blurSpread } ); - this._blurMaterial.needsUpdate = true; + if ( this.stochastic === false ) { + + this._buildBlurMaterial(); + + } this._copyMaterial.fragmentNode = reflectionBuffer; this._copyMaterial.needsUpdate = true; @@ -621,6 +1302,12 @@ class SSRNode extends TempNode { } + getRenderTarget() { + + return this._ssrRenderTarget; + + } + /** * Frees internal resources. This method should be called * when the effect is no longer required. @@ -634,6 +1321,13 @@ class SSRNode extends TempNode { this._blurMaterial.dispose(); this._copyMaterial.dispose(); + if ( this._importanceEnvironment !== null ) { + + this._importanceEnvironment.dispose(); + this._importanceEnvironment = null; + + } + } } @@ -648,9 +1342,12 @@ export default SSRNode; * @param {Node} colorNode - The node that represents the beauty pass. * @param {Node} depthNode - A node that represents the beauty pass's depth. * @param {Node} normalNode - A node that represents the beauty pass's normals. - * @param {Node} metalnessNode - A node that represents the beauty pass's metalness. - * @param {?Node} [roughnessNode=null] - A node that represents the beauty pass's roughness. - * @param {?Camera} [camera=null] - The camera the scene is rendered with. + * @param {SSRNodeOptions} [options] - Optional inputs for material and environment data. * @returns {SSRNode} */ -export const ssr = ( colorNode, depthNode, normalNode, metalnessNode, roughnessNode = null, camera = null ) => new SSRNode( nodeObject( colorNode ), nodeObject( depthNode ), nodeObject( normalNode ), nodeObject( metalnessNode ), nodeObject( roughnessNode ), camera ); +export const ssr = ( colorNode, depthNode, normalNode, options = {} ) => nodeObject( new SSRNode( + nodeObject( colorNode ), + nodeObject( depthNode ), + nodeObject( normalNode ), + options +) ); diff --git a/examples/jsm/tsl/display/TemporalReprojectNode.js b/examples/jsm/tsl/display/TemporalReprojectNode.js new file mode 100644 index 00000000000000..b942eb0073873e --- /dev/null +++ b/examples/jsm/tsl/display/TemporalReprojectNode.js @@ -0,0 +1,1023 @@ +import { EPSILON, Fn, If, abs, convertToTexture, dFdx, dFdy, dot, exp, float, floor, fwidth, getViewPosition, ivec2, luminance, max, min, mix, nodeObject, normalize, passTexture, screenCoordinate, select, smoothstep, sqrt, struct, texture, textureLoad, uniform, unpackRGBToNormal, uv, vec2, vec3, vec4, velocity } from 'three/tsl'; +import { DepthTexture, HalfFloatType, Matrix4, NodeMaterial, NodeUpdateType, QuadMesh, RenderTarget, RendererUtils, TempNode, Vector2, Vector3 } from 'three/webgpu'; +import { ENV_RAY_LENGTH, ENV_RAY_LENGTH_THRESHOLD } from '../utils/SpecularHelpers.js'; + +// Reprojection helpers + +/** + * Maps a resolve (screen) texel to the corresponding beauty-input texel when resolutions differ. + * + * @tsl + */ +const beautyTexelFromScreen = Fn( ( [ screenTexel, beautySize, resolveSize ] ) => { + + return ivec2( floor( vec2( screenTexel ).mul( beautySize ).div( resolveSize ) ) ); + +} ).setLayout( { + name: 'beautyTexelFromScreen', + type: 'ivec2', + inputs: [ + { name: 'screenTexel', type: 'ivec2' }, + { name: 'beautySize', type: 'vec2' }, + { name: 'resolveSize', type: 'vec2' } + ] +} ); + +/** + * Projects a world-space position into previous-frame UV coordinates. + * + * @tsl + */ +const projectWorldToUV = Fn( ( [ worldPos, previousViewMatrix, previousProjectionMatrix ] ) => { + + const resultUV = vec2( - 1 ).toVar(); + const viewSpace = previousViewMatrix.mul( vec4( worldPos, 1.0 ) ); + const clipSpace = previousProjectionMatrix.mul( viewSpace ).toVar(); + const clipW = clipSpace.w.toVar(); + + If( abs( clipW ).greaterThan( float( 1e-5 ) ), () => { + + const ndc = clipSpace.xyz.div( clipW ); + resultUV.assign( ndc.xy.mul( 0.5 ).add( 0.5 ) ); + resultUV.y.assign( resultUV.y.oneMinus() ); + + } ); + + return resultUV; + +} ).setLayout( { + name: 'projectWorldToUV', + type: 'vec2', + inputs: [ + { name: 'worldPos', type: 'vec3' }, + { name: 'previousViewMatrix', type: 'mat4' }, + { name: 'previousProjectionMatrix', type: 'mat4' } + ] +} ); + +// YCoCg variance clipping + +/** + * @param {import('three/tsl').Node} c + * @returns {import('three/tsl').Node} + */ +const rgbToYCoCg = ( c ) => vec3( + dot( c, vec3( 0.25, 0.5, 0.25 ) ), + dot( c, vec3( 0.5, 0.0, - 0.5 ) ), + dot( c, vec3( - 0.25, 0.5, - 0.25 ) ) +); + +/** + * @param {import('three/tsl').Node} c + * @returns {import('three/tsl').Node} + */ +const ycocgToRGB = ( c ) => vec3( + c.x.add( c.y ).sub( c.z ), + c.x.add( c.z ), + c.x.sub( c.y ).sub( c.z ) +); + +const VARIANCE_CLIP_LUMA_SCALE = 10; + +/** + * Inverse-luminance compression for HDR variance clipping (Karis-style). + * Bright samples contribute less to neighbourhood moments so sun pixels do not + * inflate the YCoCg AABB and cause aggressive clipping flicker. + * + * @param {import('three/tsl').Node} rgb + * @param {import('three/tsl').Node} flickerSuppression + * @returns {import('three/tsl').Node} + */ +const dampenForVarianceClip = ( rgb, flickerSuppression ) => { + + const scale = luminance( rgb ).mul( flickerSuppression ).mul( VARIANCE_CLIP_LUMA_SCALE ).add( 1 ); + return rgb.div( scale ); + +}; + +/** + * Clips the history sample to the neighbourhood AABB by projecting it toward the box centre. + * Reference: https://github.com/playdeadgames/temporal + * + * @tsl + */ +const clipToAABB = Fn( ( [ history, boxMin, boxMax ] ) => { + + const pClip = boxMax.add( boxMin ).mul( 0.5 ); + const eClip = boxMax.sub( boxMin ).mul( 0.5 ).add( 1e-7 ); + const vClip = history.sub( pClip ); + const vUnit = vClip.div( eClip ); + const absUnit = vUnit.abs(); + const maxUnit = max( absUnit.x, absUnit.y, absUnit.z ); + + return maxUnit.greaterThan( 1 ).select( pClip.add( vClip.div( maxUnit ) ), history ); + +} ).setLayout( { + name: 'clipToAABB', + type: 'vec3', + inputs: [ + { name: 'history', type: 'vec3' }, + { name: 'boxMin', type: 'vec3' }, + { name: 'boxMax', type: 'vec3' } + ] +} ); + +const neighborhoodStruct = struct( { + mean: 'vec3', + stdColor: 'vec3', + rayLength: 'float', + envProbability: 'float', + stdDevRayLength: 'float' +} ); + +/** + * Single 3×3 neighbourhood pass over the beauty buffer. One textureLoad per tap feeds both the + * YCoCg variance-clipping box (colour) and the SSR ray-length statistics (alpha), which previously + * required two separate 3×3 fetches of the same texture. + * + * Sampling is done on the beauty-texel grid (`beautyTexel + offset`), so the taps are distinct + * source texels even when the beauty buffer is lower resolution than the resolve pass (upscaling). + * + * @tsl + */ +const collectNeighborhood = Fn( ( [ beautyTexture, beautyTexel, inputColor, flickerSuppression ] ) => { + + const offsets = [ + [ - 1, - 1 ], + [ - 1, 1 ], + [ 1, - 1 ], + [ 1, 1 ], + [ 1, 0 ], + [ 0, - 1 ], + [ 0, 1 ], + [ - 1, 0 ], + ]; + + // Colour moments (YCoCg) — centre reuses the already-fetched inputColor. + const center = rgbToYCoCg( dampenForVarianceClip( inputColor.rgb, flickerSuppression ) ); + const moment1 = center.toVar(); + const moment2 = center.pow2().toVar(); + + // Ray-length statistics (Welford) over screen-space hits only. + const rayLengthSum = float( 0 ).toVar(); + const rayLengthCount = float( 0 ).toVar(); + const meanRayLength = float( 0 ).toVar(); + const m2RayLength = float( 0 ).toVar(); + + const accumulateRayLength = ( alpha ) => { + + If( alpha.lessThan( ENV_RAY_LENGTH_THRESHOLD ), () => { + + rayLengthSum.addAssign( alpha ); + rayLengthCount.addAssign( 1 ); + + const delta = alpha.sub( meanRayLength ).toVar(); + meanRayLength.addAssign( delta.div( rayLengthCount ) ); + m2RayLength.addAssign( delta.mul( alpha.sub( meanRayLength ) ) ); + + } ); + + }; + + accumulateRayLength( inputColor.a ); + + for ( const [ x, y ] of offsets ) { + + const neighbor = textureLoad( beautyTexture, beautyTexel.add( ivec2( x, y ) ) ).max( 0 ).toVar(); + + const c = rgbToYCoCg( dampenForVarianceClip( neighbor.rgb, flickerSuppression ) ); + moment1.addAssign( c ); + moment2.addAssign( c.pow2() ); + + accumulateRayLength( neighbor.a ); + + } + + const N = float( offsets.length + 1 ); + const mean = moment1.div( N ); + const stdColor = moment2.div( N ).sub( mean.pow2() ).max( 0 ).sqrt(); + + // Continuous environment probability: fraction of the 3×3 neighbourhood that missed in screen space + // and fell back to env (0 = all hits, 1 = all env), for smooth reflection/environment transitions. + const envProbability = rayLengthCount.div( float( 9 ) ).oneMinus(); + const rayLength = rayLengthCount.lessThan( 0.5 ).select( float( ENV_RAY_LENGTH ), rayLengthSum.div( max( rayLengthCount, float( 1e-4 ) ) ) ); + const stdDevRayLength = sqrt( m2RayLength.div( max( rayLengthCount, float( 1.0 ) ) ) ).max( 1e-3 ); + + return neighborhoodStruct( mean, stdColor, rayLength, envProbability, stdDevRayLength ); + +} ); + +/** + * Variance clipping in YCoCg space (Salvi, GDC 2016). Uses the colour moments gathered by + * {@link collectNeighborhood}; `gamma` widens the AABB and is kept out of the gather so the + * neighbourhood pass stays independent of the per-pixel motion factor. + * + * @tsl + */ +const applyVarianceClipping = Fn( ( [ historyColor, mean, stdColor, gamma, flickerSuppression ] ) => { + + const stddev = stdColor.mul( gamma ); + const boxMin = mean.sub( stddev ); + const boxMax = mean.add( stddev ); + + const historyRGB = historyColor.rgb.toVar(); + const historyScale = luminance( historyRGB ).mul( flickerSuppression ).mul( VARIANCE_CLIP_LUMA_SCALE ).add( 1 ); + const clipped = clipToAABB( rgbToYCoCg( historyRGB.div( historyScale ) ), boxMin, boxMax ); + + return ycocgToRGB( clipped ).mul( historyScale ); + +} ); + +// History sampling + +const bilinearTapStruct = struct( { color: 'vec4', weight: 'float', confidence: 'float' } ); +const historyResultStruct = struct( { color: 'vec4', tapConfidence: 'float', minConfidence: 'float' } ); + +/** + * Single bilinear history tap with plane-distance and normal confidence. + * + * @tsl + */ +const sampleBilinearTap = Fn( ( [ + historyTexture, + previousDepthNode, + previousNormalNode, + resolution, + previousProjectionMatrixInverse, + previousCameraWorldMatrix, + previousCameraViewMatrix, + tapCoord, + bilinearWeight, + worldPosition, + worldNormal +] ) => { + + const color = textureLoad( historyTexture, tapCoord ).max( 0 ); + const reprojDepth = textureLoad( previousDepthNode, tapCoord ).r; + const reprojViewPos = getViewPosition( vec2( tapCoord ).add( 0.5 ).div( resolution ), reprojDepth, previousProjectionMatrixInverse ); + const reprojWorldPos = previousCameraWorldMatrix.mul( vec4( reprojViewPos, 1.0 ) ).xyz; + const reprojWorldNorm = unpackRGBToNormal( textureLoad( previousNormalNode, tapCoord ).rgb ).transformDirection( previousCameraViewMatrix ); + + const planeDiff = abs( dot( reprojWorldPos.sub( worldPosition ), worldNormal ) ).toVar(); + planeDiff.divAssign( abs( reprojViewPos.z ) ); + const normalConfidence = smoothstep( 0.95, 0.999, reprojWorldNorm.dot( worldNormal ) ); + const confidence = smoothstep( 0, 0.01, planeDiff ).oneMinus().mul( normalConfidence ); + const weight = bilinearWeight.mul( confidence ); + + return bilinearTapStruct( color.mul( weight ), weight, confidence ); + +} ); + +/** + * @param {Object} ctx - Shared {@link sampleBilinearTap} inputs plus `reprojICoord`. + * @param {import('three/tsl').Node} tapOffset + * @param {import('three/tsl').Node} bilinearWeight + */ +function bilinearHistoryTap( ctx, tapOffset, bilinearWeight ) { + + return sampleBilinearTap( + ctx.historyTexture, + ctx.previousDepthNode, + ctx.previousNormalNode, + ctx.resolution, + ctx.previousProjectionMatrixInverse, + ctx.previousCameraWorldMatrix, + ctx.previousCameraViewMatrix, + ctx.reprojICoord.add( tapOffset ), + bilinearWeight, + ctx.worldPosition, + ctx.worldNormal + ); + +} + +/** + * Geometrically-weighted 4-tap bilinear history sample. + * + * @tsl + */ +const sampleHistory4Tap = Fn( ( [ + historyTexture, + previousDepthNode, + previousNormalNode, + resolution, + previousProjectionMatrixInverse, + previousCameraWorldMatrix, + previousCameraViewMatrix, + reprojUV, + worldPosition, + worldNormal, + inputColor +] ) => { + + const reprojPixelCoord = reprojUV.mul( resolution ).sub( 0.5 ).toVar(); + const reprojICoord = ivec2( floor( reprojPixelCoord ) ); + const fCoord = reprojPixelCoord.fract(); + + const fx = fCoord.x; + const fy = fCoord.y; + const f00 = float( 1 ).sub( fx ).mul( float( 1 ).sub( fy ) ); + const f10 = fx.mul( float( 1 ).sub( fy ) ); + const f01 = float( 1 ).sub( fx ).mul( fy ); + const f11 = fx.mul( fy ); + + const tapCtx = { + historyTexture, + previousDepthNode, + previousNormalNode, + resolution, + previousProjectionMatrixInverse, + previousCameraWorldMatrix, + previousCameraViewMatrix, + reprojICoord, + worldPosition, + worldNormal + }; + + const tap00 = bilinearHistoryTap( tapCtx, ivec2( 0, 0 ), f00 ); + const tap10 = bilinearHistoryTap( tapCtx, ivec2( 1, 0 ), f10 ); + const tap01 = bilinearHistoryTap( tapCtx, ivec2( 0, 1 ), f01 ); + const tap11 = bilinearHistoryTap( tapCtx, ivec2( 1, 1 ), f11 ); + + const colorSum = tap00.get( 'color' ).add( tap10.get( 'color' ) ).add( tap01.get( 'color' ) ).add( tap11.get( 'color' ) ); + const weightSum = tap00.get( 'weight' ).add( tap10.get( 'weight' ) ).add( tap01.get( 'weight' ) ).add( tap11.get( 'weight' ) ); + const maxConf = max( max( tap00.get( 'confidence' ), tap10.get( 'confidence' ) ), max( tap01.get( 'confidence' ), tap11.get( 'confidence' ) ) ); + const minConf = min( min( tap00.get( 'confidence' ), tap10.get( 'confidence' ) ), min( tap01.get( 'confidence' ), tap11.get( 'confidence' ) ) ); + + return historyResultStruct( + select( weightSum.greaterThan( 0.01 ), colorSum.div( weightSum ), vec4( inputColor.rgb, float( 1 ) ) ), + maxConf, + minConf + ); + +} ); + +// Diffuse reprojection + +/** + * Reprojection-stretch confidence — detects history magnification (surface stretching). + * + * Differentiates the per-pixel history UV with hardware screen-space derivatives to form the + * reprojection Jacobian `J = ∂(historyPixel)/∂(screenPixel)`, then returns its **minimum + * singular value**, clamped to `[0,1]`. + * + * `σ_min < 1` means the most-stretched axis magnifies history — a few history pixels are smeared + * over many current pixels (e.g. a surface seen at grazing in the previous frame, face-on now), so + * history is undersampled and its confidence should be reduced. `σ_min ≥ 1` (history minified) is + * safe and clamps to 1. Using the minimum singular value rather than the Jacobian determinant + * catches anisotropic 1-D stretch that an area-only measure would smear out. + * + * Works for any reprojection (surface-velocity or parallax hit-point) since it differentiates the + * final history UV, so the same factor applies to both the diffuse and specular paths. + * + * @tsl + */ +const reprojectionStretchConfidence = Fn( ( [ historyUV, resolution ] ) => { + + // Jacobian columns in pixels: how the history sample position moves per screen pixel. + const jx = dFdx( historyUV ).mul( resolution ).toVar(); + const jy = dFdy( historyUV ).mul( resolution ).toVar(); + + // Singular values of the 2×2 J are sqrt( eigenvalues of JᵀJ ), with + // trace( JᵀJ ) = ‖J‖²_F and det( JᵀJ ) = det( J )². + const det = jx.x.mul( jy.y ).sub( jx.y.mul( jy.x ) ); + const fro2 = dot( jx, jx ).add( dot( jy, jy ) ); + const disc = fro2.mul( fro2 ).mul( 0.25 ).sub( det.mul( det ) ).max( 0 ).sqrt(); + const sigMin = fro2.mul( 0.5 ).sub( disc ).max( 0 ).sqrt(); + + return sigMin.saturate(); + +} ); + +// Specular reprojection + +/** + * Parallax-corrected hit-point reprojection into previous-frame UVs. + * + * @tsl + */ +const reprojectHitPoint = Fn( ( [ + rayOrig, + rayLength, + cameraWorldPosition, + previousViewMatrix, + previousProjectionMatrix +] ) => { + + const cameraRay = normalize( rayOrig.sub( cameraWorldPosition ) ).toVar(); + const parallaxHitPoint = rayOrig.add( cameraRay.mul( rayLength ) ); + + return projectWorldToUV( parallaxHitPoint, previousViewMatrix, previousProjectionMatrix ); + +} ); + +/** + * Converts screen-space velocity (NDC derivative) to a UV reprojection offset. + * + * @tsl + */ +const velocityToUVOffset = Fn( ( [ velocity ] ) => { + + return velocity.mul( vec2( 0.5, - 0.5 ) ); + +} ).setLayout( { + name: 'velocityToUVOffset', + type: 'vec2', + inputs: [ { name: 'velocity', type: 'vec2' } ] +} ); + +/** + * Current and previous-frame camera matrices for temporal reprojection passes. + * + * @param {import('three').Camera} camera + */ +function bindTemporalCameraUniforms( camera ) { + + const worldMatrix = uniform( new Matrix4().copy( camera.matrixWorld ) ); + const viewMatrix = uniform( new Matrix4().copy( camera.matrixWorldInverse ) ); + const projectionMatrix = uniform( new Matrix4().copy( camera.projectionMatrix ) ); + const projectionMatrixInverse = uniform( new Matrix4().copy( camera.projectionMatrixInverse ) ); + const worldPosition = uniform( new Vector3().copy( camera.position ) ); + + const previousWorldMatrix = uniform( new Matrix4().copy( camera.matrixWorld ) ); + const previousViewMatrix = uniform( new Matrix4().copy( camera.matrixWorldInverse ) ); + const previousProjectionMatrix = uniform( new Matrix4().copy( camera.projectionMatrix ) ); + const previousProjectionMatrixInverse = uniform( new Matrix4().copy( camera.projectionMatrixInverse ) ); + + /** + * @param {import('three').Camera} cam + */ + function updateFromCamera( cam ) { + + previousWorldMatrix.value.copy( worldMatrix.value ); + previousViewMatrix.value.copy( viewMatrix.value ); + previousProjectionMatrix.value.copy( projectionMatrix.value ); + previousProjectionMatrixInverse.value.copy( projectionMatrixInverse.value ); + + worldMatrix.value.copy( cam.matrixWorld ); + viewMatrix.value.copy( cam.matrixWorldInverse ); + projectionMatrix.value.copy( cam.projectionMatrix ); + projectionMatrixInverse.value.copy( cam.projectionMatrixInverse ); + worldPosition.value.copy( cam.position ); + + } + + return { + worldMatrix, + viewMatrix, + projectionMatrix, + projectionMatrixInverse, + worldPosition, + previousWorldMatrix, + previousViewMatrix, + previousProjectionMatrix, + previousProjectionMatrixInverse, + updateFromCamera + }; + +} + +const _quadMesh = /*@__PURE__*/ new QuadMesh(); +const _size = /*@__PURE__*/ new Vector2(); + +let _rendererState; + +const DEFAULT_MAX_VELOCITY_LENGTH = 128; +const VARIANCE_GAMMA_MIN = 0.5; +const VARIANCE_GAMMA_MAX = 1; + +/** + * @typedef {'diffuse' | 'specular'} TemporalReprojectMode + */ + +/** + * @typedef {Object} TemporalReprojectNodeOptions + * @property {TemporalReprojectMode} [mode='diffuse'] - `diffuse` for SSGI/scene colour; `specular` for SSR reflections. + * @property {boolean} [hitPointReprojection] - Parallax hit-point reprojection (specular mode only). Defaults to `true` in specular mode. + * @property {boolean} [accumulate=false] - When `true`, history is stored in this pass (classic temporal resolve). When `false`, + * use {@link TemporalReprojectNode#setHistoryTexture} to read history from another pass (e.g. denoise output). + */ + +/** + * Temporal reprojection pass for denoising screen-space effects (SSGI, SSR, etc.). + * + * Both modes share geometrically-weighted 4-tap bilinear history sampling and YCoCg variance clipping. + * Surface velocity reprojection is always sampled first. Specular mode then blends in + * hit-point parallax history on top of that surface result. + * Diffuse mode applies velocity-field divergence to detect surface stretching. + * + * Unlike jitter-based TAA/TAAU, this node does not apply camera sub-pixel jitter — it only + * reprojects and accumulates history using motion vectors. + * + * References: + * - {@link https://alextardif.com/TAA.html} + * - {@link https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail/} + * + * @augments TempNode + * @three_import import { temporalReproject } from 'three/addons/tsl/display/TemporalReprojectNode.js'; + */ +class TemporalReprojectNode extends TempNode { + + static get type() { + + return 'TemporalReprojectNode'; + + } + + /** + * @param {import('three/tsl').TextureNode} beautyNode + * @param {import('three/tsl').TextureNode} depthNode + * @param {import('three/tsl').TextureNode} normalNode + * @param {import('three/tsl').TextureNode} velocityNode + * @param {import('three').Camera} camera + * @param {TemporalReprojectNodeOptions} [options] + */ + constructor( beautyNode, depthNode, normalNode, velocityNode, camera, options = {} ) { + + super( 'vec4' ); + + const { + mode = 'diffuse', + hitPointReprojection = mode === 'specular', + accumulate = false + } = options; + + if ( mode !== 'specular' && mode !== 'diffuse' ) { + + throw new Error( 'TemporalReprojectNode: `mode` must be `diffuse` or `specular`.' ); + + } + + this.isTemporalReprojectNode = true; + this.updateBeforeType = NodeUpdateType.FRAME; + + this.beautyNode = beautyNode; + this.depthNode = depthNode; + this.normalNode = normalNode; + this.velocityNode = velocityNode; + this.camera = camera; + + /** + * @type {TemporalReprojectMode} + */ + this.mode = mode; + + /** + * When `true`, resolve output is copied into the internal history buffer each frame. + * When `false`, history is supplied externally via {@link TemporalReprojectNode#setHistoryTexture}. + * + * @type {boolean} + */ + this.accumulate = accumulate; + + this.maxVelocityLength = DEFAULT_MAX_VELOCITY_LENGTH; + + this._resolution = uniform( new Vector2() ); + + this._cameraUniforms = bindTemporalCameraUniforms( camera ); + + this.maxFrames = uniform( 32 ); + this.hitPointReprojection = uniform( hitPointReprojection, 'bool' ); + this.clampIntensity = uniform( 1 ); + this.flickerSuppression = uniform( 1 ); + + this._historyRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType, depthTexture: new DepthTexture() } ); + this._historyRenderTarget.texture.name = 'TemporalReprojectNode.history'; + this._historyTextureNode = texture( this._historyRenderTarget.texture ); + + this._resolveRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } ); + this._resolveRenderTarget.texture.name = 'TemporalReprojectNode.resolve'; + + this._resolveMaterial = new NodeMaterial(); + this._resolveMaterial.name = 'TemporalReproject.resolve'; + + this._seedMaterial = new NodeMaterial(); + this._seedMaterial.name = 'TemporalReproject.seed'; + + this._textureNode = passTexture( this, this._resolveRenderTarget.texture ); + + this._originalProjectionMatrix = new Matrix4(); + + this._placeholderPreviousDepthTexture = new DepthTexture( 1, 1 ); + this._previousDepthNode = texture( this._placeholderPreviousDepthTexture ); + this._previousNormalTexture = normalNode.value.clone(); + this._previousNormalNode = texture( this._previousNormalTexture ); + + this._needsPostProcessingSync = false; + this._externalHistoryTexture = null; + + this._syncHistoryTextureBinding(); + + } + + getTextureNode() { + + return this._textureNode; + + } + + setSize( width, height ) { + + if ( width === null || height === null ) return; + + this._historyRenderTarget.setSize( width, height ); + this._resolveRenderTarget.setSize( width, height ); + + this._resolution.value.set( width, height ); + + } + + setViewOffset() { + + this.camera.updateProjectionMatrix(); + this._originalProjectionMatrix.copy( this.camera.projectionMatrix ); + velocity.setProjectionMatrix( this._originalProjectionMatrix ); + + } + + clearViewOffset() { + + velocity.setProjectionMatrix( null ); + + } + + updateBefore( frame ) { + + const { renderer } = frame; + + this._cameraUniforms.updateFromCamera( this.camera ); + + const drawingBufferSize = renderer.getDrawingBufferSize( _size ); + const width = drawingBufferSize.width; + const height = drawingBufferSize.height; + + if ( this._needsPostProcessingSync === true ) { + + this.setViewOffset(); + this._needsPostProcessingSync = false; + + } + + _rendererState = RendererUtils.resetRendererState( renderer, _rendererState ); + + const needsRestart = this._historyRenderTarget.width !== width || this._historyRenderTarget.height !== height; + this.setSize( width, height ); + + let historySwappedForRestart = false; + + if ( needsRestart === true ) { + + renderer.initRenderTarget( this._historyRenderTarget ); + renderer.initRenderTarget( this._resolveRenderTarget ); + + this._previousNormalTexture.dispose(); + this._previousNormalTexture = this.normalNode.value.clone(); + this._previousNormalNode.value = this._previousNormalTexture; + + // External history (e.g. denoise feedback) is stale at the old resolution — use + // freshly seeded internal history for this frame instead. + if ( this.accumulate === false && this._externalHistoryTexture !== null ) { + + this._historyTextureNode.value = this._historyRenderTarget.texture; + historySwappedForRestart = true; + + } + + renderer.setRenderTarget( this._historyRenderTarget ); + _quadMesh.material = this._seedMaterial; + _quadMesh.name = 'TemporalReproject.seed'; + _quadMesh.render( renderer ); + renderer.setRenderTarget( null ); + + } + + renderer.setRenderTarget( this._resolveRenderTarget ); + + _quadMesh.material = this._resolveMaterial; + _quadMesh.name = 'TemporalReproject'; + _quadMesh.render( renderer ); + renderer.setRenderTarget( null ); + + if ( historySwappedForRestart === true ) { + + this._syncHistoryTextureBinding(); + + } else if ( this.accumulate === true ) { + + renderer.copyTextureToTexture( this._resolveRenderTarget.texture, this._historyRenderTarget.texture ); + + } + + const currentDepth = this.depthNode.value; + const srcW = currentDepth.image !== null && currentDepth.image !== undefined ? currentDepth.image.width : 0; + const srcH = currentDepth.image !== null && currentDepth.image !== undefined ? currentDepth.image.height : 0; + + if ( srcW > 0 && srcH > 0 ) { + + renderer.copyTextureToTexture( currentDepth, this._historyRenderTarget.depthTexture ); + renderer.copyTextureToTexture( this.normalNode.value, this._previousNormalTexture ); + + this._previousDepthNode.value = this._historyRenderTarget.depthTexture; + + } + + RendererUtils.restoreRendererState( renderer, _rendererState ); + + } + + setup( builder ) { + + const renderPipeline = builder.context.renderPipeline; + + if ( renderPipeline ) { + + this._needsPostProcessingSync = true; + + renderPipeline.context.onBeforeRenderPipeline = () => { + + this.setViewOffset(); + + }; + + renderPipeline.context.onAfterRenderPipeline = () => { + + this.clearViewOffset(); + + }; + + } + + this._resolveMaterial.fragmentNode = this._buildResolve( builder ); + this._resolveMaterial.needsUpdate = true; + + this._buildSeed( builder ); + + return this._textureNode; + + } + + _buildSeed( builder ) { + + const seed = Fn( () => { + + const screenTexel = ivec2( floor( screenCoordinate.xy.sub( 0.5 ) ) ); + const beautySize = this.beautyNode.size(); + const beautyTexel = beautyTexelFromScreen( screenTexel, beautySize, this._resolution ); + + return textureLoad( this.beautyNode, beautyTexel ).max( 0 ); + + } ); + + this._seedMaterial.fragmentNode = seed().context( builder.getSharedContext() ); + this._seedMaterial.needsUpdate = true; + + } + + _buildResolve( builder ) { + + const isSpecular = this.mode === 'specular'; + const cameraUniforms = this._cameraUniforms; + + const resolve = Fn( () => { + + const uvNode = uv(); + + const screenTexel = ivec2( floor( screenCoordinate.xy.sub( 0.5 ) ) ); + const depth = textureLoad( this.depthNode, screenTexel ).r.toVar(); + depth.greaterThanEqual( 1.0 ).discard(); + + const beautySize = this.beautyNode.size(); + const beautyTexel = beautyTexelFromScreen( screenTexel, beautySize, this._resolution ); + + const inputColor = textureLoad( this.beautyNode, beautyTexel ).max( 0 ).toVar(); + const viewNormal = unpackRGBToNormal( textureLoad( this.normalNode, screenTexel ).rgb ).toVar(); + + // Shared 3×3 beauty fetch: feeds both the variance-clip box and the SSR ray-length stats. + const neighborhood = collectNeighborhood( this.beautyNode, beautyTexel, inputColor, this.flickerSuppression ); + const worldNormal = viewNormal.transformDirection( cameraUniforms.viewMatrix ).toVar(); + + const viewPosition = getViewPosition( uvNode, depth, cameraUniforms.projectionMatrixInverse ).toVar(); + const worldPosition = cameraUniforms.worldMatrix.mul( vec4( viewPosition, 1.0 ) ).xyz.toVar(); + + const sampleHistory = ( reprojUV ) => sampleHistory4Tap( + this._historyTextureNode, + this._previousDepthNode, + this._previousNormalNode, + this._resolution, + cameraUniforms.previousProjectionMatrixInverse, + cameraUniforms.previousWorldMatrix, + cameraUniforms.previousViewMatrix, + reprojUV, + worldPosition, + worldNormal, + inputColor.rgb + ); + + // Surface-velocity reprojection — the base history for both modes. `historyUV` is + // reused below for the stretch guard, so it is computed once here. + const velocityOff = velocityToUVOffset( textureLoad( this.velocityNode, screenTexel ).xy ).toVar(); + const motionFactor = velocityOff.mul( this._resolution ).length().div( float( this.maxVelocityLength ) ).saturate(); + + const historyUV = uvNode.sub( velocityOff ).toVar(); + const surf = sampleHistory( historyUV ); + + const historyColor = surf.get( 'color' ).toVar(); + const totalConfidence = float( 1 ).toVar(); + const historyTrust = float( 0 ).toVar(); + + // Specular: blend parallax hit-point history on top of the surface result. Returns the resolved + // color (rgb from the blend, alpha from the surface tap), its confidence, and the hit-vs-surface trust. + const resolveSpecularHistory = () => { + + const surfValid = historyUV.x.greaterThanEqual( 0 ).and( historyUV.x.lessThanEqual( 1 ) ) + .and( historyUV.y.greaterThanEqual( 0 ) ).and( historyUV.y.lessThanEqual( 1 ) ); + + const historyUV_hit = reprojectHitPoint( + worldPosition, + neighborhood.get( 'rayLength' ), + cameraUniforms.worldPosition, + cameraUniforms.previousViewMatrix, + cameraUniforms.previousProjectionMatrix + ).toVar(); + + const hitValid = historyUV_hit.x.greaterThanEqual( 0 ).and( historyUV_hit.x.lessThanEqual( 1 ) ) + .and( historyUV_hit.y.greaterThanEqual( 0 ) ).and( historyUV_hit.y.lessThanEqual( 1 ) ) + .and( this.hitPointReprojection ); + + const hit = sampleHistory( historyUV_hit ); + + const hcHit = hit.get( 'color' ).rgb.max( 0 ); + const hcSurf = surf.get( 'color' ).rgb.max( 0 ); + + const confHit = hitValid.select( hit.get( 'tapConfidence' ), float( 0 ) ); + const confSurf = surfValid.select( surf.get( 'tapConfidence' ), float( 0 ) ); + const minConfHit = hit.get( 'minConfidence' ); + + const reflectionEdgeFactor = neighborhood.get( 'stdDevRayLength' ); + reflectionEdgeFactor.assign( reflectionEdgeFactor.mul( motionFactor.mul( 100 ).min( 1 ) ).mul( 3.5 ).min( 1 ).oneMinus() ); + + const curvatureFactor = fwidth( worldNormal.xyz ).length().mul( 50 ).clamp(); + + const envProbability = neighborhood.get( 'envProbability' ); + + const wHitRaw = minConfHit + .mul( reflectionEdgeFactor ) + .mul( curvatureFactor.oneMinus() ) + .mul( confHit ).toConst(); + + const wHit = wHitRaw.mul( envProbability.pow2().oneMinus() ); + const wSurf = wHit.oneMinus().mul( confSurf ); + const wSum = max( wHit.add( wSurf ), float( EPSILON ) ); + + const color = vec4( + hcHit.mul( wHit ).add( hcSurf.mul( wSurf ) ).div( wSum ), + surf.get( 'color' ).a + ).toVar(); + const confidence = confHit.mul( wHit ).add( confSurf.mul( wSurf ) ).div( wSum ); + + // Near-black blend means neither tap was usable — fall back to the current frame. + If( color.rgb.length().lessThan( EPSILON ), () => { + + color.assign( vec4( inputColor.rgb, 1 ) ); + + } ); + + return { color, confidence, trust: wHitRaw }; // without env probability + + }; + + if ( isSpecular ) { + + const spec = resolveSpecularHistory(); + historyColor.assign( spec.color ); + totalConfidence.assign( spec.confidence ); + historyTrust.assign( spec.trust ); + + } + + const a = historyColor.a.max( EPSILON ); + + // Universal stretch guard: reduce confidence where a "small area" is projected over a "large area". + const stretchConfidence = reprojectionStretchConfidence( historyUV, this._resolution ); + totalConfidence.mulAssign( stretchConfidence.pow( 2 ) ); + + const varianceGamma = mix( float( VARIANCE_GAMMA_MIN ), float( VARIANCE_GAMMA_MAX ), motionFactor.oneMinus().pow2() ); + + const clippedRGB = applyVarianceClipping( + historyColor, + neighborhood.get( 'mean' ), + neighborhood.get( 'stdColor' ), + varianceGamma, + this.flickerSuppression + ).toVar(); + + const clampIntensity = this.clampIntensity.mul( max( motionFactor.mul( 10 ).min( 1 ), 0.25 ) ).mul( + float( 1 ).add( stretchConfidence.oneMinus().add( historyTrust.oneMinus() ).clamp() ) + ); + const originalHistoryColor = vec3( historyColor.rgb ); + historyColor.rgb.assign( mix( historyColor.rgb, clippedRGB, clampIntensity ) ); + + totalConfidence.mulAssign( exp( originalHistoryColor.sub( clippedRGB ).length().mul( clampIntensity ).mul( 30 ).negate() ) ); + totalConfidence.mulAssign( mix( float( 1 ), historyTrust.mul( 0.05 ).add( 0.95 ), motionFactor.mul( 100 ).clamp() ) ); + + If( totalConfidence.lessThan( EPSILON ), () => { + + historyColor.assign( vec4( inputColor.rgb, 1 ) ); + + } ); + + const currentFrameCount = float( 1 ).div( a ).mul( totalConfidence ).add( 1 ).min( this.maxFrames ).toVar(); + + if ( isSpecular ) { + + // A black current sample means no reflection was found this frame (a miss, not dark). + // Since no valid sample was found, decrement the frame count (as the next accumulating pass will increase it). + If( inputColor.rgb.length().lessThan( EPSILON ), () => { + + currentFrameCount.assign( currentFrameCount.sub( 1 ).max( 1 ) ); + + } ); + + } + + return vec4( historyColor.rgb, float( 1 ).div( currentFrameCount ) ); + + } ); + + return resolve().context( builder.getSharedContext() ); + + } + + _syncHistoryTextureBinding() { + + if ( this.accumulate === true || this._externalHistoryTexture === null ) { + + this._historyTextureNode.value = this._historyRenderTarget.texture; + + } else { + + this._historyTextureNode.value = this._externalHistoryTexture; + + } + + } + + /** + * Supplies an external history source (e.g. a {@link RecurrentDenoiseNode} or its + * texture). Only used when {@link TemporalReprojectNode#accumulate} is `false`. + * + * @param {?(Object|import('three').Texture)} source + */ + setHistoryTexture( source ) { + + this._externalHistoryTexture = ( source && typeof source.getRenderTarget === 'function' ) + ? source.getRenderTarget().texture + : source; + this._syncHistoryTextureBinding(); + + } + + dispose() { + + this._previousNormalTexture.dispose(); + + if ( this._previousDepthNode.value !== this._historyRenderTarget.depthTexture ) { + + this._previousDepthNode.value.dispose(); + + } + + if ( this._placeholderPreviousDepthTexture !== this._historyRenderTarget.depthTexture ) { + + this._placeholderPreviousDepthTexture.dispose(); + + } + + this._historyRenderTarget.dispose(); + this._resolveRenderTarget.dispose(); + this._resolveMaterial.dispose(); + this._seedMaterial.dispose(); + + } + +} + +export default TemporalReprojectNode; + +/** + * @param {import('three/tsl').TextureNode} beautyNode + * @param {import('three/tsl').TextureNode} depthNode + * @param {import('three/tsl').TextureNode} normalNode + * @param {import('three/tsl').TextureNode} velocityNode + * @param {import('three').Camera} camera + * @param {TemporalReprojectNodeOptions} [options] + * @returns {TemporalReprojectNode} + */ +export const temporalReproject = ( beautyNode, depthNode, normalNode, velocityNode, camera, options = {} ) => nodeObject( new TemporalReprojectNode( + convertToTexture( beautyNode ), + nodeObject( depthNode ), + nodeObject( normalNode ), + nodeObject( velocityNode ), + camera, + options +) ); diff --git a/examples/jsm/tsl/utils/RNoise.js b/examples/jsm/tsl/utils/RNoise.js new file mode 100644 index 00000000000000..effb0c62e24810 --- /dev/null +++ b/examples/jsm/tsl/utils/RNoise.js @@ -0,0 +1,51 @@ +import { float, Fn, fract, int, vec2, vec4 } from 'three/tsl'; + +/** + * Returns a TSL function that samples texture-free analytic R² noise. + * Index 0 uses continuous screen pixels; other indices tile-shift with an R² + * sequence into a 64×64 period. Values are four independent R² dimensions + * hashed from the sample coordinates. + * + * @param {import('three/tsl').UniformNode} resolution + * @param {number} [seed=0] - Added to the coordinate hash so each pass gets an independent R² phase. + */ +export function bindAnalyticNoise( resolution, seed = 0 ) { + + const seedOffset = int( seed ); + + const r4 = ( coords ) => { + + const P = 1.32471795724474602596; + + const t = coords.x.mul( 1 / P ).add( coords.y.mul( 1 / P ** 2 ) ).add( float( seed ) ); + + return vec4( + fract( t.mul( P ).mul( 1 / P ) ), + fract( t.mul( P * 2 ).mul( 1 / P ** 2 ) ), + fract( t.mul( P * 3 ).mul( 0.4198754210 ) ), // this is not 1 / P ** 3, however this magic constant gives better noise + fract( t.mul( P * 4 ).mul( 1 / P ** 3 ) ) + ); + + }; + + return Fn( ( [ uvCoord, sampleIndex ] ) => { + + const index = int( sampleIndex ).add( seedOffset ); + const noise = vec4().toVar(); + + const tileSize = float( 32 ); + + const screenPixel = uvCoord.mul( resolution ).floor(); + const offset = fract( vec2( + float( index ).mul( 0.7548776662 ), + float( index ).mul( 0.5698402910 ) + ) ).mul( tileSize ).floor(); + const coords = screenPixel.add( offset ).mod( tileSize ); + + noise.assign( r4( coords ) ); + + return noise; + + } ); + +} diff --git a/examples/jsm/tsl/utils/SpecularHelpers.js b/examples/jsm/tsl/utils/SpecularHelpers.js new file mode 100644 index 00000000000000..71c7046c078a4c --- /dev/null +++ b/examples/jsm/tsl/utils/SpecularHelpers.js @@ -0,0 +1,325 @@ +import { Fn, If, PI, clamp, cos, cross, dot, equirectUV, float, log, max, mix, normalize, pow, reflect, sin, sqrt, struct, vec3 } from 'three/tsl'; + +/** + * Specular / microfacet BRDF helpers: VNDF sampling, GTR distribution, Smith geometry, + * Fresnel, reflection importance sampling, parallax-corrected ray-length terms, and + * equirectangular environment sampling / MIS helpers. + * Pure TSL functions of their inputs (no scene/camera state). + */ + +/** + * Sentinel ray length the SSR pass writes for environment misses (no screen-space hit), set far above + * any real hit distance so a single magnitude test separates misses from hits and survives `.max( 0 )`. + * + * @type {number} + */ +export const ENV_RAY_LENGTH = 1e4; + +/** + * Classification threshold for {@link ENV_RAY_LENGTH}: above this is an env miss, below a real hit. + * An order of magnitude under the sentinel, robust to fp16 storage and bilinear blending at borders. + * + * @type {number} + */ +export const ENV_RAY_LENGTH_THRESHOLD = 1e3; + +// Bounded-VNDF sampler (Eto & Tokuyoshi 2023; spherical-cap form, Dupuy & Benyoub 2023) +const SampleGGXVNDF = Fn( ( [ V, ax, ay, r1, r2 ] ) => { + + // Warp the view direction to the hemisphere ("standard") configuration. + const wiStd = normalize( vec3( ax.mul( V.x ), ay.mul( V.y ), V.z ) ).toVar(); + + // Isotropic bound on the spherical cap (Eto & Tokuyoshi eq. 5). alpha ∈ [0,1] here, + // so the sign term in `s` is always +1 and is dropped. + const a = ax.min( ay ).toVar(); + const s = float( 1.0 ).add( V.xy.length() ).toVar(); + const a2 = a.mul( a ).toVar(); + const s2 = s.mul( s ).toVar(); + const k = a2.oneMinus().mul( s2 ).div( s2.add( a2.mul( V.z ).mul( V.z ) ) ).toVar(); + + // Tighten the cap with the bound (upper hemisphere; N·V ≥ 0 in our usage). + const b = wiStd.z.mul( k ).toVar(); + + // Sample the (bounded) spherical cap. + const phi = float( 6.283185307179586 ).mul( r1 ).toVar(); // 2*pi + const z = r2.oneMinus().mul( float( 1.0 ).add( b ) ).sub( b ).toVar(); + const sinTheta = sqrt( max( float( 0.0 ), float( 1.0 ).sub( z.mul( z ) ) ) ).toVar(); + const c = vec3( sinTheta.mul( cos( phi ) ), sinTheta.mul( sin( phi ) ), z ).toVar(); + + // Microfacet normal in the standard config, then warp back to the ellipsoid (unstretch). + const wmStd = c.add( wiStd ).toVar(); + const Ne = normalize( vec3( + ax.mul( wmStd.x ), + ay.mul( wmStd.y ), + max( float( 0.0 ), wmStd.z ) + ) ).toVar(); + + return Ne; + +}, { + name: 'SampleGGXVNDF', + type: 'vec3', + inputs: [ + { name: 'V', type: 'vec3' }, + { name: 'ax', type: 'float' }, + { name: 'ay', type: 'float' }, + { name: 'r1', type: 'float' }, + { name: 'r2', type: 'float' }, + ] +} ); + +// Generalized Trowbridge-Reitz (GTR). For GGX set k=2. +// D_GTR(roughness, NoH, k) where roughness = α (not α²). +export const D_GTR = Fn( ( [ roughness, NoH, k ] ) => { + + // see: Filament - Normal distribution function (specular D) - 4.4.1 + const a2 = roughness.mul( roughness ).toVar(); // α² + const NoH2 = NoH.mul( NoH ).toVar(); + const base = NoH2.mul( a2.sub( float( 1.0 ) ) ).add( float( 1.0 ) ).toVar(); + // a² / (π * base^k) + return a2.div( PI.mul( pow( base, k ) ) ).toVar(); // float + +} ); + +// Smith G1 (Heitz): expects alpha (not squared); it squares internally +export const SmithG = Fn( ( [ NDotX, alpha ] ) => { + + // see: Filament - Geometric shadowing (specular G) - 4.4.2 + + const a2 = alpha.mul( alpha ).toVar(); // α² + const NDotX2 = NDotX.mul( NDotX ).toVar(); // (N·X)² + return float( 2.0 ).mul( NDotX ).div( + NDotX.add( sqrt( + a2.add( a2.oneMinus().mul( NDotX2 ) ) + ) ) + ); + +} ); + +// Geometry term G = G1(N·L, α_G) * G1(N·V, α_G) (α_G is NOT squared here) +export const GeometryTerm = Fn( ( [ NoL, NoV, alphaG ] ) => { + + const G1v = SmithG( NoV, alphaG ).toVar(); + const G1l = SmithG( NoL, alphaG ).toVar(); + return G1v.mul( G1l ).toVar(); + +} ); + +// Bounded VNDF direction PDF (reflection mapping), matching SampleGGXVNDF above. +// p(L) = D_GTR(roughness, NoH, 2) / ( 2 * (k * N·V + t) ) (Eto & Tokuyoshi eq. 8) +// with the isotropic cap bound k and t = ‖(α·V.xy, V.z)‖. Here 'roughness' is α, not α². +const GGXVNDFPdf = Fn( ( [ NoH, NoV, roughness ] ) => { + + const D = D_GTR( roughness, NoH, float( 2.0 ) ).toVar(); + const a2 = roughness.mul( roughness ).toVar(); + const sinV2 = max( float( 0.0 ), float( 1.0 ).sub( NoV.mul( NoV ) ) ).toVar(); // ‖V.xy‖² + const s = float( 1.0 ).add( sqrt( sinV2 ) ).toVar(); + const s2 = s.mul( s ).toVar(); + const k = float( 1.0 ).sub( a2 ).mul( s2 ).div( s2.add( a2.mul( NoV ).mul( NoV ) ) ).toVar(); + const t = sqrt( a2.mul( sinV2 ).add( NoV.mul( NoV ) ) ).toVar(); + return D.div( max( float( 1e-6 ), float( 2.0 ).mul( k.mul( NoV ).add( t ) ) ) ).toVar(); + +} ); + +/** + * Fresnel reflectance for the Schlick approximation. + */ +export const F_Schlick = Fn( ( [ f0, theta ] ) => { + + const oneMinus = float( 1.0 ).sub( theta ).toVar(); + const oneMinus2 = oneMinus.mul( oneMinus ).toVar(); + const oneMinus5 = oneMinus2.mul( oneMinus2 ).mul( oneMinus ).toVar(); + return f0.add( vec3( 1.0 ).sub( f0 ).mul( oneMinus5 ) ).toVar(); // vec3 + +} ); + +/** + * Specular dominant factor for parallax-corrected ray length. + * From REBLUR: A Hierarchical Recurrent Denoiser (NRD). + */ +export const getSpecularDominantFactor = Fn( ( [ NoV, roughness ] ) => { + + const a = float( 0.298475 ).mul( + log( float( 39.4115 ).sub( float( 39.0029 ).mul( roughness ) ) ) + ); + const f = float( 1.0 ).sub( NoV ).pow( 10.8649 ) + .mul( float( 1.0 ).sub( a ) ) + .add( a ); + return clamp( f ); + +} ).setLayout( { + name: 'getSpecularDominantFactor', + type: 'float', + inputs: [ + { name: 'NoV', type: 'float' }, + { name: 'roughness', type: 'float' } + ] +} ); + +/** + * Everything a single GGX reflection sample produces. `reflectDir` and `sampleWeight` + * drive the SSR ray-march and compositing; `pdf`, `NdotV`, `alpha` and `f0` are the GGX + * terms the env-miss MIS fallback needs so the caller never re-derives microfacet math. + */ +const ggxReflectionStruct = struct( { + reflectDir: 'vec3', // view-space reflected ray direction + sampleWeight: 'vec3', // chromatic weight (incl. Fresnel tint) to multiply gathered radiance by + pdf: 'float', // VNDF direction pdf (for MIS against the env CDF) + NdotV: 'float', + alpha: 'float', // GGX alpha (roughness²), clamped + f0: 'vec3' // Fresnel reflectance at normal incidence +} ); + +/** + * Importance-samples the GGX/VNDF specular lobe for one pixel and returns the reflected + * ray direction plus the Monte-Carlo weight to apply to the gathered radiance, along with + * the GGX terms the SSR env-miss MIS fallback needs. + * + * @param {Node} N - View-space shading normal (normalized). + * @param {Node} V - View-space surface→camera direction (normalized). + * @param {Node} roughness - Perceptual roughness in `[0,1]`. + * @param {Node} metalness - Metalness in `[0,1]`. + * @param {Node} albedo - Surface base color; tints the metal Fresnel reflectance (`f0`). + * @param {Node} Xi - Per-pixel random numbers; only `.xy` are used. + * @return {ggxReflectionStruct} + */ +export const ggxReflectionSample = Fn( ( [ N, V, roughness, metalness, albedo, Xi ] ) => { + + // GGX alpha (use r^2, clamp to avoid degenerate) + const a = roughness.mul( roughness ).max( 0.001 ).toVar(); + const ax = a.toVar(); + const ay = a.toVar(); + + // TBN from view-space normal + const up = vec3( 0, 0, 1 ); + let T = cross( up, N ).toVar(); + T = T.normalize().toVar(); + If( T.length().lessThan( 1e-3 ), () => { + + T.assign( cross( vec3( 0, 1, 0 ), N ).normalize() ); + + } ); + const B = cross( N, T ).normalize().toVar(); + + // transform V to LOCAL frame (N = +Z) + const Vlocal = vec3( dot( T, V ), dot( B, V ), dot( N, V ) ).toVar(); + + // VNDF sample **in local frame** + const Hlocal = SampleGGXVNDF( Vlocal, ax, ay, Xi.x, Xi.y ).toVar(); + If( Hlocal.z.lessThan( 0 ), () => { + + Hlocal.assign( Hlocal.negate() ); + + } ); + + // transform H back to VIEW space + const h = normalize( T.mul( Hlocal.x ).add( B.mul( Hlocal.y ) ).add( N.mul( Hlocal.z ) ) ).toVar(); + + // reflect with V (surface->camera) and H + const viewReflectDir = reflect( V.negate(), h ).normalize().toVar(); + + // BRDF/PDF evaluation for the sampled direction + // V: surface -> camera, L: reflected direction, N: normal, H: half-vector + const L = viewReflectDir.toVar(); + const H = normalize( V.add( L ) ).toVar(); // ~h; recomputed for robustness + + const NdotV = max( float( 0.0 ), dot( N, V ) ).toVar(); + const NdotL = max( float( 0.0 ), dot( N, L ) ).toVar(); + const NdotH = max( float( 0.0 ), dot( N, H ) ).toVar(); + const VdotH = max( float( 0.0 ), dot( V, H ) ).toVar(); + + const f0 = mix( vec3( 0.04 ), albedo, metalness ).toVar(); + // Chromatic Fresnel reflectance: for metals f0 = albedo, so the reflection is tinted and desaturates + // toward white at grazing angles. Kept as vec3 so colored metals reflect with the correct chroma. + const fresnelWeight = F_Schlick( f0, VdotH ).toVar(); // vec3 + + // Bounded-VNDF direction pdf — still needed for the env-miss MIS path. + const pdf = GGXVNDFPdf( NdotH, NdotV, ax ).toVar(); + + // Numerically stable importance weight: brdf·NdotL/pdf ≡ fresnel·G2·(k·NdotV + t)/(2·NdotV), which + // cancels the GGX D analytically. Evaluating D explicitly is catastrophic at low roughness + // (D → 3e5 at α = 0.001 wrecks f32 precision); the cancelled form stays stable down to a mirror. + // (k·NdotV + t) is the bounded-cap normalization; k shrinks the cap to drop below-horizon samples. + const a2 = ax.mul( ax ).toVar(); + const sinV2 = NdotV.mul( NdotV ).oneMinus().max( 0.0 ).toVar(); // ‖V.xy‖² + const sB = float( 1.0 ).add( sqrt( sinV2 ) ).toVar(); + const s2B = sB.mul( sB ).toVar(); + const kB = a2.oneMinus().mul( s2B ).div( s2B.add( a2.mul( NdotV ).mul( NdotV ) ) ).toVar(); + const tB = sqrt( a2.mul( sinV2 ).add( NdotV.mul( NdotV ) ) ).toVar(); + const glossyWeight = fresnelWeight + .mul( GeometryTerm( NdotL, NdotV, ax ) ) + .mul( kB.mul( NdotV ).add( tB ) ) + .div( float( 2.0 ).mul( NdotV ).max( 1e-4 ) ).toVar(); + + return ggxReflectionStruct( viewReflectDir, glossyWeight, pdf, NdotV, ax, f0 ); + +} ); + +// Equirectangular environment sampling + +/** + * Equirectangular direction / UV / PDF helpers and MIS weighting shared by environment sampling code. + * Env-miss MIS integration lives in {@link ImportanceSampledEnvironment}. + * + * Equirectangular parameterization helpers used with CDF importance sampling are adapted from + * [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer). + * + * @see {@link https://github.com/gkjohnson/three-gpu-pathtracer} + */ + +// uv -> direction (equirectangular) +export const equirectUvToDir = Fn( ( [ uvIn ] ) => { + + const phi = uvIn.x.mul( Math.PI * 2 ).sub( Math.PI ); + const lat = uvIn.y.sub( 0.5 ).mul( Math.PI ); + const cosLat = cos( lat ); + return normalize( vec3( + cosLat.mul( cos( phi ) ), + sin( lat ), + cosLat.mul( sin( phi ) ) + ) ); + +} ).setLayout( { + name: 'equirectUvToDir', + type: 'vec3', + inputs: [ { name: 'uv', type: 'vec2' } ] +} ); + +// Solid-angle PDF of a direction under equirectangular parameterization. +export const equirectDirPdf = Fn( ( [ direction ] ) => { + + const uvDir = equirectUV( direction ); + const sinTheta = sin( uvDir.y.mul( Math.PI ) ); + return sinTheta.abs().lessThan( float( 1e-6 ) ).select( + float( 0 ), + float( 1 ).div( float( 2 * Math.PI * Math.PI ).mul( sinTheta ) ) + ); + +} ).setLayout( { + name: 'equirectDirPdf', + type: 'float', + inputs: [ { name: 'direction', type: 'vec3' } ] +} ); + +/** + * MIS power heuristic with β = 2: `pdfA² / (pdfA² + pdfB²)`. + * Weights the contribution of the strategy that produced `pdfA` against the other strategy. + * + * @see Eric Veach, *Optimally Combining Sampling Techniques for Monte Carlo Rendering* + * @tsl + */ +export const misPowerHeuristic = Fn( ( [ pdfA, pdfB ] ) => { + + const pdfASq = pdfA.mul( pdfA ); + const pdfBSq = pdfB.mul( pdfB ); + return pdfASq.div( pdfASq.add( pdfBSq ) ); + +} ).setLayout( { + name: 'misPowerHeuristic', + type: 'float', + inputs: [ + { name: 'pdfA', type: 'float' }, + { name: 'pdfB', type: 'float' } + ] +} ); + diff --git a/examples/screenshots/webgpu_postprocessing_ssr_denoise.jpg b/examples/screenshots/webgpu_postprocessing_ssr_denoise.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a656e532c2f97e3cd2616d387f619a77d5abf8c8 GIT binary patch literal 93899 zcmbrlXH-*9)IJ(S1r$X!f5Rhf$Oe~;Cu9s#KT=lmtgMR5S-6L5=C zbtv_}@}5%vd**-kZ>>Sj9%4GG26v^z#Kq(Q7qftu|9$KKQTeZt|Dy)}d-$RQz(@-? zrTR)u#RRy-NJY&^bA_7099CwC7|FK?)iZ%AlZc*K{; zsKlh?l+?6u-_vvR@(T*#Ma3mGwRMPkWJ6<9S9ecuU;nRx!Ew~Y

^(2D6M^SzTM- z_`A7<-#<7!A{-M>PXEJ21)%=lSN8vki;;rs(&fw4muddPMRm!WQmGj)U%4lKmFc-I zjkPQD{ii{+w_hautnQ?HAfdO%^1*HF8Y`dVGC%%5X#a!k{~NI2|6j=d7uf%U3j?61 zrlJfUH6uU;aGu9o+um^o{^{mWWpZEXTHh1pprbHU+543a}xC-*+_<`JSJpeXv1oXxzuJlZp=xb|4TwXF8tE+}^Y#p0DquN{tNwc}` z9=_=OB4>_%JW-u~0q8QiFRGNynj4tjO=@b5YX_ywz>v!io6A&L+Yj5jEv$?J7#+@3 z{H20l^(DRk_E)v+fpSFX_U<;SjdKr92qwDXGrGM41&ENcPHZf*&SZrLXRDg<4WQ0G z!psb;)mZ+91CbIt1gURBq}absXJ>uSq$afNv(<|V9I0ozy*lonBG{l&o>Z%;ZDD$5 zp^TJ*t23C288~oM>8)+RWpxw>Aj6#bm|EYX1GMu7!Ho#3xLe1el$R#VBw|!u`|Dkp zfq}u?@1D;kKdSkt62TZU$2>8uC-6aCNVhK^Ca9*Zrm?A+X0Qge5{{L7ljc2g`HA%8 zJeOa(4{R;w=`B@A$WZaRRIx7nvuAS23l?q;=2D@z$%$ENx#^mW&Xxz7Lq>=vy?t>^ z$y=THKwjjIq;FFnlGh;Oy?-^cW5B1%NgrArV0C|L&UO?hNSM;EMyA14hO>L7d%hmH;yg<58#z= zuA$D=1}%2Y?(9|&A?M2Fv*UfRZJx)4oR?ccMzFK0dK@ut-(BPUGnsXIZWLBV48&lj z7UabEv$2>v*2p-1kX^co?F^T$DhE5C&z<{1srJ_zEC0AevqoYW!rK|^1gxZ_Dyg7D z8jkzMzb(|e(^G!Fm=8Ibb^k-1tx>ko@%?4=IYD|;#!m5R^8)&`v}UswLGY?t?twN8 zm@$0TMpRan7uL77A!ZgZR?bMP4EgxZ#hL5c-*GF%kUig-pCYyDo-6TJy6{WZ-H}qB zb`LUx6;{}XVa&b;^^t|3A%cRWU3HyaDdbw1UGNjbXU_n7v;e?W;Wfv#@A?HaLU-y4 z?hGZ^RPCk=2!Mw~G>zKGBciPz>n#!Hfhyk%xUQf;$dED=y3X~~V%tH$=1de_egSw7 zq@4k}7n59x3^uMem-$3j7HQ_@*rk)!`dz@q=5O}7Zhc8e{QV*hsM60%NLN!>EXF6%1b0I?@j6T1$a5~3<}SDK(Kl<{TjN~�zKm2z{6{w>P~ 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 A2><{9 literal 0 HcmV?d00001 diff --git a/examples/webgpu_postprocessing_ssr.html b/examples/webgpu_postprocessing_ssr.html index db374d77111c47..77dc35bc71365c 100644 --- a/examples/webgpu_postprocessing_ssr.html +++ b/examples/webgpu_postprocessing_ssr.html @@ -58,8 +58,9 @@ quality: 0.5, blurQuality: 1, maxDistance: 1, - opacity: 1, + intensity: 1, thickness: 0.03, + binaryRefine: false, roughness: 1, enabled: true }; @@ -179,7 +180,10 @@ // - ssrPass = ssr( scenePassColor, scenePassDepth, sceneNormal, scenePassMetalRough.r, scenePassMetalRough.g ).toInspector( 'SSR' ); + ssrPass = ssr( scenePassColor, scenePassDepth, sceneNormal, { + metalnessNode: scenePassMetalRough.r, + roughnessNode: scenePassMetalRough.g + } ).toInspector( 'SSR' ); // blend SSR over beauty (SSR outputs premultiplied color, so use additive blending) @@ -202,8 +206,9 @@ ssrFolder.add( params, 'quality', 0, 1 ).onChange( updateParameters ); ssrFolder.add( params, 'blurQuality', 1, 3, 1 ).onChange( updateParameters ); ssrFolder.add( params, 'maxDistance', 0, 1 ).onChange( updateParameters ); - ssrFolder.add( params, 'opacity', 0, 1 ).onChange( updateParameters ); + ssrFolder.add( params, 'intensity', 0, 1 ).onChange( updateParameters ); ssrFolder.add( params, 'thickness', 0, 0.05 ).onChange( updateParameters ); + ssrFolder.add( params, 'binaryRefine' ).name( 'binary refine' ).onChange( updateParameters ); ssrFolder.add( params, 'enabled' ).onChange( () => { if ( params.enabled === true ) { @@ -241,10 +246,14 @@ function updateParameters() { ssrPass.quality.value = params.quality; - ssrPass.blurQuality.value = params.blurQuality; + // blurQuality is a build-time constant: assigning it recompiles the blur material + // (the setter no-ops when the value is unchanged). + ssrPass.blurQuality = params.blurQuality; ssrPass.maxDistance.value = params.maxDistance; - ssrPass.opacity.value = params.opacity; + ssrPass.intensity.value = params.intensity; ssrPass.thickness.value = params.thickness; + // build-time constant: assigning it recompiles the SSR material (setter no-ops if unchanged) + ssrPass.binaryRefine = params.binaryRefine; } @@ -262,7 +271,7 @@ controls.update(); renderPipeline.render(); - + } diff --git a/examples/webgpu_postprocessing_ssr_denoise.html b/examples/webgpu_postprocessing_ssr_denoise.html new file mode 100644 index 00000000000000..80902a7252e768 --- /dev/null +++ b/examples/webgpu_postprocessing_ssr_denoise.html @@ -0,0 +1,574 @@ + + + + + three.js webgpu - postprocessing - Screen Space Reflections (SSR) + denoise + + + + + + + + + + + + +

+ +
+ + +
+ three.jsSSR + Denoising +
+ + + Screen Space Reflections with Spatiotemporal Denoising by 0beqz.
+ Dungeon - Low Poly Game Level Challenge by + Warkarma.
+
+
+ + + + + + + \ No newline at end of file diff --git a/test/e2e/puppeteer.js b/test/e2e/puppeteer.js index 2a3be6b574e809..68b8444c638548 100644 --- a/test/e2e/puppeteer.js +++ b/test/e2e/puppeteer.js @@ -57,6 +57,7 @@ const exceptionList = [ 'webgpu_materials_matcap', 'webgpu_morphtargets_face', 'webgpu_shadowmap_progressive', + 'webgpu_postprocessing_ssr_denoise', // Video hangs the CI? 'css3d_youtube', From 4e2b42dee843ef9ee5027366923fb5629e82b3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E7=82=B3=E6=9D=83?= <695601626@qq.com> Date: Thu, 25 Jun 2026 17:13:05 +0800 Subject: [PATCH 3/9] devtools: clean up (#33879) --- devtools/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/manifest.json b/devtools/manifest.json index 6796e995899527..2384a5ce216315 100644 --- a/devtools/manifest.json +++ b/devtools/manifest.json @@ -29,4 +29,4 @@ "activeTab", "webNavigation" ] -} +} From 1b4637dd8530a76dafc5565d49dc62b76982a82b Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 25 Jun 2026 18:20:44 +0900 Subject: [PATCH 4/9] TSL: Fix JSDoc type expressions for docs build. Drop TS-style import('three') / import('three/tsl') prefixes from JSDoc tags so the type parser accepts them, matching the bare-name convention used elsewhere. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../display/ImportanceSampledEnvironment.js | 40 ++++++++--------- .../jsm/tsl/display/TemporalReprojectNode.js | 44 +++++++++---------- examples/jsm/tsl/utils/RNoise.js | 2 +- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/examples/jsm/tsl/display/ImportanceSampledEnvironment.js b/examples/jsm/tsl/display/ImportanceSampledEnvironment.js index 188d3de2ef60e0..1a5260bbede3ff 100644 --- a/examples/jsm/tsl/display/ImportanceSampledEnvironment.js +++ b/examples/jsm/tsl/display/ImportanceSampledEnvironment.js @@ -329,7 +329,7 @@ class ImportanceSampledEnvironment { } /** - * @param {import('three').Texture} hdr - Equirectangular HDR environment map. + * @param {Texture} hdr - Equirectangular HDR environment map. */ updateFrom( hdr ) { @@ -388,10 +388,10 @@ class ImportanceSampledEnvironment { * Simple environment lookup along the reflected direction (no MIS). * * @param {Object} params - * @param {import('three/tsl').UniformNode} params.cameraWorldMatrix - * @param {import('three/tsl').Node} params.viewReflectDir - * @param {import('three/tsl').Node} [params.sampleWeight] - Optional radiance scale (defaults to 1). - * @return {import('three/tsl').Node} + * @param {UniformNode} params.cameraWorldMatrix + * @param {Node} params.viewReflectDir + * @param {Node} [params.sampleWeight] - Optional radiance scale (defaults to 1). + * @return {Node} */ sampleReflect( { cameraWorldMatrix, viewReflectDir, sampleWeight = float( 1 ) } ) { @@ -409,13 +409,13 @@ class ImportanceSampledEnvironment { * Environment reflection for a screen-space miss using only the BRDF / reflected-ray direction. * * @param {Object} params - * @param {import('three/tsl').UniformNode} params.cameraWorldMatrix - * @param {import('three/tsl').Node} params.viewReflectDir - View-space GGX-sampled reflected ray. - * @param {import('three/tsl').Node} params.N - View-space shading normal. - * @param {import('three/tsl').Node} params.V - View-space direction to camera. - * @param {import('three/tsl').Node} params.alpha - GGX roughness (alpha). - * @param {import('three/tsl').Node} params.f0 - * @return {import('three/tsl').Node} + * @param {UniformNode} params.cameraWorldMatrix + * @param {Node} params.viewReflectDir - View-space GGX-sampled reflected ray. + * @param {Node} params.N - View-space shading normal. + * @param {Node} params.V - View-space direction to camera. + * @param {Node} params.alpha - GGX roughness (alpha). + * @param {Node} params.f0 + * @return {Node} */ sampleEnvironmentBRDF( { cameraWorldMatrix, @@ -455,14 +455,14 @@ class ImportanceSampledEnvironment { * @see {@link https://github.com/gkjohnson/three-gpu-pathtracer} * * @param {Object} params - * @param {import('three/tsl').UniformNode} params.cameraWorldMatrix - * @param {import('three/tsl').Node} params.viewReflectDir - View-space GGX-sampled reflected ray. - * @param {import('three/tsl').Node} params.N - View-space shading normal. - * @param {import('three/tsl').Node} params.V - View-space direction to camera. - * @param {import('three/tsl').Node} params.alpha - GGX roughness (alpha). - * @param {import('three/tsl').Node} params.f0 - * @param {import('three/tsl').Node} params.Xi2 - Second blue-noise sample (zw used for the CDF). - * @return {import('three/tsl').Node} + * @param {UniformNode} params.cameraWorldMatrix + * @param {Node} params.viewReflectDir - View-space GGX-sampled reflected ray. + * @param {Node} params.N - View-space shading normal. + * @param {Node} params.V - View-space direction to camera. + * @param {Node} params.alpha - GGX roughness (alpha). + * @param {Node} params.f0 + * @param {Node} params.Xi2 - Second blue-noise sample (zw used for the CDF). + * @return {Node} */ sampleEnvironmentMIS( { cameraWorldMatrix, diff --git a/examples/jsm/tsl/display/TemporalReprojectNode.js b/examples/jsm/tsl/display/TemporalReprojectNode.js index b942eb0073873e..a1443f922b359b 100644 --- a/examples/jsm/tsl/display/TemporalReprojectNode.js +++ b/examples/jsm/tsl/display/TemporalReprojectNode.js @@ -58,8 +58,8 @@ const projectWorldToUV = Fn( ( [ worldPos, previousViewMatrix, previousProjectio // YCoCg variance clipping /** - * @param {import('three/tsl').Node} c - * @returns {import('three/tsl').Node} + * @param {Node} c + * @returns {Node} */ const rgbToYCoCg = ( c ) => vec3( dot( c, vec3( 0.25, 0.5, 0.25 ) ), @@ -68,8 +68,8 @@ const rgbToYCoCg = ( c ) => vec3( ); /** - * @param {import('three/tsl').Node} c - * @returns {import('three/tsl').Node} + * @param {Node} c + * @returns {Node} */ const ycocgToRGB = ( c ) => vec3( c.x.add( c.y ).sub( c.z ), @@ -84,9 +84,9 @@ const VARIANCE_CLIP_LUMA_SCALE = 10; * Bright samples contribute less to neighbourhood moments so sun pixels do not * inflate the YCoCg AABB and cause aggressive clipping flicker. * - * @param {import('three/tsl').Node} rgb - * @param {import('three/tsl').Node} flickerSuppression - * @returns {import('three/tsl').Node} + * @param {Node} rgb + * @param {Node} flickerSuppression + * @returns {Node} */ const dampenForVarianceClip = ( rgb, flickerSuppression ) => { @@ -270,8 +270,8 @@ const sampleBilinearTap = Fn( ( [ /** * @param {Object} ctx - Shared {@link sampleBilinearTap} inputs plus `reprojICoord`. - * @param {import('three/tsl').Node} tapOffset - * @param {import('three/tsl').Node} bilinearWeight + * @param {Node} tapOffset + * @param {Node} bilinearWeight */ function bilinearHistoryTap( ctx, tapOffset, bilinearWeight ) { @@ -429,7 +429,7 @@ const velocityToUVOffset = Fn( ( [ velocity ] ) => { /** * Current and previous-frame camera matrices for temporal reprojection passes. * - * @param {import('three').Camera} camera + * @param {Camera} camera */ function bindTemporalCameraUniforms( camera ) { @@ -445,7 +445,7 @@ function bindTemporalCameraUniforms( camera ) { const previousProjectionMatrixInverse = uniform( new Matrix4().copy( camera.projectionMatrixInverse ) ); /** - * @param {import('three').Camera} cam + * @param {Camera} cam */ function updateFromCamera( cam ) { @@ -525,11 +525,11 @@ class TemporalReprojectNode extends TempNode { } /** - * @param {import('three/tsl').TextureNode} beautyNode - * @param {import('three/tsl').TextureNode} depthNode - * @param {import('three/tsl').TextureNode} normalNode - * @param {import('three/tsl').TextureNode} velocityNode - * @param {import('three').Camera} camera + * @param {TextureNode} beautyNode + * @param {TextureNode} depthNode + * @param {TextureNode} normalNode + * @param {TextureNode} velocityNode + * @param {Camera} camera * @param {TemporalReprojectNodeOptions} [options] */ constructor( beautyNode, depthNode, normalNode, velocityNode, camera, options = {} ) { @@ -966,7 +966,7 @@ class TemporalReprojectNode extends TempNode { * Supplies an external history source (e.g. a {@link RecurrentDenoiseNode} or its * texture). Only used when {@link TemporalReprojectNode#accumulate} is `false`. * - * @param {?(Object|import('three').Texture)} source + * @param {?(Object|Texture)} source */ setHistoryTexture( source ) { @@ -1005,11 +1005,11 @@ class TemporalReprojectNode extends TempNode { export default TemporalReprojectNode; /** - * @param {import('three/tsl').TextureNode} beautyNode - * @param {import('three/tsl').TextureNode} depthNode - * @param {import('three/tsl').TextureNode} normalNode - * @param {import('three/tsl').TextureNode} velocityNode - * @param {import('three').Camera} camera + * @param {TextureNode} beautyNode + * @param {TextureNode} depthNode + * @param {TextureNode} normalNode + * @param {TextureNode} velocityNode + * @param {Camera} camera * @param {TemporalReprojectNodeOptions} [options] * @returns {TemporalReprojectNode} */ diff --git a/examples/jsm/tsl/utils/RNoise.js b/examples/jsm/tsl/utils/RNoise.js index effb0c62e24810..d5ddee5b8351d2 100644 --- a/examples/jsm/tsl/utils/RNoise.js +++ b/examples/jsm/tsl/utils/RNoise.js @@ -6,7 +6,7 @@ import { float, Fn, fract, int, vec2, vec4 } from 'three/tsl'; * sequence into a 64×64 period. Values are four independent R² dimensions * hashed from the sample coordinates. * - * @param {import('three/tsl').UniformNode} resolution + * @param {UniformNode} resolution * @param {number} [seed=0] - Added to the coordinate hash so each pass gets an independent R² phase. */ export function bindAnalyticNoise( resolution, seed = 0 ) { From 7e784334e62dc1c0a4a86dd7b0a8210c236463b7 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 25 Jun 2026 18:22:44 +0900 Subject: [PATCH 5/9] Updated docs. --- docs/index.html | 42 ++ docs/llms-full.txt | 4 + docs/pages/EnvMapCDFGenerator.html | 34 ++ docs/pages/EnvMapCDFGenerator.html.md | 11 + docs/pages/ImportanceSampledEnvironment.html | 267 +++++++++++ .../ImportanceSampledEnvironment.html.md | 103 ++++ docs/pages/RecurrentDenoiseNode.html | 97 ++++ docs/pages/RecurrentDenoiseNode.html.md | 55 +++ docs/pages/SSRNode.html | 265 +++++++++-- docs/pages/SSRNode.html.md | 154 +++++- docs/pages/TSL.html | 441 +++++++++++++++++- docs/pages/TSL.html.md | 269 ++++++++++- docs/pages/TemporalReprojectNode.html | 124 +++++ docs/pages/TemporalReprojectNode.html.md | 58 +++ docs/pages/global.html | 432 +++++++++++++++++ docs/pages/global.html.md | 223 +++++++++ docs/search.json | 308 +++++++++++- 17 files changed, 2805 insertions(+), 82 deletions(-) create mode 100644 docs/pages/EnvMapCDFGenerator.html create mode 100644 docs/pages/EnvMapCDFGenerator.html.md create mode 100644 docs/pages/ImportanceSampledEnvironment.html create mode 100644 docs/pages/ImportanceSampledEnvironment.html.md create mode 100644 docs/pages/RecurrentDenoiseNode.html create mode 100644 docs/pages/RecurrentDenoiseNode.html.md create mode 100644 docs/pages/TemporalReprojectNode.html create mode 100644 docs/pages/TemporalReprojectNode.html.md diff --git a/docs/index.html b/docs/index.html index 0da8d36579ea89..1e06fd435f53ba 100644 --- a/docs/index.html +++ b/docs/index.html @@ -897,6 +897,7 @@

TSL

  • DirectionalLightDataNode
  • DotScreenNode
  • DynamicLightsNode
  • +
  • EnvMapCDFGenerator
  • FSR1Node
  • FXAANode
  • FilmNode
  • @@ -904,6 +905,7 @@

    TSL

  • GaussianBlurNode
  • GodraysNode
  • HemisphereLightDataNode
  • +
  • ImportanceSampledEnvironment
  • LensflareNode
  • Lut3DNode
  • OutlineNode
  • @@ -912,6 +914,7 @@

    TSL

  • PixelationPassNode
  • PointLightDataNode
  • RGBShiftNode
  • +
  • RecurrentDenoiseNode
  • RetroPassNode
  • SMAANode
  • SSAAPassNode
  • @@ -925,6 +928,7 @@

    TSL

  • StereoPassNode
  • TAAUNode
  • TRAANode
  • +
  • TemporalReprojectNode
  • TileShadowNode
  • TileShadowNodeHelper
  • TransitionNode
  • @@ -1014,6 +1018,7 @@

    TSL

  • any
  • ao
  • append
  • +
  • applyVarianceClipping
  • array
  • asin
  • asinh
  • @@ -1042,6 +1047,7 @@

    TSL

  • barrelUV
  • barrier
  • batch
  • +
  • beautyTexelFromScreen
  • bentNormalView
  • bilateralBlur
  • billboarding
  • @@ -1093,15 +1099,19 @@

    TSL

  • clearcoatNormalView
  • clearcoatRoughness
  • clipSpace
  • +
  • clipToAABB
  • clipping
  • clippingAlpha
  • clusteredLights
  • code
  • +
  • collectNeighborhood
  • colorBleeding
  • colorSpaceToWorking
  • colorToDirection
  • compute
  • computeBuiltin
  • +
  • computeFrustumSize
  • +
  • computeHitDistFactor
  • computeKernel
  • computeSkinning
  • context
  • @@ -1134,6 +1144,7 @@

    TSL

  • determinant
  • difference
  • diffuseColor
  • +
  • diffuseColorDistance
  • diffuseContribution
  • directionToColor
  • directionToFaceDirection
  • @@ -1176,6 +1187,8 @@

    TSL

  • getScreenPosition
  • getShadowMaterial
  • getShadowRenderObjectFunction
  • +
  • getSpecularDominantDirection
  • +
  • getTemporalVarianceFactor
  • getViewPosition
  • globalId
  • glsl
  • @@ -1210,6 +1223,7 @@

    TSL

  • iridescenceThickness
  • isolate
  • js
  • +
  • karisTemporalBlend
  • label
  • length
  • lengthSq
  • @@ -1225,12 +1239,15 @@

    TSL

  • lights
  • linearDepth
  • linearToneMapping
  • +
  • lobeNormalFalloff
  • +
  • lobeNormalWeight
  • localId
  • log
  • log2
  • logarithmicDepthToViewZ
  • luminance
  • lut3D
  • +
  • mapAo
  • matcapUV
  • materialAO
  • materialAlphaTest
  • @@ -1279,6 +1296,7 @@

    TSL

  • mediumpModelViewMatrix
  • metalness
  • min
  • +
  • misPowerHeuristic
  • mix
  • mixElement
  • mod
  • @@ -1347,6 +1365,7 @@

    TSL

  • permute
  • perspectiveDepthToViewZ
  • pixelationPass
  • +
  • planeDistance
  • pmremTexture
  • pointShadow
  • pointUV
  • @@ -1365,6 +1384,7 @@

    TSL

  • pow4
  • premultipliedGaussianBlur
  • premultiplyAlpha
  • +
  • projectWorldToUV
  • property
  • quadBroadcast
  • quadSwapDiagonal
  • @@ -1376,6 +1396,7 @@

    TSL

  • range
  • rangeFogFactor
  • reciprocal
  • +
  • recurrentDenoise
  • reference
  • referenceBuffer
  • reflect
  • @@ -1392,6 +1413,8 @@

    TSL

  • renderOutput
  • rendererReference
  • replaceDefaultUV
  • +
  • reprojectHitPoint
  • +
  • reprojectionStretchConfidence
  • retroPass
  • rgbShift
  • rotate
  • @@ -1401,6 +1424,8 @@

    TSL

  • rtt
  • sRGBTransferEOTF
  • sRGBTransferOETF
  • +
  • sampleBilinearTap
  • +
  • sampleHistory4Tap
  • sampler
  • samplerComparison
  • saturate
  • @@ -1437,6 +1462,7 @@

    TSL

  • specularColor
  • specularColorBlended
  • specularF90
  • +
  • specularLobeTanHalfAngle
  • spherizeUV
  • spritesheetUV
  • sqrt
  • @@ -1537,6 +1563,7 @@

    TSL

  • varying
  • varyingProperty
  • velocity
  • +
  • velocityToUVOffset
  • vertexColor
  • vertexIndex
  • vertexStage
  • @@ -1558,6 +1585,7 @@

    TSL

  • viewportTexture
  • viewportUV
  • vignette
  • +
  • vogelDisk
  • vogelDiskSample
  • wgsl
  • workgroupArray
  • @@ -1613,11 +1641,14 @@

    Global

  • DynamicCopyUsage
  • DynamicDrawUsage
  • DynamicReadUsage
  • +
  • ENV_RAY_LENGTH
  • +
  • ENV_RAY_LENGTH_THRESHOLD
  • EqualCompare
  • EqualDepth
  • EqualStencilFunc
  • EquirectangularReflectionMapping
  • EquirectangularRefractionMapping
  • +
  • F_Schlick
  • FloatType
  • FrontSide
  • GLSL1
  • @@ -1803,6 +1834,9 @@

    Global

  • addSpandrelBands
  • bakeGroups
  • batchColor
  • +
  • bilinearHistoryTap
  • +
  • bindAnalyticNoise
  • +
  • bindTemporalCameraUniforms
  • buildData3DTexture
  • buildFaces
  • buildFootprint
  • @@ -1822,11 +1856,13 @@

    Global

  • createSkyscraperMaterial
  • createTreeMaterial
  • damp
  • +
  • dampenForVarianceClip
  • degToRad
  • denormalize
  • depthAwareBlend
  • disposeShadowMaterial
  • enhanceLogMessage
  • +
  • equirectUvToDir
  • error
  • euclideanModulo
  • fill
  • @@ -1854,11 +1890,14 @@

    Global

  • getPreviousSkinnedPosition
  • getSkinnedNormalAndTangent
  • getSkinnedPosition
  • +
  • getSpecularDominantFactor
  • getStrideLength
  • getTextureIndex
  • getUniforms
  • getVectorLength
  • getViewZNode
  • +
  • ggxReflectionSample
  • +
  • ggxReflectionStruct
  • instanceColor
  • inverseLerp
  • isPowerOfTwo
  • @@ -1874,6 +1913,7 @@

    Global

  • randFloat
  • randFloatSpread
  • randInt
  • +
  • rgbToYCoCg
  • sample
  • seededRandom
  • setConsoleFunction
  • @@ -1885,6 +1925,7 @@

    Global

  • smootherstep
  • sortedArray
  • subclip
  • +
  • temporalReproject
  • toHalfFloat
  • totalDiffuse
  • totalSpecular
  • @@ -1897,6 +1938,7 @@

    Global

  • worldEnd
  • worldPos
  • worldStart
  • +
  • ycocgToRGB
  • yieldToMain
  • diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 45b95ec6cbd95f..db7e82be20ffcb 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -1979,6 +1979,7 @@ The following documentation pages are available in markdown format at `https://t - [Earcut](https://threejs.org/docs/pages/Earcut.html.md) - [EdgeSplitModifier](https://threejs.org/docs/pages/EdgeSplitModifier.html.md) - [EffectComposer](https://threejs.org/docs/pages/EffectComposer.html.md) +- [EnvMapCDFGenerator](https://threejs.org/docs/pages/EnvMapCDFGenerator.html.md) - [EventDispatcher](https://threejs.org/docs/pages/EventDispatcher.html.md) - [FaceFrame](https://threejs.org/docs/pages/FaceFrame.html.md) - [FigureEightPolynomialKnot](https://threejs.org/docs/pages/FigureEightPolynomialKnot.html.md) @@ -2002,6 +2003,7 @@ The following documentation pages are available in markdown format at `https://t - [Gyroscope](https://threejs.org/docs/pages/Gyroscope.html.md) - [HTMLMesh](https://threejs.org/docs/pages/HTMLMesh.html.md) - [ImageUtils](https://threejs.org/docs/pages/ImageUtils.html.md) +- [ImportanceSampledEnvironment](https://threejs.org/docs/pages/ImportanceSampledEnvironment.html.md) - [ImprovedNoise](https://threejs.org/docs/pages/ImprovedNoise.html.md) - [IndirectStorageBufferAttribute](https://threejs.org/docs/pages/IndirectStorageBufferAttribute.html.md) - [Info](https://threejs.org/docs/pages/Info.html.md) @@ -2626,6 +2628,7 @@ The following documentation pages are available in markdown format at `https://t - [RTTNode](https://threejs.org/docs/pages/RTTNode.html.md) - [RangeNode](https://threejs.org/docs/pages/RangeNode.html.md) - [RectAreaLightNode](https://threejs.org/docs/pages/RectAreaLightNode.html.md) +- [RecurrentDenoiseNode](https://threejs.org/docs/pages/RecurrentDenoiseNode.html.md) - [ReferenceBaseNode](https://threejs.org/docs/pages/ReferenceBaseNode.html.md) - [ReferenceElementNode](https://threejs.org/docs/pages/ReferenceElementNode.html.md) - [ReferenceNode](https://threejs.org/docs/pages/ReferenceNode.html.md) @@ -2659,6 +2662,7 @@ The following documentation pages are available in markdown format at `https://t - [TAAUNode](https://threejs.org/docs/pages/TAAUNode.html.md) - [TRAANode](https://threejs.org/docs/pages/TRAANode.html.md) - [TempNode](https://threejs.org/docs/pages/TempNode.html.md) +- [TemporalReprojectNode](https://threejs.org/docs/pages/TemporalReprojectNode.html.md) - [Texture3DNode](https://threejs.org/docs/pages/Texture3DNode.html.md) - [TextureNode](https://threejs.org/docs/pages/TextureNode.html.md) - [TextureSizeNode](https://threejs.org/docs/pages/TextureSizeNode.html.md) diff --git a/docs/pages/EnvMapCDFGenerator.html b/docs/pages/EnvMapCDFGenerator.html new file mode 100644 index 00000000000000..896a299e1742ac --- /dev/null +++ b/docs/pages/EnvMapCDFGenerator.html @@ -0,0 +1,34 @@ + + + + + EnvMapCDFGenerator - Three.js Docs + + + + + + +

    EnvMapCDFGenerator

    +
    +
    +

    Precomputes marginal and conditional CDF textures from an equirectangular HDR environment map +for luminance importance sampling.

    +
    + +
    + + + + \ No newline at end of file diff --git a/docs/pages/EnvMapCDFGenerator.html.md b/docs/pages/EnvMapCDFGenerator.html.md new file mode 100644 index 00000000000000..62880f5ffdef1c --- /dev/null +++ b/docs/pages/EnvMapCDFGenerator.html.md @@ -0,0 +1,11 @@ +# EnvMapCDFGenerator + +Precomputes marginal and conditional CDF textures from an equirectangular HDR environment map for luminance importance sampling. + +## Constructor + +### new EnvMapCDFGenerator() + +## Source + +[examples/jsm/tsl/display/ImportanceSampledEnvironment.js](https://github.com/mrdoob/three.js/blob/master/examples/jsm/tsl/display/ImportanceSampledEnvironment.js) \ No newline at end of file diff --git a/docs/pages/ImportanceSampledEnvironment.html b/docs/pages/ImportanceSampledEnvironment.html new file mode 100644 index 00000000000000..a7e983e9761b7a --- /dev/null +++ b/docs/pages/ImportanceSampledEnvironment.html @@ -0,0 +1,267 @@ + + + + + ImportanceSampledEnvironment - Three.js Docs + + + + + + +

    ImportanceSampledEnvironment

    +
    +
    +

    Manages a preprocessed HDR environment map (CDF textures, uniforms) and exposes +TSL helpers for BRDF-direction lookups and MIS importance sampling.

    +
    +
    +
    +

    Constructor

    +

    new ImportanceSampledEnvironment( importanceSampling : boolean )

    +
    + + + + + + + +
    + importanceSampling + +

    When true, builds luminance CDF tables and enables MIS env sampling.

    +

    Default is false.

    +
    +
    +
    See:
    +
    + +
    +
    +
    +
    +

    Methods

    +

    .sampleEnvironmentBRDF( params : Object ) : Node.<vec3>

    +
    +
    +

    Environment reflection for a screen-space miss using only the BRDF / reflected-ray direction.

    +
    + + + + + + + +
    + params + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + cameraWorldMatrix + +
    + viewReflectDir + +

    View-space GGX-sampled reflected ray.

    +
    + N + +

    View-space shading normal.

    +
    + V + +

    View-space direction to camera.

    +
    + alpha + +

    GGX roughness (alpha).

    +
    + f0 + +
    +
    +
    +

    .sampleEnvironmentMIS( params : Object ) : Node.<vec3>

    +
    +
    +

    Environment reflection for a screen-space miss, estimated with multiple importance +sampling (MIS) between the BRDF / reflected-ray direction and the env-luminance CDF +direction. Both techniques use consistent solid-angle PDFs (D·G1(N·V)/(4·N·V)), so +the power heuristic is unbiased. Adapted from three-gpu-pathtracer.

    +
    + + + + + + + +
    + params + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + cameraWorldMatrix + +
    + viewReflectDir + +

    View-space GGX-sampled reflected ray.

    +
    + N + +

    View-space shading normal.

    +
    + V + +

    View-space direction to camera.

    +
    + alpha + +

    GGX roughness (alpha).

    +
    + f0 + +
    + Xi2 + +

    Second blue-noise sample (zw used for the CDF).

    +
    +
    +
    +
    See:
    +
    + +
    +
    +
    +

    .sampleReflect( params : Object ) : Node.<vec3>

    +
    +
    +

    Simple environment lookup along the reflected direction (no MIS).

    +
    + + + + + + + +
    + params + + + + + + + + + + + + + + + + +
    + cameraWorldMatrix + +
    + viewReflectDir + +
    + sampleWeight + +

    Optional radiance scale (defaults to 1).

    +
    +
    +
    +

    .updateFrom( hdr : Texture )

    +
    + + + + + + + +
    + hdr + +

    Equirectangular HDR environment map.

    +
    +
    +

    Source

    +

    + examples/jsm/tsl/display/ImportanceSampledEnvironment.js +

    +
    +
    + + + + \ No newline at end of file diff --git a/docs/pages/ImportanceSampledEnvironment.html.md b/docs/pages/ImportanceSampledEnvironment.html.md new file mode 100644 index 00000000000000..5f02881fdc274d --- /dev/null +++ b/docs/pages/ImportanceSampledEnvironment.html.md @@ -0,0 +1,103 @@ +# ImportanceSampledEnvironment + +Manages a preprocessed HDR environment map (CDF textures, uniforms) and exposes TSL helpers for BRDF-direction lookups and MIS importance sampling. + +## Constructor + +### new ImportanceSampledEnvironment( importanceSampling : boolean ) + +**importanceSampling** + +When `true`, builds luminance CDF tables and enables MIS env sampling. + +Default is `false`. + +See: + +* [https://github.com/gkjohnson/three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer) + +## Methods + +### .sampleEnvironmentBRDF( params : Object ) : Node. + +Environment reflection for a screen-space miss using only the BRDF / reflected-ray direction. + +**params** + +**cameraWorldMatrix** + +**viewReflectDir** + +View-space GGX-sampled reflected ray. + +**N** + +View-space shading normal. + +**V** + +View-space direction to camera. + +**alpha** + +GGX roughness (alpha). + +**f0** + +### .sampleEnvironmentMIS( params : Object ) : Node. + +Environment reflection for a screen-space miss, estimated with multiple importance sampling (MIS) between the BRDF / reflected-ray direction and the env-luminance CDF direction. Both techniques use consistent solid-angle PDFs (`D·G1(N·V)/(4·N·V)`), so the power heuristic is unbiased. Adapted from three-gpu-pathtracer. + +**params** + +**cameraWorldMatrix** + +**viewReflectDir** + +View-space GGX-sampled reflected ray. + +**N** + +View-space shading normal. + +**V** + +View-space direction to camera. + +**alpha** + +GGX roughness (alpha). + +**f0** + +**Xi2** + +Second blue-noise sample (zw used for the CDF). + +See: + +* [https://github.com/gkjohnson/three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer) + +### .sampleReflect( params : Object ) : Node. + +Simple environment lookup along the reflected direction (no MIS). + +**params** + +**cameraWorldMatrix** + +**viewReflectDir** + +**sampleWeight** + +Optional radiance scale (defaults to 1). + +### .updateFrom( hdr : Texture ) + +**hdr** + +Equirectangular HDR environment map. + +## Source + +[examples/jsm/tsl/display/ImportanceSampledEnvironment.js](https://github.com/mrdoob/three.js/blob/master/examples/jsm/tsl/display/ImportanceSampledEnvironment.js) \ No newline at end of file diff --git a/docs/pages/RecurrentDenoiseNode.html b/docs/pages/RecurrentDenoiseNode.html new file mode 100644 index 00000000000000..320b83276a8067 --- /dev/null +++ b/docs/pages/RecurrentDenoiseNode.html @@ -0,0 +1,97 @@ + + + + + RecurrentDenoiseNode - Three.js Docs + + + + + + +

    EventDispatcherNodeTempNode

    +

    RecurrentDenoiseNode

    +
    +
    +

    Post processing node for denoising temporally-accumulated screen-space effects +such as SSGI (ambient occlusion / indirect diffuse) and SSR (specular reflections).

    +

    The denoising kernel is selected at construction time via mode: +'diffuse' (SSGI) or 'specular' (SSR). The kernel uses a fixed 8-sample Vogel disk.

    +
    +
    +

    Import

    +

    RecurrentDenoiseNode is an addon, and must be imported explicitly, see Installation#Addons.

    +
    import { recurrentDenoise } from 'three/addons/tsl/display/RecurrentDenoiseNode.js';
    +
    +

    Constructor

    +

    new RecurrentDenoiseNode( inputTexture : TextureNode, camera : Camera, options : RecurrentDenoiseNodeOptions )

    +
    + + + + + + + + + + + + + + + +
    + inputTexture + +

    Temporally filtered input to denoise (e.g. TRAA output).

    +
    + camera + +
    + options + +

    Default is {}.

    +
    +
    +
    +

    Properties

    +
    +

    .accumulate : boolean

    +
    +

    When true, apply temporal blending after spatial denoising. When false, output spatially +filtered colour only (alpha is passed through from the input temporal pass).

    +
    +
    +
    +

    .alphaSource : DenoiseAlphaSource

    +
    +

    Which channel of the raw texture drives alpha-based edge stopping. +'raylength' — alpha encodes SSR ray length; 'ao' — alpha encodes AO factor; +'none' — skip alpha-based edge stopping.

    +

    Default is 'raylength'.

    +
    +
    +
    +

    .mode : DenoiseMode

    +
    +

    Denoising kernel type.

    +
    +
    +

    Methods

    +

    .getRenderTarget() : RenderTarget

    +
    +
    +

    Returns the internal output render target (e.g. for temporal reprojection/SSGI temporal feedback loops).

    +
    +
    +

    Source

    +

    + examples/jsm/tsl/display/RecurrentDenoiseNode.js +

    +
    +
    + + + + \ No newline at end of file diff --git a/docs/pages/RecurrentDenoiseNode.html.md b/docs/pages/RecurrentDenoiseNode.html.md new file mode 100644 index 00000000000000..a13042fd86eaf0 --- /dev/null +++ b/docs/pages/RecurrentDenoiseNode.html.md @@ -0,0 +1,55 @@ +*Inheritance: EventDispatcher → Node → TempNode →* + +# RecurrentDenoiseNode + +Post processing node for denoising temporally-accumulated screen-space effects such as SSGI (ambient occlusion / indirect diffuse) and SSR (specular reflections). + +The denoising kernel is selected at construction time via `mode`: `'diffuse'` (SSGI) or `'specular'` (SSR). The kernel uses a fixed 8-sample Vogel disk. + +## Import + +RecurrentDenoiseNode is an addon, and must be imported explicitly, see [Installation#Addons](https://threejs.org/manual/#en/installation). + +```js +import { recurrentDenoise } from 'three/addons/tsl/display/RecurrentDenoiseNode.js'; +``` + +## Constructor + +### new RecurrentDenoiseNode( inputTexture : TextureNode, camera : Camera, options : RecurrentDenoiseNodeOptions ) + +**inputTexture** + +Temporally filtered input to denoise (e.g. TRAA output). + +**camera** + +**options** + +Default is `{}`. + +## Properties + +### .accumulate : boolean + +When `true`, apply temporal blending after spatial denoising. When `false`, output spatially filtered colour only (alpha is passed through from the input temporal pass). + +### .alphaSource : DenoiseAlphaSource + +Which channel of the raw texture drives alpha-based edge stopping. `'raylength'` — alpha encodes SSR ray length; `'ao'` — alpha encodes AO factor; `'none'` — skip alpha-based edge stopping. + +Default is `'raylength'`. + +### .mode : DenoiseMode + +Denoising kernel type. + +## Methods + +### .getRenderTarget() : RenderTarget + +Returns the internal output render target (e.g. for temporal reprojection/SSGI temporal feedback loops). + +## Source + +[examples/jsm/tsl/display/RecurrentDenoiseNode.js](https://github.com/mrdoob/three.js/blob/master/examples/jsm/tsl/display/RecurrentDenoiseNode.js) \ No newline at end of file diff --git a/docs/pages/SSRNode.html b/docs/pages/SSRNode.html index 0608bae2c55334..02d639ef2a6548 100644 --- a/docs/pages/SSRNode.html +++ b/docs/pages/SSRNode.html @@ -22,7 +22,7 @@

    Import

    import { ssr } from 'three/addons/tsl/display/SSRNode.js';

    Constructor

    -

    new SSRNode( colorNode : Node.<vec4>, depthNode : Node.<float>, normalNode : Node.<vec3>, metalnessNode : Node.<float>, roughnessNode : Node.<float>, camera : Camera )

    +

    new SSRNode( colorNode : Node.<vec4>, depthNode : Node.<float>, normalNode : Node.<vec3>, options : SSRNodeOptions )

    Properties

    -

    .blurQuality : UniformNode.<int>

    +

    ._binaryRefine : boolean

    +
    +

    Enables sub-step binary-search refinement of a detected hit. When on, a coarse +crossing is bisected toward the exact intersection (sharper hits, less step +aliasing) at the cost of extra depth samples. Baked into the shader as a +compile-time constant; assigning a new value rebuilds the SSR material.

    +

    Default is false.

    +
    +
    +
    +

    ._blurQuality : number

    The quality of the blur. Must be an integer in the range [1,3].

    +

    Baked into the blur shader as a compile-time constant so the (size*2+1)² +sample loop unrolls; assigning a new value recompiles the blur material.

    +

    Default is 2.

    +
    +
    +
    +

    ._reflectNonMetals : boolean

    +
    +

    Only used when SSRNode#stochastic is false. When false, non-metallic +surfaces are discarded for a noticeable performance gain; set true to also +reflect dielectrics. Baked into the shader as a compile-time constant; assigning a +new value recompiles the SSR material.

    +

    Default is false.

    +
    +
    +
    +

    ._screenEdgeFadeBlack : boolean

    +
    +

    When true, SSR fades to zero near screen borders instead of blending toward +the environment map. Hits are faded by the reflection sample UV; misses are +faded by the surface pixel UV.

    +

    Baked into the shader as a compile-time constant so the unused fade branch is +eliminated; assigning a new value recompiles the SSR material.

    +

    Default is false.

    +
    +
    +
    +

    ._stepExponent : number

    +
    +

    Non-linear step distribution exponent. 1 = uniform steps; > 1 concentrates +samples near the ray origin — where most short-range reflections are missed — and +spaces them out toward maxDistance, as s = (i / steps) ^ stepExponent.

    +

    Baked into the shader as a compile-time constant so pow() folds to a few +multiplies; assigning a new value recompiles the SSR material. Only used by the +stochastic reflection path.

    +

    Default is 2.

    +
    +
    +
    +

    .binaryRefine : boolean

    +
    +

    Whether sub-step binary-search hit refinement is enabled (compile-time constant). +Assigning a new value rebuilds the SSR material.

    +
    +
    +
    +

    .blurQuality : number

    +
    +

    Blur kernel size (compile-time constant). Assigning a new value recompiles the +blur material.

    +
    +

    .diffuseNode : Node.<vec4>

    +
    +

    A node that represents the scene's diffuse color (typically the MRT diffuseColor attachment). +When null, the shader uses vec3(1).

    +
    +
    +
    +

    .envImportanceSampling : boolean

    +
    +

    When true, env-luminance CDF tables are built and MIS is used for environment misses. +Fixed at construction time.

    +
    +
    +
    +

    .envMapIntensity : UniformNode.<float>

    +
    +

    Intensity multiplier for the importance-sampled env contribution. +Only available after setEnvMap has been called.

    +
    +
    +
    +

    .environmentIntensity : UniformNode.<float>

    +
    +

    Intensity multiplier applied to environment-map reflections on screen-space +misses and at screen edges. Defaults to π to match the former hardcoded multiplier.

    +

    Default is Math.PI.

    +
    +
    +
    +

    .environmentNode : Texture

    +
    +

    HDR environment map for screen-space misses.

    +
    +
    +
    +

    .historyTexture : Texture

    +
    +

    A node that represents the history texture for multi-bounce reflections.

    +
    +
    +
    +

    .intensity : UniformNode.<float>

    +
    +

    A multiplier for the overall reflection intensity. 1 leaves the +reflections unchanged, lower values dim them and higher values boost them.

    +

    Default is 1.

    +
    +
    +
    +

    .maxLuminance : UniformNode.<float>

    +
    +

    Absolute env luminance cap. HDR env samples above this are scaled down (hue preserved).

    +

    Default is 10.

    +
    +

    .metalnessNode : Node.<float>

    -

    A node that represents the beauty pass's metalness.

    +

    Per-pixel metalness, used to drive the GGX reflection sampling and the non-metal +early-out. When null, the shader treats surfaces as non-metallic.

    -

    .normalNode : Node.<vec3>

    +

    .mirrorBias : UniformNode.<float>

    -

    A node that represents the beauty pass's normals.

    +

    Mirror bias for the stochastic GGX sampling. Concentrates the reflected rays toward +the lobe's narrow (near-mirror) core, trading a small amount of bias for less noise. +0 samples the full VNDF lobe; values toward 1 tighten the cone. Range [0,1].

    +

    Default is 0.5.

    -

    .opacity : UniformNode.<float>

    +

    .normalNode : Node.<vec3>

    -

    Controls how the SSR reflections are blended with the beauty pass.

    +

    A node that represents the beauty pass's normals.

    @@ -144,6 +246,13 @@

    .quality

    +
    +

    .reflectNonMetals : boolean

    +
    +

    Whether dielectrics are reflected in the non-stochastic path (compile-time constant). +Assigning a new value rebuilds the SSR material.

    +
    +

    .resolutionScale : number

    @@ -154,6 +263,45 @@

    .Default is 1.

    +
    +

    .roughnessNode : Node.<float>

    +
    +

    Per-pixel roughness, used to drive the GGX reflection sampling and the blur mip +selection. When null, the shader treats surfaces as fully smooth.

    +
    +
    +
    +

    .screenEdgeFade : UniformNode.<float>

    +
    +

    Screen-edge fade width, in UV units. As a screen-space hit approaches a screen +border, the reflection is faded over this distance — either toward the environment +reflection (SSRNode#screenEdgeFadeBlack false) or to zero intensity +(true). 0 disables it.

    +

    Default is 0.2.

    +
    +
    +
    +

    .screenEdgeFadeBlack : boolean

    +
    +

    Whether SSR fades to black near screen borders (compile-time constant). Assigning +a new value recompiles the SSR material.

    +
    +
    +
    +

    .stepExponent : number

    +
    +

    Non-linear step distribution exponent (compile-time constant). See the backing +field for details. Assigning a new value recompiles the SSR material.

    +
    +
    +
    +

    .stochastic : boolean

    +
    +

    When true, the reflection direction is varied per pixel with stochastic GGX rays +(second-generation SSR). When false, a single mirror reflection is traced and +roughness is softened with a blur pass (first-generation SSR).

    +
    +

    .thickness : UniformNode.<float>

    @@ -171,6 +319,12 @@

    .Overrides: TempNode#updateBeforeType

    +
    +

    .velocityTexture : Node.<vec2>

    +
    +

    A node that represents the velocity texture for reprojection.

    +
    +

    Methods

    .dispose()

    +

    .setEnvMap( hdr : Texture | null )

    +
    +
    +

    Sets the environment map for importance-sampled env lighting when +screen-space rays miss. Call this whenever the scene's env map changes.

    +

    Uses ImportanceSampledEnvironment (CDF + MIS adapted from +three-gpu-pathtracer).

    +
    + + + + + + + +
    + hdr + +

    The equirectangular HDR environment map, or null to disable.

    +
    +
    +
    See:
    +
    + +
    +
    +
    +

    .setHistory( history : Texture, velocity : Node.<vec2> )

    +
    +
    +

    Wires the feedback inputs for multi-bounce reflections: the previous frame's +denoised result (history) and the velocity buffer used to reproject it +(velocity). history accepts the producing node (e.g. a +RecurrentDenoiseNode) — its output render target is used — or a raw +texture. Pass null for both to disable multi-bounce.

    +
    + + + + + + + + + + + +
    + history + +
    + velocity + +
    +

    .setSize( width : number, height : number )

    diff --git a/docs/pages/SSRNode.html.md b/docs/pages/SSRNode.html.md index 80d08b0da9a93a..eb6ab5e6148cc8 100644 --- a/docs/pages/SSRNode.html.md +++ b/docs/pages/SSRNode.html.md @@ -16,7 +16,7 @@ import { ssr } from 'three/addons/tsl/display/SSRNode.js'; ## Constructor -### new SSRNode( colorNode : Node., depthNode : Node., normalNode : Node., metalnessNode : Node., roughnessNode : Node., camera : Camera ) +### new SSRNode( colorNode : Node., depthNode : Node., normalNode : Node., options : SSRNodeOptions ) Constructs a new SSR node. @@ -32,27 +32,55 @@ A node that represents the beauty pass's depth. A node that represents the beauty pass's normals. -**metalnessNode** +**options** -A node that represents the beauty pass's metalness. +Optional inputs for material and environment data. -**roughnessNode** +## Properties -A node that represents the beauty pass's roughness. +### ._binaryRefine : boolean -Default is `null`. +Enables sub-step binary-search refinement of a detected hit. When on, a coarse crossing is bisected toward the exact intersection (sharper hits, less step aliasing) at the cost of extra depth samples. Baked into the shader as a compile-time constant; assigning a new value rebuilds the SSR material. -**camera** +Default is `false`. -The camera the scene is rendered with. +### ._blurQuality : number -Default is `null`. +The quality of the blur. Must be an integer in the range `[1,3]`. -## Properties +Baked into the blur shader as a compile-time constant so the `(size*2+1)²` sample loop unrolls; assigning a new value recompiles the blur material. -### .blurQuality : UniformNode. +Default is `2`. -The quality of the blur. Must be an integer in the range `[1,3]`. +### ._reflectNonMetals : boolean + +Only used when [SSRNode#stochastic](SSRNode.html#stochastic) is `false`. When `false`, non-metallic surfaces are discarded for a noticeable performance gain; set `true` to also reflect dielectrics. Baked into the shader as a compile-time constant; assigning a new value recompiles the SSR material. + +Default is `false`. + +### ._screenEdgeFadeBlack : boolean + +When `true`, SSR fades to zero near screen borders instead of blending toward the environment map. Hits are faded by the reflection sample UV; misses are faded by the surface pixel UV. + +Baked into the shader as a compile-time constant so the unused fade branch is eliminated; assigning a new value recompiles the SSR material. + +Default is `false`. + +### ._stepExponent : number + +Non-linear step distribution exponent. `1` = uniform steps; `> 1` concentrates samples near the ray origin — where most short-range reflections are missed — and spaces them out toward maxDistance, as `s = (i / steps) ^ stepExponent`. + +Baked into the shader as a compile-time constant so `pow()` folds to a few multiplies; assigning a new value recompiles the SSR material. Only used by the stochastic reflection path. + +Default is `2`. + +### .binaryRefine : boolean + +Whether sub-step binary-search hit refinement is enabled (compile-time constant). Assigning a new value rebuilds the SSR material. + +### .blurQuality : number + +Blur kernel size (compile-time constant). Assigning a new value recompiles the blur material. ### .camera : Camera @@ -66,21 +94,61 @@ The node that represents the beauty pass. A node that represents the beauty pass's depth. +### .diffuseNode : Node. + +A node that represents the scene's diffuse color (typically the MRT `diffuseColor` attachment). When `null`, the shader uses `vec3(1)`. + +### .envImportanceSampling : boolean + +When `true`, env-luminance CDF tables are built and MIS is used for environment misses. Fixed at construction time. + +### .envMapIntensity : UniformNode. + +Intensity multiplier for the importance-sampled env contribution. Only available after setEnvMap has been called. + +### .environmentIntensity : UniformNode. + +Intensity multiplier applied to environment-map reflections on screen-space misses and at screen edges. Defaults to π to match the former hardcoded multiplier. + +Default is `Math.PI`. + +### .environmentNode : Texture + +HDR environment map for screen-space misses. + +### .historyTexture : Texture + +A node that represents the history texture for multi-bounce reflections. + +### .intensity : UniformNode. + +A multiplier for the overall reflection intensity. `1` leaves the reflections unchanged, lower values dim them and higher values boost them. + +Default is `1`. + ### .maxDistance : UniformNode. Controls how far a fragment can reflect. Increasing this value result in more computational overhead but also increases the reflection distance. +### .maxLuminance : UniformNode. + +Absolute env luminance cap. HDR env samples above this are scaled down (hue preserved). + +Default is `10`. + ### .metalnessNode : Node. -A node that represents the beauty pass's metalness. +Per-pixel metalness, used to drive the GGX reflection sampling and the non-metal early-out. When `null`, the shader treats surfaces as non-metallic. -### .normalNode : Node. +### .mirrorBias : UniformNode. -A node that represents the beauty pass's normals. +Mirror bias for the stochastic GGX sampling. Concentrates the reflected rays toward the lobe's narrow (near-mirror) core, trading a small amount of bias for less noise. `0` samples the full VNDF lobe; values toward `1` tighten the cone. Range `[0,1]`. + +Default is `0.5`. -### .opacity : UniformNode. +### .normalNode : Node. -Controls how the SSR reflections are blended with the beauty pass. +A node that represents the beauty pass's normals. ### .quality : UniformNode. @@ -88,12 +156,38 @@ This parameter controls how detailed the raymarching process works. The value ra A quality of `0.5` is usually sufficient for most use cases. Try to keep this parameter as low as possible. Larger values result in noticeable more overhead. +### .reflectNonMetals : boolean + +Whether dielectrics are reflected in the non-stochastic path (compile-time constant). Assigning a new value rebuilds the SSR material. + ### .resolutionScale : number The resolution scale. Valid values are in the range `[0,1]`. `1` means best quality but also results in more computational overhead. Setting to `0.5` means the effect is computed in half-resolution. Default is `1`. +### .roughnessNode : Node. + +Per-pixel roughness, used to drive the GGX reflection sampling and the blur mip selection. When `null`, the shader treats surfaces as fully smooth. + +### .screenEdgeFade : UniformNode. + +Screen-edge fade width, in UV units. As a screen-space hit approaches a screen border, the reflection is faded over this distance — either toward the environment reflection ([SSRNode#screenEdgeFadeBlack](SSRNode.html#screenEdgeFadeBlack) `false`) or to zero intensity (`true`). `0` disables it. + +Default is `0.2`. + +### .screenEdgeFadeBlack : boolean + +Whether SSR fades to black near screen borders (compile-time constant). Assigning a new value recompiles the SSR material. + +### .stepExponent : number + +Non-linear step distribution exponent (compile-time constant). See the backing field for details. Assigning a new value recompiles the SSR material. + +### .stochastic : boolean + +When `true`, the reflection direction is varied per pixel with stochastic GGX rays (second-generation SSR). When `false`, a single mirror reflection is traced and roughness is softened with a blur pass (first-generation SSR). + ### .thickness : UniformNode. Controls the cutoff between what counts as a possible reflection hit and what does not. @@ -106,6 +200,10 @@ Default is `'frame'`. **Overrides:** [TempNode#updateBeforeType](TempNode.html#updateBeforeType) +### .velocityTexture : Node. + +A node that represents the velocity texture for reprojection. + ## Methods ### .dispose() @@ -120,6 +218,28 @@ Returns the result of the effect as a texture node. **Returns:** A texture node that represents the result of the effect. +### .setEnvMap( hdr : Texture | null ) + +Sets the environment map for importance-sampled env lighting when screen-space rays miss. Call this whenever the scene's env map changes. + +Uses [ImportanceSampledEnvironment](ImportanceSampledEnvironment.html) (CDF + MIS adapted from [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer)). + +**hdr** + +The equirectangular HDR environment map, or null to disable. + +See: + +* [https://github.com/gkjohnson/three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer) + +### .setHistory( history : Texture, velocity : Node. ) + +Wires the feedback inputs for multi-bounce reflections: the previous frame's denoised result (`history`) and the velocity buffer used to reproject it (`velocity`). `history` accepts the producing node (e.g. a [RecurrentDenoiseNode](RecurrentDenoiseNode.html)) — its output render target is used — or a raw texture. Pass `null` for both to disable multi-bounce. + +**history** + +**velocity** + ### .setSize( width : number, height : number ) Sets the size of the effect. diff --git a/docs/pages/TSL.html b/docs/pages/TSL.html index 063f96b5cd7002..2d33fe535a4585 100644 --- a/docs/pages/TSL.html +++ b/docs/pages/TSL.html @@ -100,6 +100,14 @@

    .anisotr

    TSL object that represents the shader variable AnisotropyT.

    +
    +

    .applyVarianceClipping (constant)

    +
    +

    Variance clipping in YCoCg space (Salvi, GDC 2016). Uses the colour moments gathered by +collectNeighborhood; gamma widens the AABB and is kept out of the gather so the +neighbourhood pass stays independent of the per-pixel motion factor.

    +
    +

    .attenuationColor : PropertyNode.<color> (constant)

    @@ -130,6 +138,12 @@

    . +

    .beautyTexelFromScreen (constant)

    +
    +

    Maps a resolve (screen) texel to the corresponding beauty-input texel when resolutions differ.

    +
    +

    +
    +

    .clipToAABB (constant)

    +
    +

    Clips the history sample to the neighbourhood AABB by projecting it toward the box centre. +Reference: https://github.com/playdeadgames/temporal

    +
    +
    +
    +

    .collectNeighborhood (constant)

    +
    +

    Single 3×3 neighbourhood pass over the beauty buffer. One textureLoad per tap feeds both the +YCoCg variance-clipping box (colour) and the SSR ray-length statistics (alpha), which previously +required two separate 3×3 fetches of the same texture.

    +

    Sampling is done on the beauty-texel grid (beautyTexel + offset), so the taps are distinct +source texels even when the beauty buffer is lower resolution than the resolve pass (upscaling).

    +
    +
    +
    +

    .computeFrustumSize (constant)

    +
    +

    World-space frustum height at viewZ. Algorithm originally from REBLUR (NRD). +tanHalfFovY is tan( verticalFov / 2 ), hoisted by the caller since it is loop-invariant.

    +
    +
    +
    +

    .computeHitDistFactor (constant)

    +
    +

    Maps world-space SSR ray length to [0, 1]. Environment rays (worldRayLength == 0) map to 1. +Algorithm originally from REBLUR (NRD).

    +
    +
    +
    +

    .diffuseColorDistance (constant)

    +
    +

    Chromatic color-similarity distance between two linear base colors (albedo).

    +
    +

    .diffuseContribution : PropertyNode.<vec3> (constant)

    @@ -325,6 +376,18 @@

    .gapSizeTSL object that represents the shader variable gapSize.

    +
    +

    .getSpecularDominantDirection (constant)

    +
    +

    Specular dominant direction — smooth surfaces lean toward reflection, rough toward normal.

    +
    +
    +
    +

    .getTemporalVarianceFactor (constant)

    +
    +

    Temporal accumulation variance factor in [0, 1]. Higher values mean more history confidence.

    +
    +

    .globalId : ComputeBuiltinNode.<uvec3> (constant)

    @@ -387,12 +450,43 @@

    . +

    .karisTemporalBlend (constant)

    +
    +

    Inverse-luminance temporal blend with optional adaptive trust (Karis-style).

    +
    +

    +
    +

    .lobeNormalFalloff (constant)

    +
    +

    Loop-invariant part of the adaptive normal edge-stopping weight: the Gaussian falloff +constant 2·EXP_WEIGHT_SCALE / lobeHalfAngle². roughness/aggressivity/invNormalPhi +are constant across the kernel, so this is hoisted out of the tap loop and evaluated once +per pixel. Lobe half-angle from REBLUR (NRD).

    +
    +
    +
    +

    .lobeNormalWeight (constant)

    +
    +

    Adaptive lobe normal edge-stopping weight

    +

    Evaluated entirely in cosine space: with angle² ≈ 2(1 − cosθ), the original +exp( −SCALE·angle/halfAngle ) becomes a Gaussian exp( falloff·(cosθ − 1) ), so a +single exp replaces the per-tap acos. Matches the original at the half-angle for +narrow lobes and is slightly more permissive for wide (diffuse) ones.

    +
    +

    .localId : ComputeBuiltinNode.<uvec3> (constant)

    A non-linearized 3-dimensional representation of the current invocation's position within a 3D workgroup grid.

    +
    +

    .mapAo (constant)

    +
    +

    Maps an AO factor for edge-stopping comparisons.

    +
    +
    +
    +

    .misPowerHeuristic (constant)

    +
    +

    MIS power heuristic with β = 2: pdfA² / (pdfA² + pdfB²). +Weights the contribution of the strategy that produced pdfA against the other strategy.

    +
    +
    +
    See:
    +
    +
      +
    • Eric Veach, *Optimally Combining Sampling Techniques for Monte Carlo Rendering*
    • +
    +
    +
    +

    .modelDirection : ModelNode.<vec3> (constant)

    @@ -816,6 +925,12 @@

    . +

    .planeDistance (constant)

    +
    +

    View-space plane distance between two surface points (edge-stopping geometry term).

    +
    +

    .pointUV : PointUVNode (constant)

    @@ -874,6 +989,15 @@

    . +

    .projectWorldToUV (constant)

    +
    +

    Projects a world-space position into previous-frame UV coordinates.

    +
    +

    +
    +

    .recurrentDenoise (constant)

    +
    +
    +

    .reprojectHitPoint (constant)

    +
    +

    Parallax-corrected hit-point reprojection into previous-frame UVs.

    +
    +
    +
    +

    .reprojectionStretchConfidence (constant)

    +
    +

    Reprojection-stretch confidence — detects history magnification (surface stretching).

    +

    Differentiates the per-pixel history UV with hardware screen-space derivatives to form the +reprojection Jacobian J = ∂(historyPixel)/∂(screenPixel), then returns its minimum +singular value, clamped to [0,1].

    +

    σ_min < 1 means the most-stretched axis magnifies history — a few history pixels are smeared +over many current pixels (e.g. a surface seen at grazing in the previous frame, face-on now), so +history is undersampled and its confidence should be reduced. σ_min ≥ 1 (history minified) is +safe and clamps to 1. Using the minimum singular value rather than the Jacobian determinant +catches anisotropic 1-D stretch that an area-only measure would smear out.

    +

    Works for any reprojection (surface-velocity or parallax hit-point) since it differentiates the +final history UV, so the same factor applies to both the diffuse and specular paths.

    +
    +

    .roughness : PropertyNode.<float> (constant)

    TSL object that represents the shader variable Roughness.

    +
    +

    .sampleBilinearTap (constant)

    +
    +

    Single bilinear history tap with plane-distance and normal confidence.

    +
    +
    +
    +

    .sampleHistory4Tap (constant)

    +
    +

    Geometrically-weighted 4-tap bilinear history sample.

    +
    +
    +
    +

    .specularLobeTanHalfAngle (constant)

    +
    +

    GGX inverse-CDF: half-angle tangent enclosing percent of the specular lobe volume. +roughness is perceptual (alpha = roughness²).

    +
    +

    .subgroupIndex : IndexNode (constant)

    @@ -1077,6 +1242,12 @@

    .velocity<

    TSL object that represents the velocity of a render pass.

    +
    +

    .velocityToUVOffset (constant)

    +
    +

    Converts screen-space velocity (NDC derivative) to a UV reprojection offset.

    +
    +
    +
    +

    .vogelDisk (constant)

    +
    +

    Golden-angle Vogel disk offset.

    +
    +

    .workgroupId : ComputeBuiltinNode.<uvec3> (constant)

    @@ -9862,7 +10039,7 @@

    .ssgi<

    -

    .ssr( colorNode : Node.<vec4>, depthNode : Node.<float>, normalNode : Node.<vec3>, metalnessNode : Node.<float>, roughnessNode : Node.<float>, camera : Camera ) : SSRNode

    +

    .ssr( colorNode : Node.<vec4>, depthNode : Node.<float>, normalNode : Node.<vec3>, options : SSRNodeOptions ) : SSRNode

    TSL function for creating screen space reflections (SSR).

    @@ -9895,28 +10072,10 @@

    .ssr - metalnessNode - - -

    A node that represents the beauty pass's metalness.

    - - - - - roughnessNode - - -

    A node that represents the beauty pass's roughness.

    -

    Default is null.

    - - - - - camera + options -

    The camera the scene is rendered with.

    -

    Default is null.

    +

    Optional inputs for material and environment data.

    @@ -13058,6 +13217,203 @@

    .DebugCo + +

    + + +
    +

    .RecurrentDenoiseNodeOptions

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + depth +
    +Node.<float> +
    +

    Scene depth buffer for view-space edge stopping.

    +

    Default is null.

    +
    + normal +
    +Node.<vec3> +
    +

    View-space normals for geometric edge stopping.

    +

    Default is null.

    +
    + metalRoughness +
    +Node.<vec4> +
    +

    Roughness/metalness G-buffer for specular edge stopping.

    +

    Default is null.

    +
    + diffuse +
    +Node.<vec4> +
    +

    Scene base color (albedo) G-buffer for chromatic edge stopping.

    +

    Default is null.

    +
    + raw +
    +Node.<vec4> +
    +

    Unfiltered input (e.g. raw SSR/SSGI) for secondary sampling and temporal blend.

    +

    Default is null.

    +
    + mode +
    +DenoiseMode +
    +

    Denoising kernel type.

    +

    Default is 'diffuse'.

    +
    + accumulate +
    +boolean +
    +

    When true, temporally blend the spatially-denoised result +(Karis-style) and write frame weight to alpha for feedback loops. When false, only spatial filtering is applied.

    +

    Default is true.

    +
    +
    +
    +

    .SSRNodeOptions

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + stochastic +
    +boolean +
    +

    When false, traces a single mirror reflection and softens roughness with a blur pass (first-generation SSR). When true, varies the reflection direction per pixel with stochastic GGX rays (second-generation SSR); higher quality on rough/glossy surfaces but noisier, so it expects a temporal/spatial denoiser downstream.

    +

    Default is false.

    +
    + metalnessNode +
    +Node.<float> +
    +

    Per-pixel metalness. Drives GGX reflection sampling and, with reflectNonMetals=false, the non-metal early-out.

    +

    Default is null.

    +
    + roughnessNode +
    +Node.<float> +
    +

    Per-pixel roughness. Drives GGX sampling and the blur mip selection.

    +

    Default is null.

    +
    + reflectNonMetals +
    +boolean +
    +

    Only used when stochastic=false. When false, non-metallic surfaces are discarded for a noticeable performance gain; set true to also reflect dielectrics (e.g. marble, polished wood, plastic).

    +

    Default is false.

    +
    + environmentNode +
    +Texture +
    +

    Equirectangular HDR environment map with CPU-side image.data (e.g. from RGBELoader). Not compatible with PMREM / scene.environment cubemaps.

    +

    Default is null.

    +
    + envImportanceSampling +
    +boolean +
    +

    When true, precomputes env-luminance CDF tables and uses MIS for environment misses. Build-time only.

    +

    Default is false.

    +
    + diffuseNode +
    +Node +
    +

    Scene diffuse / base color. Defaults to vec3(1) in the shader when omitted.

    +

    Default is null.

    +
    + binaryRefine +
    +boolean +
    +

    Sub-step binary-search refinement of detected hits. Compile-time constant (baked into the shader at construction).

    +

    Default is false.

    +
    + camera +
    +Camera +
    +

    Camera the scene is rendered with. Inferred from the color pass when omitted.

    +

    Default is null.

    +
    @@ -13098,6 +13454,49 @@

    . + +

    + +
    +

    .TemporalReprojectNodeOptions

    + + + + + + + + + + + + + + +
    + mode +
    +TemporalReprojectMode +
    +

    diffuse for SSGI/scene colour; specular for SSR reflections.

    +

    Default is 'diffuse'.

    +
    + hitPointReprojection +
    +boolean +
    +

    Parallax hit-point reprojection (specular mode only). Defaults to true in specular mode.

    +
    + accumulate +
    +boolean +
    +

    When true, history is stored in this pass (classic temporal resolve). When false, +use TemporalReprojectNode#setHistoryTexture to read history from another pass (e.g. denoise output).

    +

    Default is false.

    +
    diff --git a/docs/pages/TSL.html.md b/docs/pages/TSL.html.md index 162d5693527064..f52427985b24ce 100644 --- a/docs/pages/TSL.html.md +++ b/docs/pages/TSL.html.md @@ -56,6 +56,10 @@ TSL object that represents the shader variable `AnisotropyB`. TSL object that represents the shader variable `AnisotropyT`. +### .applyVarianceClipping (constant) + +Variance clipping in YCoCg space (Salvi, GDC 2016). Uses the colour moments gathered by [collectNeighborhood](TSL.html#collectNeighborhood); `gamma` widens the AABB and is kept out of the gather so the neighbourhood pass stays independent of the per-pixel motion factor. + ### .attenuationColor : PropertyNode. (constant) TSL object that represents the shader variable `AttenuationColor`. @@ -76,6 +80,10 @@ TSL object that represents the scene's background intensity. TSL object that represents the scene's background rotation. +### .beautyTexelFromScreen (constant) + +Maps a resolve (screen) texel to the corresponding beauty-input texel when resolutions differ. + ### .bitangentGeometry : Node. (constant) TSL object that represents the bitangent attribute of the current rendered object. @@ -154,6 +162,24 @@ TSL object that represents the shader variable `ClearcoatRoughness`. TSL object that represents the clip space position of the current rendered object. +### .clipToAABB (constant) + +Clips the history sample to the neighbourhood AABB by projecting it toward the box centre. Reference: https://github.com/playdeadgames/temporal + +### .collectNeighborhood (constant) + +Single 3×3 neighbourhood pass over the beauty buffer. One textureLoad per tap feeds both the YCoCg variance-clipping box (colour) and the SSR ray-length statistics (alpha), which previously required two separate 3×3 fetches of the same texture. + +Sampling is done on the beauty-texel grid (`beautyTexel + offset`), so the taps are distinct source texels even when the beauty buffer is lower resolution than the resolve pass (upscaling). + +### .computeFrustumSize (constant) + +World-space frustum height at `viewZ`. Algorithm originally from REBLUR (NRD). `tanHalfFovY` is `tan( verticalFov / 2 )`, hoisted by the caller since it is loop-invariant. + +### .computeHitDistFactor (constant) + +Maps world-space SSR ray length to `[0, 1]`. Environment rays (`worldRayLength == 0`) map to `1`. Algorithm originally from REBLUR (NRD). + ### .dashSize : PropertyNode. (constant) TSL object that represents the shader variable `dashSize`. @@ -170,6 +196,10 @@ TSL object that represents the depth value for the current fragment. TSL object that represents the shader variable `DiffuseColor`. +### .diffuseColorDistance (constant) + +Chromatic color-similarity distance between two linear base colors (albedo). + ### .diffuseContribution : PropertyNode. (constant) TSL object that represents the shader variable `DiffuseContribution`. @@ -206,6 +236,14 @@ TSL object that represents whether a primitive is front or back facing TSL object that represents the shader variable `gapSize`. +### .getSpecularDominantDirection (constant) + +Specular dominant direction — smooth surfaces lean toward reflection, rough toward normal. + +### .getTemporalVarianceFactor (constant) + +Temporal accumulation variance factor in `[0, 1]`. Higher values mean more history confidence. + ### .globalId : ComputeBuiltinNode. (constant) A non-linearized 3-dimensional representation of the current invocation's position within a 3D global grid. @@ -246,10 +284,28 @@ TSL object that represents the shader variable `IridescenceIOR`. TSL object that represents the shader variable `IridescenceThickness`. +### .karisTemporalBlend (constant) + +Inverse-luminance temporal blend with optional adaptive trust (Karis-style). + +### .lobeNormalFalloff (constant) + +Loop-invariant part of the adaptive normal edge-stopping weight: the Gaussian falloff constant `2·EXP_WEIGHT_SCALE / lobeHalfAngle²`. `roughness`/`aggressivity`/`invNormalPhi` are constant across the kernel, so this is hoisted out of the tap loop and evaluated once per pixel. Lobe half-angle from REBLUR (NRD). + +### .lobeNormalWeight (constant) + +Adaptive lobe normal edge-stopping weight + +Evaluated entirely in cosine space: with `angle² ≈ 2(1 − cosθ)`, the original `exp( −SCALE·angle/halfAngle )` becomes a Gaussian `exp( falloff·(cosθ − 1) )`, so a single `exp` replaces the per-tap `acos`. Matches the original at the half-angle for narrow lobes and is slightly more permissive for wide (diffuse) ones. + ### .localId : ComputeBuiltinNode. (constant) A non-linearized 3-dimensional representation of the current invocation's position within a 3D workgroup grid. +### .mapAo (constant) + +Maps an AO factor for edge-stopping comparisons. + ### .materialAO : Node. (constant) TSL object that represents the ambient occlusion map of the current material. The value is composed via `aoMap.r` - 1 \* `aoMapIntensity` + 1. @@ -422,6 +478,14 @@ TSL object that represents the object's model view in `mediump` precision. TSL object that represents the shader variable `Metalness`. +### .misPowerHeuristic (constant) + +MIS power heuristic with β = 2: `pdfA² / (pdfA² + pdfB²)`. Weights the contribution of the strategy that produced `pdfA` against the other strategy. + +See: + +* Eric Veach, \*Optimally Combining Sampling Techniques for Monte Carlo Rendering\* + ### .modelDirection : ModelNode. (constant) TSL object that represents the object's direction in world space. @@ -523,6 +587,10 @@ TSL object that represents the shader variable `Output`. TSL object that represents the parallax direction. +### .planeDistance (constant) + +View-space plane distance between two surface points (edge-stopping geometry term). + ### .pointUV : PointUVNode (constant) TSL object that represents the uv coordinates of points. @@ -561,6 +629,12 @@ TSL object that represents the vertex position in world space of the current ren TSL object that represents the position world direction of the current rendered object. +### .projectWorldToUV (constant) + +Projects a world-space position into previous-frame UV coordinates. + +### .recurrentDenoise (constant) + ### .reflectVector : Node. (constant) Used for sampling cube maps when using cube reflection mapping. @@ -581,10 +655,32 @@ The refract vector in view space. TSL object that represents a shared uniform group node which is updated once per render. +### .reprojectHitPoint (constant) + +Parallax-corrected hit-point reprojection into previous-frame UVs. + +### .reprojectionStretchConfidence (constant) + +Reprojection-stretch confidence — detects history magnification (surface stretching). + +Differentiates the per-pixel history UV with hardware screen-space derivatives to form the reprojection Jacobian `J = ∂(historyPixel)/∂(screenPixel)`, then returns its **minimum singular value**, clamped to `[0,1]`. + +`σ_min < 1` means the most-stretched axis magnifies history — a few history pixels are smeared over many current pixels (e.g. a surface seen at grazing in the previous frame, face-on now), so history is undersampled and its confidence should be reduced. `σ_min ≥ 1` (history minified) is safe and clamps to 1. Using the minimum singular value rather than the Jacobian determinant catches anisotropic 1-D stretch that an area-only measure would smear out. + +Works for any reprojection (surface-velocity or parallax hit-point) since it differentiates the final history UV, so the same factor applies to both the diffuse and specular paths. + ### .roughness : PropertyNode. (constant) TSL object that represents the shader variable `Roughness`. +### .sampleBilinearTap (constant) + +Single bilinear history tap with plane-distance and normal confidence. + +### .sampleHistory4Tap (constant) + +Geometrically-weighted 4-tap bilinear history sample. + ### .screenCoordinate : ScreenNode. (constant) TSL object that represents the current `x`/`y` pixel position on the screen in physical pixel units. @@ -629,6 +725,10 @@ TSL object that represents the shader variable `SpecularColorBlended`. TSL object that represents the shader variable `SpecularF90`. +### .specularLobeTanHalfAngle (constant) + +GGX inverse-CDF: half-angle tangent enclosing `percent` of the specular lobe volume. `roughness` is perceptual (alpha = roughness²). + ### .subgroupIndex : IndexNode (constant) TSL object that represents the index of the subgroup the current compute invocation belongs to. @@ -697,6 +797,10 @@ TSL object that represents the shader variable `Transmission`. TSL object that represents the velocity of a render pass. +### .velocityToUVOffset (constant) + +Converts screen-space velocity (NDC derivative) to a UV reprojection offset. + ### .vertexIndex : IndexNode (constant) TSL object that represents the index of a vertex within a mesh. @@ -721,6 +825,10 @@ TSL object that represents the viewport resolution in physical pixel units. TSL object that represents normalized viewport coordinates, unitless in `[0, 1]`. +### .vogelDisk (constant) + +Golden-angle Vogel disk offset. + ### .workgroupId : ComputeBuiltinNode. (constant) Represents the 3-dimensional index of the workgroup the current compute invocation belongs to. @@ -5112,7 +5220,7 @@ A texture node that represents the scene's normals. The camera the scene is rendered with. -### .ssr( colorNode : Node., depthNode : Node., normalNode : Node., metalnessNode : Node., roughnessNode : Node., camera : Camera ) : SSRNode +### .ssr( colorNode : Node., depthNode : Node., normalNode : Node., options : SSRNodeOptions ) : SSRNode TSL function for creating screen space reflections (SSR). @@ -5128,21 +5236,9 @@ A node that represents the beauty pass's depth. A node that represents the beauty pass's normals. -**metalnessNode** - -A node that represents the beauty pass's metalness. - -**roughnessNode** - -A node that represents the beauty pass's roughness. - -Default is `null`. - -**camera** - -The camera the scene is rendered with. +**options** -Default is `null`. +Optional inputs for material and environment data. ### .sss( depthNode : TextureNode, camera : Camera, mainLight : DirectionalLight ) : SSSNode @@ -6741,6 +6837,126 @@ function Allows the get the raw shader code for the given scene, camera and 3D object. +### .DenoiseAlphaSource + +### .DenoiseMode + +### .RecurrentDenoiseNodeOptions + +**depth** +[Node](Node.html). + +Scene depth buffer for view-space edge stopping. + +Default is `null`. + +**normal** +[Node](Node.html). + +View-space normals for geometric edge stopping. + +Default is `null`. + +**metalRoughness** +[Node](Node.html). + +Roughness/metalness G-buffer for specular edge stopping. + +Default is `null`. + +**diffuse** +[Node](Node.html). + +Scene base color (albedo) G-buffer for chromatic edge stopping. + +Default is `null`. + +**raw** +[Node](Node.html). + +Unfiltered input (e.g. raw SSR/SSGI) for secondary sampling and temporal blend. + +Default is `null`. + +**mode** +[DenoiseMode](global.html#DenoiseMode) + +Denoising kernel type. + +Default is `'diffuse'`. + +**accumulate** +boolean + +When `true`, temporally blend the spatially-denoised result (Karis-style) and write frame weight to alpha for feedback loops. When `false`, only spatial filtering is applied. + +Default is `true`. + +### .SSRNodeOptions + +**stochastic** +boolean + +When `false`, traces a single mirror reflection and softens roughness with a blur pass (first-generation SSR). When `true`, varies the reflection direction per pixel with stochastic GGX rays (second-generation SSR); higher quality on rough/glossy surfaces but noisier, so it expects a temporal/spatial denoiser downstream. + +Default is `false`. + +**metalnessNode** +[Node](Node.html). + +Per-pixel metalness. Drives GGX reflection sampling and, with `reflectNonMetals=false`, the non-metal early-out. + +Default is `null`. + +**roughnessNode** +[Node](Node.html). + +Per-pixel roughness. Drives GGX sampling and the blur mip selection. + +Default is `null`. + +**reflectNonMetals** +boolean + +Only used when `stochastic=false`. When `false`, non-metallic surfaces are discarded for a noticeable performance gain; set `true` to also reflect dielectrics (e.g. marble, polished wood, plastic). + +Default is `false`. + +**environmentNode** +[Texture](Texture.html) + +Equirectangular HDR environment map with CPU-side `image.data` (e.g. from RGBELoader). Not compatible with PMREM / `scene.environment` cubemaps. + +Default is `null`. + +**envImportanceSampling** +boolean + +When `true`, precomputes env-luminance CDF tables and uses MIS for environment misses. Build-time only. + +Default is `false`. + +**diffuseNode** +[Node](Node.html) + +Scene diffuse / base color. Defaults to `vec3(1)` in the shader when omitted. + +Default is `null`. + +**binaryRefine** +boolean + +Sub-step binary-search refinement of detected hits. Compile-time constant (baked into the shader at construction). + +Default is `false`. + +**camera** +[Camera](Camera.html) + +Camera the scene is rendered with. Inferred from the color pass when omitted. + +Default is `null`. + ### .ShadowMapConfig Shadow map configuration @@ -6760,6 +6976,29 @@ number The shadow map type. +### .TemporalReprojectMode + +### .TemporalReprojectNodeOptions + +**mode** +[TemporalReprojectMode](global.html#TemporalReprojectMode) + +`diffuse` for SSGI/scene colour; `specular` for SSR reflections. + +Default is `'diffuse'`. + +**hitPointReprojection** +boolean + +Parallax hit-point reprojection (specular mode only). Defaults to `true` in specular mode. + +**accumulate** +boolean + +When `true`, history is stored in this pass (classic temporal resolve). When `false`, use [TemporalReprojectNode#setHistoryTexture](TemporalReprojectNode.html#setHistoryTexture) to read history from another pass (e.g. denoise output). + +Default is `false`. + ### .XRConfig XR configuration. diff --git a/docs/pages/TemporalReprojectNode.html b/docs/pages/TemporalReprojectNode.html new file mode 100644 index 00000000000000..5d6cd0338a1b20 --- /dev/null +++ b/docs/pages/TemporalReprojectNode.html @@ -0,0 +1,124 @@ + + + + + TemporalReprojectNode - Three.js Docs + + + + + + +

    EventDispatcherNodeTempNode

    +

    TemporalReprojectNode

    +
    +
    +

    Temporal reprojection pass for denoising screen-space effects (SSGI, SSR, etc.).

    +

    Both modes share geometrically-weighted 4-tap bilinear history sampling and YCoCg variance clipping. +Surface velocity reprojection is always sampled first. Specular mode then blends in +hit-point parallax history on top of that surface result. +Diffuse mode applies velocity-field divergence to detect surface stretching.

    +

    Unlike jitter-based TAA/TAAU, this node does not apply camera sub-pixel jitter — it only +reprojects and accumulates history using motion vectors.

    +

    References:

    +
    +
    +
    +

    Import

    +

    TemporalReprojectNode is an addon, and must be imported explicitly, see Installation#Addons.

    +
    import { temporalReproject } from 'three/addons/tsl/display/TemporalReprojectNode.js';
    +
    +

    Constructor

    +

    new TemporalReprojectNode( beautyNode : TextureNode, depthNode : TextureNode, normalNode : TextureNode, velocityNode : TextureNode, camera : Camera, options : TemporalReprojectNodeOptions )

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + beautyNode + +
    + depthNode + +
    + normalNode + +
    + velocityNode + +
    + camera + +
    + options + +
    +
    +
    +

    Properties

    +
    +

    .accumulate : boolean

    +
    +

    When true, resolve output is copied into the internal history buffer each frame. +When false, history is supplied externally via TemporalReprojectNode#setHistoryTexture.

    +
    +
    + +

    Methods

    +

    .setHistoryTexture( source : Object | Texture )

    +
    +
    +

    Supplies an external history source (e.g. a RecurrentDenoiseNode or its +texture). Only used when TemporalReprojectNode#accumulate is false.

    +
    + + + + + + + +
    + source + +
    +
    +

    Source

    +

    + examples/jsm/tsl/display/TemporalReprojectNode.js +

    +
    +
    + + + + \ No newline at end of file diff --git a/docs/pages/TemporalReprojectNode.html.md b/docs/pages/TemporalReprojectNode.html.md new file mode 100644 index 00000000000000..14a4edee019c2d --- /dev/null +++ b/docs/pages/TemporalReprojectNode.html.md @@ -0,0 +1,58 @@ +*Inheritance: EventDispatcher → Node → TempNode →* + +# TemporalReprojectNode + +Temporal reprojection pass for denoising screen-space effects (SSGI, SSR, etc.). + +Both modes share geometrically-weighted 4-tap bilinear history sampling and YCoCg variance clipping. Surface velocity reprojection is always sampled first. Specular mode then blends in hit-point parallax history on top of that surface result. Diffuse mode applies velocity-field divergence to detect surface stretching. + +Unlike jitter-based TAA/TAAU, this node does not apply camera sub-pixel jitter — it only reprojects and accumulates history using motion vectors. + +References: + +* [https://alextardif.com/TAA.html](https://alextardif.com/TAA.html) +* [https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail/](https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail/) + +## Import + +TemporalReprojectNode is an addon, and must be imported explicitly, see [Installation#Addons](https://threejs.org/manual/#en/installation). + +```js +import { temporalReproject } from 'three/addons/tsl/display/TemporalReprojectNode.js'; +``` + +## Constructor + +### new TemporalReprojectNode( beautyNode : TextureNode, depthNode : TextureNode, normalNode : TextureNode, velocityNode : TextureNode, camera : Camera, options : TemporalReprojectNodeOptions ) + +**beautyNode** + +**depthNode** + +**normalNode** + +**velocityNode** + +**camera** + +**options** + +## Properties + +### .accumulate : boolean + +When `true`, resolve output is copied into the internal history buffer each frame. When `false`, history is supplied externally via [TemporalReprojectNode#setHistoryTexture](TemporalReprojectNode.html#setHistoryTexture). + +### .mode : TemporalReprojectMode + +## Methods + +### .setHistoryTexture( source : Object | Texture ) + +Supplies an external history source (e.g. a [RecurrentDenoiseNode](RecurrentDenoiseNode.html) or its texture). Only used when [TemporalReprojectNode#accumulate](TemporalReprojectNode.html#accumulate) is `false`. + +**source** + +## Source + +[examples/jsm/tsl/display/TemporalReprojectNode.js](https://github.com/mrdoob/three.js/blob/master/examples/jsm/tsl/display/TemporalReprojectNode.js) \ No newline at end of file diff --git a/docs/pages/global.html b/docs/pages/global.html index 1c92e39d77d90a..ca227ea340c119 100644 --- a/docs/pages/global.html +++ b/docs/pages/global.html @@ -390,6 +390,20 @@

    . +

    .ENV_RAY_LENGTH : number (constant)

    +
    +

    Sentinel ray length the SSR pass writes for environment misses (no screen-space hit), set far above +any real hit distance so a single magnitude test separates misses from hits and survives .max( 0 ).

    +
    +

    +
    +

    .ENV_RAY_LENGTH_THRESHOLD : number (constant)

    +
    +

    Classification threshold for ENV_RAY_LENGTH: above this is an env miss, below a real hit. +An order of magnitude under the sentinel, robust to fp16 storage and bilinear blending at borders.

    +
    +

    .EqualCompare : number (constant)

    @@ -420,6 +434,12 @@

    . +

    .F_Schlick (constant)

    +
    +

    Fresnel reflectance for the Schlick approximation.

    +
    +

    .FloatType : number (constant)

    @@ -1756,6 +1776,23 @@

    . +

    .equirectUvToDir (constant)

    +
    +

    Equirectangular direction / UV / PDF helpers and MIS weighting shared by environment sampling code. +Env-miss MIS integration lives in ImportanceSampledEnvironment.

    +

    Equirectangular parameterization helpers used with CDF importance sampling are adapted from +three-gpu-pathtracer.

    +
    +
    +
    See:
    +
    + +
    +
    +

    .getBatchingColor (constant)

    @@ -1774,6 +1811,29 @@

    .getMorph

    TSL function that retrieves and scales the morphed attribute (position or normal) texel value.

    +
    +

    .getSpecularDominantFactor (constant)

    +
    +

    Specular dominant factor for parallax-corrected ray length. +From REBLUR: A Hierarchical Recurrent Denoiser (NRD).

    +
    +
    +
    +

    .ggxReflectionSample (constant)

    +
    +

    Importance-samples the GGX/VNDF specular lobe for one pixel and returns the reflected +ray direction plus the Monte-Carlo weight to apply to the gathered radiance, along with +the GGX terms the SSR env-miss MIS fallback needs.

    +
    +
    +
    +

    .ggxReflectionStruct (constant)

    +
    +

    Everything a single GGX reflection sample produces. reflectDir and sampleWeight +drive the SSR ray-march and compositing; pdf, NdotV, alpha and f0 are the GGX +terms the env-miss MIS fallback needs so the caller never re-derives microfacet math.

    +
    +

    .instanceColor : VaryingNode.<vec3> (constant)

    +
    +

    .temporalReproject (constant)

    +

    .totalDiffuse : Node.<vec3> (constant)

    @@ -2141,6 +2204,81 @@

    ..bilinearHistoryTap( ctx : Object, tapOffset : Node.<ivec2>, bilinearWeight : Node.<float> )

    +
    + + + + + + + + + + + + + + + +
    + ctx + +

    Shared sampleBilinearTap inputs plus reprojICoord.

    +
    + tapOffset + +
    + bilinearWeight + +
    +
    +

    .bindAnalyticNoise( resolution : UniformNode.<Vector2>, seed : number )

    +
    +
    +

    Returns a TSL function that samples texture-free analytic R² noise. +Index 0 uses continuous screen pixels; other indices tile-shift with an R² +sequence into a 64×64 period. Values are four independent R² dimensions +hashed from the sample coordinates.

    +
    + + + + + + + + + + + +
    + resolution + +
    + seed + +

    Added to the coordinate hash so each pass gets an independent R² phase.

    +

    Default is 0.

    +
    +
    +

    .bindTemporalCameraUniforms( camera : Camera )

    +
    +
    +

    Current and previous-frame camera matrices for temporal reprojection passes.

    +
    + + + + + + + +
    + camera + +
    +

    .buildData3DTexture( chunk : Object ) : Data3DTexture

    @@ -2554,6 +2692,32 @@

    .damp<
    Returns: The interpolated value.

    +

    .dampenForVarianceClip( rgb : Node.<vec3>, flickerSuppression : Node.<float> ) : Node.<vec3>

    +
    +
    +

    Inverse-luminance compression for HDR variance clipping (Karis-style). +Bright samples contribute less to neighbourhood moments so sun pixels do not +inflate the YCoCg AABB and cause aggressive clipping flicker.

    +
    + + + + + + + + + + + +
    + rgb + +
    + flickerSuppression + +
    +

    .degToRad( degrees : number ) : number

    @@ -3856,6 +4020,20 @@

    .ran
    Returns: A random integer.

    +

    .rgbToYCoCg( c : Node.<vec3> ) : Node.<vec3>

    +
    + + + + + + + +
    + c + +
    +

    .sample( callback : function, uv : Node.<vec2> ) : SampleNode

    @@ -4421,6 +4599,20 @@

    .w

    +

    .ycocgToRGB( c : Node.<vec3> ) : Node.<vec3>

    +
    + + + + + + + +
    + c + +
    +

    .yieldToMain() : Promise.<void>

    @@ -4717,6 +4909,203 @@

    .DebugCo + +

    + + +
    +

    .RecurrentDenoiseNodeOptions

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + depth +
    +Node.<float> +
    +

    Scene depth buffer for view-space edge stopping.

    +

    Default is null.

    +
    + normal +
    +Node.<vec3> +
    +

    View-space normals for geometric edge stopping.

    +

    Default is null.

    +
    + metalRoughness +
    +Node.<vec4> +
    +

    Roughness/metalness G-buffer for specular edge stopping.

    +

    Default is null.

    +
    + diffuse +
    +Node.<vec4> +
    +

    Scene base color (albedo) G-buffer for chromatic edge stopping.

    +

    Default is null.

    +
    + raw +
    +Node.<vec4> +
    +

    Unfiltered input (e.g. raw SSR/SSGI) for secondary sampling and temporal blend.

    +

    Default is null.

    +
    + mode +
    +DenoiseMode +
    +

    Denoising kernel type.

    +

    Default is 'diffuse'.

    +
    + accumulate +
    +boolean +
    +

    When true, temporally blend the spatially-denoised result +(Karis-style) and write frame weight to alpha for feedback loops. When false, only spatial filtering is applied.

    +

    Default is true.

    +
    +
    +
    +

    .SSRNodeOptions

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + stochastic +
    +boolean +
    +

    When false, traces a single mirror reflection and softens roughness with a blur pass (first-generation SSR). When true, varies the reflection direction per pixel with stochastic GGX rays (second-generation SSR); higher quality on rough/glossy surfaces but noisier, so it expects a temporal/spatial denoiser downstream.

    +

    Default is false.

    +
    + metalnessNode +
    +Node.<float> +
    +

    Per-pixel metalness. Drives GGX reflection sampling and, with reflectNonMetals=false, the non-metal early-out.

    +

    Default is null.

    +
    + roughnessNode +
    +Node.<float> +
    +

    Per-pixel roughness. Drives GGX sampling and the blur mip selection.

    +

    Default is null.

    +
    + reflectNonMetals +
    +boolean +
    +

    Only used when stochastic=false. When false, non-metallic surfaces are discarded for a noticeable performance gain; set true to also reflect dielectrics (e.g. marble, polished wood, plastic).

    +

    Default is false.

    +
    + environmentNode +
    +Texture +
    +

    Equirectangular HDR environment map with CPU-side image.data (e.g. from RGBELoader). Not compatible with PMREM / scene.environment cubemaps.

    +

    Default is null.

    +
    + envImportanceSampling +
    +boolean +
    +

    When true, precomputes env-luminance CDF tables and uses MIS for environment misses. Build-time only.

    +

    Default is false.

    +
    + diffuseNode +
    +Node +
    +

    Scene diffuse / base color. Defaults to vec3(1) in the shader when omitted.

    +

    Default is null.

    +
    + binaryRefine +
    +boolean +
    +

    Sub-step binary-search refinement of detected hits. Compile-time constant (baked into the shader at construction).

    +

    Default is false.

    +
    + camera +
    +Camera +
    +

    Camera the scene is rendered with. Inferred from the color pass when omitted.

    +

    Default is null.

    +
    @@ -4757,6 +5146,49 @@

    . + +

    + +
    +

    .TemporalReprojectNodeOptions

    + + + + + + + + + + + + + + +
    + mode +
    +TemporalReprojectMode +
    +

    diffuse for SSGI/scene colour; specular for SSR reflections.

    +

    Default is 'diffuse'.

    +
    + hitPointReprojection +
    +boolean +
    +

    Parallax hit-point reprojection (specular mode only). Defaults to true in specular mode.

    +
    + accumulate +
    +boolean +
    +

    When true, history is stored in this pass (classic temporal resolve). When false, +use TemporalReprojectNode#setHistoryTexture to read history from another pass (e.g. denoise output).

    +

    Default is false.

    +
    diff --git a/docs/pages/global.html.md b/docs/pages/global.html.md index c7e5f4a5feb994..3644df477ba5d9 100644 --- a/docs/pages/global.html.md +++ b/docs/pages/global.html.md @@ -210,6 +210,14 @@ The contents are intended to be respecified repeatedly by the application, and u The contents are intended to be respecified repeatedly by reading data from the 3D API, and queried many times by the application. +### .ENV_RAY_LENGTH : number (constant) + +Sentinel ray length the SSR pass writes for environment misses (no screen-space hit), set far above any real hit distance so a single magnitude test separates misses from hits and survives `.max( 0 )`. + +### .ENV_RAY_LENGTH_THRESHOLD : number (constant) + +Classification threshold for [ENV\_RAY\_LENGTH](global.html#ENV_RAY_LENGTH): above this is an env miss, below a real hit. An order of magnitude under the sentinel, robust to fp16 storage and bilinear blending at borders. + ### .EqualCompare : number (constant) Pass if the incoming value equals the texture value. @@ -230,6 +238,10 @@ Reflection mapping for equirectangular textures. Refraction mapping for equirectangular textures. +### .F_Schlick (constant) + +Fresnel reflectance for the Schlick approximation. + ### .FloatType : number (constant) A float data type for textures. @@ -1069,6 +1081,16 @@ Performs a depth-aware blend between a base scene and a secondary effect (like g Disposes the shadow material for the given light source. +### .equirectUvToDir (constant) + +Equirectangular direction / UV / PDF helpers and MIS weighting shared by environment sampling code. Env-miss MIS integration lives in [ImportanceSampledEnvironment](ImportanceSampledEnvironment.html). + +Equirectangular parameterization helpers used with CDF importance sampling are adapted from [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer). + +See: + +* [https://github.com/gkjohnson/three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer) + ### .getBatchingColor (constant) TSL function that retrieves the batching color for a given instance ID from a colors texture. @@ -1081,6 +1103,18 @@ TSL function that retrieves the indirect index for a given batch ID. TSL function that retrieves and scales the morphed attribute (position or normal) texel value. +### .getSpecularDominantFactor (constant) + +Specular dominant factor for parallax-corrected ray length. From REBLUR: A Hierarchical Recurrent Denoiser (NRD). + +### .ggxReflectionSample (constant) + +Importance-samples the GGX/VNDF specular lobe for one pixel and returns the reflected ray direction plus the Monte-Carlo weight to apply to the gathered radiance, along with the GGX terms the SSR env-miss MIS fallback needs. + +### .ggxReflectionStruct (constant) + +Everything a single GGX reflection sample produces. `reflectDir` and `sampleWeight` drive the SSR ray-march and compositing; `pdf`, `NdotV`, `alpha` and `f0` are the GGX terms the env-miss MIS fallback needs so the caller never re-derives microfacet math. + ### .instanceColor : VaryingNode. (constant) TSL object representing a varying property for the instanced color vector. @@ -1093,6 +1127,8 @@ Varying node representing the accumulated distance along the line. Crucial for c A node representing the outgoing light. +### .temporalReproject (constant) + ### .totalDiffuse : Node. (constant) A node representing the total diffuse light. @@ -1263,6 +1299,34 @@ Horizontal terracotta bands at every floor line. Together with the projecting pi Bakes a list of instance groups into one non-indexed BufferGeometry. Each group is a base geometry ( position + normal + uv ), an array of Matrix4 placements and a `partId` written to a per-vertex attribute. Transforming straight into preallocated typed arrays avoids mergeGeometries' per-instance allocations; the result is one geometry, ready for a single draw call and the compute rasterizer. +### .bilinearHistoryTap( ctx : Object, tapOffset : Node., bilinearWeight : Node. ) + +**ctx** + +Shared [sampleBilinearTap](TSL.html#sampleBilinearTap) inputs plus `reprojICoord`. + +**tapOffset** + +**bilinearWeight** + +### .bindAnalyticNoise( resolution : UniformNode., seed : number ) + +Returns a TSL function that samples texture-free analytic R² noise. Index 0 uses continuous screen pixels; other indices tile-shift with an R² sequence into a 64×64 period. Values are four independent R² dimensions hashed from the sample coordinates. + +**resolution** + +**seed** + +Added to the coordinate hash so each pass gets an independent R² phase. + +Default is `0`. + +### .bindTemporalCameraUniforms( camera : Camera ) + +Current and previous-frame camera matrices for temporal reprojection passes. + +**camera** + ### .buildData3DTexture( chunk : Object ) : Data3DTexture Builds a 3D texture from a VOX chunk. @@ -1453,6 +1517,14 @@ Delta time in seconds. **Returns:** The interpolated value. +### .dampenForVarianceClip( rgb : Node., flickerSuppression : Node. ) : Node. + +Inverse-luminance compression for HDR variance clipping (Karis-style). Bright samples contribute less to neighbourhood moments so sun pixels do not inflate the YCoCg AABB and cause aggressive clipping flicker. + +**rgb** + +**flickerSuppression** + ### .degToRad( degrees : number ) : number Converts degrees to radians. @@ -2087,6 +2159,10 @@ The upper value boundary **Returns:** A random integer. +### .rgbToYCoCg( c : Node. ) : Node. + +**c** + ### .sample( callback : function, uv : Node. ) : SampleNode Helper function to create a SampleNode wrapped as a node object. @@ -2359,6 +2435,10 @@ This function maintains an internal cache of warning messages and will only outp The warning message components. +### .ycocgToRGB( c : Node. ) : Node. + +**c** + ### .yieldToMain() : Promise. Yields execution to the main thread to allow rendering and other tasks. Uses scheduler.yield() when available (Chrome 115+), falls back to requestAnimationFrame. @@ -2504,6 +2584,126 @@ function Allows the get the raw shader code for the given scene, camera and 3D object. +### .DenoiseAlphaSource + +### .DenoiseMode + +### .RecurrentDenoiseNodeOptions + +**depth** +[Node](Node.html). + +Scene depth buffer for view-space edge stopping. + +Default is `null`. + +**normal** +[Node](Node.html). + +View-space normals for geometric edge stopping. + +Default is `null`. + +**metalRoughness** +[Node](Node.html). + +Roughness/metalness G-buffer for specular edge stopping. + +Default is `null`. + +**diffuse** +[Node](Node.html). + +Scene base color (albedo) G-buffer for chromatic edge stopping. + +Default is `null`. + +**raw** +[Node](Node.html). + +Unfiltered input (e.g. raw SSR/SSGI) for secondary sampling and temporal blend. + +Default is `null`. + +**mode** +[DenoiseMode](global.html#DenoiseMode) + +Denoising kernel type. + +Default is `'diffuse'`. + +**accumulate** +boolean + +When `true`, temporally blend the spatially-denoised result (Karis-style) and write frame weight to alpha for feedback loops. When `false`, only spatial filtering is applied. + +Default is `true`. + +### .SSRNodeOptions + +**stochastic** +boolean + +When `false`, traces a single mirror reflection and softens roughness with a blur pass (first-generation SSR). When `true`, varies the reflection direction per pixel with stochastic GGX rays (second-generation SSR); higher quality on rough/glossy surfaces but noisier, so it expects a temporal/spatial denoiser downstream. + +Default is `false`. + +**metalnessNode** +[Node](Node.html). + +Per-pixel metalness. Drives GGX reflection sampling and, with `reflectNonMetals=false`, the non-metal early-out. + +Default is `null`. + +**roughnessNode** +[Node](Node.html). + +Per-pixel roughness. Drives GGX sampling and the blur mip selection. + +Default is `null`. + +**reflectNonMetals** +boolean + +Only used when `stochastic=false`. When `false`, non-metallic surfaces are discarded for a noticeable performance gain; set `true` to also reflect dielectrics (e.g. marble, polished wood, plastic). + +Default is `false`. + +**environmentNode** +[Texture](Texture.html) + +Equirectangular HDR environment map with CPU-side `image.data` (e.g. from RGBELoader). Not compatible with PMREM / `scene.environment` cubemaps. + +Default is `null`. + +**envImportanceSampling** +boolean + +When `true`, precomputes env-luminance CDF tables and uses MIS for environment misses. Build-time only. + +Default is `false`. + +**diffuseNode** +[Node](Node.html) + +Scene diffuse / base color. Defaults to `vec3(1)` in the shader when omitted. + +Default is `null`. + +**binaryRefine** +boolean + +Sub-step binary-search refinement of detected hits. Compile-time constant (baked into the shader at construction). + +Default is `false`. + +**camera** +[Camera](Camera.html) + +Camera the scene is rendered with. Inferred from the color pass when omitted. + +Default is `null`. + ### .ShadowMapConfig Shadow map configuration @@ -2523,6 +2723,29 @@ number The shadow map type. +### .TemporalReprojectMode + +### .TemporalReprojectNodeOptions + +**mode** +[TemporalReprojectMode](global.html#TemporalReprojectMode) + +`diffuse` for SSGI/scene colour; `specular` for SSR reflections. + +Default is `'diffuse'`. + +**hitPointReprojection** +boolean + +Parallax hit-point reprojection (specular mode only). Defaults to `true` in specular mode. + +**accumulate** +boolean + +When `true`, history is stored in this pass (classic temporal resolve). When `false`, use [TemporalReprojectNode#setHistoryTexture](TemporalReprojectNode.html#setHistoryTexture) to read history from another pass (e.g. denoise output). + +Default is `false`. + ### .XRConfig XR configuration. diff --git a/docs/search.json b/docs/search.json index 38916feea1e1c6..89bb883c50014e 100644 --- a/docs/search.json +++ b/docs/search.json @@ -17690,6 +17690,10 @@ "title": "EffectComposer#writeBuffer", "kind": "member" }, + { + "title": "EnvMapCDFGenerator", + "kind": "class" + }, { "title": "FBXLoader", "kind": "class" @@ -18518,6 +18522,26 @@ "title": "IESLoader#type", "kind": "member" }, + { + "title": "ImportanceSampledEnvironment", + "kind": "class" + }, + { + "title": "ImportanceSampledEnvironment#sampleEnvironmentBRDF", + "kind": "function" + }, + { + "title": "ImportanceSampledEnvironment#sampleEnvironmentMIS", + "kind": "function" + }, + { + "title": "ImportanceSampledEnvironment#sampleReflect", + "kind": "function" + }, + { + "title": "ImportanceSampledEnvironment#updateFrom", + "kind": "function" + }, { "title": "ImprovedNoise", "kind": "class" @@ -20918,6 +20942,26 @@ "title": "RectAreaLightUniformsLib", "kind": "class" }, + { + "title": "RecurrentDenoiseNode", + "kind": "class" + }, + { + "title": "RecurrentDenoiseNode#accumulate", + "kind": "member" + }, + { + "title": "RecurrentDenoiseNode#alphaSource", + "kind": "member" + }, + { + "title": "RecurrentDenoiseNode#getRenderTarget", + "kind": "function" + }, + { + "title": "RecurrentDenoiseNode#mode", + "kind": "member" + }, { "title": "Reflector", "kind": "class" @@ -21474,6 +21518,30 @@ "title": "SSRNode", "kind": "class" }, + { + "title": "SSRNode#_binaryRefine", + "kind": "member" + }, + { + "title": "SSRNode#_blurQuality", + "kind": "member" + }, + { + "title": "SSRNode#_reflectNonMetals", + "kind": "member" + }, + { + "title": "SSRNode#_screenEdgeFadeBlack", + "kind": "member" + }, + { + "title": "SSRNode#_stepExponent", + "kind": "member" + }, + { + "title": "SSRNode#binaryRefine", + "kind": "member" + }, { "title": "SSRNode#blurQuality", "kind": "member" @@ -21490,38 +21558,94 @@ "title": "SSRNode#depthNode", "kind": "member" }, + { + "title": "SSRNode#diffuseNode", + "kind": "member" + }, { "title": "SSRNode#dispose", "kind": "function" }, + { + "title": "SSRNode#envImportanceSampling", + "kind": "member" + }, + { + "title": "SSRNode#envMapIntensity", + "kind": "member" + }, + { + "title": "SSRNode#environmentIntensity", + "kind": "member" + }, + { + "title": "SSRNode#environmentNode", + "kind": "member" + }, { "title": "SSRNode#getTextureNode", "kind": "function" }, + { + "title": "SSRNode#historyTexture", + "kind": "member" + }, + { + "title": "SSRNode#intensity", + "kind": "member" + }, { "title": "SSRNode#maxDistance", "kind": "member" }, + { + "title": "SSRNode#maxLuminance", + "kind": "member" + }, { "title": "SSRNode#metalnessNode", "kind": "member" }, { - "title": "SSRNode#normalNode", + "title": "SSRNode#mirrorBias", "kind": "member" }, { - "title": "SSRNode#opacity", + "title": "SSRNode#normalNode", "kind": "member" }, { "title": "SSRNode#quality", "kind": "member" }, + { + "title": "SSRNode#reflectNonMetals", + "kind": "member" + }, { "title": "SSRNode#resolutionScale", "kind": "member" }, + { + "title": "SSRNode#roughnessNode", + "kind": "member" + }, + { + "title": "SSRNode#screenEdgeFade", + "kind": "member" + }, + { + "title": "SSRNode#screenEdgeFadeBlack", + "kind": "member" + }, + { + "title": "SSRNode#setEnvMap", + "kind": "function" + }, + { + "title": "SSRNode#setHistory", + "kind": "function" + }, { "title": "SSRNode#setSize", "kind": "function" @@ -21530,6 +21654,14 @@ "title": "SSRNode#setup", "kind": "function" }, + { + "title": "SSRNode#stepExponent", + "kind": "member" + }, + { + "title": "SSRNode#stochastic", + "kind": "member" + }, { "title": "SSRNode#thickness", "kind": "member" @@ -21542,6 +21674,10 @@ "title": "SSRNode#updateBeforeType", "kind": "member" }, + { + "title": "SSRNode#velocityTexture", + "kind": "member" + }, { "title": "SSRPass", "kind": "class" @@ -22406,6 +22542,22 @@ "title": "TeapotGeometry", "kind": "class" }, + { + "title": "TemporalReprojectNode", + "kind": "class" + }, + { + "title": "TemporalReprojectNode#accumulate", + "kind": "member" + }, + { + "title": "TemporalReprojectNode#mode", + "kind": "member" + }, + { + "title": "TemporalReprojectNode#setHistoryTexture", + "kind": "function" + }, { "title": "TerrainGenerator", "kind": "class" @@ -24832,6 +24984,14 @@ "title": "DynamicReadUsage", "kind": "member" }, + { + "title": "ENV_RAY_LENGTH", + "kind": "member" + }, + { + "title": "ENV_RAY_LENGTH_THRESHOLD", + "kind": "member" + }, { "title": "Earcut.triangulate", "kind": "function" @@ -24864,6 +25024,10 @@ "title": "ExtrudeGeometry.fromJSON", "kind": "function" }, + { + "title": "F_Schlick", + "kind": "member" + }, { "title": "FloatType", "kind": "member" @@ -26020,6 +26184,18 @@ "title": "batchColor", "kind": "member" }, + { + "title": "bilinearHistoryTap", + "kind": "function" + }, + { + "title": "bindAnalyticNoise", + "kind": "function" + }, + { + "title": "bindTemporalCameraUniforms", + "kind": "function" + }, { "title": "buildData3DTexture", "kind": "function" @@ -26100,6 +26276,10 @@ "title": "damp", "kind": "function" }, + { + "title": "dampenForVarianceClip", + "kind": "function" + }, { "title": "degToRad", "kind": "function" @@ -26120,6 +26300,10 @@ "title": "enhanceLogMessage", "kind": "function" }, + { + "title": "equirectUvToDir", + "kind": "member" + }, { "title": "error", "kind": "function" @@ -26228,6 +26412,10 @@ "title": "getSkinnedPosition", "kind": "function" }, + { + "title": "getSpecularDominantFactor", + "kind": "member" + }, { "title": "getStrideLength", "kind": "function" @@ -26248,6 +26436,14 @@ "title": "getViewZNode", "kind": "function" }, + { + "title": "ggxReflectionSample", + "kind": "member" + }, + { + "title": "ggxReflectionStruct", + "kind": "member" + }, { "title": "instanceColor", "kind": "member" @@ -26368,6 +26564,10 @@ "title": "randInt", "kind": "function" }, + { + "title": "rgbToYCoCg", + "kind": "function" + }, { "title": "sample", "kind": "function" @@ -26416,6 +26616,10 @@ "title": "subclip", "kind": "function" }, + { + "title": "temporalReproject", + "kind": "member" + }, { "title": "toHalfFloat", "kind": "function" @@ -26464,6 +26668,10 @@ "title": "worldStart", "kind": "member" }, + { + "title": "ycocgToRGB", + "kind": "function" + }, { "title": "yieldToMain", "kind": "function" @@ -26614,6 +26822,10 @@ "title": "append", "kind": "function" }, + { + "title": "applyVarianceClipping", + "kind": "member" + }, { "title": "array", "kind": "function" @@ -26726,6 +26938,10 @@ "title": "batch", "kind": "function" }, + { + "title": "beautyTexelFromScreen", + "kind": "member" + }, { "title": "bentNormalView", "kind": "function" @@ -26930,6 +27146,10 @@ "title": "clipSpace", "kind": "member" }, + { + "title": "clipToAABB", + "kind": "member" + }, { "title": "clipping", "kind": "function" @@ -26946,6 +27166,10 @@ "title": "code", "kind": "function" }, + { + "title": "collectNeighborhood", + "kind": "member" + }, { "title": "colorBleeding", "kind": "function" @@ -26966,6 +27190,14 @@ "title": "computeBuiltin", "kind": "function" }, + { + "title": "computeFrustumSize", + "kind": "member" + }, + { + "title": "computeHitDistFactor", + "kind": "member" + }, { "title": "computeKernel", "kind": "function" @@ -27094,6 +27326,10 @@ "title": "diffuseColor", "kind": "member" }, + { + "title": "diffuseColorDistance", + "kind": "member" + }, { "title": "diffuseContribution", "kind": "member" @@ -27262,6 +27498,14 @@ "title": "getShadowRenderObjectFunction", "kind": "function" }, + { + "title": "getSpecularDominantDirection", + "kind": "member" + }, + { + "title": "getTemporalVarianceFactor", + "kind": "member" + }, { "title": "getViewPosition", "kind": "function" @@ -27398,6 +27642,10 @@ "title": "js", "kind": "function" }, + { + "title": "karisTemporalBlend", + "kind": "member" + }, { "title": "label", "kind": "function" @@ -27458,6 +27706,14 @@ "title": "linearToneMapping", "kind": "function" }, + { + "title": "lobeNormalFalloff", + "kind": "member" + }, + { + "title": "lobeNormalWeight", + "kind": "member" + }, { "title": "localId", "kind": "member" @@ -27482,6 +27738,10 @@ "title": "lut3D", "kind": "function" }, + { + "title": "mapAo", + "kind": "member" + }, { "title": "matcapUV", "kind": "function" @@ -27674,6 +27934,10 @@ "title": "min", "kind": "function" }, + { + "title": "misPowerHeuristic", + "kind": "member" + }, { "title": "mix", "kind": "function" @@ -27962,6 +28226,10 @@ "title": "pixelationPass", "kind": "function" }, + { + "title": "planeDistance", + "kind": "member" + }, { "title": "pmremTexture", "kind": "function" @@ -28034,6 +28302,10 @@ "title": "premultiplyAlpha", "kind": "function" }, + { + "title": "projectWorldToUV", + "kind": "member" + }, { "title": "property", "kind": "function" @@ -28078,6 +28350,10 @@ "title": "reciprocal", "kind": "function" }, + { + "title": "recurrentDenoise", + "kind": "member" + }, { "title": "reference", "kind": "function" @@ -28150,6 +28426,14 @@ "title": "replaceDefaultUV", "kind": "function" }, + { + "title": "reprojectHitPoint", + "kind": "member" + }, + { + "title": "reprojectionStretchConfidence", + "kind": "member" + }, { "title": "retroPass", "kind": "function" @@ -28186,6 +28470,14 @@ "title": "sRGBTransferOETF", "kind": "function" }, + { + "title": "sampleBilinearTap", + "kind": "member" + }, + { + "title": "sampleHistory4Tap", + "kind": "member" + }, { "title": "sampler", "kind": "function" @@ -28330,6 +28622,10 @@ "title": "specularF90", "kind": "member" }, + { + "title": "specularLobeTanHalfAngle", + "kind": "member" + }, { "title": "spherizeUV", "kind": "function" @@ -28730,6 +29026,10 @@ "title": "velocity", "kind": "member" }, + { + "title": "velocityToUVOffset", + "kind": "member" + }, { "title": "vertexColor", "kind": "function" @@ -28814,6 +29114,10 @@ "title": "vignette", "kind": "function" }, + { + "title": "vogelDisk", + "kind": "member" + }, { "title": "vogelDiskSample", "kind": "function" From 6c3f7f528cdcf8d62bbb24447412d29d0ec31c33 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 25 Jun 2026 18:37:37 +0900 Subject: [PATCH 6/9] r185 --- build/three.cjs | 2 +- build/three.core.js | 2 +- build/three.core.min.js | 2 +- package.json | 2 +- src/constants.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/three.cjs b/build/three.cjs index ce31cd4b5e3a68..8da4af5d4d5e33 100644 --- a/build/three.cjs +++ b/build/three.cjs @@ -5,7 +5,7 @@ */ 'use strict'; -const REVISION = '185dev'; +const REVISION = '185'; /** * Represents mouse buttons and interaction types in context of controls. diff --git a/build/three.core.js b/build/three.core.js index d22a1b33047fa9..95a00ecfe7659c 100644 --- a/build/three.core.js +++ b/build/three.core.js @@ -3,7 +3,7 @@ * Copyright 2010-2026 Three.js Authors * SPDX-License-Identifier: MIT */ -const REVISION = '185dev'; +const REVISION = '185'; /** * Represents mouse buttons and interaction types in context of controls. diff --git a/build/three.core.min.js b/build/three.core.min.js index fa274ab288d211..bf26ba30787e57 100644 --- a/build/three.core.min.js +++ b/build/three.core.min.js @@ -3,4 +3,4 @@ * Copyright 2010-2026 Three.js Authors * SPDX-License-Identifier: MIT */ -const t="185dev",e={LEFT:0,MIDDLE:1,RIGHT:2,ROTATE:0,DOLLY:1,PAN:2},s={ROTATE:0,PAN:1,DOLLY_PAN:2,DOLLY_ROTATE:3},i=0,r=1,n=2,a=3,o=0,h=1,l=2,c=3,u=0,d=1,p=2,m=0,y=1,g=2,f=3,x=4,b=5,v=6,w=100,M=101,S=102,_=103,A=104,T=200,z=201,C=202,I=203,B=204,k=205,O=206,P=207,R=208,E=209,N=210,V=211,L=212,F=213,D=214,U=0,j=1,W=2,J=3,q=4,H=5,X=6,Y=7,Z=0,G=1,$=2,Q=0,K=1,tt=2,et=3,st=4,it=5,rt=6,nt=7,at="attached",ot="detached",ht=300,lt=301,ct=302,ut=303,dt=304,pt=306,mt=1e3,yt=1001,gt=1002,ft=1003,xt=1004,bt=1004,vt=1005,wt=1005,Mt=1006,St=1007,_t=1007,At=1008,Tt=1008,zt=1009,Ct=1010,It=1011,Bt=1012,kt=1013,Ot=1014,Pt=1015,Rt=1016,Et=1017,Nt=1018,Vt=1020,Lt=35902,Ft=35899,Dt=1021,Ut=1022,jt=1023,Wt=1026,Jt=1027,qt=1028,Ht=1029,Xt=1030,Yt=1031,Zt=1032,Gt=1033,$t=33776,Qt=33777,Kt=33778,te=33779,ee=35840,se=35841,ie=35842,re=35843,ne=36196,ae=37492,oe=37496,he=37488,le=37489,ce=37490,ue=37491,de=37808,pe=37809,me=37810,ye=37811,ge=37812,fe=37813,xe=37814,be=37815,ve=37816,we=37817,Me=37818,Se=37819,_e=37820,Ae=37821,Te=36492,ze=36494,Ce=36495,Ie=36283,Be=36284,ke=36285,Oe=36286,Pe=2200,Re=2201,Ee=2202,Ne=2300,Ve=2301,Le=2302,Fe=2303,De=2400,Ue=2401,je=2402,We=2500,Je=2501,qe=0,He=1,Xe=2,Ye=3200,Ze=3201,Ge=3202,$e=3203,Qe=0,Ke=1,ts="",es="srgb",ss="srgb-linear",is="linear",rs="srgb",ns="",as="rg",os="ga",hs=0,ls=7680,cs=7681,us=7682,ds=7683,ps=34055,ms=34056,ys=5386,gs=512,fs=513,xs=514,bs=515,vs=516,ws=517,Ms=518,Ss=519,_s=512,As=513,Ts=514,zs=515,Cs=516,Is=517,Bs=518,ks=519,Os=35044,Ps=35048,Rs=35040,Es=35045,Ns=35049,Vs=35041,Ls=35046,Fs=35050,Ds=35042,Us="100",js="300 es",Ws=2e3,Js=2001,qs={COMPUTE:"compute",RENDER:"render"},Hs={PERSPECTIVE:"perspective",LINEAR:"linear",FLAT:"flat"},Xs={NORMAL:"normal",CENTROID:"centroid",SAMPLE:"sample",FIRST:"first",EITHER:"either"},Ys={TEXTURE_COMPARE:"depthTextureCompare"};const Zs={Int8Array:Int8Array,Uint8Array:Uint8Array,Uint8ClampedArray:Uint8ClampedArray,Int16Array:Int16Array,Uint16Array:Uint16Array,Int32Array:Int32Array,Uint32Array:Uint32Array,Float32Array:Float32Array,Float64Array:Float64Array};function Gs(t,e){return new Zs[t](e)}function $s(t){return ArrayBuffer.isView(t)&&!(t instanceof DataView)}function Qs(t){return document.createElementNS("http://www.w3.org/1999/xhtml",t)}function Ks(){const t=Qs("canvas");return t.style.display="block",t}const ti={};let ei=null;function si(t){ei=t}function ii(){return ei}function ri(...t){const e="THREE."+t.shift();ei?ei("log",e,...t):console.log(e,...t)}function ni(t){const e=t[0];if("string"==typeof e&&e.startsWith("TSL:")){const e=t[1];e&&e.isStackTrace?t[0]+=" "+e.getLocation():t[1]='Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.'}return t}function ai(...t){const e="THREE."+(t=ni(t)).shift();if(ei)ei("warn",e,...t);else{const s=t[0];s&&s.isStackTrace?console.warn(s.getError(e)):console.warn(e,...t)}}function oi(...t){const e="THREE."+(t=ni(t)).shift();if(ei)ei("error",e,...t);else{const s=t[0];s&&s.isStackTrace?console.error(s.getError(e)):console.error(e,...t)}}function hi(...t){const e=t.join(" ");e in ti||(ti[e]=!0,ai(...t))}function li(){return"undefined"!=typeof self&&void 0!==self.scheduler&&void 0!==self.scheduler.yield?self.scheduler.yield():new Promise(t=>{requestAnimationFrame(t)})}function ci(t,e,s){return new Promise(function(i,r){setTimeout(function n(){switch(t.clientWaitSync(e,t.SYNC_FLUSH_COMMANDS_BIT,0)){case t.WAIT_FAILED:r();break;case t.TIMEOUT_EXPIRED:setTimeout(n,s);break;default:i()}},s)})}const ui={[U]:1,[W]:6,[q]:7,[J]:5,[j]:0,[X]:2,[Y]:4,[H]:3};class di{addEventListener(t,e){void 0===this._listeners&&(this._listeners={});const s=this._listeners;void 0===s[t]&&(s[t]=[]),-1===s[t].indexOf(e)&&s[t].push(e)}hasEventListener(t,e){const s=this._listeners;return void 0!==s&&(void 0!==s[t]&&-1!==s[t].indexOf(e))}removeEventListener(t,e){const s=this._listeners;if(void 0===s)return;const i=s[t];if(void 0!==i){const t=i.indexOf(e);-1!==t&&i.splice(t,1)}}dispatchEvent(t){const e=this._listeners;if(void 0===e)return;const s=e[t.type];if(void 0!==s){t.target=this;const e=s.slice(0);for(let s=0,i=e.length;s>8&255]+pi[t>>16&255]+pi[t>>24&255]+"-"+pi[255&e]+pi[e>>8&255]+"-"+pi[e>>16&15|64]+pi[e>>24&255]+"-"+pi[63&s|128]+pi[s>>8&255]+"-"+pi[s>>16&255]+pi[s>>24&255]+pi[255&i]+pi[i>>8&255]+pi[i>>16&255]+pi[i>>24&255]).toLowerCase()}function xi(t,e,s){return Math.max(e,Math.min(s,t))}function bi(t,e){return(t%e+e)%e}function vi(t,e,s){return(1-s)*t+s*e}function wi(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return t/4294967295;case Uint16Array:return t/65535;case Uint8Array:return t/255;case Int32Array:return Math.max(t/2147483647,-1);case Int16Array:return Math.max(t/32767,-1);case Int8Array:return Math.max(t/127,-1);default:throw new Error("THREE.MathUtils: Invalid component type.")}}function Mi(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return Math.round(4294967295*t);case Uint16Array:return Math.round(65535*t);case Uint8Array:return Math.round(255*t);case Int32Array:return Math.round(2147483647*t);case Int16Array:return Math.round(32767*t);case Int8Array:return Math.round(127*t);default:throw new Error("THREE.MathUtils: Invalid component type.")}}const Si={DEG2RAD:yi,RAD2DEG:gi,generateUUID:fi,clamp:xi,euclideanModulo:bi,mapLinear:function(t,e,s,i,r){return i+(t-e)*(r-i)/(s-e)},inverseLerp:function(t,e,s){return t!==e?(s-t)/(e-t):0},lerp:vi,damp:function(t,e,s,i){return vi(t,e,1-Math.exp(-s*i))},pingpong:function(t,e=1){return e-Math.abs(bi(t,2*e)-e)},smoothstep:function(t,e,s){return t<=e?0:t>=s?1:(t=(t-e)/(s-e))*t*(3-2*t)},smootherstep:function(t,e,s){return t<=e?0:t>=s?1:(t=(t-e)/(s-e))*t*t*(t*(6*t-15)+10)},randInt:function(t,e){return t+Math.floor(Math.random()*(e-t+1))},randFloat:function(t,e){return t+Math.random()*(e-t)},randFloatSpread:function(t){return t*(.5-Math.random())},seededRandom:function(t){void 0!==t&&(mi=t);let e=mi+=1831565813;return e=Math.imul(e^e>>>15,1|e),e^=e+Math.imul(e^e>>>7,61|e),((e^e>>>14)>>>0)/4294967296},degToRad:function(t){return t*yi},radToDeg:function(t){return t*gi},isPowerOfTwo:function(t){return!(t&t-1)&&0!==t},ceilPowerOfTwo:function(t){return Math.pow(2,Math.ceil(Math.log(t)/Math.LN2))},floorPowerOfTwo:function(t){return Math.pow(2,Math.floor(Math.log(t)/Math.LN2))},setQuaternionFromProperEuler:function(t,e,s,i,r){const n=Math.cos,a=Math.sin,o=n(s/2),h=a(s/2),l=n((e+i)/2),c=a((e+i)/2),u=n((e-i)/2),d=a((e-i)/2),p=n((i-e)/2),m=a((i-e)/2);switch(r){case"XYX":t.set(o*c,h*u,h*d,o*l);break;case"YZY":t.set(h*d,o*c,h*u,o*l);break;case"ZXZ":t.set(h*u,h*d,o*c,o*l);break;case"XZX":t.set(o*c,h*m,h*p,o*l);break;case"YXY":t.set(h*p,o*c,h*m,o*l);break;case"ZYZ":t.set(h*m,h*p,o*c,o*l);break;default:ai("MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: "+r)}},normalize:Mi,denormalize:wi};class _i{static{_i.prototype.isVector2=!0}constructor(t=0,e=0){this.x=t,this.y=e}get width(){return this.x}set width(t){this.x=t}get height(){return this.y}set height(t){this.y=t}set(t,e){return this.x=t,this.y=e,this}setScalar(t){return this.x=t,this.y=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;default:throw new Error("THREE.Vector2: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;default:throw new Error("THREE.Vector2: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y)}copy(t){return this.x=t.x,this.y=t.y,this}add(t){return this.x+=t.x,this.y+=t.y,this}addScalar(t){return this.x+=t,this.y+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this}subScalar(t){return this.x-=t,this.y-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this}multiply(t){return this.x*=t.x,this.y*=t.y,this}multiplyScalar(t){return this.x*=t,this.y*=t,this}divide(t){return this.x/=t.x,this.y/=t.y,this}divideScalar(t){return this.multiplyScalar(1/t)}applyMatrix3(t){const e=this.x,s=this.y,i=t.elements;return this.x=i[0]*e+i[3]*s+i[6],this.y=i[1]*e+i[4]*s+i[7],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this}clamp(t,e){return this.x=xi(this.x,t.x,e.x),this.y=xi(this.y,t.y,e.y),this}clampScalar(t,e){return this.x=xi(this.x,t,e),this.y=xi(this.y,t,e),this}clampLength(t,e){const s=this.length();return this.divideScalar(s||1).multiplyScalar(xi(s,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this}negate(){return this.x=-this.x,this.y=-this.y,this}dot(t){return this.x*t.x+this.y*t.y}cross(t){return this.x*t.y-this.y*t.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){return Math.atan2(-this.y,-this.x)+Math.PI}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(0===e)return Math.PI/2;const s=this.dot(t)/e;return Math.acos(xi(s,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,s=this.y-t.y;return e*e+s*s}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this}lerpVectors(t,e,s){return this.x=t.x+(e.x-t.x)*s,this.y=t.y+(e.y-t.y)*s,this}equals(t){return t.x===this.x&&t.y===this.y}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this}rotateAround(t,e){const s=Math.cos(e),i=Math.sin(e),r=this.x-t.x,n=this.y-t.y;return this.x=r*s-n*i+t.x,this.y=r*i+n*s+t.y,this}random(){return this.x=Math.random(),this.y=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y}}class Ai{constructor(t=0,e=0,s=0,i=1){this.isQuaternion=!0,this._x=t,this._y=e,this._z=s,this._w=i}static slerpFlat(t,e,s,i,r,n,a){let o=s[i+0],h=s[i+1],l=s[i+2],c=s[i+3],u=r[n+0],d=r[n+1],p=r[n+2],m=r[n+3];if(c!==m||o!==u||h!==d||l!==p){let t=o*u+h*d+l*p+c*m;t<0&&(u=-u,d=-d,p=-p,m=-m,t=-t);let e=1-a;if(t<.9995){const s=Math.acos(t),i=Math.sin(s);e=Math.sin(e*s)/i,o=o*e+u*(a=Math.sin(a*s)/i),h=h*e+d*a,l=l*e+p*a,c=c*e+m*a}else{o=o*e+u*a,h=h*e+d*a,l=l*e+p*a,c=c*e+m*a;const t=1/Math.sqrt(o*o+h*h+l*l+c*c);o*=t,h*=t,l*=t,c*=t}}t[e]=o,t[e+1]=h,t[e+2]=l,t[e+3]=c}static multiplyQuaternionsFlat(t,e,s,i,r,n){const a=s[i],o=s[i+1],h=s[i+2],l=s[i+3],c=r[n],u=r[n+1],d=r[n+2],p=r[n+3];return t[e]=a*p+l*c+o*d-h*u,t[e+1]=o*p+l*u+h*c-a*d,t[e+2]=h*p+l*d+a*u-o*c,t[e+3]=l*p-a*c-o*u-h*d,t}get x(){return this._x}set x(t){this._x=t,this._onChangeCallback()}get y(){return this._y}set y(t){this._y=t,this._onChangeCallback()}get z(){return this._z}set z(t){this._z=t,this._onChangeCallback()}get w(){return this._w}set w(t){this._w=t,this._onChangeCallback()}set(t,e,s,i){return this._x=t,this._y=e,this._z=s,this._w=i,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(t){return this._x=t.x,this._y=t.y,this._z=t.z,this._w=t.w,this._onChangeCallback(),this}setFromEuler(t,e=!0){const s=t._x,i=t._y,r=t._z,n=t._order,a=Math.cos,o=Math.sin,h=a(s/2),l=a(i/2),c=a(r/2),u=o(s/2),d=o(i/2),p=o(r/2);switch(n){case"XYZ":this._x=u*l*c+h*d*p,this._y=h*d*c-u*l*p,this._z=h*l*p+u*d*c,this._w=h*l*c-u*d*p;break;case"YXZ":this._x=u*l*c+h*d*p,this._y=h*d*c-u*l*p,this._z=h*l*p-u*d*c,this._w=h*l*c+u*d*p;break;case"ZXY":this._x=u*l*c-h*d*p,this._y=h*d*c+u*l*p,this._z=h*l*p+u*d*c,this._w=h*l*c-u*d*p;break;case"ZYX":this._x=u*l*c-h*d*p,this._y=h*d*c+u*l*p,this._z=h*l*p-u*d*c,this._w=h*l*c+u*d*p;break;case"YZX":this._x=u*l*c+h*d*p,this._y=h*d*c+u*l*p,this._z=h*l*p-u*d*c,this._w=h*l*c-u*d*p;break;case"XZY":this._x=u*l*c-h*d*p,this._y=h*d*c-u*l*p,this._z=h*l*p+u*d*c,this._w=h*l*c+u*d*p;break;default:ai("Quaternion: .setFromEuler() encountered an unknown order: "+n)}return!0===e&&this._onChangeCallback(),this}setFromAxisAngle(t,e){const s=e/2,i=Math.sin(s);return this._x=t.x*i,this._y=t.y*i,this._z=t.z*i,this._w=Math.cos(s),this._onChangeCallback(),this}setFromRotationMatrix(t){const e=t.elements,s=e[0],i=e[4],r=e[8],n=e[1],a=e[5],o=e[9],h=e[2],l=e[6],c=e[10],u=s+a+c;if(u>0){const t=.5/Math.sqrt(u+1);this._w=.25/t,this._x=(l-o)*t,this._y=(r-h)*t,this._z=(n-i)*t}else if(s>a&&s>c){const t=2*Math.sqrt(1+s-a-c);this._w=(l-o)/t,this._x=.25*t,this._y=(i+n)/t,this._z=(r+h)/t}else if(a>c){const t=2*Math.sqrt(1+a-s-c);this._w=(r-h)/t,this._x=(i+n)/t,this._y=.25*t,this._z=(o+l)/t}else{const t=2*Math.sqrt(1+c-s-a);this._w=(n-i)/t,this._x=(r+h)/t,this._y=(o+l)/t,this._z=.25*t}return this._onChangeCallback(),this}setFromUnitVectors(t,e){let s=t.dot(e)+1;return s<1e-8?(s=0,Math.abs(t.x)>Math.abs(t.z)?(this._x=-t.y,this._y=t.x,this._z=0,this._w=s):(this._x=0,this._y=-t.z,this._z=t.y,this._w=s)):(this._x=t.y*e.z-t.z*e.y,this._y=t.z*e.x-t.x*e.z,this._z=t.x*e.y-t.y*e.x,this._w=s),this.normalize()}angleTo(t){return 2*Math.acos(Math.abs(xi(this.dot(t),-1,1)))}rotateTowards(t,e){const s=this.angleTo(t);if(0===s)return this;const i=Math.min(1,e/s);return this.slerp(t,i),this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this}dot(t){return this._x*t._x+this._y*t._y+this._z*t._z+this._w*t._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let t=this.length();return 0===t?(this._x=0,this._y=0,this._z=0,this._w=1):(t=1/t,this._x=this._x*t,this._y=this._y*t,this._z=this._z*t,this._w=this._w*t),this._onChangeCallback(),this}multiply(t){return this.multiplyQuaternions(this,t)}premultiply(t){return this.multiplyQuaternions(t,this)}multiplyQuaternions(t,e){const s=t._x,i=t._y,r=t._z,n=t._w,a=e._x,o=e._y,h=e._z,l=e._w;return this._x=s*l+n*a+i*h-r*o,this._y=i*l+n*o+r*a-s*h,this._z=r*l+n*h+s*o-i*a,this._w=n*l-s*a-i*o-r*h,this._onChangeCallback(),this}slerp(t,e){let s=t._x,i=t._y,r=t._z,n=t._w,a=this.dot(t);a<0&&(s=-s,i=-i,r=-r,n=-n,a=-a);let o=1-e;if(a<.9995){const t=Math.acos(a),h=Math.sin(t);o=Math.sin(o*t)/h,e=Math.sin(e*t)/h,this._x=this._x*o+s*e,this._y=this._y*o+i*e,this._z=this._z*o+r*e,this._w=this._w*o+n*e,this._onChangeCallback()}else this._x=this._x*o+s*e,this._y=this._y*o+i*e,this._z=this._z*o+r*e,this._w=this._w*o+n*e,this.normalize();return this}slerpQuaternions(t,e,s){return this.copy(t).slerp(e,s)}random(){const t=2*Math.PI*Math.random(),e=2*Math.PI*Math.random(),s=Math.random(),i=Math.sqrt(1-s),r=Math.sqrt(s);return this.set(i*Math.sin(t),i*Math.cos(t),r*Math.sin(e),r*Math.cos(e))}equals(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._w===this._w}fromArray(t,e=0){return this._x=t[e],this._y=t[e+1],this._z=t[e+2],this._w=t[e+3],this._onChangeCallback(),this}toArray(t=[],e=0){return t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._w,t}fromBufferAttribute(t,e){return this._x=t.getX(e),this._y=t.getY(e),this._z=t.getZ(e),this._w=t.getW(e),this._onChangeCallback(),this}toJSON(){return this.toArray()}_onChange(t){return this._onChangeCallback=t,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._w}}class Ti{static{Ti.prototype.isVector3=!0}constructor(t=0,e=0,s=0){this.x=t,this.y=e,this.z=s}set(t,e,s){return void 0===s&&(s=this.z),this.x=t,this.y=e,this.z=s,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;default:throw new Error("THREE.Vector3: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("THREE.Vector3: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this}multiplyVectors(t,e){return this.x=t.x*e.x,this.y=t.y*e.y,this.z=t.z*e.z,this}applyEuler(t){return this.applyQuaternion(Ci.setFromEuler(t))}applyAxisAngle(t,e){return this.applyQuaternion(Ci.setFromAxisAngle(t,e))}applyMatrix3(t){const e=this.x,s=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[3]*s+r[6]*i,this.y=r[1]*e+r[4]*s+r[7]*i,this.z=r[2]*e+r[5]*s+r[8]*i,this}applyNormalMatrix(t){return this.applyMatrix3(t).normalize()}applyMatrix4(t){const e=this.x,s=this.y,i=this.z,r=t.elements,n=1/(r[3]*e+r[7]*s+r[11]*i+r[15]);return this.x=(r[0]*e+r[4]*s+r[8]*i+r[12])*n,this.y=(r[1]*e+r[5]*s+r[9]*i+r[13])*n,this.z=(r[2]*e+r[6]*s+r[10]*i+r[14])*n,this}applyQuaternion(t){const e=this.x,s=this.y,i=this.z,r=t.x,n=t.y,a=t.z,o=t.w,h=2*(n*i-a*s),l=2*(a*e-r*i),c=2*(r*s-n*e);return this.x=e+o*h+n*c-a*l,this.y=s+o*l+a*h-r*c,this.z=i+o*c+r*l-n*h,this}project(t){return this.applyMatrix4(t.matrixWorldInverse).applyMatrix4(t.projectionMatrix)}unproject(t){return this.applyMatrix4(t.projectionMatrixInverse).applyMatrix4(t.matrixWorld)}transformDirection(t){const e=this.x,s=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[4]*s+r[8]*i,this.y=r[1]*e+r[5]*s+r[9]*i,this.z=r[2]*e+r[6]*s+r[10]*i,this.normalize()}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this}divideScalar(t){return this.multiplyScalar(1/t)}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this}clamp(t,e){return this.x=xi(this.x,t.x,e.x),this.y=xi(this.y,t.y,e.y),this.z=xi(this.z,t.z,e.z),this}clampScalar(t,e){return this.x=xi(this.x,t,e),this.y=xi(this.y,t,e),this.z=xi(this.z,t,e),this}clampLength(t,e){const s=this.length();return this.divideScalar(s||1).multiplyScalar(xi(s,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this}lerpVectors(t,e,s){return this.x=t.x+(e.x-t.x)*s,this.y=t.y+(e.y-t.y)*s,this.z=t.z+(e.z-t.z)*s,this}cross(t){return this.crossVectors(this,t)}crossVectors(t,e){const s=t.x,i=t.y,r=t.z,n=e.x,a=e.y,o=e.z;return this.x=i*o-r*a,this.y=r*n-s*o,this.z=s*a-i*n,this}projectOnVector(t){const e=t.lengthSq();if(0===e)return this.set(0,0,0);const s=t.dot(this)/e;return this.copy(t).multiplyScalar(s)}projectOnPlane(t){return zi.copy(this).projectOnVector(t),this.sub(zi)}reflect(t){return this.sub(zi.copy(t).multiplyScalar(2*this.dot(t)))}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(0===e)return Math.PI/2;const s=this.dot(t)/e;return Math.acos(xi(s,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,s=this.y-t.y,i=this.z-t.z;return e*e+s*s+i*i}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)+Math.abs(this.z-t.z)}setFromSpherical(t){return this.setFromSphericalCoords(t.radius,t.phi,t.theta)}setFromSphericalCoords(t,e,s){const i=Math.sin(e)*t;return this.x=i*Math.sin(s),this.y=Math.cos(e)*t,this.z=i*Math.cos(s),this}setFromCylindrical(t){return this.setFromCylindricalCoords(t.radius,t.theta,t.y)}setFromCylindricalCoords(t,e,s){return this.x=t*Math.sin(e),this.y=s,this.z=t*Math.cos(e),this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this}setFromMatrixScale(t){const e=this.setFromMatrixColumn(t,0).length(),s=this.setFromMatrixColumn(t,1).length(),i=this.setFromMatrixColumn(t,2).length();return this.x=e,this.y=s,this.z=i,this}setFromMatrixColumn(t,e){return this.fromArray(t.elements,4*e)}setFromMatrix3Column(t,e){return this.fromArray(t.elements,3*e)}setFromEuler(t){return this.x=t._x,this.y=t._y,this.z=t._z,this}setFromColor(t){return this.x=t.r,this.y=t.g,this.z=t.b,this}equals(t){return t.x===this.x&&t.y===this.y&&t.z===this.z}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this.z=t[e+2],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this}randomDirection(){const t=Math.random()*Math.PI*2,e=2*Math.random()-1,s=Math.sqrt(1-e*e);return this.x=s*Math.cos(t),this.y=e,this.z=s*Math.sin(t),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z}}const zi=new Ti,Ci=new Ai;class Ii{static{Ii.prototype.isMatrix3=!0}constructor(t,e,s,i,r,n,a,o,h){this.elements=[1,0,0,0,1,0,0,0,1],void 0!==t&&this.set(t,e,s,i,r,n,a,o,h)}set(t,e,s,i,r,n,a,o,h){const l=this.elements;return l[0]=t,l[1]=i,l[2]=a,l[3]=e,l[4]=r,l[5]=o,l[6]=s,l[7]=n,l[8]=h,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(t){const e=this.elements,s=t.elements;return e[0]=s[0],e[1]=s[1],e[2]=s[2],e[3]=s[3],e[4]=s[4],e[5]=s[5],e[6]=s[6],e[7]=s[7],e[8]=s[8],this}extractBasis(t,e,s){return t.setFromMatrix3Column(this,0),e.setFromMatrix3Column(this,1),s.setFromMatrix3Column(this,2),this}setFromMatrix4(t){const e=t.elements;return this.set(e[0],e[4],e[8],e[1],e[5],e[9],e[2],e[6],e[10]),this}multiply(t){return this.multiplyMatrices(this,t)}premultiply(t){return this.multiplyMatrices(t,this)}multiplyMatrices(t,e){const s=t.elements,i=e.elements,r=this.elements,n=s[0],a=s[3],o=s[6],h=s[1],l=s[4],c=s[7],u=s[2],d=s[5],p=s[8],m=i[0],y=i[3],g=i[6],f=i[1],x=i[4],b=i[7],v=i[2],w=i[5],M=i[8];return r[0]=n*m+a*f+o*v,r[3]=n*y+a*x+o*w,r[6]=n*g+a*b+o*M,r[1]=h*m+l*f+c*v,r[4]=h*y+l*x+c*w,r[7]=h*g+l*b+c*M,r[2]=u*m+d*f+p*v,r[5]=u*y+d*x+p*w,r[8]=u*g+d*b+p*M,this}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[3]*=t,e[6]*=t,e[1]*=t,e[4]*=t,e[7]*=t,e[2]*=t,e[5]*=t,e[8]*=t,this}determinant(){const t=this.elements,e=t[0],s=t[1],i=t[2],r=t[3],n=t[4],a=t[5],o=t[6],h=t[7],l=t[8];return e*n*l-e*a*h-s*r*l+s*a*o+i*r*h-i*n*o}invert(){const t=this.elements,e=t[0],s=t[1],i=t[2],r=t[3],n=t[4],a=t[5],o=t[6],h=t[7],l=t[8],c=l*n-a*h,u=a*o-l*r,d=h*r-n*o,p=e*c+s*u+i*d;if(0===p)return this.set(0,0,0,0,0,0,0,0,0);const m=1/p;return t[0]=c*m,t[1]=(i*h-l*s)*m,t[2]=(a*s-i*n)*m,t[3]=u*m,t[4]=(l*e-i*o)*m,t[5]=(i*r-a*e)*m,t[6]=d*m,t[7]=(s*o-h*e)*m,t[8]=(n*e-s*r)*m,this}transpose(){let t;const e=this.elements;return t=e[1],e[1]=e[3],e[3]=t,t=e[2],e[2]=e[6],e[6]=t,t=e[5],e[5]=e[7],e[7]=t,this}getNormalMatrix(t){return this.setFromMatrix4(t).invert().transpose()}transposeIntoArray(t){const e=this.elements;return t[0]=e[0],t[1]=e[3],t[2]=e[6],t[3]=e[1],t[4]=e[4],t[5]=e[7],t[6]=e[2],t[7]=e[5],t[8]=e[8],this}setUvTransform(t,e,s,i,r,n,a){const o=Math.cos(r),h=Math.sin(r);return this.set(s*o,s*h,-s*(o*n+h*a)+n+t,-i*h,i*o,-i*(-h*n+o*a)+a+e,0,0,1),this}scale(t,e){return hi("Matrix3: .scale() is deprecated. Use .makeScale() instead."),this.premultiply(Bi.makeScale(t,e)),this}rotate(t){return hi("Matrix3: .rotate() is deprecated. Use .makeRotation() instead."),this.premultiply(Bi.makeRotation(-t)),this}translate(t,e){return hi("Matrix3: .translate() is deprecated. Use .makeTranslation() instead."),this.premultiply(Bi.makeTranslation(t,e)),this}makeTranslation(t,e){return t.isVector2?this.set(1,0,t.x,0,1,t.y,0,0,1):this.set(1,0,t,0,1,e,0,0,1),this}makeRotation(t){const e=Math.cos(t),s=Math.sin(t);return this.set(e,-s,0,s,e,0,0,0,1),this}makeScale(t,e){return this.set(t,0,0,0,e,0,0,0,1),this}equals(t){const e=this.elements,s=t.elements;for(let t=0;t<9;t++)if(e[t]!==s[t])return!1;return!0}fromArray(t,e=0){for(let s=0;s<9;s++)this.elements[s]=t[s+e];return this}toArray(t=[],e=0){const s=this.elements;return t[e]=s[0],t[e+1]=s[1],t[e+2]=s[2],t[e+3]=s[3],t[e+4]=s[4],t[e+5]=s[5],t[e+6]=s[6],t[e+7]=s[7],t[e+8]=s[8],t}clone(){return(new this.constructor).fromArray(this.elements)}}const Bi=new Ii,ki=(new Ii).set(.4123908,.3575843,.1804808,.212639,.7151687,.0721923,.0193308,.1191948,.9505322),Oi=(new Ii).set(3.2409699,-1.5373832,-.4986108,-.9692436,1.8759675,.0415551,.0556301,-.203977,1.0569715);function Pi(){const t={enabled:!0,workingColorSpace:ss,spaces:{},convert:function(t,e,s){return!1!==this.enabled&&e!==s&&e&&s?(this.spaces[e].transfer===rs&&(t.r=Ei(t.r),t.g=Ei(t.g),t.b=Ei(t.b)),this.spaces[e].primaries!==this.spaces[s].primaries&&(t.applyMatrix3(this.spaces[e].toXYZ),t.applyMatrix3(this.spaces[s].fromXYZ)),this.spaces[s].transfer===rs&&(t.r=Ni(t.r),t.g=Ni(t.g),t.b=Ni(t.b)),t):t},workingToColorSpace:function(t,e){return this.convert(t,this.workingColorSpace,e)},colorSpaceToWorking:function(t,e){return this.convert(t,e,this.workingColorSpace)},getPrimaries:function(t){return this.spaces[t].primaries},getTransfer:function(t){return""===t?is:this.spaces[t].transfer},getToneMappingMode:function(t){return this.spaces[t].outputColorSpaceConfig.toneMappingMode||"standard"},getLuminanceCoefficients:function(t,e=this.workingColorSpace){return t.fromArray(this.spaces[e].luminanceCoefficients)},define:function(t){Object.assign(this.spaces,t)},_getMatrix:function(t,e,s){return t.copy(this.spaces[e].toXYZ).multiply(this.spaces[s].fromXYZ)},_getDrawingBufferColorSpace:function(t){return this.spaces[t].outputColorSpaceConfig.drawingBufferColorSpace},_getUnpackColorSpace:function(t=this.workingColorSpace){return this.spaces[t].workingColorSpaceConfig.unpackColorSpace},fromWorkingColorSpace:function(e,s){return hi("ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace()."),t.workingToColorSpace(e,s)},toWorkingColorSpace:function(e,s){return hi("ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking()."),t.colorSpaceToWorking(e,s)}},e=[.64,.33,.3,.6,.15,.06],s=[.2126,.7152,.0722],i=[.3127,.329];return t.define({[ss]:{primaries:e,whitePoint:i,transfer:is,toXYZ:ki,fromXYZ:Oi,luminanceCoefficients:s,workingColorSpaceConfig:{unpackColorSpace:es},outputColorSpaceConfig:{drawingBufferColorSpace:es}},[es]:{primaries:e,whitePoint:i,transfer:rs,toXYZ:ki,fromXYZ:Oi,luminanceCoefficients:s,outputColorSpaceConfig:{drawingBufferColorSpace:es}}}),t}const Ri=Pi();function Ei(t){return t<.04045?.0773993808*t:Math.pow(.9478672986*t+.0521327014,2.4)}function Ni(t){return t<.0031308?12.92*t:1.055*Math.pow(t,.41666)-.055}let Vi;class Li{static getDataURL(t,e="image/png"){if(/^data:/i.test(t.src))return t.src;if("undefined"==typeof HTMLCanvasElement)return t.src;let s;if(t instanceof HTMLCanvasElement)s=t;else{void 0===Vi&&(Vi=Qs("canvas")),Vi.width=t.width,Vi.height=t.height;const e=Vi.getContext("2d");t instanceof ImageData?e.putImageData(t,0,0):e.drawImage(t,0,0,t.width,t.height),s=Vi}return s.toDataURL(e)}static sRGBToLinear(t){if("undefined"!=typeof HTMLImageElement&&t instanceof HTMLImageElement||"undefined"!=typeof HTMLCanvasElement&&t instanceof HTMLCanvasElement||"undefined"!=typeof ImageBitmap&&t instanceof ImageBitmap){const e=Qs("canvas");e.width=t.width,e.height=t.height;const s=e.getContext("2d");s.drawImage(t,0,0,t.width,t.height);const i=s.getImageData(0,0,t.width,t.height),r=i.data;for(let t=0;t1),this.pmremVersion=0,this.normalized=!1}get width(){return this.source.getSize(Wi).x}get height(){return this.source.getSize(Wi).y}get depth(){return this.source.getSize(Wi).z}get image(){return this.source.data}set image(t){this.source.data=t}updateMatrix(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}clone(){return(new this.constructor).copy(this)}copy(t){return this.name=t.name,this.source=t.source,this.mipmaps=t.mipmaps.slice(0),this.mapping=t.mapping,this.channel=t.channel,this.wrapS=t.wrapS,this.wrapT=t.wrapT,this.magFilter=t.magFilter,this.minFilter=t.minFilter,this.anisotropy=t.anisotropy,this.format=t.format,this.internalFormat=t.internalFormat,this.type=t.type,this.normalized=t.normalized,this.offset.copy(t.offset),this.repeat.copy(t.repeat),this.center.copy(t.center),this.rotation=t.rotation,this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrix.copy(t.matrix),this.generateMipmaps=t.generateMipmaps,this.premultiplyAlpha=t.premultiplyAlpha,this.flipY=t.flipY,this.unpackAlignment=t.unpackAlignment,this.colorSpace=t.colorSpace,this.renderTarget=t.renderTarget,this.isRenderTargetTexture=t.isRenderTargetTexture,this.isArrayTexture=t.isArrayTexture,this.userData=JSON.parse(JSON.stringify(t.userData)),this.needsUpdate=!0,this}setValues(t){for(const e in t){const s=t[e];if(void 0===s){ai(`Texture.setValues(): parameter '${e}' has value of undefined.`);continue}const i=this[e];void 0!==i?i&&s&&i.isVector2&&s.isVector2||i&&s&&i.isVector3&&s.isVector3||i&&s&&i.isMatrix3&&s.isMatrix3?i.copy(s):this[e]=s:ai(`Texture.setValues(): property '${e}' does not exist.`)}}toJSON(t){const e=void 0===t||"string"==typeof t;if(!e&&void 0!==t.textures[this.uuid])return t.textures[this.uuid];const s={metadata:{version:4.7,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,image:this.source.toJSON(t).uuid,mapping:this.mapping,channel:this.channel,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,internalFormat:this.internalFormat,type:this.type,normalized:this.normalized,colorSpace:this.colorSpace,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,generateMipmaps:this.generateMipmaps,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};return Object.keys(this.userData).length>0&&(s.userData=this.userData),e||(t.textures[this.uuid]=s),s}dispose(){this.dispatchEvent({type:"dispose"})}transformUv(t){if(this.mapping!==ht)return t;if(t.applyMatrix3(this.matrix),t.x<0||t.x>1)switch(this.wrapS){case mt:t.x=t.x-Math.floor(t.x);break;case yt:t.x=t.x<0?0:1;break;case gt:1===Math.abs(Math.floor(t.x)%2)?t.x=Math.ceil(t.x)-t.x:t.x=t.x-Math.floor(t.x)}if(t.y<0||t.y>1)switch(this.wrapT){case mt:t.y=t.y-Math.floor(t.y);break;case yt:t.y=t.y<0?0:1;break;case gt:1===Math.abs(Math.floor(t.y)%2)?t.y=Math.ceil(t.y)-t.y:t.y=t.y-Math.floor(t.y)}return this.flipY&&(t.y=1-t.y),t}set needsUpdate(t){!0===t&&(this.version++,this.source.needsUpdate=!0)}set needsPMREMUpdate(t){!0===t&&this.pmremVersion++}}Ji.DEFAULT_IMAGE=null,Ji.DEFAULT_MAPPING=ht,Ji.DEFAULT_ANISOTROPY=1;class qi{static{qi.prototype.isVector4=!0}constructor(t=0,e=0,s=0,i=1){this.x=t,this.y=e,this.z=s,this.w=i}get width(){return this.z}set width(t){this.z=t}get height(){return this.w}set height(t){this.w=t}set(t,e,s,i){return this.x=t,this.y=e,this.z=s,this.w=i,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this.w=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setW(t){return this.w=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;case 3:this.w=e;break;default:throw new Error("THREE.Vector4: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("THREE.Vector4: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this.w=void 0!==t.w?t.w:1,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this.w+=t.w,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this.w+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this.w=t.w+e.w,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this.w+=t.w*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this.w-=t.w,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this.w-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this.w=t.w-e.w,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this.w*=t.w,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this.w*=t,this}applyMatrix4(t){const e=this.x,s=this.y,i=this.z,r=this.w,n=t.elements;return this.x=n[0]*e+n[4]*s+n[8]*i+n[12]*r,this.y=n[1]*e+n[5]*s+n[9]*i+n[13]*r,this.z=n[2]*e+n[6]*s+n[10]*i+n[14]*r,this.w=n[3]*e+n[7]*s+n[11]*i+n[15]*r,this}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this.w/=t.w,this}divideScalar(t){return this.multiplyScalar(1/t)}setAxisAngleFromQuaternion(t){this.w=2*Math.acos(t.w);const e=Math.sqrt(1-t.w*t.w);return e<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=t.x/e,this.y=t.y/e,this.z=t.z/e),this}setAxisAngleFromRotationMatrix(t){let e,s,i,r;const n=.01,a=.1,o=t.elements,h=o[0],l=o[4],c=o[8],u=o[1],d=o[5],p=o[9],m=o[2],y=o[6],g=o[10];if(Math.abs(l-u)o&&t>f?tf?o1);this.dispose()}this.viewport.set(0,0,t,e),this.scissor.set(0,0,t,e)}clone(){return(new this.constructor).copy(this)}copy(t){this.width=t.width,this.height=t.height,this.depth=t.depth,this.scissor.copy(t.scissor),this.scissorTest=t.scissorTest,this.viewport.copy(t.viewport),this.textures.length=0;for(let e=0,s=t.textures.length;e>>0}enable(t){this.mask|=1<1){for(let t=0;t1){for(let t=0;t0&&(i.userData=this.userData),i.layers=this.layers.mask,i.matrix=this.matrix.toArray(),i.up=this.up.toArray(),null!==this.pivot&&(i.pivot=this.pivot.toArray()),!1===this.matrixAutoUpdate&&(i.matrixAutoUpdate=!1),void 0!==this.morphTargetDictionary&&(i.morphTargetDictionary=Object.assign({},this.morphTargetDictionary)),void 0!==this.morphTargetInfluences&&(i.morphTargetInfluences=this.morphTargetInfluences.slice()),this.isInstancedMesh&&(i.type="InstancedMesh",i.count=this.count,i.instanceMatrix=this.instanceMatrix.toJSON(),null!==this.instanceColor&&(i.instanceColor=this.instanceColor.toJSON())),this.isBatchedMesh&&(i.type="BatchedMesh",i.perObjectFrustumCulled=this.perObjectFrustumCulled,i.sortObjects=this.sortObjects,i.drawRanges=this._drawRanges,i.reservedRanges=this._reservedRanges,i.geometryInfo=this._geometryInfo.map(t=>({...t,boundingBox:t.boundingBox?t.boundingBox.toJSON():void 0,boundingSphere:t.boundingSphere?t.boundingSphere.toJSON():void 0})),i.instanceInfo=this._instanceInfo.map(t=>({...t})),i.availableInstanceIds=this._availableInstanceIds.slice(),i.availableGeometryIds=this._availableGeometryIds.slice(),i.nextIndexStart=this._nextIndexStart,i.nextVertexStart=this._nextVertexStart,i.geometryCount=this._geometryCount,i.maxInstanceCount=this._maxInstanceCount,i.maxVertexCount=this._maxVertexCount,i.maxIndexCount=this._maxIndexCount,i.geometryInitialized=this._geometryInitialized,i.matricesTexture=this._matricesTexture.toJSON(t),i.indirectTexture=this._indirectTexture.toJSON(t),null!==this._colorsTexture&&(i.colorsTexture=this._colorsTexture.toJSON(t)),null!==this.boundingSphere&&(i.boundingSphere=this.boundingSphere.toJSON()),null!==this.boundingBox&&(i.boundingBox=this.boundingBox.toJSON())),this.isScene)this.background&&(this.background.isColor?i.background=this.background.toJSON():this.background.isTexture&&(i.background=this.background.toJSON(t).uuid)),this.environment&&this.environment.isTexture&&!0!==this.environment.isRenderTargetTexture&&(i.environment=this.environment.toJSON(t).uuid);else if(this.isMesh||this.isLine||this.isPoints){i.geometry=r(t.geometries,this.geometry);const e=this.geometry.parameters;if(void 0!==e&&void 0!==e.shapes){const s=e.shapes;if(Array.isArray(s))for(let e=0,i=s.length;e0){i.children=[];for(let e=0;e0){i.animations=[];for(let e=0;e0&&(s.geometries=e),i.length>0&&(s.materials=i),r.length>0&&(s.textures=r),a.length>0&&(s.images=a),o.length>0&&(s.shapes=o),h.length>0&&(s.skeletons=h),l.length>0&&(s.animations=l),c.length>0&&(s.nodes=c)}return s.object=i,s;function n(t){const e=[];for(const s in t){const i=t[s];delete i.metadata,e.push(i)}return e}}clone(t){return(new this.constructor).copy(this,t)}copy(t,e=!0){if(this.name=t.name,this.up.copy(t.up),this.position.copy(t.position),this.rotation.order=t.rotation.order,this.quaternion.copy(t.quaternion),this.scale.copy(t.scale),this.pivot=null!==t.pivot?t.pivot.clone():null,this.matrix.copy(t.matrix),this.matrixWorld.copy(t.matrixWorld),this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrixWorldAutoUpdate=t.matrixWorldAutoUpdate,this.matrixWorldNeedsUpdate=t.matrixWorldNeedsUpdate,this.layers.mask=t.layers.mask,this.visible=t.visible,this.castShadow=t.castShadow,this.receiveShadow=t.receiveShadow,this.frustumCulled=t.frustumCulled,this.renderOrder=t.renderOrder,this.static=t.static,this.animations=t.animations.slice(),this.userData=JSON.parse(JSON.stringify(t.userData)),!0===e)for(let e=0;eo+l?(h.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:t.handedness,target:this})):!h.inputState.pinching&&a<=o-l&&(h.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:t.handedness,target:this}))}else null!==o&&t.gripSpace&&(r=e.getPose(t.gripSpace,s),null!==r&&(o.matrix.fromArray(r.transform.matrix),o.matrix.decompose(o.position,o.rotation,o.scale),o.matrixWorldNeedsUpdate=!0,r.linearVelocity?(o.hasLinearVelocity=!0,o.linearVelocity.copy(r.linearVelocity)):o.hasLinearVelocity=!1,r.angularVelocity?(o.hasAngularVelocity=!0,o.angularVelocity.copy(r.angularVelocity)):o.hasAngularVelocity=!1,o.eventsEnabled&&o.dispatchEvent({type:"gripUpdated",data:t,target:this})));null!==a&&(i=e.getPose(t.targetRaySpace,s),null===i&&null!==r&&(i=r),null!==i&&(a.matrix.fromArray(i.transform.matrix),a.matrix.decompose(a.position,a.rotation,a.scale),a.matrixWorldNeedsUpdate=!0,i.linearVelocity?(a.hasLinearVelocity=!0,a.linearVelocity.copy(i.linearVelocity)):a.hasLinearVelocity=!1,i.angularVelocity?(a.hasAngularVelocity=!0,a.angularVelocity.copy(i.angularVelocity)):a.hasAngularVelocity=!1,this.dispatchEvent(zr)))}return null!==a&&(a.visible=null!==i),null!==o&&(o.visible=null!==r),null!==h&&(h.visible=null!==n),this}_getHandJoint(t,e){if(void 0===t.joints[e.jointName]){const s=new Tr;s.matrixAutoUpdate=!1,s.visible=!1,t.joints[e.jointName]=s,t.add(s)}return t.joints[e.jointName]}}const Ir={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},Br={h:0,s:0,l:0},kr={h:0,s:0,l:0};function Or(t,e,s){return s<0&&(s+=1),s>1&&(s-=1),s<1/6?t+6*(e-t)*s:s<.5?e:s<2/3?t+6*(e-t)*(2/3-s):t}class Pr{constructor(t,e,s){return this.isColor=!0,this.r=1,this.g=1,this.b=1,this.set(t,e,s)}set(t,e,s){if(void 0===e&&void 0===s){const e=t;e&&e.isColor?this.copy(e):"number"==typeof e?this.setHex(e):"string"==typeof e&&this.setStyle(e)}else this.setRGB(t,e,s);return this}setScalar(t){return this.r=t,this.g=t,this.b=t,this}setHex(t,e=es){return t=Math.floor(t),this.r=(t>>16&255)/255,this.g=(t>>8&255)/255,this.b=(255&t)/255,Ri.colorSpaceToWorking(this,e),this}setRGB(t,e,s,i=Ri.workingColorSpace){return this.r=t,this.g=e,this.b=s,Ri.colorSpaceToWorking(this,i),this}setHSL(t,e,s,i=Ri.workingColorSpace){if(t=bi(t,1),e=xi(e,0,1),s=xi(s,0,1),0===e)this.r=this.g=this.b=s;else{const i=s<=.5?s*(1+e):s+e-s*e,r=2*s-i;this.r=Or(r,i,t+1/3),this.g=Or(r,i,t),this.b=Or(r,i,t-1/3)}return Ri.colorSpaceToWorking(this,i),this}setStyle(t,e=es){function s(e){void 0!==e&&parseFloat(e)<1&&ai("Color: Alpha component of "+t+" will be ignored.")}let i;if(i=/^(\w+)\(([^\)]*)\)/.exec(t)){let r;const n=i[1],a=i[2];switch(n){case"rgb":case"rgba":if(r=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return s(r[4]),this.setRGB(Math.min(255,parseInt(r[1],10))/255,Math.min(255,parseInt(r[2],10))/255,Math.min(255,parseInt(r[3],10))/255,e);if(r=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return s(r[4]),this.setRGB(Math.min(100,parseInt(r[1],10))/100,Math.min(100,parseInt(r[2],10))/100,Math.min(100,parseInt(r[3],10))/100,e);break;case"hsl":case"hsla":if(r=/^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return s(r[4]),this.setHSL(parseFloat(r[1])/360,parseFloat(r[2])/100,parseFloat(r[3])/100,e);break;default:ai("Color: Unknown color model "+t)}}else if(i=/^\#([A-Fa-f\d]+)$/.exec(t)){const s=i[1],r=s.length;if(3===r)return this.setRGB(parseInt(s.charAt(0),16)/15,parseInt(s.charAt(1),16)/15,parseInt(s.charAt(2),16)/15,e);if(6===r)return this.setHex(parseInt(s,16),e);ai("Color: Invalid hex color "+t)}else if(t&&t.length>0)return this.setColorName(t,e);return this}setColorName(t,e=es){const s=Ir[t.toLowerCase()];return void 0!==s?this.setHex(s,e):ai("Color: Unknown color "+t),this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(t){return this.r=t.r,this.g=t.g,this.b=t.b,this}copySRGBToLinear(t){return this.r=Ei(t.r),this.g=Ei(t.g),this.b=Ei(t.b),this}copyLinearToSRGB(t){return this.r=Ni(t.r),this.g=Ni(t.g),this.b=Ni(t.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(t=es){return Ri.workingToColorSpace(Rr.copy(this),t),65536*Math.round(xi(255*Rr.r,0,255))+256*Math.round(xi(255*Rr.g,0,255))+Math.round(xi(255*Rr.b,0,255))}getHexString(t=es){return("000000"+this.getHex(t).toString(16)).slice(-6)}getHSL(t,e=Ri.workingColorSpace){Ri.workingToColorSpace(Rr.copy(this),e);const s=Rr.r,i=Rr.g,r=Rr.b,n=Math.max(s,i,r),a=Math.min(s,i,r);let o,h;const l=(a+n)/2;if(a===n)o=0,h=0;else{const t=n-a;switch(h=l<=.5?t/(n+a):t/(2-n-a),n){case s:o=(i-r)/t+(i0&&(e.object.backgroundBlurriness=this.backgroundBlurriness),1!==this.backgroundIntensity&&(e.object.backgroundIntensity=this.backgroundIntensity),e.object.backgroundRotation=this.backgroundRotation.toArray(),1!==this.environmentIntensity&&(e.object.environmentIntensity=this.environmentIntensity),e.object.environmentRotation=this.environmentRotation.toArray(),e}}const Lr=new Ti,Fr=new Ti,Dr=new Ti,Ur=new Ti,jr=new Ti,Wr=new Ti,Jr=new Ti,qr=new Ti,Hr=new Ti,Xr=new Ti,Yr=new qi,Zr=new qi,Gr=new qi;class $r{constructor(t=new Ti,e=new Ti,s=new Ti){this.a=t,this.b=e,this.c=s}static getNormal(t,e,s,i){i.subVectors(s,e),Lr.subVectors(t,e),i.cross(Lr);const r=i.lengthSq();return r>0?i.multiplyScalar(1/Math.sqrt(r)):i.set(0,0,0)}static getBarycoord(t,e,s,i,r){Lr.subVectors(i,e),Fr.subVectors(s,e),Dr.subVectors(t,e);const n=Lr.dot(Lr),a=Lr.dot(Fr),o=Lr.dot(Dr),h=Fr.dot(Fr),l=Fr.dot(Dr),c=n*h-a*a;if(0===c)return r.set(0,0,0),null;const u=1/c,d=(h*o-a*l)*u,p=(n*l-a*o)*u;return r.set(1-d-p,p,d)}static containsPoint(t,e,s,i){return null!==this.getBarycoord(t,e,s,i,Ur)&&(Ur.x>=0&&Ur.y>=0&&Ur.x+Ur.y<=1)}static getInterpolation(t,e,s,i,r,n,a,o){return null===this.getBarycoord(t,e,s,i,Ur)?(o.x=0,o.y=0,"z"in o&&(o.z=0),"w"in o&&(o.w=0),null):(o.setScalar(0),o.addScaledVector(r,Ur.x),o.addScaledVector(n,Ur.y),o.addScaledVector(a,Ur.z),o)}static getInterpolatedAttribute(t,e,s,i,r,n){return Yr.setScalar(0),Zr.setScalar(0),Gr.setScalar(0),Yr.fromBufferAttribute(t,e),Zr.fromBufferAttribute(t,s),Gr.fromBufferAttribute(t,i),n.setScalar(0),n.addScaledVector(Yr,r.x),n.addScaledVector(Zr,r.y),n.addScaledVector(Gr,r.z),n}static isFrontFacing(t,e,s,i){return Lr.subVectors(s,e),Fr.subVectors(t,e),Lr.cross(Fr).dot(i)<0}set(t,e,s){return this.a.copy(t),this.b.copy(e),this.c.copy(s),this}setFromPointsAndIndices(t,e,s,i){return this.a.copy(t[e]),this.b.copy(t[s]),this.c.copy(t[i]),this}setFromAttributeAndIndices(t,e,s,i){return this.a.fromBufferAttribute(t,e),this.b.fromBufferAttribute(t,s),this.c.fromBufferAttribute(t,i),this}clone(){return(new this.constructor).copy(this)}copy(t){return this.a.copy(t.a),this.b.copy(t.b),this.c.copy(t.c),this}getArea(){return Lr.subVectors(this.c,this.b),Fr.subVectors(this.a,this.b),.5*Lr.cross(Fr).length()}getMidpoint(t){return t.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(t){return $r.getNormal(this.a,this.b,this.c,t)}getPlane(t){return t.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(t,e){return $r.getBarycoord(t,this.a,this.b,this.c,e)}getInterpolation(t,e,s,i,r){return $r.getInterpolation(t,this.a,this.b,this.c,e,s,i,r)}containsPoint(t){return $r.containsPoint(t,this.a,this.b,this.c)}isFrontFacing(t){return $r.isFrontFacing(this.a,this.b,this.c,t)}intersectsBox(t){return t.intersectsTriangle(this)}closestPointToPoint(t,e){const s=this.a,i=this.b,r=this.c;let n,a;jr.subVectors(i,s),Wr.subVectors(r,s),qr.subVectors(t,s);const o=jr.dot(qr),h=Wr.dot(qr);if(o<=0&&h<=0)return e.copy(s);Hr.subVectors(t,i);const l=jr.dot(Hr),c=Wr.dot(Hr);if(l>=0&&c<=l)return e.copy(i);const u=o*c-l*h;if(u<=0&&o>=0&&l<=0)return n=o/(o-l),e.copy(s).addScaledVector(jr,n);Xr.subVectors(t,r);const d=jr.dot(Xr),p=Wr.dot(Xr);if(p>=0&&d<=p)return e.copy(r);const m=d*h-o*p;if(m<=0&&h>=0&&p<=0)return a=h/(h-p),e.copy(s).addScaledVector(Wr,a);const y=l*p-d*c;if(y<=0&&c-l>=0&&d-p>=0)return Jr.subVectors(r,i),a=(c-l)/(c-l+(d-p)),e.copy(i).addScaledVector(Jr,a);const g=1/(y+m+u);return n=m*g,a=u*g,e.copy(s).addScaledVector(jr,n).addScaledVector(Wr,a)}equals(t){return t.a.equals(this.a)&&t.b.equals(this.b)&&t.c.equals(this.c)}}class Qr{constructor(t=new Ti(1/0,1/0,1/0),e=new Ti(-1/0,-1/0,-1/0)){this.isBox3=!0,this.min=t,this.max=e}set(t,e){return this.min.copy(t),this.max.copy(e),this}setFromArray(t){this.makeEmpty();for(let e=0,s=t.length;e=this.min.x&&t.x<=this.max.x&&t.y>=this.min.y&&t.y<=this.max.y&&t.z>=this.min.z&&t.z<=this.max.z}containsBox(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y&&this.min.z<=t.min.z&&t.max.z<=this.max.z}getParameter(t,e){return e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y),(t.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(t){return t.max.x>=this.min.x&&t.min.x<=this.max.x&&t.max.y>=this.min.y&&t.min.y<=this.max.y&&t.max.z>=this.min.z&&t.min.z<=this.max.z}intersectsSphere(t){return this.clampPoint(t.center,tn),tn.distanceToSquared(t.center)<=t.radius*t.radius}intersectsPlane(t){let e,s;return t.normal.x>0?(e=t.normal.x*this.min.x,s=t.normal.x*this.max.x):(e=t.normal.x*this.max.x,s=t.normal.x*this.min.x),t.normal.y>0?(e+=t.normal.y*this.min.y,s+=t.normal.y*this.max.y):(e+=t.normal.y*this.max.y,s+=t.normal.y*this.min.y),t.normal.z>0?(e+=t.normal.z*this.min.z,s+=t.normal.z*this.max.z):(e+=t.normal.z*this.max.z,s+=t.normal.z*this.min.z),e<=-t.constant&&s>=-t.constant}intersectsTriangle(t){if(this.isEmpty())return!1;this.getCenter(ln),cn.subVectors(this.max,ln),sn.subVectors(t.a,ln),rn.subVectors(t.b,ln),nn.subVectors(t.c,ln),an.subVectors(rn,sn),on.subVectors(nn,rn),hn.subVectors(sn,nn);let e=[0,-an.z,an.y,0,-on.z,on.y,0,-hn.z,hn.y,an.z,0,-an.x,on.z,0,-on.x,hn.z,0,-hn.x,-an.y,an.x,0,-on.y,on.x,0,-hn.y,hn.x,0];return!!pn(e,sn,rn,nn,cn)&&(e=[1,0,0,0,1,0,0,0,1],!!pn(e,sn,rn,nn,cn)&&(un.crossVectors(an,on),e=[un.x,un.y,un.z],pn(e,sn,rn,nn,cn)))}clampPoint(t,e){return e.copy(t).clamp(this.min,this.max)}distanceToPoint(t){return this.clampPoint(t,tn).distanceTo(t)}getBoundingSphere(t){return this.isEmpty()?t.makeEmpty():(this.getCenter(t.center),t.radius=.5*this.getSize(tn).length()),t}intersect(t){return this.min.max(t.min),this.max.min(t.max),this.isEmpty()&&this.makeEmpty(),this}union(t){return this.min.min(t.min),this.max.max(t.max),this}applyMatrix4(t){return this.isEmpty()||(Kr[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(t),Kr[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(t),Kr[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(t),Kr[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(t),Kr[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(t),Kr[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(t),Kr[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(t),Kr[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(t),this.setFromPoints(Kr)),this}translate(t){return this.min.add(t),this.max.add(t),this}equals(t){return t.min.equals(this.min)&&t.max.equals(this.max)}toJSON(){return{min:this.min.toArray(),max:this.max.toArray()}}fromJSON(t){return this.min.fromArray(t.min),this.max.fromArray(t.max),this}}const Kr=[new Ti,new Ti,new Ti,new Ti,new Ti,new Ti,new Ti,new Ti],tn=new Ti,en=new Qr,sn=new Ti,rn=new Ti,nn=new Ti,an=new Ti,on=new Ti,hn=new Ti,ln=new Ti,cn=new Ti,un=new Ti,dn=new Ti;function pn(t,e,s,i,r){for(let n=0,a=t.length-3;n<=a;n+=3){dn.fromArray(t,n);const a=r.x*Math.abs(dn.x)+r.y*Math.abs(dn.y)+r.z*Math.abs(dn.z),o=e.dot(dn),h=s.dot(dn),l=i.dot(dn);if(Math.max(-Math.max(o,h,l),Math.min(o,h,l))>a)return!1}return!0}const mn=yn();function yn(){const t=new ArrayBuffer(4),e=new Float32Array(t),s=new Uint32Array(t),i=new Uint32Array(512),r=new Uint32Array(512);for(let t=0;t<256;++t){const e=t-127;e<-27?(i[t]=0,i[256|t]=32768,r[t]=24,r[256|t]=24):e<-14?(i[t]=1024>>-e-14,i[256|t]=1024>>-e-14|32768,r[t]=-e-1,r[256|t]=-e-1):e<=15?(i[t]=e+15<<10,i[256|t]=e+15<<10|32768,r[t]=13,r[256|t]=13):e<128?(i[t]=31744,i[256|t]=64512,r[t]=24,r[256|t]=24):(i[t]=31744,i[256|t]=64512,r[t]=13,r[256|t]=13)}const n=new Uint32Array(2048),a=new Uint32Array(64),o=new Uint32Array(64);for(let t=1;t<1024;++t){let e=t<<13,s=0;for(;!(8388608&e);)e<<=1,s-=8388608;e&=-8388609,s+=947912704,n[t]=e|s}for(let t=1024;t<2048;++t)n[t]=939524096+(t-1024<<13);for(let t=1;t<31;++t)a[t]=t<<23;a[31]=1199570944,a[32]=2147483648;for(let t=33;t<63;++t)a[t]=2147483648+(t-32<<23);a[63]=3347054592;for(let t=1;t<64;++t)32!==t&&(o[t]=1024);return{floatView:e,uint32View:s,baseTable:i,shiftTable:r,mantissaTable:n,exponentTable:a,offsetTable:o}}function gn(t){Math.abs(t)>65504&&ai("DataUtils.toHalfFloat(): Value out of range."),t=xi(t,-65504,65504),mn.floatView[0]=t;const e=mn.uint32View[0],s=e>>23&511;return mn.baseTable[s]+((8388607&e)>>mn.shiftTable[s])}function fn(t){const e=t>>10;return mn.uint32View[0]=mn.mantissaTable[mn.offsetTable[e]+(1023&t)]+mn.exponentTable[e],mn.floatView[0]}class xn{static toHalfFloat(t){return gn(t)}static fromHalfFloat(t){return fn(t)}}const bn=new Ti,vn=new _i;let wn=0;class Mn extends di{constructor(t,e,s=!1){if(super(),Array.isArray(t))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.isBufferAttribute=!0,Object.defineProperty(this,"id",{value:wn++}),this.name="",this.array=t,this.itemSize=e,this.count=void 0!==t?t.length/e:0,this.normalized=s,this.usage=Os,this.updateRanges=[],this.gpuType=Pt,this.version=0}onUploadCallback(){}set needsUpdate(t){!0===t&&this.version++}setUsage(t){return this.usage=t,this}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}copy(t){return this.name=t.name,this.array=new t.array.constructor(t.array),this.itemSize=t.itemSize,this.count=t.count,this.normalized=t.normalized,this.usage=t.usage,this.gpuType=t.gpuType,this}copyAt(t,e,s){t*=this.itemSize,s*=e.itemSize;for(let i=0,r=this.itemSize;ithis.radius*this.radius&&(e.sub(this.center).normalize(),e.multiplyScalar(this.radius).add(this.center)),e}getBoundingBox(t){return this.isEmpty()?(t.makeEmpty(),t):(t.set(this.center,this.center),t.expandByScalar(this.radius),t)}applyMatrix4(t){return this.center.applyMatrix4(t),this.radius=this.radius*t.getMaxScaleOnAxis(),this}translate(t){return this.center.add(t),this}expandByPoint(t){if(this.isEmpty())return this.center.copy(t),this.radius=0,this;Pn.subVectors(t,this.center);const e=Pn.lengthSq();if(e>this.radius*this.radius){const t=Math.sqrt(e),s=.5*(t-this.radius);this.center.addScaledVector(Pn,s/t),this.radius+=s}return this}union(t){return t.isEmpty()?this:this.isEmpty()?(this.copy(t),this):(!0===this.center.equals(t.center)?this.radius=Math.max(this.radius,t.radius):(Rn.subVectors(t.center,this.center).setLength(t.radius),this.expandByPoint(Pn.copy(t.center).add(Rn)),this.expandByPoint(Pn.copy(t.center).sub(Rn))),this)}equals(t){return t.center.equals(this.center)&&t.radius===this.radius}clone(){return(new this.constructor).copy(this)}toJSON(){return{radius:this.radius,center:this.center.toArray()}}fromJSON(t){return this.radius=t.radius,this.center.fromArray(t.center),this}}let Nn=0;const Vn=new Qi,Ln=new Ar,Fn=new Ti,Dn=new Qr,Un=new Qr,jn=new Ti;class Wn extends di{constructor(){super(),this.isBufferGeometry=!0,Object.defineProperty(this,"id",{value:Nn++}),this.uuid=fi(),this.name="",this.type="BufferGeometry",this.index=null,this.indirect=null,this.indirectOffset=0,this.attributes={},this.morphAttributes={},this.morphTargetsRelative=!1,this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0},this.userData={},this._transformed=!1}getIndex(){return this.index}setIndex(t){return Array.isArray(t)?this.index=new(function(t){for(let e=t.length-1;e>=0;--e)if(t[e]>=65535)return!0;return!1}(t)?In:zn)(t,1):this.index=t,this}setIndirect(t,e=0){return this.indirect=t,this.indirectOffset=e,this}getIndirect(){return this.indirect}getAttribute(t){return this.attributes[t]}setAttribute(t,e){return this.attributes[t]=e,this}deleteAttribute(t){return delete this.attributes[t],this}hasAttribute(t){return void 0!==this.attributes[t]}addGroup(t,e,s=0){this.groups.push({start:t,count:e,materialIndex:s})}clearGroups(){this.groups=[]}setDrawRange(t,e){this.drawRange.start=t,this.drawRange.count=e}applyMatrix4(t){const e=this.attributes.position;void 0!==e&&(e.applyMatrix4(t),e.needsUpdate=!0);const s=this.attributes.normal;if(void 0!==s){const e=(new Ii).getNormalMatrix(t);s.applyNormalMatrix(e),s.needsUpdate=!0}const i=this.attributes.tangent;return void 0!==i&&(i.transformDirection(t),i.needsUpdate=!0),null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere(),this._transformed=!0,this}applyQuaternion(t){return Vn.makeRotationFromQuaternion(t),this.applyMatrix4(Vn),this}rotateX(t){return Vn.makeRotationX(t),this.applyMatrix4(Vn),this}rotateY(t){return Vn.makeRotationY(t),this.applyMatrix4(Vn),this}rotateZ(t){return Vn.makeRotationZ(t),this.applyMatrix4(Vn),this}translate(t,e,s){return Vn.makeTranslation(t,e,s),this.applyMatrix4(Vn),this}scale(t,e,s){return Vn.makeScale(t,e,s),this.applyMatrix4(Vn),this}lookAt(t){return Ln.lookAt(t),Ln.updateMatrix(),this.applyMatrix4(Ln.matrix),this}center(){return this.computeBoundingBox(),this.boundingBox.getCenter(Fn).negate(),this.translate(Fn.x,Fn.y,Fn.z),this}setFromPoints(t){const e=this.getAttribute("position");if(void 0===e){const e=[];for(let s=0,i=t.length;se.count&&ai("BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry."),e.needsUpdate=!0}return this}computeBoundingBox(){null===this.boundingBox&&(this.boundingBox=new Qr);const t=this.attributes.position,e=this.morphAttributes.position;if(t&&t.isGLBufferAttribute)return oi("BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.",this),void this.boundingBox.set(new Ti(-1/0,-1/0,-1/0),new Ti(1/0,1/0,1/0));if(void 0!==t){if(this.boundingBox.setFromBufferAttribute(t),e)for(let t=0,s=e.length;t0&&(t.userData=this.userData),void 0!==this.parameters&&!0!==this._transformed){const e=this.parameters;for(const s in e)void 0!==e[s]&&(t[s]=e[s]);return t}t.data={attributes:{}};const e=this.index;null!==e&&(t.data.index={type:e.array.constructor.name,array:Array.prototype.slice.call(e.array)});const s=this.attributes;for(const e in s){const i=s[e];t.data.attributes[e]=i.toJSON(t.data)}const i={};let r=!1;for(const e in this.morphAttributes){const s=this.morphAttributes[e],n=[];for(let e=0,i=s.length;e0&&(i[e]=n,r=!0)}r&&(t.data.morphAttributes=i,t.data.morphTargetsRelative=this.morphTargetsRelative);const n=this.groups;n.length>0&&(t.data.groups=JSON.parse(JSON.stringify(n)));const a=this.boundingSphere;return null!==a&&(t.data.boundingSphere=a.toJSON()),t}clone(){return(new this.constructor).copy(this)}copy(t){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;const e={};this.name=t.name;const s=t.index;null!==s&&this.setIndex(s.clone());const i=t.attributes;for(const t in i){const s=i[t];this.setAttribute(t,s.clone(e))}const r=t.morphAttributes;for(const t in r){const s=[],i=r[t];for(let t=0,r=i.length;t0!=t>0&&this.version++,this._alphaTest=t}onBeforeRender(){}onBeforeCompile(){}customProgramCacheKey(){return this.onBeforeCompile.toString()}setValues(t){if(void 0!==t)for(const e in t){const s=t[e];if(void 0===s){ai(`Material: parameter '${e}' has value of undefined.`);continue}const i=this[e];void 0!==i?i&&i.isColor?i.set(s):i&&i.isVector2&&s&&s.isVector2||i&&i.isEuler&&s&&s.isEuler||i&&i.isVector3&&s&&s.isVector3?i.copy(s):this[e]=s:ai(`Material: '${e}' is not a property of THREE.${this.type}.`)}}toJSON(t){const e=void 0===t||"string"==typeof t;e&&(t={textures:{},images:{}});const s={metadata:{version:4.7,type:"Material",generator:"Material.toJSON"}};function i(t){const e=[];for(const s in t){const i=t[s];delete i.metadata,e.push(i)}return e}if(s.uuid=this.uuid,s.type=this.type,""!==this.name&&(s.name=this.name),this.color&&this.color.isColor&&(s.color=this.color.getHex()),void 0!==this.roughness&&(s.roughness=this.roughness),void 0!==this.metalness&&(s.metalness=this.metalness),void 0!==this.sheen&&(s.sheen=this.sheen),this.sheenColor&&this.sheenColor.isColor&&(s.sheenColor=this.sheenColor.getHex()),void 0!==this.sheenRoughness&&(s.sheenRoughness=this.sheenRoughness),this.emissive&&this.emissive.isColor&&(s.emissive=this.emissive.getHex()),void 0!==this.emissiveIntensity&&1!==this.emissiveIntensity&&(s.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(s.specular=this.specular.getHex()),void 0!==this.specularIntensity&&(s.specularIntensity=this.specularIntensity),this.specularColor&&this.specularColor.isColor&&(s.specularColor=this.specularColor.getHex()),void 0!==this.shininess&&(s.shininess=this.shininess),void 0!==this.clearcoat&&(s.clearcoat=this.clearcoat),void 0!==this.clearcoatRoughness&&(s.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(s.clearcoatMap=this.clearcoatMap.toJSON(t).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(s.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(t).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(s.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(t).uuid,s.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.sheenColorMap&&this.sheenColorMap.isTexture&&(s.sheenColorMap=this.sheenColorMap.toJSON(t).uuid),this.sheenRoughnessMap&&this.sheenRoughnessMap.isTexture&&(s.sheenRoughnessMap=this.sheenRoughnessMap.toJSON(t).uuid),void 0!==this.dispersion&&(s.dispersion=this.dispersion),void 0!==this.iridescence&&(s.iridescence=this.iridescence),void 0!==this.iridescenceIOR&&(s.iridescenceIOR=this.iridescenceIOR),void 0!==this.iridescenceThicknessRange&&(s.iridescenceThicknessRange=this.iridescenceThicknessRange),this.iridescenceMap&&this.iridescenceMap.isTexture&&(s.iridescenceMap=this.iridescenceMap.toJSON(t).uuid),this.iridescenceThicknessMap&&this.iridescenceThicknessMap.isTexture&&(s.iridescenceThicknessMap=this.iridescenceThicknessMap.toJSON(t).uuid),void 0!==this.anisotropy&&(s.anisotropy=this.anisotropy),void 0!==this.anisotropyRotation&&(s.anisotropyRotation=this.anisotropyRotation),this.anisotropyMap&&this.anisotropyMap.isTexture&&(s.anisotropyMap=this.anisotropyMap.toJSON(t).uuid),this.map&&this.map.isTexture&&(s.map=this.map.toJSON(t).uuid),this.matcap&&this.matcap.isTexture&&(s.matcap=this.matcap.toJSON(t).uuid),this.alphaMap&&this.alphaMap.isTexture&&(s.alphaMap=this.alphaMap.toJSON(t).uuid),this.lightMap&&this.lightMap.isTexture&&(s.lightMap=this.lightMap.toJSON(t).uuid,s.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(s.aoMap=this.aoMap.toJSON(t).uuid,s.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(s.bumpMap=this.bumpMap.toJSON(t).uuid,s.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(s.normalMap=this.normalMap.toJSON(t).uuid,s.normalMapType=this.normalMapType,s.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(s.displacementMap=this.displacementMap.toJSON(t).uuid,s.displacementScale=this.displacementScale,s.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(s.roughnessMap=this.roughnessMap.toJSON(t).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(s.metalnessMap=this.metalnessMap.toJSON(t).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(s.emissiveMap=this.emissiveMap.toJSON(t).uuid),this.specularMap&&this.specularMap.isTexture&&(s.specularMap=this.specularMap.toJSON(t).uuid),this.specularIntensityMap&&this.specularIntensityMap.isTexture&&(s.specularIntensityMap=this.specularIntensityMap.toJSON(t).uuid),this.specularColorMap&&this.specularColorMap.isTexture&&(s.specularColorMap=this.specularColorMap.toJSON(t).uuid),this.envMap&&this.envMap.isTexture&&(s.envMap=this.envMap.toJSON(t).uuid,void 0!==this.combine&&(s.combine=this.combine)),void 0!==this.envMapRotation&&(s.envMapRotation=this.envMapRotation.toArray()),void 0!==this.envMapIntensity&&(s.envMapIntensity=this.envMapIntensity),void 0!==this.reflectivity&&(s.reflectivity=this.reflectivity),void 0!==this.refractionRatio&&(s.refractionRatio=this.refractionRatio),this.gradientMap&&this.gradientMap.isTexture&&(s.gradientMap=this.gradientMap.toJSON(t).uuid),void 0!==this.transmission&&(s.transmission=this.transmission),this.transmissionMap&&this.transmissionMap.isTexture&&(s.transmissionMap=this.transmissionMap.toJSON(t).uuid),void 0!==this.thickness&&(s.thickness=this.thickness),this.thicknessMap&&this.thicknessMap.isTexture&&(s.thicknessMap=this.thicknessMap.toJSON(t).uuid),void 0!==this.attenuationDistance&&this.attenuationDistance!==1/0&&(s.attenuationDistance=this.attenuationDistance),void 0!==this.attenuationColor&&(s.attenuationColor=this.attenuationColor.getHex()),void 0!==this.size&&(s.size=this.size),null!==this.shadowSide&&(s.shadowSide=this.shadowSide),void 0!==this.sizeAttenuation&&(s.sizeAttenuation=this.sizeAttenuation),1!==this.blending&&(s.blending=this.blending),0!==this.side&&(s.side=this.side),!0===this.vertexColors&&(s.vertexColors=!0),this.opacity<1&&(s.opacity=this.opacity),!0===this.transparent&&(s.transparent=!0),204!==this.blendSrc&&(s.blendSrc=this.blendSrc),205!==this.blendDst&&(s.blendDst=this.blendDst),100!==this.blendEquation&&(s.blendEquation=this.blendEquation),null!==this.blendSrcAlpha&&(s.blendSrcAlpha=this.blendSrcAlpha),null!==this.blendDstAlpha&&(s.blendDstAlpha=this.blendDstAlpha),null!==this.blendEquationAlpha&&(s.blendEquationAlpha=this.blendEquationAlpha),this.blendColor&&this.blendColor.isColor&&(s.blendColor=this.blendColor.getHex()),0!==this.blendAlpha&&(s.blendAlpha=this.blendAlpha),3!==this.depthFunc&&(s.depthFunc=this.depthFunc),!1===this.depthTest&&(s.depthTest=this.depthTest),!1===this.depthWrite&&(s.depthWrite=this.depthWrite),!1===this.colorWrite&&(s.colorWrite=this.colorWrite),255!==this.stencilWriteMask&&(s.stencilWriteMask=this.stencilWriteMask),519!==this.stencilFunc&&(s.stencilFunc=this.stencilFunc),0!==this.stencilRef&&(s.stencilRef=this.stencilRef),255!==this.stencilFuncMask&&(s.stencilFuncMask=this.stencilFuncMask),this.stencilFail!==ls&&(s.stencilFail=this.stencilFail),this.stencilZFail!==ls&&(s.stencilZFail=this.stencilZFail),this.stencilZPass!==ls&&(s.stencilZPass=this.stencilZPass),!0===this.stencilWrite&&(s.stencilWrite=this.stencilWrite),void 0!==this.rotation&&0!==this.rotation&&(s.rotation=this.rotation),!0===this.polygonOffset&&(s.polygonOffset=!0),0!==this.polygonOffsetFactor&&(s.polygonOffsetFactor=this.polygonOffsetFactor),0!==this.polygonOffsetUnits&&(s.polygonOffsetUnits=this.polygonOffsetUnits),void 0!==this.linewidth&&1!==this.linewidth&&(s.linewidth=this.linewidth),void 0!==this.dashSize&&(s.dashSize=this.dashSize),void 0!==this.gapSize&&(s.gapSize=this.gapSize),void 0!==this.scale&&(s.scale=this.scale),!0===this.dithering&&(s.dithering=!0),this.alphaTest>0&&(s.alphaTest=this.alphaTest),!0===this.alphaHash&&(s.alphaHash=!0),!0===this.alphaToCoverage&&(s.alphaToCoverage=!0),!0===this.premultipliedAlpha&&(s.premultipliedAlpha=!0),!0===this.forceSinglePass&&(s.forceSinglePass=!0),!1===this.allowOverride&&(s.allowOverride=!1),!0===this.wireframe&&(s.wireframe=!0),this.wireframeLinewidth>1&&(s.wireframeLinewidth=this.wireframeLinewidth),"round"!==this.wireframeLinecap&&(s.wireframeLinecap=this.wireframeLinecap),"round"!==this.wireframeLinejoin&&(s.wireframeLinejoin=this.wireframeLinejoin),!0===this.flatShading&&(s.flatShading=!0),!1===this.visible&&(s.visible=!1),!1===this.toneMapped&&(s.toneMapped=!1),!1===this.fog&&(s.fog=!1),Object.keys(this.userData).length>0&&(s.userData=this.userData),e){const e=i(t.textures),r=i(t.images);e.length>0&&(s.textures=e),r.length>0&&(s.images=r)}return s}fromJSON(t,e){if(void 0!==t.uuid&&(this.uuid=t.uuid),void 0!==t.name&&(this.name=t.name),void 0!==t.color&&void 0!==this.color&&this.color.setHex(t.color),void 0!==t.roughness&&(this.roughness=t.roughness),void 0!==t.metalness&&(this.metalness=t.metalness),void 0!==t.sheen&&(this.sheen=t.sheen),void 0!==t.sheenColor&&(this.sheenColor=(new Pr).setHex(t.sheenColor)),void 0!==t.sheenRoughness&&(this.sheenRoughness=t.sheenRoughness),void 0!==t.emissive&&void 0!==this.emissive&&this.emissive.setHex(t.emissive),void 0!==t.specular&&void 0!==this.specular&&this.specular.setHex(t.specular),void 0!==t.specularIntensity&&(this.specularIntensity=t.specularIntensity),void 0!==t.specularColor&&void 0!==this.specularColor&&this.specularColor.setHex(t.specularColor),void 0!==t.shininess&&(this.shininess=t.shininess),void 0!==t.clearcoat&&(this.clearcoat=t.clearcoat),void 0!==t.clearcoatRoughness&&(this.clearcoatRoughness=t.clearcoatRoughness),void 0!==t.dispersion&&(this.dispersion=t.dispersion),void 0!==t.iridescence&&(this.iridescence=t.iridescence),void 0!==t.iridescenceIOR&&(this.iridescenceIOR=t.iridescenceIOR),void 0!==t.iridescenceThicknessRange&&(this.iridescenceThicknessRange=t.iridescenceThicknessRange),void 0!==t.transmission&&(this.transmission=t.transmission),void 0!==t.thickness&&(this.thickness=t.thickness),void 0!==t.attenuationDistance&&(this.attenuationDistance=t.attenuationDistance),void 0!==t.attenuationColor&&void 0!==this.attenuationColor&&this.attenuationColor.setHex(t.attenuationColor),void 0!==t.anisotropy&&(this.anisotropy=t.anisotropy),void 0!==t.anisotropyRotation&&(this.anisotropyRotation=t.anisotropyRotation),void 0!==t.fog&&(this.fog=t.fog),void 0!==t.flatShading&&(this.flatShading=t.flatShading),void 0!==t.blending&&(this.blending=t.blending),void 0!==t.combine&&(this.combine=t.combine),void 0!==t.side&&(this.side=t.side),void 0!==t.shadowSide&&(this.shadowSide=t.shadowSide),void 0!==t.opacity&&(this.opacity=t.opacity),void 0!==t.transparent&&(this.transparent=t.transparent),void 0!==t.alphaTest&&(this.alphaTest=t.alphaTest),void 0!==t.alphaHash&&(this.alphaHash=t.alphaHash),void 0!==t.depthFunc&&(this.depthFunc=t.depthFunc),void 0!==t.depthTest&&(this.depthTest=t.depthTest),void 0!==t.depthWrite&&(this.depthWrite=t.depthWrite),void 0!==t.colorWrite&&(this.colorWrite=t.colorWrite),void 0!==t.blendSrc&&(this.blendSrc=t.blendSrc),void 0!==t.blendDst&&(this.blendDst=t.blendDst),void 0!==t.blendEquation&&(this.blendEquation=t.blendEquation),void 0!==t.blendSrcAlpha&&(this.blendSrcAlpha=t.blendSrcAlpha),void 0!==t.blendDstAlpha&&(this.blendDstAlpha=t.blendDstAlpha),void 0!==t.blendEquationAlpha&&(this.blendEquationAlpha=t.blendEquationAlpha),void 0!==t.blendColor&&void 0!==this.blendColor&&this.blendColor.setHex(t.blendColor),void 0!==t.blendAlpha&&(this.blendAlpha=t.blendAlpha),void 0!==t.stencilWriteMask&&(this.stencilWriteMask=t.stencilWriteMask),void 0!==t.stencilFunc&&(this.stencilFunc=t.stencilFunc),void 0!==t.stencilRef&&(this.stencilRef=t.stencilRef),void 0!==t.stencilFuncMask&&(this.stencilFuncMask=t.stencilFuncMask),void 0!==t.stencilFail&&(this.stencilFail=t.stencilFail),void 0!==t.stencilZFail&&(this.stencilZFail=t.stencilZFail),void 0!==t.stencilZPass&&(this.stencilZPass=t.stencilZPass),void 0!==t.stencilWrite&&(this.stencilWrite=t.stencilWrite),void 0!==t.wireframe&&(this.wireframe=t.wireframe),void 0!==t.wireframeLinewidth&&(this.wireframeLinewidth=t.wireframeLinewidth),void 0!==t.wireframeLinecap&&(this.wireframeLinecap=t.wireframeLinecap),void 0!==t.wireframeLinejoin&&(this.wireframeLinejoin=t.wireframeLinejoin),void 0!==t.rotation&&(this.rotation=t.rotation),void 0!==t.linewidth&&(this.linewidth=t.linewidth),void 0!==t.dashSize&&(this.dashSize=t.dashSize),void 0!==t.gapSize&&(this.gapSize=t.gapSize),void 0!==t.scale&&(this.scale=t.scale),void 0!==t.polygonOffset&&(this.polygonOffset=t.polygonOffset),void 0!==t.polygonOffsetFactor&&(this.polygonOffsetFactor=t.polygonOffsetFactor),void 0!==t.polygonOffsetUnits&&(this.polygonOffsetUnits=t.polygonOffsetUnits),void 0!==t.dithering&&(this.dithering=t.dithering),void 0!==t.alphaToCoverage&&(this.alphaToCoverage=t.alphaToCoverage),void 0!==t.premultipliedAlpha&&(this.premultipliedAlpha=t.premultipliedAlpha),void 0!==t.forceSinglePass&&(this.forceSinglePass=t.forceSinglePass),void 0!==t.allowOverride&&(this.allowOverride=t.allowOverride),void 0!==t.visible&&(this.visible=t.visible),void 0!==t.toneMapped&&(this.toneMapped=t.toneMapped),void 0!==t.userData&&(this.userData=t.userData),void 0!==t.vertexColors&&("number"==typeof t.vertexColors?this.vertexColors=t.vertexColors>0:this.vertexColors=t.vertexColors),void 0!==t.size&&(this.size=t.size),void 0!==t.sizeAttenuation&&(this.sizeAttenuation=t.sizeAttenuation),void 0!==t.map&&(this.map=e[t.map]||null),void 0!==t.matcap&&(this.matcap=e[t.matcap]||null),void 0!==t.alphaMap&&(this.alphaMap=e[t.alphaMap]||null),void 0!==t.bumpMap&&(this.bumpMap=e[t.bumpMap]||null),void 0!==t.bumpScale&&(this.bumpScale=t.bumpScale),void 0!==t.normalMap&&(this.normalMap=e[t.normalMap]||null),void 0!==t.normalMapType&&(this.normalMapType=t.normalMapType),void 0!==t.normalScale){let e=t.normalScale;!1===Array.isArray(e)&&(e=[e,e]),this.normalScale=(new _i).fromArray(e)}return void 0!==t.displacementMap&&(this.displacementMap=e[t.displacementMap]||null),void 0!==t.displacementScale&&(this.displacementScale=t.displacementScale),void 0!==t.displacementBias&&(this.displacementBias=t.displacementBias),void 0!==t.roughnessMap&&(this.roughnessMap=e[t.roughnessMap]||null),void 0!==t.metalnessMap&&(this.metalnessMap=e[t.metalnessMap]||null),void 0!==t.emissiveMap&&(this.emissiveMap=e[t.emissiveMap]||null),void 0!==t.emissiveIntensity&&(this.emissiveIntensity=t.emissiveIntensity),void 0!==t.specularMap&&(this.specularMap=e[t.specularMap]||null),void 0!==t.specularIntensityMap&&(this.specularIntensityMap=e[t.specularIntensityMap]||null),void 0!==t.specularColorMap&&(this.specularColorMap=e[t.specularColorMap]||null),void 0!==t.envMap&&(this.envMap=e[t.envMap]||null),void 0!==t.envMapRotation&&this.envMapRotation.fromArray(t.envMapRotation),void 0!==t.envMapIntensity&&(this.envMapIntensity=t.envMapIntensity),void 0!==t.reflectivity&&(this.reflectivity=t.reflectivity),void 0!==t.refractionRatio&&(this.refractionRatio=t.refractionRatio),void 0!==t.lightMap&&(this.lightMap=e[t.lightMap]||null),void 0!==t.lightMapIntensity&&(this.lightMapIntensity=t.lightMapIntensity),void 0!==t.aoMap&&(this.aoMap=e[t.aoMap]||null),void 0!==t.aoMapIntensity&&(this.aoMapIntensity=t.aoMapIntensity),void 0!==t.gradientMap&&(this.gradientMap=e[t.gradientMap]||null),void 0!==t.clearcoatMap&&(this.clearcoatMap=e[t.clearcoatMap]||null),void 0!==t.clearcoatRoughnessMap&&(this.clearcoatRoughnessMap=e[t.clearcoatRoughnessMap]||null),void 0!==t.clearcoatNormalMap&&(this.clearcoatNormalMap=e[t.clearcoatNormalMap]||null),void 0!==t.clearcoatNormalScale&&(this.clearcoatNormalScale=(new _i).fromArray(t.clearcoatNormalScale)),void 0!==t.iridescenceMap&&(this.iridescenceMap=e[t.iridescenceMap]||null),void 0!==t.iridescenceThicknessMap&&(this.iridescenceThicknessMap=e[t.iridescenceThicknessMap]||null),void 0!==t.transmissionMap&&(this.transmissionMap=e[t.transmissionMap]||null),void 0!==t.thicknessMap&&(this.thicknessMap=e[t.thicknessMap]||null),void 0!==t.anisotropyMap&&(this.anisotropyMap=e[t.anisotropyMap]||null),void 0!==t.sheenColorMap&&(this.sheenColorMap=e[t.sheenColorMap]||null),void 0!==t.sheenRoughnessMap&&(this.sheenRoughnessMap=e[t.sheenRoughnessMap]||null),this}clone(){return(new this.constructor).copy(this)}copy(t){this.name=t.name,this.blending=t.blending,this.side=t.side,this.vertexColors=t.vertexColors,this.opacity=t.opacity,this.transparent=t.transparent,this.blendSrc=t.blendSrc,this.blendDst=t.blendDst,this.blendEquation=t.blendEquation,this.blendSrcAlpha=t.blendSrcAlpha,this.blendDstAlpha=t.blendDstAlpha,this.blendEquationAlpha=t.blendEquationAlpha,this.blendColor.copy(t.blendColor),this.blendAlpha=t.blendAlpha,this.depthFunc=t.depthFunc,this.depthTest=t.depthTest,this.depthWrite=t.depthWrite,this.stencilWriteMask=t.stencilWriteMask,this.stencilFunc=t.stencilFunc,this.stencilRef=t.stencilRef,this.stencilFuncMask=t.stencilFuncMask,this.stencilFail=t.stencilFail,this.stencilZFail=t.stencilZFail,this.stencilZPass=t.stencilZPass,this.stencilWrite=t.stencilWrite;const e=t.clippingPlanes;let s=null;if(null!==e){const t=e.length;s=new Array(t);for(let i=0;i!==t;++i)s[i]=e[i].clone()}return this.clippingPlanes=s,this.clipIntersection=t.clipIntersection,this.clipShadows=t.clipShadows,this.shadowSide=t.shadowSide,this.colorWrite=t.colorWrite,this.precision=t.precision,this.polygonOffset=t.polygonOffset,this.polygonOffsetFactor=t.polygonOffsetFactor,this.polygonOffsetUnits=t.polygonOffsetUnits,this.dithering=t.dithering,this.alphaTest=t.alphaTest,this.alphaHash=t.alphaHash,this.alphaToCoverage=t.alphaToCoverage,this.premultipliedAlpha=t.premultipliedAlpha,this.forceSinglePass=t.forceSinglePass,this.allowOverride=t.allowOverride,this.visible=t.visible,this.toneMapped=t.toneMapped,this.userData=JSON.parse(JSON.stringify(t.userData)),this}dispose(){this.dispatchEvent({type:"dispose"})}set needsUpdate(t){!0===t&&this.version++}}class Gn extends Zn{constructor(t){super(),this.isSpriteMaterial=!0,this.type="SpriteMaterial",this.color=new Pr(16777215),this.map=null,this.alphaMap=null,this.rotation=0,this.sizeAttenuation=!0,this.transparent=!0,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.alphaMap=t.alphaMap,this.rotation=t.rotation,this.sizeAttenuation=t.sizeAttenuation,this.fog=t.fog,this}}const $n=new Ti,Qn=new Ti,Kn=new Ti,ta=new _i,ea=new _i,sa=new Qi,ia=new Ti,ra=new Ti,na=new Ti,aa=new _i,oa=new _i,ha=new _i;class la extends Ar{constructor(t=new Gn){if(super(),this.isSprite=!0,this.type="Sprite",void 0===Xn){Xn=new Wn;const t=new Float32Array([-.5,-.5,0,0,0,.5,-.5,0,1,0,.5,.5,0,1,1,-.5,.5,0,0,1]),e=new Jn(t,5);Xn.setIndex([0,1,2,0,2,3]),Xn.setAttribute("position",new Hn(e,3,0,!1)),Xn.setAttribute("uv",new Hn(e,2,3,!1))}this.geometry=Xn,this.material=t,this.center=new _i(.5,.5),this.count=1}raycast(t,e){null===t.camera&&oi('Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.'),Qn.setFromMatrixScale(this.matrixWorld),sa.copy(t.camera.matrixWorld),this.modelViewMatrix.multiplyMatrices(t.camera.matrixWorldInverse,this.matrixWorld),Kn.setFromMatrixPosition(this.modelViewMatrix),t.camera.isPerspectiveCamera&&!1===this.material.sizeAttenuation&&Qn.multiplyScalar(-Kn.z);const s=this.material.rotation;let i,r;0!==s&&(r=Math.cos(s),i=Math.sin(s));const n=this.center;ca(ia.set(-.5,-.5,0),Kn,n,Qn,i,r),ca(ra.set(.5,-.5,0),Kn,n,Qn,i,r),ca(na.set(.5,.5,0),Kn,n,Qn,i,r),aa.set(0,0),oa.set(1,0),ha.set(1,1);let a=t.ray.intersectTriangle(ia,ra,na,!1,$n);if(null===a&&(ca(ra.set(-.5,.5,0),Kn,n,Qn,i,r),oa.set(0,1),a=t.ray.intersectTriangle(ia,na,ra,!1,$n),null===a))return;const o=t.ray.origin.distanceTo($n);ot.far||e.push({distance:o,point:$n.clone(),uv:$r.getInterpolation($n,ia,ra,na,aa,oa,ha,new _i),face:null,object:this})}copy(t,e){return super.copy(t,e),void 0!==t.center&&this.center.copy(t.center),this.material=t.material,this}}function ca(t,e,s,i,r,n){ta.subVectors(t,s).addScalar(.5).multiply(i),void 0!==r?(ea.x=n*ta.x-r*ta.y,ea.y=r*ta.x+n*ta.y):ea.copy(ta),t.copy(e),t.x+=ea.x,t.y+=ea.y,t.applyMatrix4(sa)}const ua=new Ti,da=new Ti;class pa extends Ar{constructor(){super(),this.isLOD=!0,this._currentLevel=0,this.type="LOD",Object.defineProperties(this,{levels:{enumerable:!0,value:[]}}),this.autoUpdate=!0}copy(t){super.copy(t,!1);const e=t.levels;for(let t=0,s=e.length;t0){let s,i;for(s=1,i=e.length;s0){ua.setFromMatrixPosition(this.matrixWorld);const s=t.ray.origin.distanceTo(ua);this.getObjectForDistance(s).raycast(t,e)}}update(t){const e=this.levels;if(e.length>1){ua.setFromMatrixPosition(t.matrixWorld),da.setFromMatrixPosition(this.matrixWorld);const s=ua.distanceTo(da)/t.zoom;let i,r;for(e[0].object.visible=!0,i=1,r=e.length;i=t))break;e[i-1].object.visible=!1,e[i].object.visible=!0}for(this._currentLevel=i-1;i0)if(c=n*o-a,u=n*a-o,p=r*l,c>=0)if(u>=-p)if(u<=p){const t=1/l;c*=t,u*=t,d=c*(c+n*u+2*a)+u*(n*c+u+2*o)+h}else u=r,c=Math.max(0,-(n*u+a)),d=-c*c+u*(u+2*o)+h;else u=-r,c=Math.max(0,-(n*u+a)),d=-c*c+u*(u+2*o)+h;else u<=-p?(c=Math.max(0,-(-n*r+a)),u=c>0?-r:Math.min(Math.max(-r,-o),r),d=-c*c+u*(u+2*o)+h):u<=p?(c=0,u=Math.min(Math.max(-r,-o),r),d=u*(u+2*o)+h):(c=Math.max(0,-(n*r+a)),u=c>0?r:Math.min(Math.max(-r,-o),r),d=-c*c+u*(u+2*o)+h);else u=n>0?-r:r,c=Math.max(0,-(n*u+a)),d=-c*c+u*(u+2*o)+h;return s&&s.copy(this.origin).addScaledVector(this.direction,c),i&&i.copy(ya).addScaledVector(ga,u),d}intersectSphere(t,e){ma.subVectors(t.center,this.origin);const s=ma.dot(this.direction),i=ma.dot(ma)-s*s,r=t.radius*t.radius;if(i>r)return null;const n=Math.sqrt(r-i),a=s-n,o=s+n;return o<0?null:a<0?this.at(o,e):this.at(a,e)}intersectsSphere(t){return!(t.radius<0)&&this.distanceSqToPoint(t.center)<=t.radius*t.radius}distanceToPlane(t){const e=t.normal.dot(this.direction);if(0===e)return 0===t.distanceToPoint(this.origin)?0:null;const s=-(this.origin.dot(t.normal)+t.constant)/e;return s>=0?s:null}intersectPlane(t,e){const s=this.distanceToPlane(t);return null===s?null:this.at(s,e)}intersectsPlane(t){const e=t.distanceToPoint(this.origin);if(0===e)return!0;return t.normal.dot(this.direction)*e<0}intersectBox(t,e){let s,i,r,n,a,o;const h=1/this.direction.x,l=1/this.direction.y,c=1/this.direction.z,u=this.origin;return h>=0?(s=(t.min.x-u.x)*h,i=(t.max.x-u.x)*h):(s=(t.max.x-u.x)*h,i=(t.min.x-u.x)*h),l>=0?(r=(t.min.y-u.y)*l,n=(t.max.y-u.y)*l):(r=(t.max.y-u.y)*l,n=(t.min.y-u.y)*l),s>n||r>i?null:((r>s||isNaN(s))&&(s=r),(n=0?(a=(t.min.z-u.z)*c,o=(t.max.z-u.z)*c):(a=(t.max.z-u.z)*c,o=(t.min.z-u.z)*c),s>o||a>i?null:((a>s||s!=s)&&(s=a),(o=0?s:i,e)))}intersectsBox(t){return null!==this.intersectBox(t,ma)}intersectTriangle(t,e,s,i,r){xa.subVectors(e,t),ba.subVectors(s,t),va.crossVectors(xa,ba);let n,a=this.direction.dot(va);if(a>0){if(i)return null;n=1}else{if(!(a<0))return null;n=-1,a=-a}fa.subVectors(this.origin,t);const o=n*this.direction.dot(ba.crossVectors(fa,ba));if(o<0)return null;const h=n*this.direction.dot(xa.cross(fa));if(h<0)return null;if(o+h>a)return null;const l=-n*fa.dot(va);return l<0?null:this.at(l/a,r)}applyMatrix4(t){return this.origin.applyMatrix4(t),this.direction.transformDirection(t),this}equals(t){return t.origin.equals(this.origin)&&t.direction.equals(this.direction)}clone(){return(new this.constructor).copy(this)}}class Ma extends Zn{constructor(t){super(),this.isMeshBasicMaterial=!0,this.type="MeshBasicMaterial",this.color=new Pr(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new hr,this.combine=0,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.fog=t.fog,this}}const Sa=new Qi,_a=new wa,Aa=new En,Ta=new Ti,za=new Ti,Ca=new Ti,Ia=new Ti,Ba=new Ti,ka=new Ti,Oa=new Ti,Pa=new Ti;class Ra extends Ar{constructor(t=new Wn,e=new Ma){super(),this.isMesh=!0,this.type="Mesh",this.geometry=t,this.material=e,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.count=1,this.updateMorphTargets()}copy(t,e){return super.copy(t,e),void 0!==t.morphTargetInfluences&&(this.morphTargetInfluences=t.morphTargetInfluences.slice()),void 0!==t.morphTargetDictionary&&(this.morphTargetDictionary=Object.assign({},t.morphTargetDictionary)),this.material=Array.isArray(t.material)?t.material.slice():t.material,this.geometry=t.geometry,this}updateMorphTargets(){const t=this.geometry.morphAttributes,e=Object.keys(t);if(e.length>0){const s=t[e[0]];if(void 0!==s){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let t=0,e=s.length;t(t.far-t.near)**2)return}Sa.copy(r).invert(),_a.copy(t.ray).applyMatrix4(Sa),null!==s.boundingBox&&!1===_a.intersectsBox(s.boundingBox)||this._computeIntersections(t,e,_a)}}_computeIntersections(t,e,s){let i;const r=this.geometry,n=this.material,a=r.index,o=r.attributes.position,h=r.attributes.uv,l=r.attributes.uv1,c=r.attributes.normal,u=r.groups,d=r.drawRange;if(null!==a)if(Array.isArray(n))for(let r=0,o=u.length;rs.far?null:{distance:l,point:Pa.clone(),object:t}}(t,e,s,i,za,Ca,Ia,Oa);if(c){const t=new Ti;$r.getBarycoord(Oa,za,Ca,Ia,t),r&&(c.uv=$r.getInterpolatedAttribute(r,o,h,l,t,new _i)),n&&(c.uv1=$r.getInterpolatedAttribute(n,o,h,l,t,new _i)),a&&(c.normal=$r.getInterpolatedAttribute(a,o,h,l,t,new Ti),c.normal.dot(i.direction)>0&&c.normal.multiplyScalar(-1));const e={a:o,b:h,c:l,normal:new Ti,materialIndex:0};$r.getNormal(za,Ca,Ia,e.normal),c.face=e,c.barycoord=t}return c}const Na=new qi,Va=new qi,La=new qi,Fa=new qi,Da=new Qi,Ua=new Ti,ja=new En,Wa=new Qi,Ja=new wa;class qa extends Ra{constructor(t,e){super(t,e),this.isSkinnedMesh=!0,this.type="SkinnedMesh",this.bindMode=at,this.bindMatrix=new Qi,this.bindMatrixInverse=new Qi,this.boundingBox=null,this.boundingSphere=null}computeBoundingBox(){const t=this.geometry;null===this.boundingBox&&(this.boundingBox=new Qr),this.boundingBox.makeEmpty();const e=t.getAttribute("position");for(let t=0;t1)?null:e.copy(t.start).addScaledVector(i,n)}intersectsLine(t){const e=this.distanceToPoint(t.start),s=this.distanceToPoint(t.end);return e<0&&s>0||s<0&&e>0}intersectsBox(t){return t.intersectsPlane(this)}intersectsSphere(t){return t.intersectsPlane(this)}coplanarPoint(t){return t.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(t,e){const s=e||ho.getNormalMatrix(t),i=this.coplanarPoint(ao).applyMatrix4(t),r=this.normal.applyMatrix3(s).normalize();return this.constant=-i.dot(r),this}translate(t){return this.constant-=t.dot(this.normal),this}equals(t){return t.normal.equals(this.normal)&&t.constant===this.constant}clone(){return(new this.constructor).copy(this)}}const co=new En,uo=new _i(.5,.5),po=new Ti;class mo{constructor(t=new lo,e=new lo,s=new lo,i=new lo,r=new lo,n=new lo){this.planes=[t,e,s,i,r,n]}set(t,e,s,i,r,n){const a=this.planes;return a[0].copy(t),a[1].copy(e),a[2].copy(s),a[3].copy(i),a[4].copy(r),a[5].copy(n),this}copy(t){const e=this.planes;for(let s=0;s<6;s++)e[s].copy(t.planes[s]);return this}setFromProjectionMatrix(t,e=2e3,s=!1){const i=this.planes,r=t.elements,n=r[0],a=r[1],o=r[2],h=r[3],l=r[4],c=r[5],u=r[6],d=r[7],p=r[8],m=r[9],y=r[10],g=r[11],f=r[12],x=r[13],b=r[14],v=r[15];if(i[0].setComponents(h-n,d-l,g-p,v-f).normalize(),i[1].setComponents(h+n,d+l,g+p,v+f).normalize(),i[2].setComponents(h+a,d+c,g+m,v+x).normalize(),i[3].setComponents(h-a,d-c,g-m,v-x).normalize(),s)i[4].setComponents(o,u,y,b).normalize(),i[5].setComponents(h-o,d-u,g-y,v-b).normalize();else if(i[4].setComponents(h-o,d-u,g-y,v-b).normalize(),e===Ws)i[5].setComponents(h+o,d+u,g+y,v+b).normalize();else{if(e!==Js)throw new Error("THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: "+e);i[5].setComponents(o,u,y,b).normalize()}return this}intersectsObject(t){if(void 0!==t.boundingSphere)null===t.boundingSphere&&t.computeBoundingSphere(),co.copy(t.boundingSphere).applyMatrix4(t.matrixWorld);else{const e=t.geometry;null===e.boundingSphere&&e.computeBoundingSphere(),co.copy(e.boundingSphere).applyMatrix4(t.matrixWorld)}return this.intersectsSphere(co)}intersectsSprite(t){co.center.set(0,0,0);const e=uo.distanceTo(t.center);return co.radius=.7071067811865476+e,co.applyMatrix4(t.matrixWorld),this.intersectsSphere(co)}intersectsSphere(t){const e=this.planes,s=t.center,i=-t.radius;for(let t=0;t<6;t++){if(e[t].distanceToPoint(s)0?t.max.x:t.min.x,po.y=i.normal.y>0?t.max.y:t.min.y,po.z=i.normal.z>0?t.max.z:t.min.z,i.distanceToPoint(po)<0)return!1}return!0}containsPoint(t){const e=this.planes;for(let s=0;s<6;s++)if(e[s].distanceToPoint(t)<0)return!1;return!0}clone(){return(new this.constructor).copy(this)}}const yo=new Qi;class go{constructor(){this.coordinateSystem=Ws,this._frustums=[],this._count=0}setFromArrayCamera(t){const e=t.cameras,s=this._frustums;for(let t=0;t=r.length&&r.push({start:-1,count:-1,z:-1,index:-1});const a=r[this.index];n.push(a),this.index++,a.start=t,a.count=e,a.z=s,a.index=i}reset(){this.list.length=0,this.index=0}}const wo=new Qi,Mo=new Pr(1,1,1),So=new mo,_o=new go,Ao=new Qr,To=new En,zo=new Ti,Co=new Ti,Io=new Ti,Bo=new vo,ko=new Ra,Oo=[];function Po(t,e,s=0){const i=e.itemSize;if(t.isInterleavedBufferAttribute||t.array.constructor!==e.array.constructor){const r=t.count;for(let n=0;n65535?new Uint32Array(i):new Uint16Array(i);e.setIndex(new Mn(t,1))}this._geometryInitialized=!0}}_validateGeometry(t){const e=this.geometry;if(Boolean(t.getIndex())!==Boolean(e.getIndex()))throw new Error('THREE.BatchedMesh: All geometries must consistently have "index".');for(const s in e.attributes){if(!t.hasAttribute(s))throw new Error(`THREE.BatchedMesh: Added geometry missing "${s}". All geometries must have consistent attributes.`);const i=t.getAttribute(s),r=e.getAttribute(s);if(i.itemSize!==r.itemSize||i.normalized!==r.normalized)throw new Error("THREE.BatchedMesh: All attributes must have a consistent itemSize and normalized value.")}}validateInstanceId(t){const e=this._instanceInfo;if(t<0||t>=e.length||!1===e[t].active)throw new Error(`THREE.BatchedMesh: Invalid instanceId ${t}. Instance is either out of range or has been deleted.`)}validateGeometryId(t){const e=this._geometryInfo;if(t<0||t>=e.length||!1===e[t].active)throw new Error(`THREE.BatchedMesh: Invalid geometryId ${t}. Geometry is either out of range or has been deleted.`)}setCustomSort(t){return this.customSort=t,this}computeBoundingBox(){null===this.boundingBox&&(this.boundingBox=new Qr);const t=this.boundingBox,e=this._instanceInfo;t.makeEmpty();for(let s=0,i=e.length;s=this.maxInstanceCount&&0===this._availableInstanceIds.length)throw new Error("THREE.BatchedMesh: Maximum item count reached.");const e={visible:!0,active:!0,geometryIndex:t};let s=null;this._availableInstanceIds.length>0?(this._availableInstanceIds.sort(fo),s=this._availableInstanceIds.shift(),this._instanceInfo[s]=e):(s=this._instanceInfo.length,this._instanceInfo.push(e));const i=this._matricesTexture;wo.identity().toArray(i.image.data,16*s),i.needsUpdate=!0;const r=this._colorsTexture;return r&&(Mo.toArray(r.image.data,4*s),r.needsUpdate=!0),this._visibilityChanged=!0,s}addGeometry(t,e=-1,s=-1){this._initializeGeometry(t),this._validateGeometry(t);const i={vertexStart:-1,vertexCount:-1,reservedVertexCount:-1,indexStart:-1,indexCount:-1,reservedIndexCount:-1,start:-1,count:-1,boundingBox:null,boundingSphere:null,active:!0},r=this._geometryInfo;i.vertexStart=this._nextVertexStart,i.reservedVertexCount=-1===e?t.getAttribute("position").count:e;const n=t.getIndex();if(null!==n&&(i.indexStart=this._nextIndexStart,i.reservedIndexCount=-1===s?n.count:s),-1!==i.indexStart&&i.indexStart+i.reservedIndexCount>this._maxIndexCount||i.vertexStart+i.reservedVertexCount>this._maxVertexCount)throw new Error("THREE.BatchedMesh: Reserved space request exceeds the maximum buffer size.");let a;return this._availableGeometryIds.length>0?(this._availableGeometryIds.sort(fo),a=this._availableGeometryIds.shift(),r[a]=i):(a=this._geometryCount,this._geometryCount++,r.push(i)),this.setGeometryAt(a,t),this._nextIndexStart=i.indexStart+i.reservedIndexCount,this._nextVertexStart=i.vertexStart+i.reservedVertexCount,a}setGeometryAt(t,e){if(t>=this._geometryCount)throw new Error("THREE.BatchedMesh: Maximum geometry count reached.");this._validateGeometry(e);const s=this.geometry,i=null!==s.getIndex(),r=s.getIndex(),n=e.getIndex(),a=this._geometryInfo[t];if(i&&n.count>a.reservedIndexCount||e.attributes.position.count>a.reservedVertexCount)throw new Error("THREE.BatchedMesh: Reserved space not large enough for provided geometry.");const o=a.vertexStart,h=a.reservedVertexCount;a.vertexCount=e.getAttribute("position").count;for(const t in s.attributes){const i=e.getAttribute(t),r=s.getAttribute(t);Po(i,r,o);const n=i.itemSize;for(let t=i.count,e=h;t=e.length||!1===e[t].active)return this;const s=this._instanceInfo;for(let e=0,i=s.length;ee).sort((t,e)=>s[t].vertexStart-s[e].vertexStart),r=this.geometry;for(let n=0,a=s.length;n=this._geometryCount)return null;const s=this.geometry,i=this._geometryInfo[t];if(null===i.boundingBox){const t=new Qr,e=s.index,r=s.attributes.position;for(let s=i.start,n=i.start+i.count;s=this._geometryCount)return null;const s=this.geometry,i=this._geometryInfo[t];if(null===i.boundingSphere){const e=new En;this.getBoundingBoxAt(t,Ao),Ao.getCenter(e.center);const r=s.index,n=s.attributes.position;let a=0;for(let t=i.start,s=i.start+i.count;tt.active);if(Math.max(...s.map(t=>t.vertexStart+t.reservedVertexCount))>t)throw new Error(`THREE.BatchedMesh: Geometry vertex values are being used outside the range ${e}. Cannot shrink further.`);if(this.geometry.index){if(Math.max(...s.map(t=>t.indexStart+t.reservedIndexCount))>e)throw new Error(`THREE.BatchedMesh: Geometry index values are being used outside the range ${e}. Cannot shrink further.`)}const i=this.geometry;i.dispose(),this._maxVertexCount=t,this._maxIndexCount=e,this._geometryInitialized&&(this._geometryInitialized=!1,this.geometry=new Wn,this._initializeGeometry(i));const r=this.geometry;i.index&&Ro(i.index.array,r.index.array);for(const t in i.attributes)Ro(i.attributes[t].array,r.attributes[t].array)}raycast(t,e){const s=this._instanceInfo,i=this._geometryInfo,r=this.matrixWorld,n=this.geometry;ko.material=this.material,ko.geometry.index=n.index,ko.geometry.attributes=n.attributes,null===ko.geometry.boundingBox&&(ko.geometry.boundingBox=new Qr),null===ko.geometry.boundingSphere&&(ko.geometry.boundingSphere=new En);for(let n=0,a=s.length;n({...t,boundingBox:null!==t.boundingBox?t.boundingBox.clone():null,boundingSphere:null!==t.boundingSphere?t.boundingSphere.clone():null})),this._instanceInfo=t._instanceInfo.map(t=>({...t})),this._availableInstanceIds=t._availableInstanceIds.slice(),this._availableGeometryIds=t._availableGeometryIds.slice(),this._nextIndexStart=t._nextIndexStart,this._nextVertexStart=t._nextVertexStart,this._geometryCount=t._geometryCount,this._maxInstanceCount=t._maxInstanceCount,this._maxVertexCount=t._maxVertexCount,this._maxIndexCount=t._maxIndexCount,this._geometryInitialized=t._geometryInitialized,this._multiDrawCounts=t._multiDrawCounts.slice(),this._multiDrawStarts=t._multiDrawStarts.slice(),this._indirectTexture=t._indirectTexture.clone(),this._indirectTexture.image.data=this._indirectTexture.image.data.slice(),this._matricesTexture=t._matricesTexture.clone(),this._matricesTexture.image.data=this._matricesTexture.image.data.slice(),null!==this._colorsTexture&&(this._colorsTexture=t._colorsTexture.clone(),this._colorsTexture.image.data=this._colorsTexture.image.data.slice()),this}dispose(){this.geometry.dispose(),this._matricesTexture.dispose(),this._matricesTexture=null,this._indirectTexture.dispose(),this._indirectTexture=null,null!==this._colorsTexture&&(this._colorsTexture.dispose(),this._colorsTexture=null)}onBeforeRender(t,e,s,i,r){if(!this._visibilityChanged&&!this.perObjectFrustumCulled&&!this.sortObjects)return;const n=i.getIndex();let a=null===n?1:n.array.BYTES_PER_ELEMENT,o=1;r.wireframe&&(o=2,a=i.attributes.position.count>65535?4:2);const h=this._instanceInfo,l=this._multiDrawStarts,c=this._multiDrawCounts,u=this._geometryInfo,d=this.perObjectFrustumCulled,p=this._indirectTexture,m=p.image.data,y=s.isArrayCamera?_o:So;d&&(s.isArrayCamera?y.setFromArrayCamera(s):(wo.multiplyMatrices(s.projectionMatrix,s.matrixWorldInverse).multiply(this.matrixWorld),y.setFromProjectionMatrix(wo,s.coordinateSystem,s.reversedDepth)));let g=0;if(this.sortObjects){wo.copy(this.matrixWorld).invert(),zo.setFromMatrixPosition(s.matrixWorld).applyMatrix4(wo),Co.set(0,0,-1).transformDirection(s.matrixWorld).transformDirection(wo);for(let t=0,e=h.length;t0){const s=t[e[0]];if(void 0!==s){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let t=0,e=s.length;ti)return;jo.applyMatrix4(t.matrixWorld);const h=e.ray.origin.distanceTo(jo);return he.far?void 0:{distance:h,point:Wo.clone().applyMatrix4(t.matrixWorld),index:a,face:null,faceIndex:null,barycoord:null,object:t}}const Ho=new Ti,Xo=new Ti;class Yo extends Jo{constructor(t,e){super(t,e),this.isLineSegments=!0,this.type="LineSegments"}computeLineDistances(){const t=this.geometry;if(null===t.index){const e=t.attributes.position,s=[];for(let t=0,i=e.count;t0){const s=t[e[0]];if(void 0!==s){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let t=0,e=s.length;tr.far)return;n.push({distance:h,distanceToRay:Math.sqrt(o),point:s,index:e,face:null,faceIndex:null,barycoord:null,object:a})}}class ih extends Ji{constructor(t,e,s,i,r=1006,n=1006,a,o,h){super(t,e,s,i,r,n,a,o,h),this.isVideoTexture=!0,this.generateMipmaps=!1,this._requestVideoFrameCallbackId=0;const l=this;"requestVideoFrameCallback"in t&&(this._requestVideoFrameCallbackId=t.requestVideoFrameCallback(function e(){l.needsUpdate=!0,l._requestVideoFrameCallbackId=t.requestVideoFrameCallback(e)}))}clone(){return new this.constructor(this.image).copy(this)}update(){const t=this.image;!1==="requestVideoFrameCallback"in t&&t.readyState>=t.HAVE_CURRENT_DATA&&(this.needsUpdate=!0)}dispose(){0!==this._requestVideoFrameCallbackId&&(this.source.data.cancelVideoFrameCallback(this._requestVideoFrameCallbackId),this._requestVideoFrameCallbackId=0),super.dispose()}}class rh extends ih{constructor(t,e,s,i,r,n,a,o){super({},t,e,s,i,r,n,a,o),this.isVideoFrameTexture=!0}update(){}clone(){return(new this.constructor).copy(this)}setFrame(t){this.image=t,this.needsUpdate=!0}}class nh extends Ji{constructor(t,e){super({width:t,height:e}),this.isFramebufferTexture=!0,this.magFilter=ft,this.minFilter=ft,this.generateMipmaps=!1,this.needsUpdate=!0}}class ah extends Ji{constructor(t,e,s,i,r,n,a,o,h,l,c,u){super(null,n,a,o,h,l,i,r,c,u),this.isCompressedTexture=!0,this.image={width:e,height:s},this.mipmaps=t,this.flipY=!1,this.generateMipmaps=!1}}class oh extends ah{constructor(t,e,s,i,r,n){super(t,e,s,r,n),this.isCompressedArrayTexture=!0,this.image.depth=i,this.wrapR=yt,this.layerUpdates=new Set}addLayerUpdate(t){this.layerUpdates.add(t)}clearLayerUpdates(){this.layerUpdates.clear()}}class hh extends ah{constructor(t,e,s){super(void 0,t[0].width,t[0].height,e,s,lt),this.isCompressedCubeTexture=!0,this.isCubeTexture=!0,this.image=t}}class lh extends Ji{constructor(t=[],e=301,s,i,r,n,a,o,h,l){super(t,e,s,i,r,n,a,o,h,l),this.isCubeTexture=!0,this.flipY=!1}get images(){return this.image}set images(t){this.image=t}}class ch extends Ji{constructor(t,e,s,i,r,n,a,o,h){super(t,e,s,i,r,n,a,o,h),this.isCanvasTexture=!0,this.needsUpdate=!0}}class uh extends Ji{constructor(t,e,s,i,r,n,a,o,h){super(t,e,s,i,r,n,a,o,h),this.isHTMLTexture=!0,this.generateMipmaps=!1,this.needsUpdate=!0;const l=t?t.parentNode:null;null!==l&&"requestPaint"in l&&(l.onpaint=()=>{this.needsUpdate=!0},l.requestPaint())}dispose(){const t=this.image?this.image.parentNode:null;null!==t&&"onpaint"in t&&(t.onpaint=null),super.dispose()}}class dh extends Ji{constructor(t,e,s=1014,i,r,n,a=1003,o=1003,h,l=1026,c=1){if(l!==Wt&&1027!==l)throw new Error("THREE.DepthTexture: format must be either THREE.DepthFormat or THREE.DepthStencilFormat");super({width:t,height:e,depth:c},i,r,n,a,o,l,s,h),this.isDepthTexture=!0,this.flipY=!1,this.generateMipmaps=!1,this.compareFunction=null}copy(t){return super.copy(t),this.source=new Di(Object.assign({},t.image)),this.compareFunction=t.compareFunction,this}toJSON(t){const e=super.toJSON(t);return null!==this.compareFunction&&(e.compareFunction=this.compareFunction),e}}class ph extends dh{constructor(t,e=1014,s=301,i,r,n=1003,a=1003,o,h=1026){const l={width:t,height:t,depth:1},c=[l,l,l,l,l,l];super(t,t,e,s,i,r,n,a,o,h),this.image=c,this.isCubeDepthTexture=!0,this.isCubeTexture=!0}get images(){return this.image}set images(t){this.image=t}}class mh extends Ji{constructor(t=null){super(),this.sourceTexture=t,this.isExternalTexture=!0}copy(t){return super.copy(t),this.sourceTexture=t.sourceTexture,this}}class yh extends Wn{constructor(t=1,e=1,s=1,i=1,r=1,n=1){super(),this.type="BoxGeometry",this.parameters={width:t,height:e,depth:s,widthSegments:i,heightSegments:r,depthSegments:n};const a=this;i=Math.floor(i),r=Math.floor(r),n=Math.floor(n);const o=[],h=[],l=[],c=[];let u=0,d=0;function p(t,e,s,i,r,n,p,m,y,g,f){const x=n/y,b=p/g,v=n/2,w=p/2,M=m/2,S=y+1,_=g+1;let A=0,T=0;const z=new Ti;for(let n=0;n<_;n++){const a=n*b-w;for(let o=0;o0?1:-1,l.push(z.x,z.y,z.z),c.push(o/y),c.push(1-n/g),A+=1}}for(let t=0;t0){const t=(f-1)*m;for(let e=0;e0||0!==i)&&(l.push(n,a,h),x+=3),(e>0||i!==r-1)&&(l.push(a,o,h),x+=3)}h.addGroup(g,x,0),g+=x}(),!1===n&&(t>0&&f(!0),e>0&&f(!1)),this.setIndex(l),this.setAttribute("position",new kn(c,3)),this.setAttribute("normal",new kn(u,3)),this.setAttribute("uv",new kn(d,2))}copy(t){return super.copy(t),this.parameters=Object.assign({},t.parameters),this}static fromJSON(t){return new xh(t.radiusTop,t.radiusBottom,t.height,t.radialSegments,t.heightSegments,t.openEnded,t.thetaStart,t.thetaLength)}}class bh extends xh{constructor(t=1,e=1,s=32,i=1,r=!1,n=0,a=2*Math.PI){super(0,t,e,s,i,r,n,a),this.type="ConeGeometry",this.parameters={radius:t,height:e,radialSegments:s,heightSegments:i,openEnded:r,thetaStart:n,thetaLength:a}}static fromJSON(t){return new bh(t.radius,t.height,t.radialSegments,t.heightSegments,t.openEnded,t.thetaStart,t.thetaLength)}}class vh extends Wn{constructor(t=[],e=[],s=1,i=0){super(),this.type="PolyhedronGeometry",this.parameters={vertices:t,indices:e,radius:s,detail:i};const r=[],n=[];function a(t,e,s,i){const r=i+1,n=[];for(let i=0;i<=r;i++){n[i]=[];const a=t.clone().lerp(s,i/r),o=e.clone().lerp(s,i/r),h=r-i;for(let t=0;t<=h;t++)n[i][t]=0===t&&i===r?a:a.clone().lerp(o,t/h)}for(let t=0;t.9&&a<.1&&(e<.2&&(n[t+0]+=1),s<.2&&(n[t+2]+=1),i<.2&&(n[t+4]+=1))}}()}(),this.setAttribute("position",new kn(r,3)),this.setAttribute("normal",new kn(r.slice(),3)),this.setAttribute("uv",new kn(n,2)),0===i?this.computeVertexNormals():this.normalizeNormals()}copy(t){return super.copy(t),this.parameters=Object.assign({},t.parameters),this}static fromJSON(t){return new vh(t.vertices,t.indices,t.radius,t.detail)}}class wh extends vh{constructor(t=1,e=0){const s=(1+Math.sqrt(5))/2,i=1/s;super([-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-i,-s,0,-i,s,0,i,-s,0,i,s,-i,-s,0,-i,s,0,i,-s,0,i,s,0,-s,0,-i,s,0,-i,-s,0,i,s,0,i],[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2,6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,11,19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9],t,e),this.type="DodecahedronGeometry",this.parameters={radius:t,detail:e}}static fromJSON(t){return new wh(t.radius,t.detail)}}const Mh=new Ti,Sh=new Ti,_h=new Ti,Ah=new $r;class Th extends Wn{constructor(t=null,e=1){if(super(),this.type="EdgesGeometry",this.parameters={geometry:t,thresholdAngle:e},null!==t){const s=4,i=Math.pow(10,s),r=Math.cos(yi*e),n=t.getIndex(),a=t.getAttribute("position"),o=n?n.count:a.count,h=[0,0,0],l=["a","b","c"],c=new Array(3),u={},d=[];for(let t=0;t0)){h=i;break}h=i-1}if(i=h,s[i]===n)return i/(r-1);const l=s[i];return(i+(n-l)/(s[i+1]-l))/(r-1)}getTangent(t,e){const s=1e-4;let i=t-s,r=t+s;i<0&&(i=0),r>1&&(r=1);const n=this.getPoint(i),a=this.getPoint(r),o=e||(n.isVector2?new _i:new Ti);return o.copy(a).sub(n).normalize(),o}getTangentAt(t,e){const s=this.getUtoTmapping(t);return this.getTangent(s,e)}computeFrenetFrames(t,e=!1){const s=new Ti,i=[],r=[],n=[],a=new Ti,o=new Qi;for(let e=0;e<=t;e++){const s=e/t;i[e]=this.getTangentAt(s,new Ti)}r[0]=new Ti,n[0]=new Ti;let h=Number.MAX_VALUE;const l=Math.abs(i[0].x),c=Math.abs(i[0].y),u=Math.abs(i[0].z);l<=h&&(h=l,s.set(1,0,0)),c<=h&&(h=c,s.set(0,1,0)),u<=h&&s.set(0,0,1),a.crossVectors(i[0],s).normalize(),r[0].crossVectors(i[0],a),n[0].crossVectors(i[0],r[0]);for(let e=1;e<=t;e++){if(r[e]=r[e-1].clone(),n[e]=n[e-1].clone(),a.crossVectors(i[e-1],i[e]),a.length()>Number.EPSILON){a.normalize();const t=Math.acos(xi(i[e-1].dot(i[e]),-1,1));r[e].applyMatrix4(o.makeRotationAxis(a,t))}n[e].crossVectors(i[e],r[e])}if(!0===e){let e=Math.acos(xi(r[0].dot(r[t]),-1,1));e/=t,i[0].dot(a.crossVectors(r[0],r[t]))>0&&(e=-e);for(let s=1;s<=t;s++)r[s].applyMatrix4(o.makeRotationAxis(i[s],e*s)),n[s].crossVectors(i[s],r[s])}return{tangents:i,normals:r,binormals:n}}clone(){return(new this.constructor).copy(this)}copy(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}toJSON(){const t={metadata:{version:4.7,type:"Curve",generator:"Curve.toJSON"}};return t.arcLengthDivisions=this.arcLengthDivisions,t.type=this.type,t}fromJSON(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}}class Ch extends zh{constructor(t=0,e=0,s=1,i=1,r=0,n=2*Math.PI,a=!1,o=0){super(),this.isEllipseCurve=!0,this.type="EllipseCurve",this.aX=t,this.aY=e,this.xRadius=s,this.yRadius=i,this.aStartAngle=r,this.aEndAngle=n,this.aClockwise=a,this.aRotation=o}getPoint(t,e=new _i){const s=e,i=2*Math.PI;let r=this.aEndAngle-this.aStartAngle;const n=Math.abs(r)i;)r-=i;r0?0:(Math.floor(Math.abs(h)/r)+1)*r:0===l&&h===r-1&&(h=r-2,l=1),this.closed||h>0?a=i[(h-1)%r]:(Oh.subVectors(i[0],i[1]).add(i[0]),a=Oh);const c=i[h%r],u=i[(h+1)%r];if(this.closed||h+2i.length-2?i.length-1:n+1],c=i[n>i.length-3?i.length-1:n+2];return s.set(Vh(a,o.x,h.x,l.x,c.x),Vh(a,o.y,h.y,l.y,c.y)),s}copy(t){super.copy(t),this.points=[];for(let e=0,s=t.points.length;e=s){const t=i[r]-s,n=this.curves[r],a=n.getLength(),o=0===a?0:1-t/a;return n.getPointAt(o,e)}r++}return null}getLength(){const t=this.getCurveLengths();return t[t.length-1]}updateArcLengths(){this.needsUpdate=!0,this.cacheLengths=null,this.getCurveLengths()}getCurveLengths(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;const t=[];let e=0;for(let s=0,i=this.curves.length;s1&&!e[e.length-1].equals(e[0])&&e.push(e[0]),e}copy(t){super.copy(t),this.curves=[];for(let e=0,s=t.curves.length;e0){const t=h.getPoint(0);t.equals(this.currentPoint)||this.lineTo(t.x,t.y)}this.curves.push(h);const l=h.getPoint(1);return this.currentPoint.copy(l),this}copy(t){return super.copy(t),this.currentPoint.copy(t.currentPoint),this}toJSON(){const t=super.toJSON();return t.currentPoint=this.currentPoint.toArray(),t}fromJSON(t){return super.fromJSON(t),this.currentPoint.fromArray(t.currentPoint),this}}class Gh extends Zh{constructor(t){super(t),this.uuid=fi(),this.type="Shape",this.holes=[]}getPointsHoles(t){const e=[];for(let s=0,i=this.holes.length;s80*s){o=t[0],h=t[1];let e=o,i=h;for(let n=s;ne&&(e=s),r>i&&(i=r)}l=Math.max(e-o,i-h),l=0!==l?32767/l:0}return tl(n,a,s,o,h,l,0),a}function Qh(t,e,s,i,r){let n;if(r===function(t,e,s,i){let r=0;for(let n=e,a=s-i;n0)for(let r=e;r=e;r-=i)n=vl(r/i|0,t[r],t[r+1],n);return n&&ml(n,n.next)&&(wl(n),n=n.next),n}function Kh(t,e){if(!t)return t;e||(e=t);let s,i=t;do{if(s=!1,i.steiner||!ml(i,i.next)&&0!==pl(i.prev,i,i.next))i=i.next;else{if(wl(i),i=e=i.prev,i===i.next)break;s=!0}}while(s||i!==e);return e}function tl(t,e,s,i,r,n,a){if(!t)return;!a&&n&&function(t,e,s,i){let r=t;do{0===r.z&&(r.z=hl(r.x,r.y,e,s,i)),r.prevZ=r.prev,r.nextZ=r.next,r=r.next}while(r!==t);r.prevZ.nextZ=null,r.prevZ=null,function(t){let e,s=1;do{let i,r=t;t=null;let n=null;for(e=0;r;){e++;let a=r,o=0;for(let t=0;t0||h>0&&a;)0!==o&&(0===h||!a||r.z<=a.z)?(i=r,r=r.nextZ,o--):(i=a,a=a.nextZ,h--),n?n.nextZ=i:t=i,i.prevZ=n,n=i;r=a}n.nextZ=null,s*=2}while(e>1)}(r)}(t,i,r,n);let o=t;for(;t.prev!==t.next;){const h=t.prev,l=t.next;if(n?sl(t,i,r,n):el(t))e.push(h.i,t.i,l.i),wl(t),t=l.next,o=l.next;else if((t=l)===o){a?1===a?tl(t=il(Kh(t),e),e,s,i,r,n,2):2===a&&rl(t,e,s,i,r,n):tl(Kh(t),e,s,i,r,n,1);break}}}function el(t){const e=t.prev,s=t,i=t.next;if(pl(e,s,i)>=0)return!1;const r=e.x,n=s.x,a=i.x,o=e.y,h=s.y,l=i.y,c=Math.min(r,n,a),u=Math.min(o,h,l),d=Math.max(r,n,a),p=Math.max(o,h,l);let m=i.next;for(;m!==e;){if(m.x>=c&&m.x<=d&&m.y>=u&&m.y<=p&&ul(r,o,n,h,a,l,m.x,m.y)&&pl(m.prev,m,m.next)>=0)return!1;m=m.next}return!0}function sl(t,e,s,i){const r=t.prev,n=t,a=t.next;if(pl(r,n,a)>=0)return!1;const o=r.x,h=n.x,l=a.x,c=r.y,u=n.y,d=a.y,p=Math.min(o,h,l),m=Math.min(c,u,d),y=Math.max(o,h,l),g=Math.max(c,u,d),f=hl(p,m,e,s,i),x=hl(y,g,e,s,i);let b=t.prevZ,v=t.nextZ;for(;b&&b.z>=f&&v&&v.z<=x;){if(b.x>=p&&b.x<=y&&b.y>=m&&b.y<=g&&b!==r&&b!==a&&ul(o,c,h,u,l,d,b.x,b.y)&&pl(b.prev,b,b.next)>=0)return!1;if(b=b.prevZ,v.x>=p&&v.x<=y&&v.y>=m&&v.y<=g&&v!==r&&v!==a&&ul(o,c,h,u,l,d,v.x,v.y)&&pl(v.prev,v,v.next)>=0)return!1;v=v.nextZ}for(;b&&b.z>=f;){if(b.x>=p&&b.x<=y&&b.y>=m&&b.y<=g&&b!==r&&b!==a&&ul(o,c,h,u,l,d,b.x,b.y)&&pl(b.prev,b,b.next)>=0)return!1;b=b.prevZ}for(;v&&v.z<=x;){if(v.x>=p&&v.x<=y&&v.y>=m&&v.y<=g&&v!==r&&v!==a&&ul(o,c,h,u,l,d,v.x,v.y)&&pl(v.prev,v,v.next)>=0)return!1;v=v.nextZ}return!0}function il(t,e){let s=t;do{const i=s.prev,r=s.next.next;!ml(i,r)&&yl(i,s,s.next,r)&&xl(i,r)&&xl(r,i)&&(e.push(i.i,s.i,r.i),wl(s),wl(s.next),s=t=r),s=s.next}while(s!==t);return Kh(s)}function rl(t,e,s,i,r,n){let a=t;do{let t=a.next.next;for(;t!==a.prev;){if(a.i!==t.i&&dl(a,t)){let o=bl(a,t);return a=Kh(a,a.next),o=Kh(o,o.next),tl(a,e,s,i,r,n,0),void tl(o,e,s,i,r,n,0)}t=t.next}a=a.next}while(a!==t)}function nl(t,e){let s=t.x-e.x;if(0===s&&(s=t.y-e.y,0===s)){s=(t.next.y-t.y)/(t.next.x-t.x)-(e.next.y-e.y)/(e.next.x-e.x)}return s}function al(t,e){const s=function(t,e){let s=e;const i=t.x,r=t.y;let n,a=-1/0;if(ml(t,s))return s;do{if(ml(t,s.next))return s.next;if(r<=s.y&&r>=s.next.y&&s.next.y!==s.y){const t=s.x+(r-s.y)*(s.next.x-s.x)/(s.next.y-s.y);if(t<=i&&t>a&&(a=t,n=s.x=s.x&&s.x>=h&&i!==s.x&&cl(rn.x||s.x===n.x&&ol(n,s)))&&(n=s,c=e)}s=s.next}while(s!==o);return n}(t,e);if(!s)return e;const i=bl(s,t);return Kh(i,i.next),Kh(s,s.next)}function ol(t,e){return pl(t.prev,t,e.prev)<0&&pl(e.next,t,t.next)<0}function hl(t,e,s,i,r){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=(t-s)*r|0)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=(e-i)*r|0)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function ll(t){let e=t,s=t;do{(e.x=(t-a)*(n-o)&&(t-a)*(i-o)>=(s-a)*(e-o)&&(s-a)*(n-o)>=(r-a)*(i-o)}function ul(t,e,s,i,r,n,a,o){return!(t===a&&e===o)&&cl(t,e,s,i,r,n,a,o)}function dl(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){let s=t;do{if(s.i!==t.i&&s.next.i!==t.i&&s.i!==e.i&&s.next.i!==e.i&&yl(s,s.next,t,e))return!0;s=s.next}while(s!==t);return!1}(t,e)&&(xl(t,e)&&xl(e,t)&&function(t,e){let s=t,i=!1;const r=(t.x+e.x)/2,n=(t.y+e.y)/2;do{s.y>n!=s.next.y>n&&s.next.y!==s.y&&r<(s.next.x-s.x)*(n-s.y)/(s.next.y-s.y)+s.x&&(i=!i),s=s.next}while(s!==t);return i}(t,e)&&(pl(t.prev,t,e.prev)||pl(t,e.prev,e))||ml(t,e)&&pl(t.prev,t,t.next)>0&&pl(e.prev,e,e.next)>0)}function pl(t,e,s){return(e.y-t.y)*(s.x-e.x)-(e.x-t.x)*(s.y-e.y)}function ml(t,e){return t.x===e.x&&t.y===e.y}function yl(t,e,s,i){const r=fl(pl(t,e,s)),n=fl(pl(t,e,i)),a=fl(pl(s,i,t)),o=fl(pl(s,i,e));return r!==n&&a!==o||(!(0!==r||!gl(t,s,e))||(!(0!==n||!gl(t,i,e))||(!(0!==a||!gl(s,t,i))||!(0!==o||!gl(s,e,i)))))}function gl(t,e,s){return e.x<=Math.max(t.x,s.x)&&e.x>=Math.min(t.x,s.x)&&e.y<=Math.max(t.y,s.y)&&e.y>=Math.min(t.y,s.y)}function fl(t){return t>0?1:t<0?-1:0}function xl(t,e){return pl(t.prev,t,t.next)<0?pl(t,e,t.next)>=0&&pl(t,t.prev,e)>=0:pl(t,e,t.prev)<0||pl(t,t.next,e)<0}function bl(t,e){const s=Ml(t.i,t.x,t.y),i=Ml(e.i,e.x,e.y),r=t.next,n=e.prev;return t.next=e,e.prev=t,s.next=r,r.prev=s,i.next=s,s.prev=i,n.next=i,i.prev=n,i}function vl(t,e,s,i){const r=Ml(t,e,s);return i?(r.next=i.next,r.prev=i,i.next.prev=r,i.next=r):(r.prev=r,r.next=r),r}function wl(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ)}function Ml(t,e,s){return{i:t,x:e,y:s,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}class Sl{static triangulate(t,e,s=2){return $h(t,e,s)}}class _l{static area(t){const e=t.length;let s=0;for(let i=e-1,r=0;r2&&t[e-1].equals(t[0])&&t.pop()}function Tl(t,e){for(let s=0;sNumber.EPSILON){const u=Math.sqrt(c),d=Math.sqrt(h*h+l*l),p=e.x-o/u,m=e.y+a/u,y=((s.x-l/d-p)*l-(s.y+h/d-m)*h)/(a*l-o*h);i=p+a*y-t.x,r=m+o*y-t.y;const g=i*i+r*r;if(g<=2)return new _i(i,r);n=Math.sqrt(g/2)}else{let t=!1;a>Number.EPSILON?h>Number.EPSILON&&(t=!0):a<-Number.EPSILON?h<-Number.EPSILON&&(t=!0):Math.sign(o)===Math.sign(l)&&(t=!0),t?(i=-o,r=a,n=Math.sqrt(c)):(i=a,r=o,n=Math.sqrt(c/2))}return new _i(i/n,r/n)}const k=[];for(let t=0,e=z.length,s=e-1,i=t+1;t=0;t--){const e=t/p,s=c*Math.cos(e*Math.PI/2),i=u*Math.sin(e*Math.PI/2)+d;for(let t=0,e=z.length;t=0;){const i=s;let r=s-1;r<0&&(r=t.length-1);for(let t=0,s=o+2*p;t0)&&d.push(e,r,h),(t!==s-1||o0&&(e.defines=this.defines),e.vertexShader=this.vertexShader,e.fragmentShader=this.fragmentShader,e.lights=this.lights,e.clipping=this.clipping;const s={};for(const t in this.extensions)!0===this.extensions[t]&&(s[t]=!0);return Object.keys(s).length>0&&(e.extensions=s),e}fromJSON(t,e){if(super.fromJSON(t,e),void 0!==t.uniforms)for(const s in t.uniforms){const i=t.uniforms[s];switch(this.uniforms[s]={},i.type){case"t":this.uniforms[s].value=e[i.value]||null;break;case"c":this.uniforms[s].value=(new Pr).setHex(i.value);break;case"v2":this.uniforms[s].value=(new _i).fromArray(i.value);break;case"v3":this.uniforms[s].value=(new Ti).fromArray(i.value);break;case"v4":this.uniforms[s].value=(new qi).fromArray(i.value);break;case"m3":this.uniforms[s].value=(new Ii).fromArray(i.value);break;case"m4":this.uniforms[s].value=(new Qi).fromArray(i.value);break;default:this.uniforms[s].value=i.value}}if(void 0!==t.defines&&(this.defines=t.defines),void 0!==t.vertexShader&&(this.vertexShader=t.vertexShader),void 0!==t.fragmentShader&&(this.fragmentShader=t.fragmentShader),void 0!==t.glslVersion&&(this.glslVersion=t.glslVersion),void 0!==t.extensions)for(const e in t.extensions)this.extensions[e]=t.extensions[e];return void 0!==t.lights&&(this.lights=t.lights),void 0!==t.clipping&&(this.clipping=t.clipping),this}}class Gl extends Zl{constructor(t){super(t),this.isRawShaderMaterial=!0,this.type="RawShaderMaterial"}}class $l extends Zn{constructor(t){super(),this.isMeshStandardMaterial=!0,this.type="MeshStandardMaterial",this.defines={STANDARD:""},this.color=new Pr(16777215),this.roughness=1,this.metalness=0,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Pr(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.roughnessMap=null,this.metalnessMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new hr,this.envMapIntensity=1,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.defines={STANDARD:""},this.color.copy(t.color),this.roughness=t.roughness,this.metalness=t.metalness,this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.roughnessMap=t.roughnessMap,this.metalnessMap=t.metalnessMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.envMapIntensity=t.envMapIntensity,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.flatShading=t.flatShading,this.fog=t.fog,this}}class Ql extends $l{constructor(t){super(),this.isMeshPhysicalMaterial=!0,this.defines={STANDARD:"",PHYSICAL:""},this.type="MeshPhysicalMaterial",this.anisotropyRotation=0,this.anisotropyMap=null,this.clearcoatMap=null,this.clearcoatRoughness=0,this.clearcoatRoughnessMap=null,this.clearcoatNormalScale=new _i(1,1),this.clearcoatNormalMap=null,this.ior=1.5,Object.defineProperty(this,"reflectivity",{get:function(){return xi(2.5*(this.ior-1)/(this.ior+1),0,1)},set:function(t){this.ior=(1+.4*t)/(1-.4*t)}}),this.iridescenceMap=null,this.iridescenceIOR=1.3,this.iridescenceThicknessRange=[100,400],this.iridescenceThicknessMap=null,this.sheenColor=new Pr(0),this.sheenColorMap=null,this.sheenRoughness=1,this.sheenRoughnessMap=null,this.transmissionMap=null,this.thickness=0,this.thicknessMap=null,this.attenuationDistance=1/0,this.attenuationColor=new Pr(1,1,1),this.specularIntensity=1,this.specularIntensityMap=null,this.specularColor=new Pr(1,1,1),this.specularColorMap=null,this._anisotropy=0,this._clearcoat=0,this._dispersion=0,this._iridescence=0,this._sheen=0,this._transmission=0,this.setValues(t)}get anisotropy(){return this._anisotropy}set anisotropy(t){this._anisotropy>0!=t>0&&this.version++,this._anisotropy=t}get clearcoat(){return this._clearcoat}set clearcoat(t){this._clearcoat>0!=t>0&&this.version++,this._clearcoat=t}get iridescence(){return this._iridescence}set iridescence(t){this._iridescence>0!=t>0&&this.version++,this._iridescence=t}get dispersion(){return this._dispersion}set dispersion(t){this._dispersion>0!=t>0&&this.version++,this._dispersion=t}get sheen(){return this._sheen}set sheen(t){this._sheen>0!=t>0&&this.version++,this._sheen=t}get transmission(){return this._transmission}set transmission(t){this._transmission>0!=t>0&&this.version++,this._transmission=t}copy(t){return super.copy(t),this.defines={STANDARD:"",PHYSICAL:""},this.anisotropy=t.anisotropy,this.anisotropyRotation=t.anisotropyRotation,this.anisotropyMap=t.anisotropyMap,this.clearcoat=t.clearcoat,this.clearcoatMap=t.clearcoatMap,this.clearcoatRoughness=t.clearcoatRoughness,this.clearcoatRoughnessMap=t.clearcoatRoughnessMap,this.clearcoatNormalMap=t.clearcoatNormalMap,this.clearcoatNormalScale.copy(t.clearcoatNormalScale),this.dispersion=t.dispersion,this.ior=t.ior,this.iridescence=t.iridescence,this.iridescenceMap=t.iridescenceMap,this.iridescenceIOR=t.iridescenceIOR,this.iridescenceThicknessRange=[...t.iridescenceThicknessRange],this.iridescenceThicknessMap=t.iridescenceThicknessMap,this.sheen=t.sheen,this.sheenColor.copy(t.sheenColor),this.sheenColorMap=t.sheenColorMap,this.sheenRoughness=t.sheenRoughness,this.sheenRoughnessMap=t.sheenRoughnessMap,this.transmission=t.transmission,this.transmissionMap=t.transmissionMap,this.thickness=t.thickness,this.thicknessMap=t.thicknessMap,this.attenuationDistance=t.attenuationDistance,this.attenuationColor.copy(t.attenuationColor),this.specularIntensity=t.specularIntensity,this.specularIntensityMap=t.specularIntensityMap,this.specularColor.copy(t.specularColor),this.specularColorMap=t.specularColorMap,this}}class Kl extends Zn{constructor(t){super(),this.isMeshPhongMaterial=!0,this.type="MeshPhongMaterial",this.color=new Pr(16777215),this.specular=new Pr(1118481),this.shininess=30,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Pr(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new hr,this.combine=0,this.reflectivity=1,this.envMapIntensity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.specular.copy(t.specular),this.shininess=t.shininess,this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.envMapIntensity=t.envMapIntensity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.flatShading=t.flatShading,this.fog=t.fog,this}}class tc extends Zn{constructor(t){super(),this.isMeshToonMaterial=!0,this.defines={TOON:""},this.type="MeshToonMaterial",this.color=new Pr(16777215),this.map=null,this.gradientMap=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Pr(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.alphaMap=null,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.gradientMap=t.gradientMap,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.alphaMap=t.alphaMap,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.fog=t.fog,this}}class ec extends Zn{constructor(t){super(),this.isMeshNormalMaterial=!0,this.type="MeshNormalMaterial",this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.flatShading=!1,this.setValues(t)}copy(t){return super.copy(t),this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.flatShading=t.flatShading,this}}class sc extends Zn{constructor(t){super(),this.isMeshLambertMaterial=!0,this.type="MeshLambertMaterial",this.color=new Pr(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Pr(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new hr,this.combine=0,this.reflectivity=1,this.envMapIntensity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.envMapIntensity=t.envMapIntensity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.flatShading=t.flatShading,this.fog=t.fog,this}}class ic extends Zn{constructor(t){super(),this.isMeshDepthMaterial=!0,this.type="MeshDepthMaterial",this.depthPacking=3200,this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(t)}copy(t){return super.copy(t),this.depthPacking=t.depthPacking,this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this}}class rc extends Zn{constructor(t){super(),this.isMeshDistanceMaterial=!0,this.type="MeshDistanceMaterial",this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.setValues(t)}copy(t){return super.copy(t),this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this}}class nc extends Zn{constructor(t){super(),this.isMeshMatcapMaterial=!0,this.defines={MATCAP:""},this.type="MeshMatcapMaterial",this.color=new Pr(16777215),this.matcap=null,this.map=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.alphaMap=null,this.wireframe=!1,this.wireframeLinewidth=1,this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.defines={MATCAP:""},this.color.copy(t.color),this.matcap=t.matcap,this.map=t.map,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.alphaMap=t.alphaMap,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.flatShading=t.flatShading,this.fog=t.fog,this}}class ac extends No{constructor(t){super(),this.isLineDashedMaterial=!0,this.type="LineDashedMaterial",this.scale=1,this.dashSize=3,this.gapSize=1,this.setValues(t)}copy(t){return super.copy(t),this.scale=t.scale,this.dashSize=t.dashSize,this.gapSize=t.gapSize,this}}function oc(t,e){return t&&t.constructor!==e?"number"==typeof e.BYTES_PER_ELEMENT?new e(t):Array.prototype.slice.call(t):t}function hc(t){const e=t.length,s=new Array(e);for(let t=0;t!==e;++t)s[t]=t;return s.sort(function(e,s){return t[e]-t[s]}),s}function lc(t,e,s){const i=t.length,r=new t.constructor(i);for(let n=0,a=0;a!==i;++n){const i=s[n]*e;for(let s=0;s!==e;++s)r[a++]=t[i+s]}return r}function cc(t,e,s,i){let r=1,n=t[0];for(;void 0!==n&&void 0===n[i];)n=t[r++];if(void 0===n)return;let a=n[i];if(void 0!==a)if(Array.isArray(a))do{a=n[i],void 0!==a&&(e.push(n.time),s.push(...a)),n=t[r++]}while(void 0!==n);else if(void 0!==a.toArray)do{a=n[i],void 0!==a&&(e.push(n.time),a.toArray(s,s.length)),n=t[r++]}while(void 0!==n);else do{a=n[i],void 0!==a&&(e.push(n.time),s.push(a)),n=t[r++]}while(void 0!==n)}class uc{static convertArray(t,e){return oc(t,e)}static isTypedArray(t){return $s(t)}static getKeyframeOrder(t){return hc(t)}static sortedArray(t,e,s){return lc(t,e,s)}static flattenJSON(t,e,s,i){cc(t,e,s,i)}static subclip(t,e,s,i,r=30){return function(t,e,s,i,r=30){const n=t.clone();n.name=e;const a=[];for(let t=0;t=i)){h.push(e.times[t]);for(let s=0;sn.tracks[t].times[0]&&(o=n.tracks[t].times[0]);for(let t=0;t=i.times[u]){const t=u*h+o,e=t+h-o;d=i.values.slice(t,e)}else{const t=i.createInterpolant(),e=o,s=h-o;t.evaluate(n),d=t.resultBuffer.slice(e,s)}"quaternion"===r&&(new Ai).fromArray(d).normalize().conjugate().toArray(d);const p=a.times.length;for(let t=0;t=r)){const a=e[1];t=r)break e}n=s,s=0;break s}break t}for(;s>>1;te;)--n;if(++n,0!==r||n!==i){r>=n&&(n=Math.max(n,1),r=n-1);const t=this.getValueSize();this.times=s.slice(r,n),this.values=this.values.slice(r*t,n*t)}return this}validate(){let t=!0;const e=this.getValueSize();e-Math.floor(e)!==0&&(oi("KeyframeTrack: Invalid value size in track.",this),t=!1);const s=this.times,i=this.values,r=s.length;0===r&&(oi("KeyframeTrack: Track is empty.",this),t=!1);let n=null;for(let e=0;e!==r;e++){const i=s[e];if("number"==typeof i&&isNaN(i)){oi("KeyframeTrack: Time is not a valid number.",this,e,i),t=!1;break}if(null!==n&&n>i){oi("KeyframeTrack: Out of order keys.",this,e,i,n),t=!1;break}n=i}if(void 0!==i&&$s(i))for(let e=0,s=i.length;e!==s;++e){const s=i[e];if(isNaN(s)){oi("KeyframeTrack: Value is not a valid number.",this,e,s),t=!1;break}}return t}optimize(){const t=this.times.slice(),e=this.values.slice(),s=this.getValueSize(),i=this.getInterpolation()===Le,r=t.length-1;let n=1;for(let a=1;a0){t[n]=t[r];for(let t=r*s,i=n*s,a=0;a!==s;++a)e[i+a]=e[t+a];++n}return n!==t.length?(this.times=t.slice(0,n),this.values=e.slice(0,n*s)):(this.times=t,this.values=e),this}clone(){const t=this.times.slice(),e=this.values.slice(),s=new(0,this.constructor)(this.name,t,e);return s.createInterpolant=this.createInterpolant,s}}fc.prototype.ValueTypeName="",fc.prototype.TimeBufferType=Float32Array,fc.prototype.ValueBufferType=Float32Array,fc.prototype.DefaultInterpolation=Ve;class xc extends fc{constructor(t,e,s){super(t,e,s)}}xc.prototype.ValueTypeName="bool",xc.prototype.ValueBufferType=Array,xc.prototype.DefaultInterpolation=Ne,xc.prototype.InterpolantFactoryMethodLinear=void 0,xc.prototype.InterpolantFactoryMethodSmooth=void 0;class bc extends fc{constructor(t,e,s,i){super(t,e,s,i)}}bc.prototype.ValueTypeName="color";class vc extends fc{constructor(t,e,s,i){super(t,e,s,i)}}vc.prototype.ValueTypeName="number";class wc extends dc{constructor(t,e,s,i){super(t,e,s,i)}interpolate_(t,e,s,i){const r=this.resultBuffer,n=this.sampleValues,a=this.valueSize,o=(s-e)/(i-e);let h=t*a;for(let t=h+a;h!==t;h+=4)Ai.slerpFlat(r,0,n,h-a,n,h,o);return r}}class Mc extends fc{constructor(t,e,s,i){super(t,e,s,i)}InterpolantFactoryMethodLinear(t){return new wc(this.times,this.values,this.getValueSize(),t)}}Mc.prototype.ValueTypeName="quaternion",Mc.prototype.InterpolantFactoryMethodSmooth=void 0;class Sc extends fc{constructor(t,e,s){super(t,e,s)}}Sc.prototype.ValueTypeName="string",Sc.prototype.ValueBufferType=Array,Sc.prototype.DefaultInterpolation=Ne,Sc.prototype.InterpolantFactoryMethodLinear=void 0,Sc.prototype.InterpolantFactoryMethodSmooth=void 0;class _c extends fc{constructor(t,e,s,i){super(t,e,s,i)}}_c.prototype.ValueTypeName="vector";class Ac{constructor(t="",e=-1,s=[],i=2500){this.name=t,this.tracks=s,this.duration=e,this.blendMode=i,this.uuid=fi(),this.userData={},this.duration<0&&this.resetDuration()}static parse(t){const e=[],s=t.tracks,i=1/(t.fps||1);for(let t=0,r=s.length;t!==r;++t)e.push(Tc(s[t]).scale(i));const r=new this(t.name,t.duration,e,t.blendMode);return r.uuid=t.uuid,r.userData=JSON.parse(t.userData||"{}"),r}static toJSON(t){const e=[],s=t.tracks,i={name:t.name,duration:t.duration,tracks:e,uuid:t.uuid,blendMode:t.blendMode,userData:JSON.stringify(t.userData)};for(let t=0,i=s.length;t!==i;++t)e.push(fc.toJSON(s[t]));return i}static CreateFromMorphTargetSequence(t,e,s,i){const r=e.length,n=[];for(let t=0;t1){const t=n[1];let e=i[t];e||(i[t]=e=[]),e.push(s)}}const n=[];for(const t in i)n.push(this.CreateFromMorphTargetSequence(t,i[t],e,s));return n}resetDuration(){let t=0;for(let e=0,s=this.tracks.length;e!==s;++e){const s=this.tracks[e];t=Math.max(t,s.times[s.times.length-1])}return this.duration=t,this}trim(){for(let t=0;t{e&&e(r),this.manager.itemEnd(t)},0);if(void 0!==Oc[t])return void Oc[t].push({onLoad:e,onProgress:s,onError:i});Oc[t]=[],Oc[t].push({onLoad:e,onProgress:s,onError:i});const n=new Request(t,{headers:new Headers(this.requestHeader),credentials:this.withCredentials?"include":"same-origin",signal:"function"==typeof AbortSignal.any?AbortSignal.any([this._abortController.signal,this.manager.abortController.signal]):this._abortController.signal}),a=this.mimeType,o=this.responseType;fetch(n).then(e=>{if(200===e.status||0===e.status){if(0===e.status&&ai("FileLoader: HTTP Status 0 received."),"undefined"==typeof ReadableStream||void 0===e.body||void 0===e.body.getReader)return e;const s=Oc[t],i=e.body.getReader(),r=e.headers.get("X-File-Size")||e.headers.get("Content-Length"),n=r?parseInt(r):0,a=0!==n;let o=0;const h=new ReadableStream({start(t){!function e(){i.read().then(({done:i,value:r})=>{if(i)t.close();else{o+=r.byteLength;const i=new ProgressEvent("progress",{lengthComputable:a,loaded:o,total:n});for(let t=0,e=s.length;t{t.error(e)})}()}});return new Response(h)}throw new Pc(`fetch for "${e.url}" responded with ${e.status}: ${e.statusText}`,e)}).then(t=>{switch(o){case"arraybuffer":return t.arrayBuffer();case"blob":return t.blob();case"document":return t.text().then(t=>(new DOMParser).parseFromString(t,a));case"json":return t.json();default:if(""===a)return t.text();{const e=/charset="?([^;"\s]*)"?/i.exec(a),s=e&&e[1]?e[1].toLowerCase():void 0,i=new TextDecoder(s);return t.arrayBuffer().then(t=>i.decode(t))}}}).then(e=>{zc.add(`file:${t}`,e);const s=Oc[t];delete Oc[t];for(let t=0,i=s.length;t{const s=Oc[t];if(void 0===s)throw this.manager.itemError(t),e;delete Oc[t];for(let t=0,i=s.length;t{this.manager.itemEnd(t)}),this.manager.itemStart(t)}setResponseType(t){return this.responseType=t,this}setMimeType(t){return this.mimeType=t,this}abort(){return this._abortController.abort(),this._abortController=new AbortController,this}}class Ec extends kc{constructor(t){super(t)}load(t,e,s,i){const r=this,n=new Rc(this.manager);n.setPath(this.path),n.setRequestHeader(this.requestHeader),n.setWithCredentials(this.withCredentials),n.load(t,function(s){try{e(r.parse(JSON.parse(s)))}catch(e){i?i(e):oi(e),r.manager.itemError(t)}},s,i)}parse(t){const e=[];for(let s=0;s0){const s=new Ic(e);r=new Lc(s),r.setCrossOrigin(this.crossOrigin);for(let e=0,s=t.length;e0){i=new Lc(this.manager),i.setCrossOrigin(this.crossOrigin);for(let e=0,i=t.length;e{let e=null,s=null;return void 0!==t.boundingBox&&(e=(new Qr).fromJSON(t.boundingBox)),void 0!==t.boundingSphere&&(s=(new En).fromJSON(t.boundingSphere)),{...t,boundingBox:e,boundingSphere:s}}),n._instanceInfo=t.instanceInfo,n._availableInstanceIds=t._availableInstanceIds,n._availableGeometryIds=t._availableGeometryIds,n._nextIndexStart=t.nextIndexStart,n._nextVertexStart=t.nextVertexStart,n._geometryCount=t.geometryCount,n._maxInstanceCount=t.maxInstanceCount,n._maxVertexCount=t.maxVertexCount,n._maxIndexCount=t.maxIndexCount,n._geometryInitialized=t.geometryInitialized,n._matricesTexture=c(t.matricesTexture.uuid),n._indirectTexture=c(t.indirectTexture.uuid),void 0!==t.colorsTexture&&(n._colorsTexture=c(t.colorsTexture.uuid)),void 0!==t.boundingSphere&&(n.boundingSphere=(new En).fromJSON(t.boundingSphere)),void 0!==t.boundingBox&&(n.boundingBox=(new Qr).fromJSON(t.boundingBox));break;case"LOD":n=new pa;break;case"Line":n=new Jo(h(t.geometry),l(t.material));break;case"LineLoop":n=new Zo(h(t.geometry),l(t.material));break;case"LineSegments":n=new Yo(h(t.geometry),l(t.material));break;case"PointCloud":case"Points":n=new eh(h(t.geometry),l(t.material));break;case"Sprite":n=new la(l(t.material));break;case"Group":n=new Tr;break;case"Bone":n=new Ha;break;default:n=new Ar}if(n.uuid=t.uuid,void 0!==t.name&&(n.name=t.name),void 0!==t.matrix?(n.matrix.fromArray(t.matrix),void 0!==t.matrixAutoUpdate&&(n.matrixAutoUpdate=t.matrixAutoUpdate),n.matrixAutoUpdate&&n.matrix.decompose(n.position,n.quaternion,n.scale)):(void 0!==t.position&&n.position.fromArray(t.position),void 0!==t.rotation&&n.rotation.fromArray(t.rotation),void 0!==t.quaternion&&n.quaternion.fromArray(t.quaternion),void 0!==t.scale&&n.scale.fromArray(t.scale)),void 0!==t.up&&n.up.fromArray(t.up),void 0!==t.pivot&&(n.pivot=(new Ti).fromArray(t.pivot)),void 0!==t.morphTargetDictionary&&(n.morphTargetDictionary=Object.assign({},t.morphTargetDictionary)),void 0!==t.morphTargetInfluences&&(n.morphTargetInfluences=t.morphTargetInfluences.slice()),void 0!==t.castShadow&&(n.castShadow=t.castShadow),void 0!==t.receiveShadow&&(n.receiveShadow=t.receiveShadow),t.shadow&&(void 0!==t.shadow.intensity&&(n.shadow.intensity=t.shadow.intensity),void 0!==t.shadow.bias&&(n.shadow.bias=t.shadow.bias),void 0!==t.shadow.normalBias&&(n.shadow.normalBias=t.shadow.normalBias),void 0!==t.shadow.radius&&(n.shadow.radius=t.shadow.radius),void 0!==t.shadow.mapSize&&n.shadow.mapSize.fromArray(t.shadow.mapSize),void 0!==t.shadow.camera&&(n.shadow.camera=this.parseObject(t.shadow.camera))),void 0!==t.visible&&(n.visible=t.visible),void 0!==t.frustumCulled&&(n.frustumCulled=t.frustumCulled),void 0!==t.renderOrder&&(n.renderOrder=t.renderOrder),void 0!==t.static&&(n.static=t.static),void 0!==t.userData&&(n.userData=t.userData),void 0!==t.layers&&(n.layers.mask=t.layers),void 0!==t.children){const a=t.children;for(let t=0;t{!0===Su.has(n)?(i&&i(Su.get(n)),r.manager.itemError(t),r.manager.itemEnd(t)):(e&&e(s),r.manager.itemEnd(t))}):void setTimeout(function(){e&&e(n),r.manager.itemEnd(t)},0);const a={};a.credentials="anonymous"===this.crossOrigin?"same-origin":"include",a.headers=this.requestHeader,a.signal="function"==typeof AbortSignal.any?AbortSignal.any([this._abortController.signal,this.manager.abortController.signal]):this._abortController.signal;const o=fetch(t,a).then(function(t){return t.blob()}).then(function(t){return createImageBitmap(t,Object.assign(r.options,{colorSpaceConversion:"none"}))}).then(function(s){zc.add(`image-bitmap:${t}`,s),e&&e(s),r.manager.itemEnd(t)}).catch(function(e){i&&i(e),Su.set(o,e),zc.remove(`image-bitmap:${t}`),r.manager.itemError(t),r.manager.itemEnd(t)});zc.add(`image-bitmap:${t}`,o),r.manager.itemStart(t)}abort(){return this._abortController.abort(),this._abortController=new AbortController,this}}let Au;class Tu{static getContext(){return void 0===Au&&(Au=new(window.AudioContext||window.webkitAudioContext)),Au}static setContext(t){Au=t}}class zu extends kc{constructor(t){super(t)}load(t,e,s,i){const r=this,n=new Rc(this.manager);function a(e){i?i(e):oi(e),r.manager.itemError(t)}n.setResponseType("arraybuffer"),n.setPath(this.path),n.setRequestHeader(this.requestHeader),n.setWithCredentials(this.withCredentials),n.load(t,function(s){try{const i=s.slice(0),n=Tu.getContext(),o=t+"#decode";r.manager.itemStart(o),n.decodeAudioData(i,function(t){e(t),r.manager.itemEnd(o)}).catch(function(t){a(t),r.manager.itemEnd(o)})}catch(t){a(t)}},s,i)}}const Cu=new Qi,Iu=new Qi,Bu=new Qi;class ku{constructor(){this.type="StereoCamera",this.aspect=1,this.eyeSep=.064,this.cameraL=new eu,this.cameraL.layers.enable(1),this.cameraL.matrixAutoUpdate=!1,this.cameraR=new eu,this.cameraR.layers.enable(2),this.cameraR.matrixAutoUpdate=!1,this._cache={focus:null,fov:null,aspect:null,near:null,far:null,zoom:null,eyeSep:null}}update(t){const e=this._cache;if(e.focus!==t.focus||e.fov!==t.fov||e.aspect!==t.aspect*this.aspect||e.near!==t.near||e.far!==t.far||e.zoom!==t.zoom||e.eyeSep!==this.eyeSep){e.focus=t.focus,e.fov=t.fov,e.aspect=t.aspect*this.aspect,e.near=t.near,e.far=t.far,e.zoom=t.zoom,e.eyeSep=this.eyeSep,Bu.copy(t.projectionMatrix);const s=e.eyeSep/2,i=s*e.near/e.focus,r=e.near*Math.tan(yi*e.fov*.5)/e.zoom;let n,a;Iu.elements[12]=-s,Cu.elements[12]=s,n=-r*e.aspect+i,a=r*e.aspect+i,Bu.elements[0]=2*e.near/(a-n),Bu.elements[8]=(a+n)/(a-n),this.cameraL.projectionMatrix.copy(Bu),n=-r*e.aspect-i,a=r*e.aspect-i,Bu.elements[0]=2*e.near/(a-n),Bu.elements[8]=(a+n)/(a-n),this.cameraR.projectionMatrix.copy(Bu)}this.cameraL.matrix.copy(t.matrixWorld).multiply(Iu),this.cameraL.matrixWorldNeedsUpdate=!0,this.cameraR.matrix.copy(t.matrixWorld).multiply(Cu),this.cameraR.matrixWorldNeedsUpdate=!0}}const Ou=-90;class Pu extends Ar{constructor(t,e,s){super(),this.type="CubeCamera",this.renderTarget=s,this.coordinateSystem=null,this.activeMipmapLevel=0;const i=new eu(Ou,1,t,e);i.layers=this.layers,this.add(i);const r=new eu(Ou,1,t,e);r.layers=this.layers,this.add(r);const n=new eu(Ou,1,t,e);n.layers=this.layers,this.add(n);const a=new eu(Ou,1,t,e);a.layers=this.layers,this.add(a);const o=new eu(Ou,1,t,e);o.layers=this.layers,this.add(o);const h=new eu(Ou,1,t,e);h.layers=this.layers,this.add(h)}updateCoordinateSystem(){const t=this.coordinateSystem,e=this.children.concat(),[s,i,r,n,a,o]=e;for(const t of e)this.remove(t);if(t===Ws)s.up.set(0,1,0),s.lookAt(1,0,0),i.up.set(0,1,0),i.lookAt(-1,0,0),r.up.set(0,0,-1),r.lookAt(0,1,0),n.up.set(0,0,1),n.lookAt(0,-1,0),a.up.set(0,1,0),a.lookAt(0,0,1),o.up.set(0,1,0),o.lookAt(0,0,-1);else{if(t!==Js)throw new Error("THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: "+t);s.up.set(0,-1,0),s.lookAt(-1,0,0),i.up.set(0,-1,0),i.lookAt(1,0,0),r.up.set(0,0,1),r.lookAt(0,1,0),n.up.set(0,0,-1),n.lookAt(0,-1,0),a.up.set(0,-1,0),a.lookAt(0,0,1),o.up.set(0,-1,0),o.lookAt(0,0,-1)}for(const t of e)this.add(t),t.updateMatrixWorld()}update(t,e){null===this.parent&&this.updateMatrixWorld();const{renderTarget:s,activeMipmapLevel:i}=this;this.coordinateSystem!==t.coordinateSystem&&(this.coordinateSystem=t.coordinateSystem,this.updateCoordinateSystem());const[r,n,a,o,h,l]=this.children,c=t.getRenderTarget(),u=t.getActiveCubeFace(),d=t.getActiveMipmapLevel(),p=t.xr.enabled;t.xr.enabled=!1;const m=s.texture.generateMipmaps;s.texture.generateMipmaps=!1;let y=!1;y=!0===t.isWebGLRenderer?t.state.buffers.depth.getReversed():t.reversedDepthBuffer,t.setRenderTarget(s,0,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,r),t.setRenderTarget(s,1,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,n),t.setRenderTarget(s,2,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,a),t.setRenderTarget(s,3,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,o),t.setRenderTarget(s,4,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,h),s.texture.generateMipmaps=m,t.setRenderTarget(s,5,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,l),t.setRenderTarget(c,u,d),t.xr.enabled=p,s.texture.needsPMREMUpdate=!0}}class Ru extends eu{constructor(t=[]){super(),this.isArrayCamera=!0,this.isMultiViewCamera=!1,this.cameras=t}}class Eu{constructor(){this._previousTime=0,this._currentTime=0,this._startTime=performance.now(),this._delta=0,this._elapsed=0,this._timescale=1,this._document=null,this._pageVisibilityHandler=null}connect(t){this._document=t,void 0!==t.hidden&&(this._pageVisibilityHandler=Nu.bind(this),t.addEventListener("visibilitychange",this._pageVisibilityHandler,!1))}disconnect(){null!==this._pageVisibilityHandler&&(this._document.removeEventListener("visibilitychange",this._pageVisibilityHandler),this._pageVisibilityHandler=null),this._document=null}getDelta(){return this._delta/1e3}getElapsed(){return this._elapsed/1e3}getTimescale(){return this._timescale}setTimescale(t){return this._timescale=t,this}reset(){return this._currentTime=performance.now()-this._startTime,this}dispose(){this.disconnect()}update(t){return null!==this._pageVisibilityHandler&&!0===this._document.hidden?this._delta=0:(this._previousTime=this._currentTime,this._currentTime=(void 0!==t?t:performance.now())-this._startTime,this._delta=(this._currentTime-this._previousTime)*this._timescale,this._elapsed+=this._delta),this}}function Nu(){!1===this._document.hidden&&this.reset()}const Vu=new Ti,Lu=new Ai,Fu=new Ti,Du=new Ti,Uu=new Ti;class ju extends Ar{constructor(){super(),this.type="AudioListener",this.context=Tu.getContext(),this.gain=this.context.createGain(),this.gain.connect(this.context.destination),this.filter=null,this.timeDelta=0,this._timer=new Eu}getInput(){return this.gain}removeFilter(){return null!==this.filter&&(this.gain.disconnect(this.filter),this.filter.disconnect(this.context.destination),this.gain.connect(this.context.destination),this.filter=null),this}getFilter(){return this.filter}setFilter(t){return null!==this.filter?(this.gain.disconnect(this.filter),this.filter.disconnect(this.context.destination)):this.gain.disconnect(this.context.destination),this.filter=t,this.gain.connect(this.filter),this.filter.connect(this.context.destination),this}getMasterVolume(){return this.gain.gain.value}setMasterVolume(t){return this.gain.gain.setTargetAtTime(t,this.context.currentTime,.01),this}updateMatrixWorld(t){super.updateMatrixWorld(t),this._timer.update();const e=this.context.listener;if(this.timeDelta=this._timer.getDelta(),this.matrixWorld.decompose(Vu,Lu,Fu),Du.set(0,0,-1).applyQuaternion(Lu),Uu.set(0,1,0).applyQuaternion(Lu),e.positionX){const t=this.context.currentTime+this.timeDelta;e.positionX.linearRampToValueAtTime(Vu.x,t),e.positionY.linearRampToValueAtTime(Vu.y,t),e.positionZ.linearRampToValueAtTime(Vu.z,t),e.forwardX.linearRampToValueAtTime(Du.x,t),e.forwardY.linearRampToValueAtTime(Du.y,t),e.forwardZ.linearRampToValueAtTime(Du.z,t),e.upX.linearRampToValueAtTime(Uu.x,t),e.upY.linearRampToValueAtTime(Uu.y,t),e.upZ.linearRampToValueAtTime(Uu.z,t)}else e.setPosition(Vu.x,Vu.y,Vu.z),e.setOrientation(Du.x,Du.y,Du.z,Uu.x,Uu.y,Uu.z)}}class Wu extends Ar{constructor(t){super(),this.type="Audio",this.listener=t,this.context=t.context,this.gain=this.context.createGain(),this.gain.connect(t.getInput()),this.autoplay=!1,this.buffer=null,this.detune=0,this.loop=!1,this.loopStart=0,this.loopEnd=0,this.offset=0,this.duration=void 0,this.playbackRate=1,this.isPlaying=!1,this.hasPlaybackControl=!0,this.source=null,this.sourceType="empty",this._startedAt=0,this._progress=0,this._connected=!1,this.filters=[]}getOutput(){return this.gain}setNodeSource(t){return this.hasPlaybackControl=!1,this.sourceType="audioNode",this.source=t,this.connect(),this}setMediaElementSource(t){return this.hasPlaybackControl=!1,this.sourceType="mediaNode",this.source=this.context.createMediaElementSource(t),this.connect(),this}setMediaStreamSource(t){return this.hasPlaybackControl=!1,this.sourceType="mediaStreamNode",this.source=this.context.createMediaStreamSource(t),this.connect(),this}setBuffer(t){return this.buffer=t,this.sourceType="buffer",this.autoplay&&this.play(),this}play(t=0){if(!0===this.isPlaying)return void ai("Audio: Audio is already playing.");if(!1===this.hasPlaybackControl)return void ai("Audio: this Audio has no playback control.");this._startedAt=this.context.currentTime+t;const e=this.context.createBufferSource();return e.buffer=this.buffer,e.loop=this.loop,e.loopStart=this.loopStart,e.loopEnd=this.loopEnd,e.onended=this.onEnded.bind(this),e.start(this._startedAt,this._progress+this.offset,this.duration),this.isPlaying=!0,this.source=e,this.setDetune(this.detune),this.setPlaybackRate(this.playbackRate),this.connect()}pause(){if(!1!==this.hasPlaybackControl)return!0===this.isPlaying&&(this._progress+=Math.max(this.context.currentTime-this._startedAt,0)*this.playbackRate,!0===this.loop&&(this._progress=this._progress%(this.duration||this.buffer.duration)),this.source.stop(),this.source.onended=null,this.isPlaying=!1),this;ai("Audio: this Audio has no playback control.")}stop(t=0){if(!1!==this.hasPlaybackControl)return this._progress=0,null!==this.source&&(this.source.stop(this.context.currentTime+t),this.source.onended=null),this.isPlaying=!1,this;ai("Audio: this Audio has no playback control.")}connect(){if(this.filters.length>0){this.source.connect(this.filters[0]);for(let t=1,e=this.filters.length;t0){this.source.disconnect(this.filters[0]);for(let t=1,e=this.filters.length;t0&&this._mixBufferRegionAdditive(s,i,this._addIndex*e,1,e);for(let t=e,r=e+e;t!==r;++t)if(s[t]!==s[t+e]){a.setValue(s,i);break}}saveOriginalState(){const t=this.binding,e=this.buffer,s=this.valueSize,i=s*this._origIndex;t.getValue(e,i);for(let t=s,r=i;t!==r;++t)e[t]=e[i+t%s];this._setIdentity(),this.cumulativeWeight=0,this.cumulativeWeightAdditive=0}restoreOriginalState(){const t=3*this.valueSize;this.binding.setValue(this.buffer,t)}_setAdditiveIdentityNumeric(){const t=this._addIndex*this.valueSize,e=t+this.valueSize;for(let s=t;s=.5)for(let i=0;i!==r;++i)t[e+i]=t[s+i]}_slerp(t,e,s,i){Ai.slerpFlat(t,e,t,e,t,s,i)}_slerpAdditive(t,e,s,i,r){const n=this._workIndex*r;Ai.multiplyQuaternionsFlat(t,n,t,e,t,s),Ai.slerpFlat(t,e,t,e,t,n,i)}_lerp(t,e,s,i,r){const n=1-i;for(let a=0;a!==r;++a){const r=e+a;t[r]=t[r]*n+t[s+a]*i}}_lerpAdditive(t,e,s,i,r){for(let n=0;n!==r;++n){const r=e+n;t[r]=t[r]+t[s+n]*i}}}const $u="\\[\\]\\.:\\/",Qu=new RegExp("["+$u+"]","g"),Ku="[^"+$u+"]",td="[^"+$u.replace("\\.","")+"]",ed=new RegExp("^"+/((?:WC+[\/:])*)/.source.replace("WC",Ku)+/(WCOD+)?/.source.replace("WCOD",td)+/(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace("WC",Ku)+/\.(WC+)(?:\[(.+)\])?/.source.replace("WC",Ku)+"$"),sd=["material","materials","bones","map"];class id{constructor(t,e,s){this.path=e,this.parsedPath=s||id.parseTrackName(e),this.node=id.findNode(t,this.parsedPath.nodeName),this.rootNode=t,this.getValue=this._getValue_unbound,this.setValue=this._setValue_unbound}static create(t,e,s){return t&&t.isAnimationObjectGroup?new id.Composite(t,e,s):new id(t,e,s)}static sanitizeNodeName(t){return t.replace(/\s/g,"_").replace(Qu,"")}static parseTrackName(t){const e=ed.exec(t);if(null===e)throw new Error("THREE.PropertyBinding: Cannot parse trackName: "+t);const s={nodeName:e[2],objectName:e[3],objectIndex:e[4],propertyName:e[5],propertyIndex:e[6]},i=s.nodeName&&s.nodeName.lastIndexOf(".");if(void 0!==i&&-1!==i){const t=s.nodeName.substring(i+1);-1!==sd.indexOf(t)&&(s.nodeName=s.nodeName.substring(0,i),s.objectName=t)}if(null===s.propertyName||0===s.propertyName.length)throw new Error("THREE.PropertyBinding: can not parse propertyName from trackName: "+t);return s}static findNode(t,e){if(void 0===e||""===e||"."===e||-1===e||e===t.name||e===t.uuid)return t;if(t.skeleton){const s=t.skeleton.getBoneByName(e);if(void 0!==s)return s}if(t.children){const s=function(t){for(let i=0;i=r){const n=r++,l=t[n];e[l.uuid]=h,t[h]=l,e[o]=n,t[n]=a;for(let t=0,e=i;t!==e;++t){const e=s[t],i=e[n],r=e[h];e[h]=i,e[n]=r}}}this.nCachedObjects_=r}uncache(){const t=this._objects,e=this._indicesByUUID,s=this._bindings,i=s.length;let r=this.nCachedObjects_,n=t.length;for(let a=0,o=arguments.length;a!==o;++a){const o=arguments[a].uuid,h=e[o];if(void 0!==h)if(delete e[o],h0&&(e[a.uuid]=h),t[h]=a,t.pop();for(let t=0,e=i;t!==e;++t){const e=s[t];e[h]=e[r],e.pop()}}}this.nCachedObjects_=r}subscribe_(t,e){const s=this._bindingsIndicesByPath;let i=s[t];const r=this._bindings;if(void 0!==i)return r[i];const n=this._paths,a=this._parsedPaths,o=this._objects,h=o.length,l=this.nCachedObjects_,c=new Array(h);i=r.length,s[t]=i,n.push(t),a.push(e),r.push(c);for(let s=l,i=o.length;s!==i;++s){const i=o[s];c[s]=new id(i,t,e)}return c}unsubscribe_(t){const e=this._bindingsIndicesByPath,s=e[t];if(void 0!==s){const i=this._paths,r=this._parsedPaths,n=this._bindings,a=n.length-1,o=n[a];e[t[a]]=s,n[s]=o,n.pop(),r[s]=r[a],r.pop(),i[s]=i[a],i.pop()}}}class nd{constructor(t,e,s=null,i=e.blendMode){this._mixer=t,this._clip=e,this._localRoot=s,this.blendMode=i;const r=e.tracks,n=r.length,a=new Array(n),o={endingStart:De,endingEnd:De};for(let t=0;t!==n;++t){const e=r[t].createInterpolant(null);a[t]=e,e.settings=o}this._interpolantSettings=o,this._interpolants=a,this._propertyBindings=new Array(n),this._cacheIndex=null,this._byClipCacheIndex=null,this._timeScaleInterpolant=null,this._restoreTimeScale=null,this._weightInterpolant=null,this.loop=2201,this._loopCount=-1,this._startTime=null,this.time=0,this.timeScale=1,this._effectiveTimeScale=1,this.weight=1,this._effectiveWeight=1,this.repetitions=1/0,this.paused=!1,this.enabled=!0,this.clampWhenFinished=!1,this.zeroSlopeAtStart=!0,this.zeroSlopeAtEnd=!0}play(){return this._mixer._activateAction(this),this}stop(){return this._mixer._deactivateAction(this),this.reset()}reset(){return this.paused=!1,this.enabled=!0,this.time=0,this._loopCount=-1,this._startTime=null,this.stopFading().stopWarping()}isRunning(){return this.enabled&&!this.paused&&0!==this.timeScale&&null===this._startTime&&this._mixer._isActiveAction(this)}isScheduled(){return this._mixer._isActiveAction(this)}startAt(t){return this._startTime=t,this}setLoop(t,e){return this.loop=t,this.repetitions=e,this}setEffectiveWeight(t){return this.weight=t,this._effectiveWeight=this.enabled?t:0,this.stopFading()}getEffectiveWeight(){return this._effectiveWeight}fadeIn(t){return this._scheduleFading(t,0,1)}fadeOut(t){return this._scheduleFading(t,1,0)}crossFadeFrom(t,e,s=!1){if(t.fadeOut(e),this.fadeIn(e),!0===s){const s=this._clip.duration,i=t._clip.duration,r=i/s,n=s/i;t._restoreTimeScale=t.timeScale,this._restoreTimeScale=this.timeScale,t.warp(1,r,e),this.warp(n,1,e)}return this}crossFadeTo(t,e,s=!1){return t.crossFadeFrom(this,e,s)}stopFading(){const t=this._weightInterpolant;return null!==t&&(this._weightInterpolant=null,this._mixer._takeBackControlInterpolant(t)),this}setEffectiveTimeScale(t){return this.timeScale=t,this._effectiveTimeScale=this.paused?0:t,this.stopWarping()}getEffectiveTimeScale(){return this._effectiveTimeScale}setDuration(t){return this.timeScale=this._clip.duration/t,this.stopWarping()}syncWith(t){return this.time=t.time,this.timeScale=t.timeScale,this.stopWarping()}halt(t){return this.warp(this._effectiveTimeScale,0,t)}warp(t,e,s){const i=this._mixer,r=i.time,n=this.timeScale;let a=this._timeScaleInterpolant;null===a&&(a=i._lendControlInterpolant(),this._timeScaleInterpolant=a);const o=a.parameterPositions,h=a.sampleValues;return o[0]=r,o[1]=r+s,h[0]=t/n,h[1]=e/n,this}stopWarping(){const t=this._timeScaleInterpolant;return null!==t&&(this._timeScaleInterpolant=null,this._mixer._takeBackControlInterpolant(t)),this._restoreTimeScale=null,this}getMixer(){return this._mixer}getClip(){return this._clip}getRoot(){return this._localRoot||this._mixer._root}_update(t,e,s,i){if(!this.enabled)return void this._updateWeight(t);const r=this._startTime;if(null!==r){const i=(t-r)*s;i<0||0===s?e=0:(this._startTime=null,e=s*i)}e*=this._updateTimeScale(t);const n=this._updateTime(e),a=this._updateWeight(t);if(a>0){const t=this._interpolants,e=this._propertyBindings;if(this.blendMode===Je)for(let s=0,i=t.length;s!==i;++s)t[s].evaluate(n),e[s].accumulateAdditive(a);else for(let s=0,r=t.length;s!==r;++s)t[s].evaluate(n),e[s].accumulate(i,a)}}_updateWeight(t){let e=0;if(this.enabled){e=this.weight;const s=this._weightInterpolant;if(null!==s){const i=s.evaluate(t)[0];e*=i,t>s.parameterPositions[1]&&(this.stopFading(),0===i&&(this.enabled=!1))}}return this._effectiveWeight=e,e}_updateTimeScale(t){let e=0;if(!this.paused){e=this.timeScale;const s=this._timeScaleInterpolant;if(null!==s){e*=s.evaluate(t)[0],t>s.parameterPositions[1]&&(0===e?this.paused=!0:(null!==this._restoreTimeScale&&(e=this._restoreTimeScale),this.timeScale=e),this.stopWarping())}}return this._effectiveTimeScale=e,e}_updateTime(t){const e=this._clip.duration,s=this.loop;let i=this.time+t,r=this._loopCount;const n=2202===s;if(0===t)return-1===r||!n||1&~r?i:e-i;if(2200===s){-1===r&&(this._loopCount=0,this._setEndings(!0,!0,!1));t:{if(i>=e)i=e;else{if(!(i<0)){this.time=i;break t}i=0}this.clampWhenFinished?this.paused=!0:this.enabled=!1,this.time=i,this._mixer.dispatchEvent({type:"finished",action:this,direction:t<0?-1:1})}}else{if(-1===r&&(t>=0?(r=0,this._setEndings(!0,0===this.repetitions,n)):this._setEndings(0===this.repetitions,!0,n)),i>=e||i<0){const s=Math.floor(i/e);i-=e*s,r+=Math.abs(s);const a=this.repetitions-r;if(a<=0)this.clampWhenFinished?this.paused=!0:this.enabled=!1,i=t>0?e:0,this.time=i,this._mixer.dispatchEvent({type:"finished",action:this,direction:t>0?1:-1});else{if(1===a){const e=t<0;this._setEndings(e,!e,n)}else this._setEndings(!1,!1,n);this._loopCount=r,this.time=i,this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:s})}}else this._loopCount=r,this.time=i;if(n&&!(1&~r))return e-i}return i}_setEndings(t,e,s){const i=this._interpolantSettings;s?(i.endingStart=Ue,i.endingEnd=Ue):(i.endingStart=t?this.zeroSlopeAtStart?Ue:De:je,i.endingEnd=e?this.zeroSlopeAtEnd?Ue:De:je)}_scheduleFading(t,e,s){const i=this._mixer,r=i.time;let n=this._weightInterpolant;null===n&&(n=i._lendControlInterpolant(),this._weightInterpolant=n);const a=n.parameterPositions,o=n.sampleValues;return a[0]=r,o[0]=e,a[1]=r+t,o[1]=s,this}}const ad=new Float32Array(1);class od extends di{constructor(t){super(),this._root=t,this._initMemoryManager(),this._accuIndex=0,this.time=0,this.timeScale=1,"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}_bindAction(t,e){const s=t._localRoot||this._root,i=t._clip.tracks,r=i.length,n=t._propertyBindings,a=t._interpolants,o=s.uuid,h=this._bindingsByRootAndName;let l=h[o];void 0===l&&(l={},h[o]=l);for(let t=0;t!==r;++t){const r=i[t],h=r.name;let c=l[h];if(void 0!==c)++c.referenceCount,n[t]=c;else{if(c=n[t],void 0!==c){null===c._cacheIndex&&(++c.referenceCount,this._addInactiveBinding(c,o,h));continue}const i=e&&e._propertyBindings[t].binding.parsedPath;c=new Gu(id.create(s,h,i),r.ValueTypeName,r.getValueSize()),++c.referenceCount,this._addInactiveBinding(c,o,h),n[t]=c}a[t].resultBuffer=c.buffer}}_activateAction(t){if(!this._isActiveAction(t)){if(null===t._cacheIndex){const e=(t._localRoot||this._root).uuid,s=t._clip.uuid,i=this._actionsByClip[s];this._bindAction(t,i&&i.knownActions[0]),this._addInactiveAction(t,s,e)}const e=t._propertyBindings;for(let t=0,s=e.length;t!==s;++t){const s=e[t];0===s.useCount++&&(this._lendBinding(s),s.saveOriginalState())}this._lendAction(t)}}_deactivateAction(t){if(this._isActiveAction(t)){const e=t._propertyBindings;for(let t=0,s=e.length;t!==s;++t){const s=e[t];0===--s.useCount&&(s.restoreOriginalState(),this._takeBackBinding(s))}this._takeBackAction(t)}}_initMemoryManager(){this._actions=[],this._nActiveActions=0,this._actionsByClip={},this._bindings=[],this._nActiveBindings=0,this._bindingsByRootAndName={},this._controlInterpolants=[],this._nActiveControlInterpolants=0;const t=this;this.stats={actions:{get total(){return t._actions.length},get inUse(){return t._nActiveActions}},bindings:{get total(){return t._bindings.length},get inUse(){return t._nActiveBindings}},controlInterpolants:{get total(){return t._controlInterpolants.length},get inUse(){return t._nActiveControlInterpolants}}}}_isActiveAction(t){const e=t._cacheIndex;return null!==e&&e=0;--e)t[e].stop();return this}update(t){t*=this.timeScale;const e=this._actions,s=this._nActiveActions,i=this.time+=t,r=Math.sign(t),n=this._accuIndex^=1;for(let a=0;a!==s;++a){e[a]._update(i,t,r,n)}const a=this._bindings,o=this._nActiveBindings;for(let t=0;t!==o;++t)a[t].apply(n);return this}setTime(t){this.time=0;for(let t=0;t=this.min.x&&t.x<=this.max.x&&t.y>=this.min.y&&t.y<=this.max.y}containsBox(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y}getParameter(t,e){return e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y))}intersectsBox(t){return t.max.x>=this.min.x&&t.min.x<=this.max.x&&t.max.y>=this.min.y&&t.min.y<=this.max.y}clampPoint(t,e){return e.copy(t).clamp(this.min,this.max)}distanceToPoint(t){return this.clampPoint(t,Md).distanceTo(t)}intersect(t){return this.min.max(t.min),this.max.min(t.max),this.isEmpty()&&this.makeEmpty(),this}union(t){return this.min.min(t.min),this.max.max(t.max),this}translate(t){return this.min.add(t),this.max.add(t),this}equals(t){return t.min.equals(this.min)&&t.max.equals(this.max)}}const _d=new Ti,Ad=new Ti,Td=new Ti,zd=new Ti,Cd=new Ti,Id=new Ti,Bd=new Ti;class kd{constructor(t=new Ti,e=new Ti){this.start=t,this.end=e}set(t,e){return this.start.copy(t),this.end.copy(e),this}copy(t){return this.start.copy(t.start),this.end.copy(t.end),this}getCenter(t){return t.addVectors(this.start,this.end).multiplyScalar(.5)}delta(t){return t.subVectors(this.end,this.start)}distanceSq(){return this.start.distanceToSquared(this.end)}distance(){return this.start.distanceTo(this.end)}at(t,e){return this.delta(e).multiplyScalar(t).add(this.start)}closestPointToPointParameter(t,e){_d.subVectors(t,this.start),Ad.subVectors(this.end,this.start);const s=Ad.dot(Ad);if(0===s)return 0;let i=Ad.dot(_d)/s;return e&&(i=xi(i,0,1)),i}closestPointToPoint(t,e,s){const i=this.closestPointToPointParameter(t,e);return this.delta(s).multiplyScalar(i).add(this.start)}distanceSqToLine3(t,e=Id,s=Bd){const i=1e-8*1e-8;let r,n;const a=this.start,o=t.start,h=this.end,l=t.end;Td.subVectors(h,a),zd.subVectors(l,o),Cd.subVectors(a,o);const c=Td.dot(Td),u=zd.dot(zd),d=zd.dot(Cd);if(c<=i&&u<=i)return e.copy(a),s.copy(o),e.sub(s),e.dot(e);if(c<=i)r=0,n=d/u,n=xi(n,0,1);else{const t=Td.dot(Cd);if(u<=i)n=0,r=xi(-t/c,0,1);else{const e=Td.dot(zd),s=c*u-e*e;r=0!==s?xi((e*d-t*u)/s,0,1):0,n=(e*r+d)/u,n<0?(n=0,r=xi(-t/c,0,1)):n>1&&(n=1,r=xi((e-t)/c,0,1))}}return e.copy(a).addScaledVector(Td,r),s.copy(o).addScaledVector(zd,n),e.distanceToSquared(s)}applyMatrix4(t){return this.start.applyMatrix4(t),this.end.applyMatrix4(t),this}equals(t){return t.start.equals(this.start)&&t.end.equals(this.end)}clone(){return(new this.constructor).copy(this)}}const Od=new Ti;class Pd extends Ar{constructor(t,e){super(),this.light=t,this.matrixAutoUpdate=!1,this.color=e,this.type="SpotLightHelper";const s=new Wn,i=[0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,-1,0,1,0,0,0,0,1,1,0,0,0,0,-1,1];for(let t=0,e=1,s=32;t1)for(let s=0;s.99999)this.quaternion.set(0,0,0,1);else if(t.y<-.99999)this.quaternion.set(1,0,0,0);else{rp.set(t.z,0,-t.x).normalize();const e=Math.acos(t.y);this.quaternion.setFromAxisAngle(rp,e)}}setLength(t,e=.2*t,s=.2*e){this.line.scale.set(1,Math.max(1e-4,t-e),1),this.line.updateMatrix(),this.cone.scale.set(s,e,s),this.cone.position.y=t,this.cone.updateMatrix()}setColor(t){this.line.material.color.set(t),this.cone.material.color.set(t)}copy(t){return super.copy(t,!1),this.line.copy(t.line),this.cone.copy(t.cone),this}dispose(){this.line.geometry.dispose(),this.line.material.dispose(),this.cone.geometry.dispose(),this.cone.material.dispose()}}class hp extends Yo{constructor(t=1){const e=[0,0,0,t,0,0,0,0,0,0,t,0,0,0,0,0,0,t],s=new Wn;s.setAttribute("position",new kn(e,3)),s.setAttribute("color",new kn([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1],3));super(s,new No({vertexColors:!0,toneMapped:!1})),this.type="AxesHelper"}setColors(t,e,s){const i=new Pr,r=this.geometry.attributes.color.array;return i.set(t),i.toArray(r,0),i.toArray(r,3),i.set(e),i.toArray(r,6),i.toArray(r,9),i.set(s),i.toArray(r,12),i.toArray(r,15),this.geometry.attributes.color.needsUpdate=!0,this}dispose(){this.geometry.dispose(),this.material.dispose()}}class lp{constructor(){this.type="ShapePath",this.color=new Pr,this.subPaths=[],this.currentPath=null,this.userData={}}moveTo(t,e){return this.currentPath=new Zh,this.subPaths.push(this.currentPath),this.currentPath.moveTo(t,e),this}lineTo(t,e){return this.currentPath.lineTo(t,e),this}quadraticCurveTo(t,e,s,i){return this.currentPath.quadraticCurveTo(t,e,s,i),this}bezierCurveTo(t,e,s,i,r,n){return this.currentPath.bezierCurveTo(t,e,s,i,r,n),this}splineThru(t){return this.currentPath.splineThru(t),this}toShapes(){function t(t,e){let s=!1;const i=e.length;for(let r=0,n=i-1;rt.y!=a.y>t.y&&t.x<(a.x-i.x)*(t.y-i.y)/(a.y-i.y)+i.x&&(s=!s)}return s}function e(e,s){const i=s.getCenter(new _i);if(t(i,e))return i;const r=i.y,n=[],a=e.length;for(let t=0;tr!=i.y>r){const t=s.x+(r-s.y)*(i.x-s.x)/(i.y-s.y);n.push(t)}}return n.length>1&&(n.sort((t,e)=>t-e),i.x=(n[0]+n[1])/2),i}let s=this.userData.style&&this.userData.style.fillRule||"nonzero";"nonzero"!==s&&"evenodd"!==s&&(ai('Fill-rule "'+s+'" is not supported, falling back to "nonzero".'),s="nonzero");const i="nonzero"===s?t=>0!==t:t=>!!(1&t),r=[];for(const t of this.subPaths){const s=t.getPoints();if(s.length<3)continue;const i=_l.area(s);if(0===i)continue;const n=new Sd;for(let t=0;te.absArea-t.absArea);for(let e=0;e=0;i--){const e=r[i];if(e.boundingBox.containsPoint(s.interiorPoint)&&t(s.interiorPoint,e.points)){s.container=e.exclude?e.container:e,n=e.winding,s.winding+=n;break}}i(s.winding)===i(n)&&(s.exclude=!0)}for(const t of r)t.exclude||(t.role=null===t.container||"hole"===t.container.role?"outer":"hole");const n=[],a=new Map;for(const t of r){if(t.exclude||"outer"!==t.role)continue;const e=new Gh;e.curves=t.subPath.curves,n.push(e),a.set(t,e)}for(const t of r){if(t.exclude||"hole"!==t.role)continue;const e=a.get(t.container);if(!e)continue;const s=new Zh;s.curves=t.subPath.curves,e.holes.push(s)}return n}}class cp extends di{constructor(t,e=null){super(),this.object=t,this.domElement=e,this.enabled=!0,this.state=-1,this.keys={},this.mouseButtons={LEFT:null,MIDDLE:null,RIGHT:null},this.touches={ONE:null,TWO:null}}connect(t){void 0!==t?(null!==this.domElement&&this.disconnect(),this.domElement=t):ai("Controls: connect() now requires an element.")}disconnect(){}dispose(){}update(){}}function up(t,e,s,i){const r=function(t){switch(t){case zt:case Ct:return{byteLength:1,components:1};case Bt:case It:case Rt:return{byteLength:2,components:1};case Et:case Nt:return{byteLength:2,components:4};case Ot:case kt:case Pt:return{byteLength:4,components:1};case Lt:case Ft:return{byteLength:4,components:3}}throw new Error(`THREE.TextureUtils: Unknown texture type ${t}.`)}(i);switch(s){case 1021:return t*e;case qt:case Ht:return t*e/r.components*r.byteLength;case 1030:case 1031:return t*e*2/r.components*r.byteLength;case 1022:return t*e*3/r.components*r.byteLength;case jt:case 1033:return t*e*4/r.components*r.byteLength;case 33776:case 33777:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case 33778:case 33779:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case 35841:case 35843:return Math.max(t,16)*Math.max(e,8)/4;case 35840:case 35842:return Math.max(t,8)*Math.max(e,8)/2;case 36196:case 37492:case 37488:case 37489:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case 37496:case 37490:case 37491:case 37808:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case 37809:return Math.floor((t+4)/5)*Math.floor((e+3)/4)*16;case 37810:return Math.floor((t+4)/5)*Math.floor((e+4)/5)*16;case 37811:return Math.floor((t+5)/6)*Math.floor((e+4)/5)*16;case 37812:return Math.floor((t+5)/6)*Math.floor((e+5)/6)*16;case 37813:return Math.floor((t+7)/8)*Math.floor((e+4)/5)*16;case 37814:return Math.floor((t+7)/8)*Math.floor((e+5)/6)*16;case 37815:return Math.floor((t+7)/8)*Math.floor((e+7)/8)*16;case 37816:return Math.floor((t+9)/10)*Math.floor((e+4)/5)*16;case 37817:return Math.floor((t+9)/10)*Math.floor((e+5)/6)*16;case 37818:return Math.floor((t+9)/10)*Math.floor((e+7)/8)*16;case 37819:return Math.floor((t+9)/10)*Math.floor((e+9)/10)*16;case 37820:return Math.floor((t+11)/12)*Math.floor((e+9)/10)*16;case 37821:return Math.floor((t+11)/12)*Math.floor((e+11)/12)*16;case 36492:case 36494:case 36495:return Math.ceil(t/4)*Math.ceil(e/4)*16;case 36283:case 36284:return Math.ceil(t/4)*Math.ceil(e/4)*8;case 36285:case 36286:return Math.ceil(t/4)*Math.ceil(e/4)*16}throw new Error(`Unable to determine texture byte length for ${s} format.`)}class dp{static contain(t,e){return function(t,e){const s=t.image&&t.image.width?t.image.width/t.image.height:1;return s>e?(t.repeat.x=1,t.repeat.y=s/e,t.offset.x=0,t.offset.y=(1-t.repeat.y)/2):(t.repeat.x=e/s,t.repeat.y=1,t.offset.x=(1-t.repeat.x)/2,t.offset.y=0),t}(t,e)}static cover(t,e){return function(t,e){const s=t.image&&t.image.width?t.image.width/t.image.height:1;return s>e?(t.repeat.x=e/s,t.repeat.y=1,t.offset.x=(1-t.repeat.x)/2,t.offset.y=0):(t.repeat.x=1,t.repeat.y=s/e,t.offset.x=0,t.offset.y=(1-t.repeat.y)/2),t}(t,e)}static fill(t){return function(t){return t.repeat.x=1,t.repeat.y=1,t.offset.x=0,t.offset.y=0,t}(t)}static getByteLength(t,e,s,i){return up(t,e,s,i)}}"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register",{detail:{revision:t}})),"undefined"!=typeof window&&(window.__THREE__?ai("WARNING: Multiple instances of Three.js being imported."):window.__THREE__=t);export{st as ACESFilmicToneMapping,w as AddEquation,$ as AddOperation,Je as AdditiveAnimationBlendMode,g as AdditiveBlending,rt as AgXToneMapping,Dt as AlphaFormat,ks as AlwaysCompare,j as AlwaysDepth,Ss as AlwaysStencilFunc,lu as AmbientLight,nd as AnimationAction,Ac as AnimationClip,Ec as AnimationLoader,od as AnimationMixer,rd as AnimationObjectGroup,uc as AnimationUtils,Ih as ArcCurve,Ru as ArrayCamera,op as ArrowHelper,at as AttachedBindMode,Wu as Audio,Zu as AudioAnalyser,Tu as AudioContext,ju as AudioListener,zu as AudioLoader,hp as AxesHelper,d as BackSide,Ye as BasicDepthPacking,o as BasicShadowMap,Eo as BatchedMesh,gc as BezierInterpolant,Ha as Bone,xc as BooleanKeyframeTrack,Sd as Box2,Qr as Box3,sp as Box3Helper,yh as BoxGeometry,ep as BoxHelper,Mn as BufferAttribute,Wn as BufferGeometry,fu as BufferGeometryLoader,Ct as ByteType,zc as Cache,$c as Camera,Qd as CameraHelper,ch as CanvasTexture,gh as CapsuleGeometry,Nh as CatmullRomCurve3,et as CineonToneMapping,fh as CircleGeometry,yt as ClampToEdgeWrapping,xd as Clock,Pr as Color,bc as ColorKeyframeTrack,Ri as ColorManagement,Ys as Compatibility,oh as CompressedArrayTexture,hh as CompressedCubeTexture,ah as CompressedTexture,Nc as CompressedTextureLoader,bh as ConeGeometry,F as ConstantAlphaFactor,V as ConstantColorFactor,cp as Controls,Pu as CubeCamera,ph as CubeDepthTexture,lt as CubeReflectionMapping,ct as CubeRefractionMapping,lh as CubeTexture,Fc as CubeTextureLoader,pt as CubeUVReflectionMapping,Dh as CubicBezierCurve,Uh as CubicBezierCurve3,pc as CubicInterpolant,r as CullFaceBack,n as CullFaceFront,a as CullFaceFrontBack,i as CullFaceNone,zh as Curve,Yh as CurvePath,b as CustomBlending,it as CustomToneMapping,xh as CylinderGeometry,vd as Cylindrical,Gi as Data3DTexture,Yi as DataArrayTexture,Xa as DataTexture,Dc as DataTextureLoader,xn as DataUtils,ds as DecrementStencilOp,ms as DecrementWrapStencilOp,Bc as DefaultLoadingManager,Wt as DepthFormat,Jt as DepthStencilFormat,dh as DepthTexture,ot as DetachedBindMode,hu as DirectionalLight,Zd as DirectionalLightHelper,yc as DiscreteInterpolant,wh as DodecahedronGeometry,p as DoubleSide,O as DstAlphaFactor,R as DstColorFactor,Fs as DynamicCopyUsage,Ps as DynamicDrawUsage,Ns as DynamicReadUsage,Th as EdgesGeometry,Ch as EllipseCurve,Ts as EqualCompare,q as EqualDepth,xs as EqualStencilFunc,ut as EquirectangularReflectionMapping,dt as EquirectangularRefractionMapping,hr as Euler,di as EventDispatcher,mh as ExternalTexture,zl as ExtrudeGeometry,Rc as FileLoader,Bn as Float16BufferAttribute,kn as Float32BufferAttribute,Pt as FloatType,Nr as Fog,Er as FogExp2,nh as FramebufferTexture,u as FrontSide,mo as Frustum,go as FrustumArray,pd as GLBufferAttribute,Us as GLSL1,js as GLSL3,Cs as GreaterCompare,X as GreaterDepth,Bs as GreaterEqualCompare,H as GreaterEqualDepth,Ms as GreaterEqualStencilFunc,vs as GreaterStencilFunc,Jd as GridHelper,Tr as Group,uh as HTMLTexture,Rt as HalfFloatType,Wc as HemisphereLight,Wd as HemisphereLightHelper,Il as IcosahedronGeometry,_u as ImageBitmapLoader,Lc as ImageLoader,Li as ImageUtils,us as IncrementStencilOp,ps as IncrementWrapStencilOp,$a as InstancedBufferAttribute,gu as InstancedBufferGeometry,dd as InstancedInterleavedBuffer,no as InstancedMesh,Tn as Int16BufferAttribute,Cn as Int32BufferAttribute,Sn as Int8BufferAttribute,kt as IntType,Jn as InterleavedBuffer,Hn as InterleavedBufferAttribute,dc as Interpolant,Fe as InterpolateBezier,Ne as InterpolateDiscrete,Ve as InterpolateLinear,Le as InterpolateSmooth,Xs as InterpolationSamplingMode,Hs as InterpolationSamplingType,ys as InvertStencilOp,ls as KeepStencilOp,fc as KeyframeTrack,pa as LOD,Bl as LatheGeometry,lr as Layers,As as LessCompare,W as LessDepth,zs as LessEqualCompare,J as LessEqualDepth,bs as LessEqualStencilFunc,fs as LessStencilFunc,jc as Light,du as LightProbe,Jo as Line,kd as Line3,No as LineBasicMaterial,jh as LineCurve,Wh as LineCurve3,ac as LineDashedMaterial,Zo as LineLoop,Yo as LineSegments,Mt as LinearFilter,mc as LinearInterpolant,Tt as LinearMipMapLinearFilter,_t as LinearMipMapNearestFilter,At as LinearMipmapLinearFilter,St as LinearMipmapNearestFilter,ss as LinearSRGBColorSpace,K as LinearToneMapping,is as LinearTransfer,kc as Loader,yu as LoaderUtils,Ic as LoadingManager,Pe as LoopOnce,Ee as LoopPingPong,Re as LoopRepeat,e as MOUSE,Zn as Material,v as MaterialBlending,mu as MaterialLoader,Si as MathUtils,wd as Matrix2,Ii as Matrix3,Qi as Matrix4,A as MaxEquation,Ra as Mesh,Ma as MeshBasicMaterial,ic as MeshDepthMaterial,rc as MeshDistanceMaterial,sc as MeshLambertMaterial,nc as MeshMatcapMaterial,ec as MeshNormalMaterial,Kl as MeshPhongMaterial,Ql as MeshPhysicalMaterial,$l as MeshStandardMaterial,tc as MeshToonMaterial,_ as MinEquation,gt as MirroredRepeatWrapping,G as MixOperation,x as MultiplyBlending,Z as MultiplyOperation,ft as NearestFilter,wt as NearestMipMapLinearFilter,bt as NearestMipMapNearestFilter,vt as NearestMipmapLinearFilter,xt as NearestMipmapNearestFilter,nt as NeutralToneMapping,_s as NeverCompare,U as NeverDepth,gs as NeverStencilFunc,m as NoBlending,ts as NoColorSpace,ns as NoNormalPacking,Q as NoToneMapping,We as NormalAnimationBlendMode,y as NormalBlending,os as NormalGAPacking,as as NormalRGPacking,Is as NotEqualCompare,Y as NotEqualDepth,ws as NotEqualStencilFunc,vc as NumberKeyframeTrack,Ar as Object3D,bu as ObjectLoader,Ke as ObjectSpaceNormalMap,kl as OctahedronGeometry,z as OneFactor,D as OneMinusConstantAlphaFactor,L as OneMinusConstantColorFactor,P as OneMinusDstAlphaFactor,E as OneMinusDstColorFactor,k as OneMinusSrcAlphaFactor,I as OneMinusSrcColorFactor,au as OrthographicCamera,h as PCFShadowMap,l as PCFSoftShadowMap,Zh as Path,eu as PerspectiveCamera,lo as Plane,Ol as PlaneGeometry,ip as PlaneHelper,nu as PointLight,Fd as PointLightHelper,eh as Points,Go as PointsMaterial,qd as PolarGridHelper,vh as PolyhedronGeometry,Yu as PositionalAudio,id as PropertyBinding,Gu as PropertyMixer,Jh as QuadraticBezierCurve,qh as QuadraticBezierCurve3,Ai as Quaternion,Mc as QuaternionKeyframeTrack,wc as QuaternionLinearInterpolant,he as R11_EAC_Format,gi as RAD2DEG,ke as RED_GREEN_RGTC2_Format,Ie as RED_RGTC1_Format,t as REVISION,ce as RG11_EAC_Format,Ze as RGBADepthPacking,jt as RGBAFormat,Gt as RGBAIntegerFormat,Se as RGBA_ASTC_10x10_Format,ve as RGBA_ASTC_10x5_Format,we as RGBA_ASTC_10x6_Format,Me as RGBA_ASTC_10x8_Format,_e as RGBA_ASTC_12x10_Format,Ae as RGBA_ASTC_12x12_Format,de as RGBA_ASTC_4x4_Format,pe as RGBA_ASTC_5x4_Format,me as RGBA_ASTC_5x5_Format,ye as RGBA_ASTC_6x5_Format,ge as RGBA_ASTC_6x6_Format,fe as RGBA_ASTC_8x5_Format,xe as RGBA_ASTC_8x6_Format,be as RGBA_ASTC_8x8_Format,Te as RGBA_BPTC_Format,oe as RGBA_ETC2_EAC_Format,re as RGBA_PVRTC_2BPPV1_Format,ie as RGBA_PVRTC_4BPPV1_Format,Qt as RGBA_S3TC_DXT1_Format,Kt as RGBA_S3TC_DXT3_Format,te as RGBA_S3TC_DXT5_Format,Ge as RGBDepthPacking,Ut as RGBFormat,Zt as RGBIntegerFormat,ze as RGB_BPTC_SIGNED_Format,Ce as RGB_BPTC_UNSIGNED_Format,ne as RGB_ETC1_Format,ae as RGB_ETC2_Format,se as RGB_PVRTC_2BPPV1_Format,ee as RGB_PVRTC_4BPPV1_Format,$t as RGB_S3TC_DXT1_Format,$e as RGDepthPacking,Xt as RGFormat,Yt as RGIntegerFormat,Gl as RawShaderMaterial,wa as Ray,yd as Raycaster,cu as RectAreaLight,qt as RedFormat,Ht as RedIntegerFormat,tt as ReinhardToneMapping,Hi as RenderTarget,hd as RenderTarget3D,mt as RepeatWrapping,cs as ReplaceStencilOp,S as ReverseSubtractEquation,ui as ReversedDepthFuncs,Pl as RingGeometry,le as SIGNED_R11_EAC_Format,Oe as SIGNED_RED_GREEN_RGTC2_Format,Be as SIGNED_RED_RGTC1_Format,ue as SIGNED_RG11_EAC_Format,es as SRGBColorSpace,rs as SRGBTransfer,Vr as Scene,Zl as ShaderMaterial,Wl as ShadowMaterial,Gh as Shape,Rl as ShapeGeometry,lp as ShapePath,_l as ShapeUtils,It as ShortType,Ga as Skeleton,Vd as SkeletonHelper,qa as SkinnedMesh,Di as Source,En as Sphere,El as SphereGeometry,bd as Spherical,uu as SphericalHarmonics3,Hh as SplineCurve,iu as SpotLight,Pd as SpotLightHelper,la as Sprite,Gn as SpriteMaterial,B as SrcAlphaFactor,N as SrcAlphaSaturateFactor,C as SrcColorFactor,Ls as StaticCopyUsage,Os as StaticDrawUsage,Es as StaticReadUsage,ku as StereoCamera,Ds as StreamCopyUsage,Rs as StreamDrawUsage,Vs as StreamReadUsage,Sc as StringKeyframeTrack,M as SubtractEquation,f as SubtractiveBlending,s as TOUCH,Qe as TangentSpaceNormalMap,Nl as TetrahedronGeometry,Ji as Texture,Uc as TextureLoader,dp as TextureUtils,Eu as Timer,qs as TimestampQuery,Vl as TorusGeometry,Ll as TorusKnotGeometry,$r as Triangle,Xe as TriangleFanDrawMode,He as TriangleStripDrawMode,qe as TrianglesDrawMode,Fl as TubeGeometry,ht as UVMapping,zn as Uint16BufferAttribute,In as Uint32BufferAttribute,_n as Uint8BufferAttribute,An as Uint8ClampedBufferAttribute,ld as Uniform,ud as UniformsGroup,Yl as UniformsUtils,zt as UnsignedByteType,Ft as UnsignedInt101111Type,Vt as UnsignedInt248Type,Lt as UnsignedInt5999Type,Ot as UnsignedIntType,Et as UnsignedShort4444Type,Nt as UnsignedShort5551Type,Bt as UnsignedShortType,c as VSMShadowMap,_i as Vector2,Ti as Vector3,qi as Vector4,_c as VectorKeyframeTrack,rh as VideoFrameTexture,ih as VideoTexture,$i as WebGL3DRenderTarget,Zi as WebGLArrayRenderTarget,Ws as WebGLCoordinateSystem,Xi as WebGLRenderTarget,Js as WebGPUCoordinateSystem,Cr as WebXRController,Dl as WireframeGeometry,je as WrapAroundEnding,De as ZeroCurvatureEnding,T as ZeroFactor,Ue as ZeroSlopeEnding,hs as ZeroStencilOp,Jl as cloneUniforms,Ks as createCanvasElement,Qs as createElementNS,oi as error,up as getByteLength,ii as getConsoleFunction,Xl as getUnlitUniformColorSpace,$s as isTypedArray,ri as log,ql as mergeUniforms,ci as probeAsync,si as setConsoleFunction,ai as warn,hi as warnOnce,li as yieldToMain}; +const t="185",e={LEFT:0,MIDDLE:1,RIGHT:2,ROTATE:0,DOLLY:1,PAN:2},s={ROTATE:0,PAN:1,DOLLY_PAN:2,DOLLY_ROTATE:3},i=0,r=1,n=2,a=3,o=0,h=1,l=2,c=3,u=0,d=1,p=2,m=0,y=1,g=2,f=3,x=4,b=5,v=6,w=100,M=101,S=102,_=103,A=104,T=200,z=201,C=202,I=203,B=204,k=205,O=206,P=207,R=208,E=209,N=210,V=211,L=212,F=213,D=214,U=0,j=1,W=2,J=3,q=4,H=5,X=6,Y=7,Z=0,G=1,$=2,Q=0,K=1,tt=2,et=3,st=4,it=5,rt=6,nt=7,at="attached",ot="detached",ht=300,lt=301,ct=302,ut=303,dt=304,pt=306,mt=1e3,yt=1001,gt=1002,ft=1003,xt=1004,bt=1004,vt=1005,wt=1005,Mt=1006,St=1007,_t=1007,At=1008,Tt=1008,zt=1009,Ct=1010,It=1011,Bt=1012,kt=1013,Ot=1014,Pt=1015,Rt=1016,Et=1017,Nt=1018,Vt=1020,Lt=35902,Ft=35899,Dt=1021,Ut=1022,jt=1023,Wt=1026,Jt=1027,qt=1028,Ht=1029,Xt=1030,Yt=1031,Zt=1032,Gt=1033,$t=33776,Qt=33777,Kt=33778,te=33779,ee=35840,se=35841,ie=35842,re=35843,ne=36196,ae=37492,oe=37496,he=37488,le=37489,ce=37490,ue=37491,de=37808,pe=37809,me=37810,ye=37811,ge=37812,fe=37813,xe=37814,be=37815,ve=37816,we=37817,Me=37818,Se=37819,_e=37820,Ae=37821,Te=36492,ze=36494,Ce=36495,Ie=36283,Be=36284,ke=36285,Oe=36286,Pe=2200,Re=2201,Ee=2202,Ne=2300,Ve=2301,Le=2302,Fe=2303,De=2400,Ue=2401,je=2402,We=2500,Je=2501,qe=0,He=1,Xe=2,Ye=3200,Ze=3201,Ge=3202,$e=3203,Qe=0,Ke=1,ts="",es="srgb",ss="srgb-linear",is="linear",rs="srgb",ns="",as="rg",os="ga",hs=0,ls=7680,cs=7681,us=7682,ds=7683,ps=34055,ms=34056,ys=5386,gs=512,fs=513,xs=514,bs=515,vs=516,ws=517,Ms=518,Ss=519,_s=512,As=513,Ts=514,zs=515,Cs=516,Is=517,Bs=518,ks=519,Os=35044,Ps=35048,Rs=35040,Es=35045,Ns=35049,Vs=35041,Ls=35046,Fs=35050,Ds=35042,Us="100",js="300 es",Ws=2e3,Js=2001,qs={COMPUTE:"compute",RENDER:"render"},Hs={PERSPECTIVE:"perspective",LINEAR:"linear",FLAT:"flat"},Xs={NORMAL:"normal",CENTROID:"centroid",SAMPLE:"sample",FIRST:"first",EITHER:"either"},Ys={TEXTURE_COMPARE:"depthTextureCompare"};const Zs={Int8Array:Int8Array,Uint8Array:Uint8Array,Uint8ClampedArray:Uint8ClampedArray,Int16Array:Int16Array,Uint16Array:Uint16Array,Int32Array:Int32Array,Uint32Array:Uint32Array,Float32Array:Float32Array,Float64Array:Float64Array};function Gs(t,e){return new Zs[t](e)}function $s(t){return ArrayBuffer.isView(t)&&!(t instanceof DataView)}function Qs(t){return document.createElementNS("http://www.w3.org/1999/xhtml",t)}function Ks(){const t=Qs("canvas");return t.style.display="block",t}const ti={};let ei=null;function si(t){ei=t}function ii(){return ei}function ri(...t){const e="THREE."+t.shift();ei?ei("log",e,...t):console.log(e,...t)}function ni(t){const e=t[0];if("string"==typeof e&&e.startsWith("TSL:")){const e=t[1];e&&e.isStackTrace?t[0]+=" "+e.getLocation():t[1]='Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.'}return t}function ai(...t){const e="THREE."+(t=ni(t)).shift();if(ei)ei("warn",e,...t);else{const s=t[0];s&&s.isStackTrace?console.warn(s.getError(e)):console.warn(e,...t)}}function oi(...t){const e="THREE."+(t=ni(t)).shift();if(ei)ei("error",e,...t);else{const s=t[0];s&&s.isStackTrace?console.error(s.getError(e)):console.error(e,...t)}}function hi(...t){const e=t.join(" ");e in ti||(ti[e]=!0,ai(...t))}function li(){return"undefined"!=typeof self&&void 0!==self.scheduler&&void 0!==self.scheduler.yield?self.scheduler.yield():new Promise(t=>{requestAnimationFrame(t)})}function ci(t,e,s){return new Promise(function(i,r){setTimeout(function n(){switch(t.clientWaitSync(e,t.SYNC_FLUSH_COMMANDS_BIT,0)){case t.WAIT_FAILED:r();break;case t.TIMEOUT_EXPIRED:setTimeout(n,s);break;default:i()}},s)})}const ui={[U]:1,[W]:6,[q]:7,[J]:5,[j]:0,[X]:2,[Y]:4,[H]:3};class di{addEventListener(t,e){void 0===this._listeners&&(this._listeners={});const s=this._listeners;void 0===s[t]&&(s[t]=[]),-1===s[t].indexOf(e)&&s[t].push(e)}hasEventListener(t,e){const s=this._listeners;return void 0!==s&&(void 0!==s[t]&&-1!==s[t].indexOf(e))}removeEventListener(t,e){const s=this._listeners;if(void 0===s)return;const i=s[t];if(void 0!==i){const t=i.indexOf(e);-1!==t&&i.splice(t,1)}}dispatchEvent(t){const e=this._listeners;if(void 0===e)return;const s=e[t.type];if(void 0!==s){t.target=this;const e=s.slice(0);for(let s=0,i=e.length;s>8&255]+pi[t>>16&255]+pi[t>>24&255]+"-"+pi[255&e]+pi[e>>8&255]+"-"+pi[e>>16&15|64]+pi[e>>24&255]+"-"+pi[63&s|128]+pi[s>>8&255]+"-"+pi[s>>16&255]+pi[s>>24&255]+pi[255&i]+pi[i>>8&255]+pi[i>>16&255]+pi[i>>24&255]).toLowerCase()}function xi(t,e,s){return Math.max(e,Math.min(s,t))}function bi(t,e){return(t%e+e)%e}function vi(t,e,s){return(1-s)*t+s*e}function wi(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return t/4294967295;case Uint16Array:return t/65535;case Uint8Array:return t/255;case Int32Array:return Math.max(t/2147483647,-1);case Int16Array:return Math.max(t/32767,-1);case Int8Array:return Math.max(t/127,-1);default:throw new Error("THREE.MathUtils: Invalid component type.")}}function Mi(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return Math.round(4294967295*t);case Uint16Array:return Math.round(65535*t);case Uint8Array:return Math.round(255*t);case Int32Array:return Math.round(2147483647*t);case Int16Array:return Math.round(32767*t);case Int8Array:return Math.round(127*t);default:throw new Error("THREE.MathUtils: Invalid component type.")}}const Si={DEG2RAD:yi,RAD2DEG:gi,generateUUID:fi,clamp:xi,euclideanModulo:bi,mapLinear:function(t,e,s,i,r){return i+(t-e)*(r-i)/(s-e)},inverseLerp:function(t,e,s){return t!==e?(s-t)/(e-t):0},lerp:vi,damp:function(t,e,s,i){return vi(t,e,1-Math.exp(-s*i))},pingpong:function(t,e=1){return e-Math.abs(bi(t,2*e)-e)},smoothstep:function(t,e,s){return t<=e?0:t>=s?1:(t=(t-e)/(s-e))*t*(3-2*t)},smootherstep:function(t,e,s){return t<=e?0:t>=s?1:(t=(t-e)/(s-e))*t*t*(t*(6*t-15)+10)},randInt:function(t,e){return t+Math.floor(Math.random()*(e-t+1))},randFloat:function(t,e){return t+Math.random()*(e-t)},randFloatSpread:function(t){return t*(.5-Math.random())},seededRandom:function(t){void 0!==t&&(mi=t);let e=mi+=1831565813;return e=Math.imul(e^e>>>15,1|e),e^=e+Math.imul(e^e>>>7,61|e),((e^e>>>14)>>>0)/4294967296},degToRad:function(t){return t*yi},radToDeg:function(t){return t*gi},isPowerOfTwo:function(t){return!(t&t-1)&&0!==t},ceilPowerOfTwo:function(t){return Math.pow(2,Math.ceil(Math.log(t)/Math.LN2))},floorPowerOfTwo:function(t){return Math.pow(2,Math.floor(Math.log(t)/Math.LN2))},setQuaternionFromProperEuler:function(t,e,s,i,r){const n=Math.cos,a=Math.sin,o=n(s/2),h=a(s/2),l=n((e+i)/2),c=a((e+i)/2),u=n((e-i)/2),d=a((e-i)/2),p=n((i-e)/2),m=a((i-e)/2);switch(r){case"XYX":t.set(o*c,h*u,h*d,o*l);break;case"YZY":t.set(h*d,o*c,h*u,o*l);break;case"ZXZ":t.set(h*u,h*d,o*c,o*l);break;case"XZX":t.set(o*c,h*m,h*p,o*l);break;case"YXY":t.set(h*p,o*c,h*m,o*l);break;case"ZYZ":t.set(h*m,h*p,o*c,o*l);break;default:ai("MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: "+r)}},normalize:Mi,denormalize:wi};class _i{static{_i.prototype.isVector2=!0}constructor(t=0,e=0){this.x=t,this.y=e}get width(){return this.x}set width(t){this.x=t}get height(){return this.y}set height(t){this.y=t}set(t,e){return this.x=t,this.y=e,this}setScalar(t){return this.x=t,this.y=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;default:throw new Error("THREE.Vector2: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;default:throw new Error("THREE.Vector2: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y)}copy(t){return this.x=t.x,this.y=t.y,this}add(t){return this.x+=t.x,this.y+=t.y,this}addScalar(t){return this.x+=t,this.y+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this}subScalar(t){return this.x-=t,this.y-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this}multiply(t){return this.x*=t.x,this.y*=t.y,this}multiplyScalar(t){return this.x*=t,this.y*=t,this}divide(t){return this.x/=t.x,this.y/=t.y,this}divideScalar(t){return this.multiplyScalar(1/t)}applyMatrix3(t){const e=this.x,s=this.y,i=t.elements;return this.x=i[0]*e+i[3]*s+i[6],this.y=i[1]*e+i[4]*s+i[7],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this}clamp(t,e){return this.x=xi(this.x,t.x,e.x),this.y=xi(this.y,t.y,e.y),this}clampScalar(t,e){return this.x=xi(this.x,t,e),this.y=xi(this.y,t,e),this}clampLength(t,e){const s=this.length();return this.divideScalar(s||1).multiplyScalar(xi(s,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this}negate(){return this.x=-this.x,this.y=-this.y,this}dot(t){return this.x*t.x+this.y*t.y}cross(t){return this.x*t.y-this.y*t.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){return Math.atan2(-this.y,-this.x)+Math.PI}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(0===e)return Math.PI/2;const s=this.dot(t)/e;return Math.acos(xi(s,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,s=this.y-t.y;return e*e+s*s}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this}lerpVectors(t,e,s){return this.x=t.x+(e.x-t.x)*s,this.y=t.y+(e.y-t.y)*s,this}equals(t){return t.x===this.x&&t.y===this.y}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this}rotateAround(t,e){const s=Math.cos(e),i=Math.sin(e),r=this.x-t.x,n=this.y-t.y;return this.x=r*s-n*i+t.x,this.y=r*i+n*s+t.y,this}random(){return this.x=Math.random(),this.y=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y}}class Ai{constructor(t=0,e=0,s=0,i=1){this.isQuaternion=!0,this._x=t,this._y=e,this._z=s,this._w=i}static slerpFlat(t,e,s,i,r,n,a){let o=s[i+0],h=s[i+1],l=s[i+2],c=s[i+3],u=r[n+0],d=r[n+1],p=r[n+2],m=r[n+3];if(c!==m||o!==u||h!==d||l!==p){let t=o*u+h*d+l*p+c*m;t<0&&(u=-u,d=-d,p=-p,m=-m,t=-t);let e=1-a;if(t<.9995){const s=Math.acos(t),i=Math.sin(s);e=Math.sin(e*s)/i,o=o*e+u*(a=Math.sin(a*s)/i),h=h*e+d*a,l=l*e+p*a,c=c*e+m*a}else{o=o*e+u*a,h=h*e+d*a,l=l*e+p*a,c=c*e+m*a;const t=1/Math.sqrt(o*o+h*h+l*l+c*c);o*=t,h*=t,l*=t,c*=t}}t[e]=o,t[e+1]=h,t[e+2]=l,t[e+3]=c}static multiplyQuaternionsFlat(t,e,s,i,r,n){const a=s[i],o=s[i+1],h=s[i+2],l=s[i+3],c=r[n],u=r[n+1],d=r[n+2],p=r[n+3];return t[e]=a*p+l*c+o*d-h*u,t[e+1]=o*p+l*u+h*c-a*d,t[e+2]=h*p+l*d+a*u-o*c,t[e+3]=l*p-a*c-o*u-h*d,t}get x(){return this._x}set x(t){this._x=t,this._onChangeCallback()}get y(){return this._y}set y(t){this._y=t,this._onChangeCallback()}get z(){return this._z}set z(t){this._z=t,this._onChangeCallback()}get w(){return this._w}set w(t){this._w=t,this._onChangeCallback()}set(t,e,s,i){return this._x=t,this._y=e,this._z=s,this._w=i,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(t){return this._x=t.x,this._y=t.y,this._z=t.z,this._w=t.w,this._onChangeCallback(),this}setFromEuler(t,e=!0){const s=t._x,i=t._y,r=t._z,n=t._order,a=Math.cos,o=Math.sin,h=a(s/2),l=a(i/2),c=a(r/2),u=o(s/2),d=o(i/2),p=o(r/2);switch(n){case"XYZ":this._x=u*l*c+h*d*p,this._y=h*d*c-u*l*p,this._z=h*l*p+u*d*c,this._w=h*l*c-u*d*p;break;case"YXZ":this._x=u*l*c+h*d*p,this._y=h*d*c-u*l*p,this._z=h*l*p-u*d*c,this._w=h*l*c+u*d*p;break;case"ZXY":this._x=u*l*c-h*d*p,this._y=h*d*c+u*l*p,this._z=h*l*p+u*d*c,this._w=h*l*c-u*d*p;break;case"ZYX":this._x=u*l*c-h*d*p,this._y=h*d*c+u*l*p,this._z=h*l*p-u*d*c,this._w=h*l*c+u*d*p;break;case"YZX":this._x=u*l*c+h*d*p,this._y=h*d*c+u*l*p,this._z=h*l*p-u*d*c,this._w=h*l*c-u*d*p;break;case"XZY":this._x=u*l*c-h*d*p,this._y=h*d*c-u*l*p,this._z=h*l*p+u*d*c,this._w=h*l*c+u*d*p;break;default:ai("Quaternion: .setFromEuler() encountered an unknown order: "+n)}return!0===e&&this._onChangeCallback(),this}setFromAxisAngle(t,e){const s=e/2,i=Math.sin(s);return this._x=t.x*i,this._y=t.y*i,this._z=t.z*i,this._w=Math.cos(s),this._onChangeCallback(),this}setFromRotationMatrix(t){const e=t.elements,s=e[0],i=e[4],r=e[8],n=e[1],a=e[5],o=e[9],h=e[2],l=e[6],c=e[10],u=s+a+c;if(u>0){const t=.5/Math.sqrt(u+1);this._w=.25/t,this._x=(l-o)*t,this._y=(r-h)*t,this._z=(n-i)*t}else if(s>a&&s>c){const t=2*Math.sqrt(1+s-a-c);this._w=(l-o)/t,this._x=.25*t,this._y=(i+n)/t,this._z=(r+h)/t}else if(a>c){const t=2*Math.sqrt(1+a-s-c);this._w=(r-h)/t,this._x=(i+n)/t,this._y=.25*t,this._z=(o+l)/t}else{const t=2*Math.sqrt(1+c-s-a);this._w=(n-i)/t,this._x=(r+h)/t,this._y=(o+l)/t,this._z=.25*t}return this._onChangeCallback(),this}setFromUnitVectors(t,e){let s=t.dot(e)+1;return s<1e-8?(s=0,Math.abs(t.x)>Math.abs(t.z)?(this._x=-t.y,this._y=t.x,this._z=0,this._w=s):(this._x=0,this._y=-t.z,this._z=t.y,this._w=s)):(this._x=t.y*e.z-t.z*e.y,this._y=t.z*e.x-t.x*e.z,this._z=t.x*e.y-t.y*e.x,this._w=s),this.normalize()}angleTo(t){return 2*Math.acos(Math.abs(xi(this.dot(t),-1,1)))}rotateTowards(t,e){const s=this.angleTo(t);if(0===s)return this;const i=Math.min(1,e/s);return this.slerp(t,i),this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this}dot(t){return this._x*t._x+this._y*t._y+this._z*t._z+this._w*t._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let t=this.length();return 0===t?(this._x=0,this._y=0,this._z=0,this._w=1):(t=1/t,this._x=this._x*t,this._y=this._y*t,this._z=this._z*t,this._w=this._w*t),this._onChangeCallback(),this}multiply(t){return this.multiplyQuaternions(this,t)}premultiply(t){return this.multiplyQuaternions(t,this)}multiplyQuaternions(t,e){const s=t._x,i=t._y,r=t._z,n=t._w,a=e._x,o=e._y,h=e._z,l=e._w;return this._x=s*l+n*a+i*h-r*o,this._y=i*l+n*o+r*a-s*h,this._z=r*l+n*h+s*o-i*a,this._w=n*l-s*a-i*o-r*h,this._onChangeCallback(),this}slerp(t,e){let s=t._x,i=t._y,r=t._z,n=t._w,a=this.dot(t);a<0&&(s=-s,i=-i,r=-r,n=-n,a=-a);let o=1-e;if(a<.9995){const t=Math.acos(a),h=Math.sin(t);o=Math.sin(o*t)/h,e=Math.sin(e*t)/h,this._x=this._x*o+s*e,this._y=this._y*o+i*e,this._z=this._z*o+r*e,this._w=this._w*o+n*e,this._onChangeCallback()}else this._x=this._x*o+s*e,this._y=this._y*o+i*e,this._z=this._z*o+r*e,this._w=this._w*o+n*e,this.normalize();return this}slerpQuaternions(t,e,s){return this.copy(t).slerp(e,s)}random(){const t=2*Math.PI*Math.random(),e=2*Math.PI*Math.random(),s=Math.random(),i=Math.sqrt(1-s),r=Math.sqrt(s);return this.set(i*Math.sin(t),i*Math.cos(t),r*Math.sin(e),r*Math.cos(e))}equals(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._w===this._w}fromArray(t,e=0){return this._x=t[e],this._y=t[e+1],this._z=t[e+2],this._w=t[e+3],this._onChangeCallback(),this}toArray(t=[],e=0){return t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._w,t}fromBufferAttribute(t,e){return this._x=t.getX(e),this._y=t.getY(e),this._z=t.getZ(e),this._w=t.getW(e),this._onChangeCallback(),this}toJSON(){return this.toArray()}_onChange(t){return this._onChangeCallback=t,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._w}}class Ti{static{Ti.prototype.isVector3=!0}constructor(t=0,e=0,s=0){this.x=t,this.y=e,this.z=s}set(t,e,s){return void 0===s&&(s=this.z),this.x=t,this.y=e,this.z=s,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;default:throw new Error("THREE.Vector3: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("THREE.Vector3: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this}multiplyVectors(t,e){return this.x=t.x*e.x,this.y=t.y*e.y,this.z=t.z*e.z,this}applyEuler(t){return this.applyQuaternion(Ci.setFromEuler(t))}applyAxisAngle(t,e){return this.applyQuaternion(Ci.setFromAxisAngle(t,e))}applyMatrix3(t){const e=this.x,s=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[3]*s+r[6]*i,this.y=r[1]*e+r[4]*s+r[7]*i,this.z=r[2]*e+r[5]*s+r[8]*i,this}applyNormalMatrix(t){return this.applyMatrix3(t).normalize()}applyMatrix4(t){const e=this.x,s=this.y,i=this.z,r=t.elements,n=1/(r[3]*e+r[7]*s+r[11]*i+r[15]);return this.x=(r[0]*e+r[4]*s+r[8]*i+r[12])*n,this.y=(r[1]*e+r[5]*s+r[9]*i+r[13])*n,this.z=(r[2]*e+r[6]*s+r[10]*i+r[14])*n,this}applyQuaternion(t){const e=this.x,s=this.y,i=this.z,r=t.x,n=t.y,a=t.z,o=t.w,h=2*(n*i-a*s),l=2*(a*e-r*i),c=2*(r*s-n*e);return this.x=e+o*h+n*c-a*l,this.y=s+o*l+a*h-r*c,this.z=i+o*c+r*l-n*h,this}project(t){return this.applyMatrix4(t.matrixWorldInverse).applyMatrix4(t.projectionMatrix)}unproject(t){return this.applyMatrix4(t.projectionMatrixInverse).applyMatrix4(t.matrixWorld)}transformDirection(t){const e=this.x,s=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[4]*s+r[8]*i,this.y=r[1]*e+r[5]*s+r[9]*i,this.z=r[2]*e+r[6]*s+r[10]*i,this.normalize()}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this}divideScalar(t){return this.multiplyScalar(1/t)}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this}clamp(t,e){return this.x=xi(this.x,t.x,e.x),this.y=xi(this.y,t.y,e.y),this.z=xi(this.z,t.z,e.z),this}clampScalar(t,e){return this.x=xi(this.x,t,e),this.y=xi(this.y,t,e),this.z=xi(this.z,t,e),this}clampLength(t,e){const s=this.length();return this.divideScalar(s||1).multiplyScalar(xi(s,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this}lerpVectors(t,e,s){return this.x=t.x+(e.x-t.x)*s,this.y=t.y+(e.y-t.y)*s,this.z=t.z+(e.z-t.z)*s,this}cross(t){return this.crossVectors(this,t)}crossVectors(t,e){const s=t.x,i=t.y,r=t.z,n=e.x,a=e.y,o=e.z;return this.x=i*o-r*a,this.y=r*n-s*o,this.z=s*a-i*n,this}projectOnVector(t){const e=t.lengthSq();if(0===e)return this.set(0,0,0);const s=t.dot(this)/e;return this.copy(t).multiplyScalar(s)}projectOnPlane(t){return zi.copy(this).projectOnVector(t),this.sub(zi)}reflect(t){return this.sub(zi.copy(t).multiplyScalar(2*this.dot(t)))}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(0===e)return Math.PI/2;const s=this.dot(t)/e;return Math.acos(xi(s,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,s=this.y-t.y,i=this.z-t.z;return e*e+s*s+i*i}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)+Math.abs(this.z-t.z)}setFromSpherical(t){return this.setFromSphericalCoords(t.radius,t.phi,t.theta)}setFromSphericalCoords(t,e,s){const i=Math.sin(e)*t;return this.x=i*Math.sin(s),this.y=Math.cos(e)*t,this.z=i*Math.cos(s),this}setFromCylindrical(t){return this.setFromCylindricalCoords(t.radius,t.theta,t.y)}setFromCylindricalCoords(t,e,s){return this.x=t*Math.sin(e),this.y=s,this.z=t*Math.cos(e),this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this}setFromMatrixScale(t){const e=this.setFromMatrixColumn(t,0).length(),s=this.setFromMatrixColumn(t,1).length(),i=this.setFromMatrixColumn(t,2).length();return this.x=e,this.y=s,this.z=i,this}setFromMatrixColumn(t,e){return this.fromArray(t.elements,4*e)}setFromMatrix3Column(t,e){return this.fromArray(t.elements,3*e)}setFromEuler(t){return this.x=t._x,this.y=t._y,this.z=t._z,this}setFromColor(t){return this.x=t.r,this.y=t.g,this.z=t.b,this}equals(t){return t.x===this.x&&t.y===this.y&&t.z===this.z}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this.z=t[e+2],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this}randomDirection(){const t=Math.random()*Math.PI*2,e=2*Math.random()-1,s=Math.sqrt(1-e*e);return this.x=s*Math.cos(t),this.y=e,this.z=s*Math.sin(t),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z}}const zi=new Ti,Ci=new Ai;class Ii{static{Ii.prototype.isMatrix3=!0}constructor(t,e,s,i,r,n,a,o,h){this.elements=[1,0,0,0,1,0,0,0,1],void 0!==t&&this.set(t,e,s,i,r,n,a,o,h)}set(t,e,s,i,r,n,a,o,h){const l=this.elements;return l[0]=t,l[1]=i,l[2]=a,l[3]=e,l[4]=r,l[5]=o,l[6]=s,l[7]=n,l[8]=h,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(t){const e=this.elements,s=t.elements;return e[0]=s[0],e[1]=s[1],e[2]=s[2],e[3]=s[3],e[4]=s[4],e[5]=s[5],e[6]=s[6],e[7]=s[7],e[8]=s[8],this}extractBasis(t,e,s){return t.setFromMatrix3Column(this,0),e.setFromMatrix3Column(this,1),s.setFromMatrix3Column(this,2),this}setFromMatrix4(t){const e=t.elements;return this.set(e[0],e[4],e[8],e[1],e[5],e[9],e[2],e[6],e[10]),this}multiply(t){return this.multiplyMatrices(this,t)}premultiply(t){return this.multiplyMatrices(t,this)}multiplyMatrices(t,e){const s=t.elements,i=e.elements,r=this.elements,n=s[0],a=s[3],o=s[6],h=s[1],l=s[4],c=s[7],u=s[2],d=s[5],p=s[8],m=i[0],y=i[3],g=i[6],f=i[1],x=i[4],b=i[7],v=i[2],w=i[5],M=i[8];return r[0]=n*m+a*f+o*v,r[3]=n*y+a*x+o*w,r[6]=n*g+a*b+o*M,r[1]=h*m+l*f+c*v,r[4]=h*y+l*x+c*w,r[7]=h*g+l*b+c*M,r[2]=u*m+d*f+p*v,r[5]=u*y+d*x+p*w,r[8]=u*g+d*b+p*M,this}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[3]*=t,e[6]*=t,e[1]*=t,e[4]*=t,e[7]*=t,e[2]*=t,e[5]*=t,e[8]*=t,this}determinant(){const t=this.elements,e=t[0],s=t[1],i=t[2],r=t[3],n=t[4],a=t[5],o=t[6],h=t[7],l=t[8];return e*n*l-e*a*h-s*r*l+s*a*o+i*r*h-i*n*o}invert(){const t=this.elements,e=t[0],s=t[1],i=t[2],r=t[3],n=t[4],a=t[5],o=t[6],h=t[7],l=t[8],c=l*n-a*h,u=a*o-l*r,d=h*r-n*o,p=e*c+s*u+i*d;if(0===p)return this.set(0,0,0,0,0,0,0,0,0);const m=1/p;return t[0]=c*m,t[1]=(i*h-l*s)*m,t[2]=(a*s-i*n)*m,t[3]=u*m,t[4]=(l*e-i*o)*m,t[5]=(i*r-a*e)*m,t[6]=d*m,t[7]=(s*o-h*e)*m,t[8]=(n*e-s*r)*m,this}transpose(){let t;const e=this.elements;return t=e[1],e[1]=e[3],e[3]=t,t=e[2],e[2]=e[6],e[6]=t,t=e[5],e[5]=e[7],e[7]=t,this}getNormalMatrix(t){return this.setFromMatrix4(t).invert().transpose()}transposeIntoArray(t){const e=this.elements;return t[0]=e[0],t[1]=e[3],t[2]=e[6],t[3]=e[1],t[4]=e[4],t[5]=e[7],t[6]=e[2],t[7]=e[5],t[8]=e[8],this}setUvTransform(t,e,s,i,r,n,a){const o=Math.cos(r),h=Math.sin(r);return this.set(s*o,s*h,-s*(o*n+h*a)+n+t,-i*h,i*o,-i*(-h*n+o*a)+a+e,0,0,1),this}scale(t,e){return hi("Matrix3: .scale() is deprecated. Use .makeScale() instead."),this.premultiply(Bi.makeScale(t,e)),this}rotate(t){return hi("Matrix3: .rotate() is deprecated. Use .makeRotation() instead."),this.premultiply(Bi.makeRotation(-t)),this}translate(t,e){return hi("Matrix3: .translate() is deprecated. Use .makeTranslation() instead."),this.premultiply(Bi.makeTranslation(t,e)),this}makeTranslation(t,e){return t.isVector2?this.set(1,0,t.x,0,1,t.y,0,0,1):this.set(1,0,t,0,1,e,0,0,1),this}makeRotation(t){const e=Math.cos(t),s=Math.sin(t);return this.set(e,-s,0,s,e,0,0,0,1),this}makeScale(t,e){return this.set(t,0,0,0,e,0,0,0,1),this}equals(t){const e=this.elements,s=t.elements;for(let t=0;t<9;t++)if(e[t]!==s[t])return!1;return!0}fromArray(t,e=0){for(let s=0;s<9;s++)this.elements[s]=t[s+e];return this}toArray(t=[],e=0){const s=this.elements;return t[e]=s[0],t[e+1]=s[1],t[e+2]=s[2],t[e+3]=s[3],t[e+4]=s[4],t[e+5]=s[5],t[e+6]=s[6],t[e+7]=s[7],t[e+8]=s[8],t}clone(){return(new this.constructor).fromArray(this.elements)}}const Bi=new Ii,ki=(new Ii).set(.4123908,.3575843,.1804808,.212639,.7151687,.0721923,.0193308,.1191948,.9505322),Oi=(new Ii).set(3.2409699,-1.5373832,-.4986108,-.9692436,1.8759675,.0415551,.0556301,-.203977,1.0569715);function Pi(){const t={enabled:!0,workingColorSpace:ss,spaces:{},convert:function(t,e,s){return!1!==this.enabled&&e!==s&&e&&s?(this.spaces[e].transfer===rs&&(t.r=Ei(t.r),t.g=Ei(t.g),t.b=Ei(t.b)),this.spaces[e].primaries!==this.spaces[s].primaries&&(t.applyMatrix3(this.spaces[e].toXYZ),t.applyMatrix3(this.spaces[s].fromXYZ)),this.spaces[s].transfer===rs&&(t.r=Ni(t.r),t.g=Ni(t.g),t.b=Ni(t.b)),t):t},workingToColorSpace:function(t,e){return this.convert(t,this.workingColorSpace,e)},colorSpaceToWorking:function(t,e){return this.convert(t,e,this.workingColorSpace)},getPrimaries:function(t){return this.spaces[t].primaries},getTransfer:function(t){return""===t?is:this.spaces[t].transfer},getToneMappingMode:function(t){return this.spaces[t].outputColorSpaceConfig.toneMappingMode||"standard"},getLuminanceCoefficients:function(t,e=this.workingColorSpace){return t.fromArray(this.spaces[e].luminanceCoefficients)},define:function(t){Object.assign(this.spaces,t)},_getMatrix:function(t,e,s){return t.copy(this.spaces[e].toXYZ).multiply(this.spaces[s].fromXYZ)},_getDrawingBufferColorSpace:function(t){return this.spaces[t].outputColorSpaceConfig.drawingBufferColorSpace},_getUnpackColorSpace:function(t=this.workingColorSpace){return this.spaces[t].workingColorSpaceConfig.unpackColorSpace},fromWorkingColorSpace:function(e,s){return hi("ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace()."),t.workingToColorSpace(e,s)},toWorkingColorSpace:function(e,s){return hi("ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking()."),t.colorSpaceToWorking(e,s)}},e=[.64,.33,.3,.6,.15,.06],s=[.2126,.7152,.0722],i=[.3127,.329];return t.define({[ss]:{primaries:e,whitePoint:i,transfer:is,toXYZ:ki,fromXYZ:Oi,luminanceCoefficients:s,workingColorSpaceConfig:{unpackColorSpace:es},outputColorSpaceConfig:{drawingBufferColorSpace:es}},[es]:{primaries:e,whitePoint:i,transfer:rs,toXYZ:ki,fromXYZ:Oi,luminanceCoefficients:s,outputColorSpaceConfig:{drawingBufferColorSpace:es}}}),t}const Ri=Pi();function Ei(t){return t<.04045?.0773993808*t:Math.pow(.9478672986*t+.0521327014,2.4)}function Ni(t){return t<.0031308?12.92*t:1.055*Math.pow(t,.41666)-.055}let Vi;class Li{static getDataURL(t,e="image/png"){if(/^data:/i.test(t.src))return t.src;if("undefined"==typeof HTMLCanvasElement)return t.src;let s;if(t instanceof HTMLCanvasElement)s=t;else{void 0===Vi&&(Vi=Qs("canvas")),Vi.width=t.width,Vi.height=t.height;const e=Vi.getContext("2d");t instanceof ImageData?e.putImageData(t,0,0):e.drawImage(t,0,0,t.width,t.height),s=Vi}return s.toDataURL(e)}static sRGBToLinear(t){if("undefined"!=typeof HTMLImageElement&&t instanceof HTMLImageElement||"undefined"!=typeof HTMLCanvasElement&&t instanceof HTMLCanvasElement||"undefined"!=typeof ImageBitmap&&t instanceof ImageBitmap){const e=Qs("canvas");e.width=t.width,e.height=t.height;const s=e.getContext("2d");s.drawImage(t,0,0,t.width,t.height);const i=s.getImageData(0,0,t.width,t.height),r=i.data;for(let t=0;t1),this.pmremVersion=0,this.normalized=!1}get width(){return this.source.getSize(Wi).x}get height(){return this.source.getSize(Wi).y}get depth(){return this.source.getSize(Wi).z}get image(){return this.source.data}set image(t){this.source.data=t}updateMatrix(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}clone(){return(new this.constructor).copy(this)}copy(t){return this.name=t.name,this.source=t.source,this.mipmaps=t.mipmaps.slice(0),this.mapping=t.mapping,this.channel=t.channel,this.wrapS=t.wrapS,this.wrapT=t.wrapT,this.magFilter=t.magFilter,this.minFilter=t.minFilter,this.anisotropy=t.anisotropy,this.format=t.format,this.internalFormat=t.internalFormat,this.type=t.type,this.normalized=t.normalized,this.offset.copy(t.offset),this.repeat.copy(t.repeat),this.center.copy(t.center),this.rotation=t.rotation,this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrix.copy(t.matrix),this.generateMipmaps=t.generateMipmaps,this.premultiplyAlpha=t.premultiplyAlpha,this.flipY=t.flipY,this.unpackAlignment=t.unpackAlignment,this.colorSpace=t.colorSpace,this.renderTarget=t.renderTarget,this.isRenderTargetTexture=t.isRenderTargetTexture,this.isArrayTexture=t.isArrayTexture,this.userData=JSON.parse(JSON.stringify(t.userData)),this.needsUpdate=!0,this}setValues(t){for(const e in t){const s=t[e];if(void 0===s){ai(`Texture.setValues(): parameter '${e}' has value of undefined.`);continue}const i=this[e];void 0!==i?i&&s&&i.isVector2&&s.isVector2||i&&s&&i.isVector3&&s.isVector3||i&&s&&i.isMatrix3&&s.isMatrix3?i.copy(s):this[e]=s:ai(`Texture.setValues(): property '${e}' does not exist.`)}}toJSON(t){const e=void 0===t||"string"==typeof t;if(!e&&void 0!==t.textures[this.uuid])return t.textures[this.uuid];const s={metadata:{version:4.7,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,image:this.source.toJSON(t).uuid,mapping:this.mapping,channel:this.channel,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,internalFormat:this.internalFormat,type:this.type,normalized:this.normalized,colorSpace:this.colorSpace,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,generateMipmaps:this.generateMipmaps,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};return Object.keys(this.userData).length>0&&(s.userData=this.userData),e||(t.textures[this.uuid]=s),s}dispose(){this.dispatchEvent({type:"dispose"})}transformUv(t){if(this.mapping!==ht)return t;if(t.applyMatrix3(this.matrix),t.x<0||t.x>1)switch(this.wrapS){case mt:t.x=t.x-Math.floor(t.x);break;case yt:t.x=t.x<0?0:1;break;case gt:1===Math.abs(Math.floor(t.x)%2)?t.x=Math.ceil(t.x)-t.x:t.x=t.x-Math.floor(t.x)}if(t.y<0||t.y>1)switch(this.wrapT){case mt:t.y=t.y-Math.floor(t.y);break;case yt:t.y=t.y<0?0:1;break;case gt:1===Math.abs(Math.floor(t.y)%2)?t.y=Math.ceil(t.y)-t.y:t.y=t.y-Math.floor(t.y)}return this.flipY&&(t.y=1-t.y),t}set needsUpdate(t){!0===t&&(this.version++,this.source.needsUpdate=!0)}set needsPMREMUpdate(t){!0===t&&this.pmremVersion++}}Ji.DEFAULT_IMAGE=null,Ji.DEFAULT_MAPPING=ht,Ji.DEFAULT_ANISOTROPY=1;class qi{static{qi.prototype.isVector4=!0}constructor(t=0,e=0,s=0,i=1){this.x=t,this.y=e,this.z=s,this.w=i}get width(){return this.z}set width(t){this.z=t}get height(){return this.w}set height(t){this.w=t}set(t,e,s,i){return this.x=t,this.y=e,this.z=s,this.w=i,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this.w=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setW(t){return this.w=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;case 3:this.w=e;break;default:throw new Error("THREE.Vector4: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("THREE.Vector4: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this.w=void 0!==t.w?t.w:1,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this.w+=t.w,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this.w+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this.w=t.w+e.w,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this.w+=t.w*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this.w-=t.w,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this.w-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this.w=t.w-e.w,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this.w*=t.w,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this.w*=t,this}applyMatrix4(t){const e=this.x,s=this.y,i=this.z,r=this.w,n=t.elements;return this.x=n[0]*e+n[4]*s+n[8]*i+n[12]*r,this.y=n[1]*e+n[5]*s+n[9]*i+n[13]*r,this.z=n[2]*e+n[6]*s+n[10]*i+n[14]*r,this.w=n[3]*e+n[7]*s+n[11]*i+n[15]*r,this}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this.w/=t.w,this}divideScalar(t){return this.multiplyScalar(1/t)}setAxisAngleFromQuaternion(t){this.w=2*Math.acos(t.w);const e=Math.sqrt(1-t.w*t.w);return e<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=t.x/e,this.y=t.y/e,this.z=t.z/e),this}setAxisAngleFromRotationMatrix(t){let e,s,i,r;const n=.01,a=.1,o=t.elements,h=o[0],l=o[4],c=o[8],u=o[1],d=o[5],p=o[9],m=o[2],y=o[6],g=o[10];if(Math.abs(l-u)o&&t>f?tf?o1);this.dispose()}this.viewport.set(0,0,t,e),this.scissor.set(0,0,t,e)}clone(){return(new this.constructor).copy(this)}copy(t){this.width=t.width,this.height=t.height,this.depth=t.depth,this.scissor.copy(t.scissor),this.scissorTest=t.scissorTest,this.viewport.copy(t.viewport),this.textures.length=0;for(let e=0,s=t.textures.length;e>>0}enable(t){this.mask|=1<1){for(let t=0;t1){for(let t=0;t0&&(i.userData=this.userData),i.layers=this.layers.mask,i.matrix=this.matrix.toArray(),i.up=this.up.toArray(),null!==this.pivot&&(i.pivot=this.pivot.toArray()),!1===this.matrixAutoUpdate&&(i.matrixAutoUpdate=!1),void 0!==this.morphTargetDictionary&&(i.morphTargetDictionary=Object.assign({},this.morphTargetDictionary)),void 0!==this.morphTargetInfluences&&(i.morphTargetInfluences=this.morphTargetInfluences.slice()),this.isInstancedMesh&&(i.type="InstancedMesh",i.count=this.count,i.instanceMatrix=this.instanceMatrix.toJSON(),null!==this.instanceColor&&(i.instanceColor=this.instanceColor.toJSON())),this.isBatchedMesh&&(i.type="BatchedMesh",i.perObjectFrustumCulled=this.perObjectFrustumCulled,i.sortObjects=this.sortObjects,i.drawRanges=this._drawRanges,i.reservedRanges=this._reservedRanges,i.geometryInfo=this._geometryInfo.map(t=>({...t,boundingBox:t.boundingBox?t.boundingBox.toJSON():void 0,boundingSphere:t.boundingSphere?t.boundingSphere.toJSON():void 0})),i.instanceInfo=this._instanceInfo.map(t=>({...t})),i.availableInstanceIds=this._availableInstanceIds.slice(),i.availableGeometryIds=this._availableGeometryIds.slice(),i.nextIndexStart=this._nextIndexStart,i.nextVertexStart=this._nextVertexStart,i.geometryCount=this._geometryCount,i.maxInstanceCount=this._maxInstanceCount,i.maxVertexCount=this._maxVertexCount,i.maxIndexCount=this._maxIndexCount,i.geometryInitialized=this._geometryInitialized,i.matricesTexture=this._matricesTexture.toJSON(t),i.indirectTexture=this._indirectTexture.toJSON(t),null!==this._colorsTexture&&(i.colorsTexture=this._colorsTexture.toJSON(t)),null!==this.boundingSphere&&(i.boundingSphere=this.boundingSphere.toJSON()),null!==this.boundingBox&&(i.boundingBox=this.boundingBox.toJSON())),this.isScene)this.background&&(this.background.isColor?i.background=this.background.toJSON():this.background.isTexture&&(i.background=this.background.toJSON(t).uuid)),this.environment&&this.environment.isTexture&&!0!==this.environment.isRenderTargetTexture&&(i.environment=this.environment.toJSON(t).uuid);else if(this.isMesh||this.isLine||this.isPoints){i.geometry=r(t.geometries,this.geometry);const e=this.geometry.parameters;if(void 0!==e&&void 0!==e.shapes){const s=e.shapes;if(Array.isArray(s))for(let e=0,i=s.length;e0){i.children=[];for(let e=0;e0){i.animations=[];for(let e=0;e0&&(s.geometries=e),i.length>0&&(s.materials=i),r.length>0&&(s.textures=r),a.length>0&&(s.images=a),o.length>0&&(s.shapes=o),h.length>0&&(s.skeletons=h),l.length>0&&(s.animations=l),c.length>0&&(s.nodes=c)}return s.object=i,s;function n(t){const e=[];for(const s in t){const i=t[s];delete i.metadata,e.push(i)}return e}}clone(t){return(new this.constructor).copy(this,t)}copy(t,e=!0){if(this.name=t.name,this.up.copy(t.up),this.position.copy(t.position),this.rotation.order=t.rotation.order,this.quaternion.copy(t.quaternion),this.scale.copy(t.scale),this.pivot=null!==t.pivot?t.pivot.clone():null,this.matrix.copy(t.matrix),this.matrixWorld.copy(t.matrixWorld),this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrixWorldAutoUpdate=t.matrixWorldAutoUpdate,this.matrixWorldNeedsUpdate=t.matrixWorldNeedsUpdate,this.layers.mask=t.layers.mask,this.visible=t.visible,this.castShadow=t.castShadow,this.receiveShadow=t.receiveShadow,this.frustumCulled=t.frustumCulled,this.renderOrder=t.renderOrder,this.static=t.static,this.animations=t.animations.slice(),this.userData=JSON.parse(JSON.stringify(t.userData)),!0===e)for(let e=0;eo+l?(h.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:t.handedness,target:this})):!h.inputState.pinching&&a<=o-l&&(h.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:t.handedness,target:this}))}else null!==o&&t.gripSpace&&(r=e.getPose(t.gripSpace,s),null!==r&&(o.matrix.fromArray(r.transform.matrix),o.matrix.decompose(o.position,o.rotation,o.scale),o.matrixWorldNeedsUpdate=!0,r.linearVelocity?(o.hasLinearVelocity=!0,o.linearVelocity.copy(r.linearVelocity)):o.hasLinearVelocity=!1,r.angularVelocity?(o.hasAngularVelocity=!0,o.angularVelocity.copy(r.angularVelocity)):o.hasAngularVelocity=!1,o.eventsEnabled&&o.dispatchEvent({type:"gripUpdated",data:t,target:this})));null!==a&&(i=e.getPose(t.targetRaySpace,s),null===i&&null!==r&&(i=r),null!==i&&(a.matrix.fromArray(i.transform.matrix),a.matrix.decompose(a.position,a.rotation,a.scale),a.matrixWorldNeedsUpdate=!0,i.linearVelocity?(a.hasLinearVelocity=!0,a.linearVelocity.copy(i.linearVelocity)):a.hasLinearVelocity=!1,i.angularVelocity?(a.hasAngularVelocity=!0,a.angularVelocity.copy(i.angularVelocity)):a.hasAngularVelocity=!1,this.dispatchEvent(zr)))}return null!==a&&(a.visible=null!==i),null!==o&&(o.visible=null!==r),null!==h&&(h.visible=null!==n),this}_getHandJoint(t,e){if(void 0===t.joints[e.jointName]){const s=new Tr;s.matrixAutoUpdate=!1,s.visible=!1,t.joints[e.jointName]=s,t.add(s)}return t.joints[e.jointName]}}const Ir={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},Br={h:0,s:0,l:0},kr={h:0,s:0,l:0};function Or(t,e,s){return s<0&&(s+=1),s>1&&(s-=1),s<1/6?t+6*(e-t)*s:s<.5?e:s<2/3?t+6*(e-t)*(2/3-s):t}class Pr{constructor(t,e,s){return this.isColor=!0,this.r=1,this.g=1,this.b=1,this.set(t,e,s)}set(t,e,s){if(void 0===e&&void 0===s){const e=t;e&&e.isColor?this.copy(e):"number"==typeof e?this.setHex(e):"string"==typeof e&&this.setStyle(e)}else this.setRGB(t,e,s);return this}setScalar(t){return this.r=t,this.g=t,this.b=t,this}setHex(t,e=es){return t=Math.floor(t),this.r=(t>>16&255)/255,this.g=(t>>8&255)/255,this.b=(255&t)/255,Ri.colorSpaceToWorking(this,e),this}setRGB(t,e,s,i=Ri.workingColorSpace){return this.r=t,this.g=e,this.b=s,Ri.colorSpaceToWorking(this,i),this}setHSL(t,e,s,i=Ri.workingColorSpace){if(t=bi(t,1),e=xi(e,0,1),s=xi(s,0,1),0===e)this.r=this.g=this.b=s;else{const i=s<=.5?s*(1+e):s+e-s*e,r=2*s-i;this.r=Or(r,i,t+1/3),this.g=Or(r,i,t),this.b=Or(r,i,t-1/3)}return Ri.colorSpaceToWorking(this,i),this}setStyle(t,e=es){function s(e){void 0!==e&&parseFloat(e)<1&&ai("Color: Alpha component of "+t+" will be ignored.")}let i;if(i=/^(\w+)\(([^\)]*)\)/.exec(t)){let r;const n=i[1],a=i[2];switch(n){case"rgb":case"rgba":if(r=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return s(r[4]),this.setRGB(Math.min(255,parseInt(r[1],10))/255,Math.min(255,parseInt(r[2],10))/255,Math.min(255,parseInt(r[3],10))/255,e);if(r=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return s(r[4]),this.setRGB(Math.min(100,parseInt(r[1],10))/100,Math.min(100,parseInt(r[2],10))/100,Math.min(100,parseInt(r[3],10))/100,e);break;case"hsl":case"hsla":if(r=/^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return s(r[4]),this.setHSL(parseFloat(r[1])/360,parseFloat(r[2])/100,parseFloat(r[3])/100,e);break;default:ai("Color: Unknown color model "+t)}}else if(i=/^\#([A-Fa-f\d]+)$/.exec(t)){const s=i[1],r=s.length;if(3===r)return this.setRGB(parseInt(s.charAt(0),16)/15,parseInt(s.charAt(1),16)/15,parseInt(s.charAt(2),16)/15,e);if(6===r)return this.setHex(parseInt(s,16),e);ai("Color: Invalid hex color "+t)}else if(t&&t.length>0)return this.setColorName(t,e);return this}setColorName(t,e=es){const s=Ir[t.toLowerCase()];return void 0!==s?this.setHex(s,e):ai("Color: Unknown color "+t),this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(t){return this.r=t.r,this.g=t.g,this.b=t.b,this}copySRGBToLinear(t){return this.r=Ei(t.r),this.g=Ei(t.g),this.b=Ei(t.b),this}copyLinearToSRGB(t){return this.r=Ni(t.r),this.g=Ni(t.g),this.b=Ni(t.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(t=es){return Ri.workingToColorSpace(Rr.copy(this),t),65536*Math.round(xi(255*Rr.r,0,255))+256*Math.round(xi(255*Rr.g,0,255))+Math.round(xi(255*Rr.b,0,255))}getHexString(t=es){return("000000"+this.getHex(t).toString(16)).slice(-6)}getHSL(t,e=Ri.workingColorSpace){Ri.workingToColorSpace(Rr.copy(this),e);const s=Rr.r,i=Rr.g,r=Rr.b,n=Math.max(s,i,r),a=Math.min(s,i,r);let o,h;const l=(a+n)/2;if(a===n)o=0,h=0;else{const t=n-a;switch(h=l<=.5?t/(n+a):t/(2-n-a),n){case s:o=(i-r)/t+(i0&&(e.object.backgroundBlurriness=this.backgroundBlurriness),1!==this.backgroundIntensity&&(e.object.backgroundIntensity=this.backgroundIntensity),e.object.backgroundRotation=this.backgroundRotation.toArray(),1!==this.environmentIntensity&&(e.object.environmentIntensity=this.environmentIntensity),e.object.environmentRotation=this.environmentRotation.toArray(),e}}const Lr=new Ti,Fr=new Ti,Dr=new Ti,Ur=new Ti,jr=new Ti,Wr=new Ti,Jr=new Ti,qr=new Ti,Hr=new Ti,Xr=new Ti,Yr=new qi,Zr=new qi,Gr=new qi;class $r{constructor(t=new Ti,e=new Ti,s=new Ti){this.a=t,this.b=e,this.c=s}static getNormal(t,e,s,i){i.subVectors(s,e),Lr.subVectors(t,e),i.cross(Lr);const r=i.lengthSq();return r>0?i.multiplyScalar(1/Math.sqrt(r)):i.set(0,0,0)}static getBarycoord(t,e,s,i,r){Lr.subVectors(i,e),Fr.subVectors(s,e),Dr.subVectors(t,e);const n=Lr.dot(Lr),a=Lr.dot(Fr),o=Lr.dot(Dr),h=Fr.dot(Fr),l=Fr.dot(Dr),c=n*h-a*a;if(0===c)return r.set(0,0,0),null;const u=1/c,d=(h*o-a*l)*u,p=(n*l-a*o)*u;return r.set(1-d-p,p,d)}static containsPoint(t,e,s,i){return null!==this.getBarycoord(t,e,s,i,Ur)&&(Ur.x>=0&&Ur.y>=0&&Ur.x+Ur.y<=1)}static getInterpolation(t,e,s,i,r,n,a,o){return null===this.getBarycoord(t,e,s,i,Ur)?(o.x=0,o.y=0,"z"in o&&(o.z=0),"w"in o&&(o.w=0),null):(o.setScalar(0),o.addScaledVector(r,Ur.x),o.addScaledVector(n,Ur.y),o.addScaledVector(a,Ur.z),o)}static getInterpolatedAttribute(t,e,s,i,r,n){return Yr.setScalar(0),Zr.setScalar(0),Gr.setScalar(0),Yr.fromBufferAttribute(t,e),Zr.fromBufferAttribute(t,s),Gr.fromBufferAttribute(t,i),n.setScalar(0),n.addScaledVector(Yr,r.x),n.addScaledVector(Zr,r.y),n.addScaledVector(Gr,r.z),n}static isFrontFacing(t,e,s,i){return Lr.subVectors(s,e),Fr.subVectors(t,e),Lr.cross(Fr).dot(i)<0}set(t,e,s){return this.a.copy(t),this.b.copy(e),this.c.copy(s),this}setFromPointsAndIndices(t,e,s,i){return this.a.copy(t[e]),this.b.copy(t[s]),this.c.copy(t[i]),this}setFromAttributeAndIndices(t,e,s,i){return this.a.fromBufferAttribute(t,e),this.b.fromBufferAttribute(t,s),this.c.fromBufferAttribute(t,i),this}clone(){return(new this.constructor).copy(this)}copy(t){return this.a.copy(t.a),this.b.copy(t.b),this.c.copy(t.c),this}getArea(){return Lr.subVectors(this.c,this.b),Fr.subVectors(this.a,this.b),.5*Lr.cross(Fr).length()}getMidpoint(t){return t.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(t){return $r.getNormal(this.a,this.b,this.c,t)}getPlane(t){return t.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(t,e){return $r.getBarycoord(t,this.a,this.b,this.c,e)}getInterpolation(t,e,s,i,r){return $r.getInterpolation(t,this.a,this.b,this.c,e,s,i,r)}containsPoint(t){return $r.containsPoint(t,this.a,this.b,this.c)}isFrontFacing(t){return $r.isFrontFacing(this.a,this.b,this.c,t)}intersectsBox(t){return t.intersectsTriangle(this)}closestPointToPoint(t,e){const s=this.a,i=this.b,r=this.c;let n,a;jr.subVectors(i,s),Wr.subVectors(r,s),qr.subVectors(t,s);const o=jr.dot(qr),h=Wr.dot(qr);if(o<=0&&h<=0)return e.copy(s);Hr.subVectors(t,i);const l=jr.dot(Hr),c=Wr.dot(Hr);if(l>=0&&c<=l)return e.copy(i);const u=o*c-l*h;if(u<=0&&o>=0&&l<=0)return n=o/(o-l),e.copy(s).addScaledVector(jr,n);Xr.subVectors(t,r);const d=jr.dot(Xr),p=Wr.dot(Xr);if(p>=0&&d<=p)return e.copy(r);const m=d*h-o*p;if(m<=0&&h>=0&&p<=0)return a=h/(h-p),e.copy(s).addScaledVector(Wr,a);const y=l*p-d*c;if(y<=0&&c-l>=0&&d-p>=0)return Jr.subVectors(r,i),a=(c-l)/(c-l+(d-p)),e.copy(i).addScaledVector(Jr,a);const g=1/(y+m+u);return n=m*g,a=u*g,e.copy(s).addScaledVector(jr,n).addScaledVector(Wr,a)}equals(t){return t.a.equals(this.a)&&t.b.equals(this.b)&&t.c.equals(this.c)}}class Qr{constructor(t=new Ti(1/0,1/0,1/0),e=new Ti(-1/0,-1/0,-1/0)){this.isBox3=!0,this.min=t,this.max=e}set(t,e){return this.min.copy(t),this.max.copy(e),this}setFromArray(t){this.makeEmpty();for(let e=0,s=t.length;e=this.min.x&&t.x<=this.max.x&&t.y>=this.min.y&&t.y<=this.max.y&&t.z>=this.min.z&&t.z<=this.max.z}containsBox(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y&&this.min.z<=t.min.z&&t.max.z<=this.max.z}getParameter(t,e){return e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y),(t.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(t){return t.max.x>=this.min.x&&t.min.x<=this.max.x&&t.max.y>=this.min.y&&t.min.y<=this.max.y&&t.max.z>=this.min.z&&t.min.z<=this.max.z}intersectsSphere(t){return this.clampPoint(t.center,tn),tn.distanceToSquared(t.center)<=t.radius*t.radius}intersectsPlane(t){let e,s;return t.normal.x>0?(e=t.normal.x*this.min.x,s=t.normal.x*this.max.x):(e=t.normal.x*this.max.x,s=t.normal.x*this.min.x),t.normal.y>0?(e+=t.normal.y*this.min.y,s+=t.normal.y*this.max.y):(e+=t.normal.y*this.max.y,s+=t.normal.y*this.min.y),t.normal.z>0?(e+=t.normal.z*this.min.z,s+=t.normal.z*this.max.z):(e+=t.normal.z*this.max.z,s+=t.normal.z*this.min.z),e<=-t.constant&&s>=-t.constant}intersectsTriangle(t){if(this.isEmpty())return!1;this.getCenter(ln),cn.subVectors(this.max,ln),sn.subVectors(t.a,ln),rn.subVectors(t.b,ln),nn.subVectors(t.c,ln),an.subVectors(rn,sn),on.subVectors(nn,rn),hn.subVectors(sn,nn);let e=[0,-an.z,an.y,0,-on.z,on.y,0,-hn.z,hn.y,an.z,0,-an.x,on.z,0,-on.x,hn.z,0,-hn.x,-an.y,an.x,0,-on.y,on.x,0,-hn.y,hn.x,0];return!!pn(e,sn,rn,nn,cn)&&(e=[1,0,0,0,1,0,0,0,1],!!pn(e,sn,rn,nn,cn)&&(un.crossVectors(an,on),e=[un.x,un.y,un.z],pn(e,sn,rn,nn,cn)))}clampPoint(t,e){return e.copy(t).clamp(this.min,this.max)}distanceToPoint(t){return this.clampPoint(t,tn).distanceTo(t)}getBoundingSphere(t){return this.isEmpty()?t.makeEmpty():(this.getCenter(t.center),t.radius=.5*this.getSize(tn).length()),t}intersect(t){return this.min.max(t.min),this.max.min(t.max),this.isEmpty()&&this.makeEmpty(),this}union(t){return this.min.min(t.min),this.max.max(t.max),this}applyMatrix4(t){return this.isEmpty()||(Kr[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(t),Kr[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(t),Kr[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(t),Kr[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(t),Kr[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(t),Kr[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(t),Kr[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(t),Kr[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(t),this.setFromPoints(Kr)),this}translate(t){return this.min.add(t),this.max.add(t),this}equals(t){return t.min.equals(this.min)&&t.max.equals(this.max)}toJSON(){return{min:this.min.toArray(),max:this.max.toArray()}}fromJSON(t){return this.min.fromArray(t.min),this.max.fromArray(t.max),this}}const Kr=[new Ti,new Ti,new Ti,new Ti,new Ti,new Ti,new Ti,new Ti],tn=new Ti,en=new Qr,sn=new Ti,rn=new Ti,nn=new Ti,an=new Ti,on=new Ti,hn=new Ti,ln=new Ti,cn=new Ti,un=new Ti,dn=new Ti;function pn(t,e,s,i,r){for(let n=0,a=t.length-3;n<=a;n+=3){dn.fromArray(t,n);const a=r.x*Math.abs(dn.x)+r.y*Math.abs(dn.y)+r.z*Math.abs(dn.z),o=e.dot(dn),h=s.dot(dn),l=i.dot(dn);if(Math.max(-Math.max(o,h,l),Math.min(o,h,l))>a)return!1}return!0}const mn=yn();function yn(){const t=new ArrayBuffer(4),e=new Float32Array(t),s=new Uint32Array(t),i=new Uint32Array(512),r=new Uint32Array(512);for(let t=0;t<256;++t){const e=t-127;e<-27?(i[t]=0,i[256|t]=32768,r[t]=24,r[256|t]=24):e<-14?(i[t]=1024>>-e-14,i[256|t]=1024>>-e-14|32768,r[t]=-e-1,r[256|t]=-e-1):e<=15?(i[t]=e+15<<10,i[256|t]=e+15<<10|32768,r[t]=13,r[256|t]=13):e<128?(i[t]=31744,i[256|t]=64512,r[t]=24,r[256|t]=24):(i[t]=31744,i[256|t]=64512,r[t]=13,r[256|t]=13)}const n=new Uint32Array(2048),a=new Uint32Array(64),o=new Uint32Array(64);for(let t=1;t<1024;++t){let e=t<<13,s=0;for(;!(8388608&e);)e<<=1,s-=8388608;e&=-8388609,s+=947912704,n[t]=e|s}for(let t=1024;t<2048;++t)n[t]=939524096+(t-1024<<13);for(let t=1;t<31;++t)a[t]=t<<23;a[31]=1199570944,a[32]=2147483648;for(let t=33;t<63;++t)a[t]=2147483648+(t-32<<23);a[63]=3347054592;for(let t=1;t<64;++t)32!==t&&(o[t]=1024);return{floatView:e,uint32View:s,baseTable:i,shiftTable:r,mantissaTable:n,exponentTable:a,offsetTable:o}}function gn(t){Math.abs(t)>65504&&ai("DataUtils.toHalfFloat(): Value out of range."),t=xi(t,-65504,65504),mn.floatView[0]=t;const e=mn.uint32View[0],s=e>>23&511;return mn.baseTable[s]+((8388607&e)>>mn.shiftTable[s])}function fn(t){const e=t>>10;return mn.uint32View[0]=mn.mantissaTable[mn.offsetTable[e]+(1023&t)]+mn.exponentTable[e],mn.floatView[0]}class xn{static toHalfFloat(t){return gn(t)}static fromHalfFloat(t){return fn(t)}}const bn=new Ti,vn=new _i;let wn=0;class Mn extends di{constructor(t,e,s=!1){if(super(),Array.isArray(t))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.isBufferAttribute=!0,Object.defineProperty(this,"id",{value:wn++}),this.name="",this.array=t,this.itemSize=e,this.count=void 0!==t?t.length/e:0,this.normalized=s,this.usage=Os,this.updateRanges=[],this.gpuType=Pt,this.version=0}onUploadCallback(){}set needsUpdate(t){!0===t&&this.version++}setUsage(t){return this.usage=t,this}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}copy(t){return this.name=t.name,this.array=new t.array.constructor(t.array),this.itemSize=t.itemSize,this.count=t.count,this.normalized=t.normalized,this.usage=t.usage,this.gpuType=t.gpuType,this}copyAt(t,e,s){t*=this.itemSize,s*=e.itemSize;for(let i=0,r=this.itemSize;ithis.radius*this.radius&&(e.sub(this.center).normalize(),e.multiplyScalar(this.radius).add(this.center)),e}getBoundingBox(t){return this.isEmpty()?(t.makeEmpty(),t):(t.set(this.center,this.center),t.expandByScalar(this.radius),t)}applyMatrix4(t){return this.center.applyMatrix4(t),this.radius=this.radius*t.getMaxScaleOnAxis(),this}translate(t){return this.center.add(t),this}expandByPoint(t){if(this.isEmpty())return this.center.copy(t),this.radius=0,this;Pn.subVectors(t,this.center);const e=Pn.lengthSq();if(e>this.radius*this.radius){const t=Math.sqrt(e),s=.5*(t-this.radius);this.center.addScaledVector(Pn,s/t),this.radius+=s}return this}union(t){return t.isEmpty()?this:this.isEmpty()?(this.copy(t),this):(!0===this.center.equals(t.center)?this.radius=Math.max(this.radius,t.radius):(Rn.subVectors(t.center,this.center).setLength(t.radius),this.expandByPoint(Pn.copy(t.center).add(Rn)),this.expandByPoint(Pn.copy(t.center).sub(Rn))),this)}equals(t){return t.center.equals(this.center)&&t.radius===this.radius}clone(){return(new this.constructor).copy(this)}toJSON(){return{radius:this.radius,center:this.center.toArray()}}fromJSON(t){return this.radius=t.radius,this.center.fromArray(t.center),this}}let Nn=0;const Vn=new Qi,Ln=new Ar,Fn=new Ti,Dn=new Qr,Un=new Qr,jn=new Ti;class Wn extends di{constructor(){super(),this.isBufferGeometry=!0,Object.defineProperty(this,"id",{value:Nn++}),this.uuid=fi(),this.name="",this.type="BufferGeometry",this.index=null,this.indirect=null,this.indirectOffset=0,this.attributes={},this.morphAttributes={},this.morphTargetsRelative=!1,this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0},this.userData={},this._transformed=!1}getIndex(){return this.index}setIndex(t){return Array.isArray(t)?this.index=new(function(t){for(let e=t.length-1;e>=0;--e)if(t[e]>=65535)return!0;return!1}(t)?In:zn)(t,1):this.index=t,this}setIndirect(t,e=0){return this.indirect=t,this.indirectOffset=e,this}getIndirect(){return this.indirect}getAttribute(t){return this.attributes[t]}setAttribute(t,e){return this.attributes[t]=e,this}deleteAttribute(t){return delete this.attributes[t],this}hasAttribute(t){return void 0!==this.attributes[t]}addGroup(t,e,s=0){this.groups.push({start:t,count:e,materialIndex:s})}clearGroups(){this.groups=[]}setDrawRange(t,e){this.drawRange.start=t,this.drawRange.count=e}applyMatrix4(t){const e=this.attributes.position;void 0!==e&&(e.applyMatrix4(t),e.needsUpdate=!0);const s=this.attributes.normal;if(void 0!==s){const e=(new Ii).getNormalMatrix(t);s.applyNormalMatrix(e),s.needsUpdate=!0}const i=this.attributes.tangent;return void 0!==i&&(i.transformDirection(t),i.needsUpdate=!0),null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere(),this._transformed=!0,this}applyQuaternion(t){return Vn.makeRotationFromQuaternion(t),this.applyMatrix4(Vn),this}rotateX(t){return Vn.makeRotationX(t),this.applyMatrix4(Vn),this}rotateY(t){return Vn.makeRotationY(t),this.applyMatrix4(Vn),this}rotateZ(t){return Vn.makeRotationZ(t),this.applyMatrix4(Vn),this}translate(t,e,s){return Vn.makeTranslation(t,e,s),this.applyMatrix4(Vn),this}scale(t,e,s){return Vn.makeScale(t,e,s),this.applyMatrix4(Vn),this}lookAt(t){return Ln.lookAt(t),Ln.updateMatrix(),this.applyMatrix4(Ln.matrix),this}center(){return this.computeBoundingBox(),this.boundingBox.getCenter(Fn).negate(),this.translate(Fn.x,Fn.y,Fn.z),this}setFromPoints(t){const e=this.getAttribute("position");if(void 0===e){const e=[];for(let s=0,i=t.length;se.count&&ai("BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry."),e.needsUpdate=!0}return this}computeBoundingBox(){null===this.boundingBox&&(this.boundingBox=new Qr);const t=this.attributes.position,e=this.morphAttributes.position;if(t&&t.isGLBufferAttribute)return oi("BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.",this),void this.boundingBox.set(new Ti(-1/0,-1/0,-1/0),new Ti(1/0,1/0,1/0));if(void 0!==t){if(this.boundingBox.setFromBufferAttribute(t),e)for(let t=0,s=e.length;t0&&(t.userData=this.userData),void 0!==this.parameters&&!0!==this._transformed){const e=this.parameters;for(const s in e)void 0!==e[s]&&(t[s]=e[s]);return t}t.data={attributes:{}};const e=this.index;null!==e&&(t.data.index={type:e.array.constructor.name,array:Array.prototype.slice.call(e.array)});const s=this.attributes;for(const e in s){const i=s[e];t.data.attributes[e]=i.toJSON(t.data)}const i={};let r=!1;for(const e in this.morphAttributes){const s=this.morphAttributes[e],n=[];for(let e=0,i=s.length;e0&&(i[e]=n,r=!0)}r&&(t.data.morphAttributes=i,t.data.morphTargetsRelative=this.morphTargetsRelative);const n=this.groups;n.length>0&&(t.data.groups=JSON.parse(JSON.stringify(n)));const a=this.boundingSphere;return null!==a&&(t.data.boundingSphere=a.toJSON()),t}clone(){return(new this.constructor).copy(this)}copy(t){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;const e={};this.name=t.name;const s=t.index;null!==s&&this.setIndex(s.clone());const i=t.attributes;for(const t in i){const s=i[t];this.setAttribute(t,s.clone(e))}const r=t.morphAttributes;for(const t in r){const s=[],i=r[t];for(let t=0,r=i.length;t0!=t>0&&this.version++,this._alphaTest=t}onBeforeRender(){}onBeforeCompile(){}customProgramCacheKey(){return this.onBeforeCompile.toString()}setValues(t){if(void 0!==t)for(const e in t){const s=t[e];if(void 0===s){ai(`Material: parameter '${e}' has value of undefined.`);continue}const i=this[e];void 0!==i?i&&i.isColor?i.set(s):i&&i.isVector2&&s&&s.isVector2||i&&i.isEuler&&s&&s.isEuler||i&&i.isVector3&&s&&s.isVector3?i.copy(s):this[e]=s:ai(`Material: '${e}' is not a property of THREE.${this.type}.`)}}toJSON(t){const e=void 0===t||"string"==typeof t;e&&(t={textures:{},images:{}});const s={metadata:{version:4.7,type:"Material",generator:"Material.toJSON"}};function i(t){const e=[];for(const s in t){const i=t[s];delete i.metadata,e.push(i)}return e}if(s.uuid=this.uuid,s.type=this.type,""!==this.name&&(s.name=this.name),this.color&&this.color.isColor&&(s.color=this.color.getHex()),void 0!==this.roughness&&(s.roughness=this.roughness),void 0!==this.metalness&&(s.metalness=this.metalness),void 0!==this.sheen&&(s.sheen=this.sheen),this.sheenColor&&this.sheenColor.isColor&&(s.sheenColor=this.sheenColor.getHex()),void 0!==this.sheenRoughness&&(s.sheenRoughness=this.sheenRoughness),this.emissive&&this.emissive.isColor&&(s.emissive=this.emissive.getHex()),void 0!==this.emissiveIntensity&&1!==this.emissiveIntensity&&(s.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(s.specular=this.specular.getHex()),void 0!==this.specularIntensity&&(s.specularIntensity=this.specularIntensity),this.specularColor&&this.specularColor.isColor&&(s.specularColor=this.specularColor.getHex()),void 0!==this.shininess&&(s.shininess=this.shininess),void 0!==this.clearcoat&&(s.clearcoat=this.clearcoat),void 0!==this.clearcoatRoughness&&(s.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(s.clearcoatMap=this.clearcoatMap.toJSON(t).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(s.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(t).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(s.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(t).uuid,s.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.sheenColorMap&&this.sheenColorMap.isTexture&&(s.sheenColorMap=this.sheenColorMap.toJSON(t).uuid),this.sheenRoughnessMap&&this.sheenRoughnessMap.isTexture&&(s.sheenRoughnessMap=this.sheenRoughnessMap.toJSON(t).uuid),void 0!==this.dispersion&&(s.dispersion=this.dispersion),void 0!==this.iridescence&&(s.iridescence=this.iridescence),void 0!==this.iridescenceIOR&&(s.iridescenceIOR=this.iridescenceIOR),void 0!==this.iridescenceThicknessRange&&(s.iridescenceThicknessRange=this.iridescenceThicknessRange),this.iridescenceMap&&this.iridescenceMap.isTexture&&(s.iridescenceMap=this.iridescenceMap.toJSON(t).uuid),this.iridescenceThicknessMap&&this.iridescenceThicknessMap.isTexture&&(s.iridescenceThicknessMap=this.iridescenceThicknessMap.toJSON(t).uuid),void 0!==this.anisotropy&&(s.anisotropy=this.anisotropy),void 0!==this.anisotropyRotation&&(s.anisotropyRotation=this.anisotropyRotation),this.anisotropyMap&&this.anisotropyMap.isTexture&&(s.anisotropyMap=this.anisotropyMap.toJSON(t).uuid),this.map&&this.map.isTexture&&(s.map=this.map.toJSON(t).uuid),this.matcap&&this.matcap.isTexture&&(s.matcap=this.matcap.toJSON(t).uuid),this.alphaMap&&this.alphaMap.isTexture&&(s.alphaMap=this.alphaMap.toJSON(t).uuid),this.lightMap&&this.lightMap.isTexture&&(s.lightMap=this.lightMap.toJSON(t).uuid,s.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(s.aoMap=this.aoMap.toJSON(t).uuid,s.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(s.bumpMap=this.bumpMap.toJSON(t).uuid,s.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(s.normalMap=this.normalMap.toJSON(t).uuid,s.normalMapType=this.normalMapType,s.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(s.displacementMap=this.displacementMap.toJSON(t).uuid,s.displacementScale=this.displacementScale,s.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(s.roughnessMap=this.roughnessMap.toJSON(t).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(s.metalnessMap=this.metalnessMap.toJSON(t).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(s.emissiveMap=this.emissiveMap.toJSON(t).uuid),this.specularMap&&this.specularMap.isTexture&&(s.specularMap=this.specularMap.toJSON(t).uuid),this.specularIntensityMap&&this.specularIntensityMap.isTexture&&(s.specularIntensityMap=this.specularIntensityMap.toJSON(t).uuid),this.specularColorMap&&this.specularColorMap.isTexture&&(s.specularColorMap=this.specularColorMap.toJSON(t).uuid),this.envMap&&this.envMap.isTexture&&(s.envMap=this.envMap.toJSON(t).uuid,void 0!==this.combine&&(s.combine=this.combine)),void 0!==this.envMapRotation&&(s.envMapRotation=this.envMapRotation.toArray()),void 0!==this.envMapIntensity&&(s.envMapIntensity=this.envMapIntensity),void 0!==this.reflectivity&&(s.reflectivity=this.reflectivity),void 0!==this.refractionRatio&&(s.refractionRatio=this.refractionRatio),this.gradientMap&&this.gradientMap.isTexture&&(s.gradientMap=this.gradientMap.toJSON(t).uuid),void 0!==this.transmission&&(s.transmission=this.transmission),this.transmissionMap&&this.transmissionMap.isTexture&&(s.transmissionMap=this.transmissionMap.toJSON(t).uuid),void 0!==this.thickness&&(s.thickness=this.thickness),this.thicknessMap&&this.thicknessMap.isTexture&&(s.thicknessMap=this.thicknessMap.toJSON(t).uuid),void 0!==this.attenuationDistance&&this.attenuationDistance!==1/0&&(s.attenuationDistance=this.attenuationDistance),void 0!==this.attenuationColor&&(s.attenuationColor=this.attenuationColor.getHex()),void 0!==this.size&&(s.size=this.size),null!==this.shadowSide&&(s.shadowSide=this.shadowSide),void 0!==this.sizeAttenuation&&(s.sizeAttenuation=this.sizeAttenuation),1!==this.blending&&(s.blending=this.blending),0!==this.side&&(s.side=this.side),!0===this.vertexColors&&(s.vertexColors=!0),this.opacity<1&&(s.opacity=this.opacity),!0===this.transparent&&(s.transparent=!0),204!==this.blendSrc&&(s.blendSrc=this.blendSrc),205!==this.blendDst&&(s.blendDst=this.blendDst),100!==this.blendEquation&&(s.blendEquation=this.blendEquation),null!==this.blendSrcAlpha&&(s.blendSrcAlpha=this.blendSrcAlpha),null!==this.blendDstAlpha&&(s.blendDstAlpha=this.blendDstAlpha),null!==this.blendEquationAlpha&&(s.blendEquationAlpha=this.blendEquationAlpha),this.blendColor&&this.blendColor.isColor&&(s.blendColor=this.blendColor.getHex()),0!==this.blendAlpha&&(s.blendAlpha=this.blendAlpha),3!==this.depthFunc&&(s.depthFunc=this.depthFunc),!1===this.depthTest&&(s.depthTest=this.depthTest),!1===this.depthWrite&&(s.depthWrite=this.depthWrite),!1===this.colorWrite&&(s.colorWrite=this.colorWrite),255!==this.stencilWriteMask&&(s.stencilWriteMask=this.stencilWriteMask),519!==this.stencilFunc&&(s.stencilFunc=this.stencilFunc),0!==this.stencilRef&&(s.stencilRef=this.stencilRef),255!==this.stencilFuncMask&&(s.stencilFuncMask=this.stencilFuncMask),this.stencilFail!==ls&&(s.stencilFail=this.stencilFail),this.stencilZFail!==ls&&(s.stencilZFail=this.stencilZFail),this.stencilZPass!==ls&&(s.stencilZPass=this.stencilZPass),!0===this.stencilWrite&&(s.stencilWrite=this.stencilWrite),void 0!==this.rotation&&0!==this.rotation&&(s.rotation=this.rotation),!0===this.polygonOffset&&(s.polygonOffset=!0),0!==this.polygonOffsetFactor&&(s.polygonOffsetFactor=this.polygonOffsetFactor),0!==this.polygonOffsetUnits&&(s.polygonOffsetUnits=this.polygonOffsetUnits),void 0!==this.linewidth&&1!==this.linewidth&&(s.linewidth=this.linewidth),void 0!==this.dashSize&&(s.dashSize=this.dashSize),void 0!==this.gapSize&&(s.gapSize=this.gapSize),void 0!==this.scale&&(s.scale=this.scale),!0===this.dithering&&(s.dithering=!0),this.alphaTest>0&&(s.alphaTest=this.alphaTest),!0===this.alphaHash&&(s.alphaHash=!0),!0===this.alphaToCoverage&&(s.alphaToCoverage=!0),!0===this.premultipliedAlpha&&(s.premultipliedAlpha=!0),!0===this.forceSinglePass&&(s.forceSinglePass=!0),!1===this.allowOverride&&(s.allowOverride=!1),!0===this.wireframe&&(s.wireframe=!0),this.wireframeLinewidth>1&&(s.wireframeLinewidth=this.wireframeLinewidth),"round"!==this.wireframeLinecap&&(s.wireframeLinecap=this.wireframeLinecap),"round"!==this.wireframeLinejoin&&(s.wireframeLinejoin=this.wireframeLinejoin),!0===this.flatShading&&(s.flatShading=!0),!1===this.visible&&(s.visible=!1),!1===this.toneMapped&&(s.toneMapped=!1),!1===this.fog&&(s.fog=!1),Object.keys(this.userData).length>0&&(s.userData=this.userData),e){const e=i(t.textures),r=i(t.images);e.length>0&&(s.textures=e),r.length>0&&(s.images=r)}return s}fromJSON(t,e){if(void 0!==t.uuid&&(this.uuid=t.uuid),void 0!==t.name&&(this.name=t.name),void 0!==t.color&&void 0!==this.color&&this.color.setHex(t.color),void 0!==t.roughness&&(this.roughness=t.roughness),void 0!==t.metalness&&(this.metalness=t.metalness),void 0!==t.sheen&&(this.sheen=t.sheen),void 0!==t.sheenColor&&(this.sheenColor=(new Pr).setHex(t.sheenColor)),void 0!==t.sheenRoughness&&(this.sheenRoughness=t.sheenRoughness),void 0!==t.emissive&&void 0!==this.emissive&&this.emissive.setHex(t.emissive),void 0!==t.specular&&void 0!==this.specular&&this.specular.setHex(t.specular),void 0!==t.specularIntensity&&(this.specularIntensity=t.specularIntensity),void 0!==t.specularColor&&void 0!==this.specularColor&&this.specularColor.setHex(t.specularColor),void 0!==t.shininess&&(this.shininess=t.shininess),void 0!==t.clearcoat&&(this.clearcoat=t.clearcoat),void 0!==t.clearcoatRoughness&&(this.clearcoatRoughness=t.clearcoatRoughness),void 0!==t.dispersion&&(this.dispersion=t.dispersion),void 0!==t.iridescence&&(this.iridescence=t.iridescence),void 0!==t.iridescenceIOR&&(this.iridescenceIOR=t.iridescenceIOR),void 0!==t.iridescenceThicknessRange&&(this.iridescenceThicknessRange=t.iridescenceThicknessRange),void 0!==t.transmission&&(this.transmission=t.transmission),void 0!==t.thickness&&(this.thickness=t.thickness),void 0!==t.attenuationDistance&&(this.attenuationDistance=t.attenuationDistance),void 0!==t.attenuationColor&&void 0!==this.attenuationColor&&this.attenuationColor.setHex(t.attenuationColor),void 0!==t.anisotropy&&(this.anisotropy=t.anisotropy),void 0!==t.anisotropyRotation&&(this.anisotropyRotation=t.anisotropyRotation),void 0!==t.fog&&(this.fog=t.fog),void 0!==t.flatShading&&(this.flatShading=t.flatShading),void 0!==t.blending&&(this.blending=t.blending),void 0!==t.combine&&(this.combine=t.combine),void 0!==t.side&&(this.side=t.side),void 0!==t.shadowSide&&(this.shadowSide=t.shadowSide),void 0!==t.opacity&&(this.opacity=t.opacity),void 0!==t.transparent&&(this.transparent=t.transparent),void 0!==t.alphaTest&&(this.alphaTest=t.alphaTest),void 0!==t.alphaHash&&(this.alphaHash=t.alphaHash),void 0!==t.depthFunc&&(this.depthFunc=t.depthFunc),void 0!==t.depthTest&&(this.depthTest=t.depthTest),void 0!==t.depthWrite&&(this.depthWrite=t.depthWrite),void 0!==t.colorWrite&&(this.colorWrite=t.colorWrite),void 0!==t.blendSrc&&(this.blendSrc=t.blendSrc),void 0!==t.blendDst&&(this.blendDst=t.blendDst),void 0!==t.blendEquation&&(this.blendEquation=t.blendEquation),void 0!==t.blendSrcAlpha&&(this.blendSrcAlpha=t.blendSrcAlpha),void 0!==t.blendDstAlpha&&(this.blendDstAlpha=t.blendDstAlpha),void 0!==t.blendEquationAlpha&&(this.blendEquationAlpha=t.blendEquationAlpha),void 0!==t.blendColor&&void 0!==this.blendColor&&this.blendColor.setHex(t.blendColor),void 0!==t.blendAlpha&&(this.blendAlpha=t.blendAlpha),void 0!==t.stencilWriteMask&&(this.stencilWriteMask=t.stencilWriteMask),void 0!==t.stencilFunc&&(this.stencilFunc=t.stencilFunc),void 0!==t.stencilRef&&(this.stencilRef=t.stencilRef),void 0!==t.stencilFuncMask&&(this.stencilFuncMask=t.stencilFuncMask),void 0!==t.stencilFail&&(this.stencilFail=t.stencilFail),void 0!==t.stencilZFail&&(this.stencilZFail=t.stencilZFail),void 0!==t.stencilZPass&&(this.stencilZPass=t.stencilZPass),void 0!==t.stencilWrite&&(this.stencilWrite=t.stencilWrite),void 0!==t.wireframe&&(this.wireframe=t.wireframe),void 0!==t.wireframeLinewidth&&(this.wireframeLinewidth=t.wireframeLinewidth),void 0!==t.wireframeLinecap&&(this.wireframeLinecap=t.wireframeLinecap),void 0!==t.wireframeLinejoin&&(this.wireframeLinejoin=t.wireframeLinejoin),void 0!==t.rotation&&(this.rotation=t.rotation),void 0!==t.linewidth&&(this.linewidth=t.linewidth),void 0!==t.dashSize&&(this.dashSize=t.dashSize),void 0!==t.gapSize&&(this.gapSize=t.gapSize),void 0!==t.scale&&(this.scale=t.scale),void 0!==t.polygonOffset&&(this.polygonOffset=t.polygonOffset),void 0!==t.polygonOffsetFactor&&(this.polygonOffsetFactor=t.polygonOffsetFactor),void 0!==t.polygonOffsetUnits&&(this.polygonOffsetUnits=t.polygonOffsetUnits),void 0!==t.dithering&&(this.dithering=t.dithering),void 0!==t.alphaToCoverage&&(this.alphaToCoverage=t.alphaToCoverage),void 0!==t.premultipliedAlpha&&(this.premultipliedAlpha=t.premultipliedAlpha),void 0!==t.forceSinglePass&&(this.forceSinglePass=t.forceSinglePass),void 0!==t.allowOverride&&(this.allowOverride=t.allowOverride),void 0!==t.visible&&(this.visible=t.visible),void 0!==t.toneMapped&&(this.toneMapped=t.toneMapped),void 0!==t.userData&&(this.userData=t.userData),void 0!==t.vertexColors&&("number"==typeof t.vertexColors?this.vertexColors=t.vertexColors>0:this.vertexColors=t.vertexColors),void 0!==t.size&&(this.size=t.size),void 0!==t.sizeAttenuation&&(this.sizeAttenuation=t.sizeAttenuation),void 0!==t.map&&(this.map=e[t.map]||null),void 0!==t.matcap&&(this.matcap=e[t.matcap]||null),void 0!==t.alphaMap&&(this.alphaMap=e[t.alphaMap]||null),void 0!==t.bumpMap&&(this.bumpMap=e[t.bumpMap]||null),void 0!==t.bumpScale&&(this.bumpScale=t.bumpScale),void 0!==t.normalMap&&(this.normalMap=e[t.normalMap]||null),void 0!==t.normalMapType&&(this.normalMapType=t.normalMapType),void 0!==t.normalScale){let e=t.normalScale;!1===Array.isArray(e)&&(e=[e,e]),this.normalScale=(new _i).fromArray(e)}return void 0!==t.displacementMap&&(this.displacementMap=e[t.displacementMap]||null),void 0!==t.displacementScale&&(this.displacementScale=t.displacementScale),void 0!==t.displacementBias&&(this.displacementBias=t.displacementBias),void 0!==t.roughnessMap&&(this.roughnessMap=e[t.roughnessMap]||null),void 0!==t.metalnessMap&&(this.metalnessMap=e[t.metalnessMap]||null),void 0!==t.emissiveMap&&(this.emissiveMap=e[t.emissiveMap]||null),void 0!==t.emissiveIntensity&&(this.emissiveIntensity=t.emissiveIntensity),void 0!==t.specularMap&&(this.specularMap=e[t.specularMap]||null),void 0!==t.specularIntensityMap&&(this.specularIntensityMap=e[t.specularIntensityMap]||null),void 0!==t.specularColorMap&&(this.specularColorMap=e[t.specularColorMap]||null),void 0!==t.envMap&&(this.envMap=e[t.envMap]||null),void 0!==t.envMapRotation&&this.envMapRotation.fromArray(t.envMapRotation),void 0!==t.envMapIntensity&&(this.envMapIntensity=t.envMapIntensity),void 0!==t.reflectivity&&(this.reflectivity=t.reflectivity),void 0!==t.refractionRatio&&(this.refractionRatio=t.refractionRatio),void 0!==t.lightMap&&(this.lightMap=e[t.lightMap]||null),void 0!==t.lightMapIntensity&&(this.lightMapIntensity=t.lightMapIntensity),void 0!==t.aoMap&&(this.aoMap=e[t.aoMap]||null),void 0!==t.aoMapIntensity&&(this.aoMapIntensity=t.aoMapIntensity),void 0!==t.gradientMap&&(this.gradientMap=e[t.gradientMap]||null),void 0!==t.clearcoatMap&&(this.clearcoatMap=e[t.clearcoatMap]||null),void 0!==t.clearcoatRoughnessMap&&(this.clearcoatRoughnessMap=e[t.clearcoatRoughnessMap]||null),void 0!==t.clearcoatNormalMap&&(this.clearcoatNormalMap=e[t.clearcoatNormalMap]||null),void 0!==t.clearcoatNormalScale&&(this.clearcoatNormalScale=(new _i).fromArray(t.clearcoatNormalScale)),void 0!==t.iridescenceMap&&(this.iridescenceMap=e[t.iridescenceMap]||null),void 0!==t.iridescenceThicknessMap&&(this.iridescenceThicknessMap=e[t.iridescenceThicknessMap]||null),void 0!==t.transmissionMap&&(this.transmissionMap=e[t.transmissionMap]||null),void 0!==t.thicknessMap&&(this.thicknessMap=e[t.thicknessMap]||null),void 0!==t.anisotropyMap&&(this.anisotropyMap=e[t.anisotropyMap]||null),void 0!==t.sheenColorMap&&(this.sheenColorMap=e[t.sheenColorMap]||null),void 0!==t.sheenRoughnessMap&&(this.sheenRoughnessMap=e[t.sheenRoughnessMap]||null),this}clone(){return(new this.constructor).copy(this)}copy(t){this.name=t.name,this.blending=t.blending,this.side=t.side,this.vertexColors=t.vertexColors,this.opacity=t.opacity,this.transparent=t.transparent,this.blendSrc=t.blendSrc,this.blendDst=t.blendDst,this.blendEquation=t.blendEquation,this.blendSrcAlpha=t.blendSrcAlpha,this.blendDstAlpha=t.blendDstAlpha,this.blendEquationAlpha=t.blendEquationAlpha,this.blendColor.copy(t.blendColor),this.blendAlpha=t.blendAlpha,this.depthFunc=t.depthFunc,this.depthTest=t.depthTest,this.depthWrite=t.depthWrite,this.stencilWriteMask=t.stencilWriteMask,this.stencilFunc=t.stencilFunc,this.stencilRef=t.stencilRef,this.stencilFuncMask=t.stencilFuncMask,this.stencilFail=t.stencilFail,this.stencilZFail=t.stencilZFail,this.stencilZPass=t.stencilZPass,this.stencilWrite=t.stencilWrite;const e=t.clippingPlanes;let s=null;if(null!==e){const t=e.length;s=new Array(t);for(let i=0;i!==t;++i)s[i]=e[i].clone()}return this.clippingPlanes=s,this.clipIntersection=t.clipIntersection,this.clipShadows=t.clipShadows,this.shadowSide=t.shadowSide,this.colorWrite=t.colorWrite,this.precision=t.precision,this.polygonOffset=t.polygonOffset,this.polygonOffsetFactor=t.polygonOffsetFactor,this.polygonOffsetUnits=t.polygonOffsetUnits,this.dithering=t.dithering,this.alphaTest=t.alphaTest,this.alphaHash=t.alphaHash,this.alphaToCoverage=t.alphaToCoverage,this.premultipliedAlpha=t.premultipliedAlpha,this.forceSinglePass=t.forceSinglePass,this.allowOverride=t.allowOverride,this.visible=t.visible,this.toneMapped=t.toneMapped,this.userData=JSON.parse(JSON.stringify(t.userData)),this}dispose(){this.dispatchEvent({type:"dispose"})}set needsUpdate(t){!0===t&&this.version++}}class Gn extends Zn{constructor(t){super(),this.isSpriteMaterial=!0,this.type="SpriteMaterial",this.color=new Pr(16777215),this.map=null,this.alphaMap=null,this.rotation=0,this.sizeAttenuation=!0,this.transparent=!0,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.alphaMap=t.alphaMap,this.rotation=t.rotation,this.sizeAttenuation=t.sizeAttenuation,this.fog=t.fog,this}}const $n=new Ti,Qn=new Ti,Kn=new Ti,ta=new _i,ea=new _i,sa=new Qi,ia=new Ti,ra=new Ti,na=new Ti,aa=new _i,oa=new _i,ha=new _i;class la extends Ar{constructor(t=new Gn){if(super(),this.isSprite=!0,this.type="Sprite",void 0===Xn){Xn=new Wn;const t=new Float32Array([-.5,-.5,0,0,0,.5,-.5,0,1,0,.5,.5,0,1,1,-.5,.5,0,0,1]),e=new Jn(t,5);Xn.setIndex([0,1,2,0,2,3]),Xn.setAttribute("position",new Hn(e,3,0,!1)),Xn.setAttribute("uv",new Hn(e,2,3,!1))}this.geometry=Xn,this.material=t,this.center=new _i(.5,.5),this.count=1}raycast(t,e){null===t.camera&&oi('Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.'),Qn.setFromMatrixScale(this.matrixWorld),sa.copy(t.camera.matrixWorld),this.modelViewMatrix.multiplyMatrices(t.camera.matrixWorldInverse,this.matrixWorld),Kn.setFromMatrixPosition(this.modelViewMatrix),t.camera.isPerspectiveCamera&&!1===this.material.sizeAttenuation&&Qn.multiplyScalar(-Kn.z);const s=this.material.rotation;let i,r;0!==s&&(r=Math.cos(s),i=Math.sin(s));const n=this.center;ca(ia.set(-.5,-.5,0),Kn,n,Qn,i,r),ca(ra.set(.5,-.5,0),Kn,n,Qn,i,r),ca(na.set(.5,.5,0),Kn,n,Qn,i,r),aa.set(0,0),oa.set(1,0),ha.set(1,1);let a=t.ray.intersectTriangle(ia,ra,na,!1,$n);if(null===a&&(ca(ra.set(-.5,.5,0),Kn,n,Qn,i,r),oa.set(0,1),a=t.ray.intersectTriangle(ia,na,ra,!1,$n),null===a))return;const o=t.ray.origin.distanceTo($n);ot.far||e.push({distance:o,point:$n.clone(),uv:$r.getInterpolation($n,ia,ra,na,aa,oa,ha,new _i),face:null,object:this})}copy(t,e){return super.copy(t,e),void 0!==t.center&&this.center.copy(t.center),this.material=t.material,this}}function ca(t,e,s,i,r,n){ta.subVectors(t,s).addScalar(.5).multiply(i),void 0!==r?(ea.x=n*ta.x-r*ta.y,ea.y=r*ta.x+n*ta.y):ea.copy(ta),t.copy(e),t.x+=ea.x,t.y+=ea.y,t.applyMatrix4(sa)}const ua=new Ti,da=new Ti;class pa extends Ar{constructor(){super(),this.isLOD=!0,this._currentLevel=0,this.type="LOD",Object.defineProperties(this,{levels:{enumerable:!0,value:[]}}),this.autoUpdate=!0}copy(t){super.copy(t,!1);const e=t.levels;for(let t=0,s=e.length;t0){let s,i;for(s=1,i=e.length;s0){ua.setFromMatrixPosition(this.matrixWorld);const s=t.ray.origin.distanceTo(ua);this.getObjectForDistance(s).raycast(t,e)}}update(t){const e=this.levels;if(e.length>1){ua.setFromMatrixPosition(t.matrixWorld),da.setFromMatrixPosition(this.matrixWorld);const s=ua.distanceTo(da)/t.zoom;let i,r;for(e[0].object.visible=!0,i=1,r=e.length;i=t))break;e[i-1].object.visible=!1,e[i].object.visible=!0}for(this._currentLevel=i-1;i0)if(c=n*o-a,u=n*a-o,p=r*l,c>=0)if(u>=-p)if(u<=p){const t=1/l;c*=t,u*=t,d=c*(c+n*u+2*a)+u*(n*c+u+2*o)+h}else u=r,c=Math.max(0,-(n*u+a)),d=-c*c+u*(u+2*o)+h;else u=-r,c=Math.max(0,-(n*u+a)),d=-c*c+u*(u+2*o)+h;else u<=-p?(c=Math.max(0,-(-n*r+a)),u=c>0?-r:Math.min(Math.max(-r,-o),r),d=-c*c+u*(u+2*o)+h):u<=p?(c=0,u=Math.min(Math.max(-r,-o),r),d=u*(u+2*o)+h):(c=Math.max(0,-(n*r+a)),u=c>0?r:Math.min(Math.max(-r,-o),r),d=-c*c+u*(u+2*o)+h);else u=n>0?-r:r,c=Math.max(0,-(n*u+a)),d=-c*c+u*(u+2*o)+h;return s&&s.copy(this.origin).addScaledVector(this.direction,c),i&&i.copy(ya).addScaledVector(ga,u),d}intersectSphere(t,e){ma.subVectors(t.center,this.origin);const s=ma.dot(this.direction),i=ma.dot(ma)-s*s,r=t.radius*t.radius;if(i>r)return null;const n=Math.sqrt(r-i),a=s-n,o=s+n;return o<0?null:a<0?this.at(o,e):this.at(a,e)}intersectsSphere(t){return!(t.radius<0)&&this.distanceSqToPoint(t.center)<=t.radius*t.radius}distanceToPlane(t){const e=t.normal.dot(this.direction);if(0===e)return 0===t.distanceToPoint(this.origin)?0:null;const s=-(this.origin.dot(t.normal)+t.constant)/e;return s>=0?s:null}intersectPlane(t,e){const s=this.distanceToPlane(t);return null===s?null:this.at(s,e)}intersectsPlane(t){const e=t.distanceToPoint(this.origin);if(0===e)return!0;return t.normal.dot(this.direction)*e<0}intersectBox(t,e){let s,i,r,n,a,o;const h=1/this.direction.x,l=1/this.direction.y,c=1/this.direction.z,u=this.origin;return h>=0?(s=(t.min.x-u.x)*h,i=(t.max.x-u.x)*h):(s=(t.max.x-u.x)*h,i=(t.min.x-u.x)*h),l>=0?(r=(t.min.y-u.y)*l,n=(t.max.y-u.y)*l):(r=(t.max.y-u.y)*l,n=(t.min.y-u.y)*l),s>n||r>i?null:((r>s||isNaN(s))&&(s=r),(n=0?(a=(t.min.z-u.z)*c,o=(t.max.z-u.z)*c):(a=(t.max.z-u.z)*c,o=(t.min.z-u.z)*c),s>o||a>i?null:((a>s||s!=s)&&(s=a),(o=0?s:i,e)))}intersectsBox(t){return null!==this.intersectBox(t,ma)}intersectTriangle(t,e,s,i,r){xa.subVectors(e,t),ba.subVectors(s,t),va.crossVectors(xa,ba);let n,a=this.direction.dot(va);if(a>0){if(i)return null;n=1}else{if(!(a<0))return null;n=-1,a=-a}fa.subVectors(this.origin,t);const o=n*this.direction.dot(ba.crossVectors(fa,ba));if(o<0)return null;const h=n*this.direction.dot(xa.cross(fa));if(h<0)return null;if(o+h>a)return null;const l=-n*fa.dot(va);return l<0?null:this.at(l/a,r)}applyMatrix4(t){return this.origin.applyMatrix4(t),this.direction.transformDirection(t),this}equals(t){return t.origin.equals(this.origin)&&t.direction.equals(this.direction)}clone(){return(new this.constructor).copy(this)}}class Ma extends Zn{constructor(t){super(),this.isMeshBasicMaterial=!0,this.type="MeshBasicMaterial",this.color=new Pr(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new hr,this.combine=0,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.fog=t.fog,this}}const Sa=new Qi,_a=new wa,Aa=new En,Ta=new Ti,za=new Ti,Ca=new Ti,Ia=new Ti,Ba=new Ti,ka=new Ti,Oa=new Ti,Pa=new Ti;class Ra extends Ar{constructor(t=new Wn,e=new Ma){super(),this.isMesh=!0,this.type="Mesh",this.geometry=t,this.material=e,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.count=1,this.updateMorphTargets()}copy(t,e){return super.copy(t,e),void 0!==t.morphTargetInfluences&&(this.morphTargetInfluences=t.morphTargetInfluences.slice()),void 0!==t.morphTargetDictionary&&(this.morphTargetDictionary=Object.assign({},t.morphTargetDictionary)),this.material=Array.isArray(t.material)?t.material.slice():t.material,this.geometry=t.geometry,this}updateMorphTargets(){const t=this.geometry.morphAttributes,e=Object.keys(t);if(e.length>0){const s=t[e[0]];if(void 0!==s){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let t=0,e=s.length;t(t.far-t.near)**2)return}Sa.copy(r).invert(),_a.copy(t.ray).applyMatrix4(Sa),null!==s.boundingBox&&!1===_a.intersectsBox(s.boundingBox)||this._computeIntersections(t,e,_a)}}_computeIntersections(t,e,s){let i;const r=this.geometry,n=this.material,a=r.index,o=r.attributes.position,h=r.attributes.uv,l=r.attributes.uv1,c=r.attributes.normal,u=r.groups,d=r.drawRange;if(null!==a)if(Array.isArray(n))for(let r=0,o=u.length;rs.far?null:{distance:l,point:Pa.clone(),object:t}}(t,e,s,i,za,Ca,Ia,Oa);if(c){const t=new Ti;$r.getBarycoord(Oa,za,Ca,Ia,t),r&&(c.uv=$r.getInterpolatedAttribute(r,o,h,l,t,new _i)),n&&(c.uv1=$r.getInterpolatedAttribute(n,o,h,l,t,new _i)),a&&(c.normal=$r.getInterpolatedAttribute(a,o,h,l,t,new Ti),c.normal.dot(i.direction)>0&&c.normal.multiplyScalar(-1));const e={a:o,b:h,c:l,normal:new Ti,materialIndex:0};$r.getNormal(za,Ca,Ia,e.normal),c.face=e,c.barycoord=t}return c}const Na=new qi,Va=new qi,La=new qi,Fa=new qi,Da=new Qi,Ua=new Ti,ja=new En,Wa=new Qi,Ja=new wa;class qa extends Ra{constructor(t,e){super(t,e),this.isSkinnedMesh=!0,this.type="SkinnedMesh",this.bindMode=at,this.bindMatrix=new Qi,this.bindMatrixInverse=new Qi,this.boundingBox=null,this.boundingSphere=null}computeBoundingBox(){const t=this.geometry;null===this.boundingBox&&(this.boundingBox=new Qr),this.boundingBox.makeEmpty();const e=t.getAttribute("position");for(let t=0;t1)?null:e.copy(t.start).addScaledVector(i,n)}intersectsLine(t){const e=this.distanceToPoint(t.start),s=this.distanceToPoint(t.end);return e<0&&s>0||s<0&&e>0}intersectsBox(t){return t.intersectsPlane(this)}intersectsSphere(t){return t.intersectsPlane(this)}coplanarPoint(t){return t.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(t,e){const s=e||ho.getNormalMatrix(t),i=this.coplanarPoint(ao).applyMatrix4(t),r=this.normal.applyMatrix3(s).normalize();return this.constant=-i.dot(r),this}translate(t){return this.constant-=t.dot(this.normal),this}equals(t){return t.normal.equals(this.normal)&&t.constant===this.constant}clone(){return(new this.constructor).copy(this)}}const co=new En,uo=new _i(.5,.5),po=new Ti;class mo{constructor(t=new lo,e=new lo,s=new lo,i=new lo,r=new lo,n=new lo){this.planes=[t,e,s,i,r,n]}set(t,e,s,i,r,n){const a=this.planes;return a[0].copy(t),a[1].copy(e),a[2].copy(s),a[3].copy(i),a[4].copy(r),a[5].copy(n),this}copy(t){const e=this.planes;for(let s=0;s<6;s++)e[s].copy(t.planes[s]);return this}setFromProjectionMatrix(t,e=2e3,s=!1){const i=this.planes,r=t.elements,n=r[0],a=r[1],o=r[2],h=r[3],l=r[4],c=r[5],u=r[6],d=r[7],p=r[8],m=r[9],y=r[10],g=r[11],f=r[12],x=r[13],b=r[14],v=r[15];if(i[0].setComponents(h-n,d-l,g-p,v-f).normalize(),i[1].setComponents(h+n,d+l,g+p,v+f).normalize(),i[2].setComponents(h+a,d+c,g+m,v+x).normalize(),i[3].setComponents(h-a,d-c,g-m,v-x).normalize(),s)i[4].setComponents(o,u,y,b).normalize(),i[5].setComponents(h-o,d-u,g-y,v-b).normalize();else if(i[4].setComponents(h-o,d-u,g-y,v-b).normalize(),e===Ws)i[5].setComponents(h+o,d+u,g+y,v+b).normalize();else{if(e!==Js)throw new Error("THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: "+e);i[5].setComponents(o,u,y,b).normalize()}return this}intersectsObject(t){if(void 0!==t.boundingSphere)null===t.boundingSphere&&t.computeBoundingSphere(),co.copy(t.boundingSphere).applyMatrix4(t.matrixWorld);else{const e=t.geometry;null===e.boundingSphere&&e.computeBoundingSphere(),co.copy(e.boundingSphere).applyMatrix4(t.matrixWorld)}return this.intersectsSphere(co)}intersectsSprite(t){co.center.set(0,0,0);const e=uo.distanceTo(t.center);return co.radius=.7071067811865476+e,co.applyMatrix4(t.matrixWorld),this.intersectsSphere(co)}intersectsSphere(t){const e=this.planes,s=t.center,i=-t.radius;for(let t=0;t<6;t++){if(e[t].distanceToPoint(s)0?t.max.x:t.min.x,po.y=i.normal.y>0?t.max.y:t.min.y,po.z=i.normal.z>0?t.max.z:t.min.z,i.distanceToPoint(po)<0)return!1}return!0}containsPoint(t){const e=this.planes;for(let s=0;s<6;s++)if(e[s].distanceToPoint(t)<0)return!1;return!0}clone(){return(new this.constructor).copy(this)}}const yo=new Qi;class go{constructor(){this.coordinateSystem=Ws,this._frustums=[],this._count=0}setFromArrayCamera(t){const e=t.cameras,s=this._frustums;for(let t=0;t=r.length&&r.push({start:-1,count:-1,z:-1,index:-1});const a=r[this.index];n.push(a),this.index++,a.start=t,a.count=e,a.z=s,a.index=i}reset(){this.list.length=0,this.index=0}}const wo=new Qi,Mo=new Pr(1,1,1),So=new mo,_o=new go,Ao=new Qr,To=new En,zo=new Ti,Co=new Ti,Io=new Ti,Bo=new vo,ko=new Ra,Oo=[];function Po(t,e,s=0){const i=e.itemSize;if(t.isInterleavedBufferAttribute||t.array.constructor!==e.array.constructor){const r=t.count;for(let n=0;n65535?new Uint32Array(i):new Uint16Array(i);e.setIndex(new Mn(t,1))}this._geometryInitialized=!0}}_validateGeometry(t){const e=this.geometry;if(Boolean(t.getIndex())!==Boolean(e.getIndex()))throw new Error('THREE.BatchedMesh: All geometries must consistently have "index".');for(const s in e.attributes){if(!t.hasAttribute(s))throw new Error(`THREE.BatchedMesh: Added geometry missing "${s}". All geometries must have consistent attributes.`);const i=t.getAttribute(s),r=e.getAttribute(s);if(i.itemSize!==r.itemSize||i.normalized!==r.normalized)throw new Error("THREE.BatchedMesh: All attributes must have a consistent itemSize and normalized value.")}}validateInstanceId(t){const e=this._instanceInfo;if(t<0||t>=e.length||!1===e[t].active)throw new Error(`THREE.BatchedMesh: Invalid instanceId ${t}. Instance is either out of range or has been deleted.`)}validateGeometryId(t){const e=this._geometryInfo;if(t<0||t>=e.length||!1===e[t].active)throw new Error(`THREE.BatchedMesh: Invalid geometryId ${t}. Geometry is either out of range or has been deleted.`)}setCustomSort(t){return this.customSort=t,this}computeBoundingBox(){null===this.boundingBox&&(this.boundingBox=new Qr);const t=this.boundingBox,e=this._instanceInfo;t.makeEmpty();for(let s=0,i=e.length;s=this.maxInstanceCount&&0===this._availableInstanceIds.length)throw new Error("THREE.BatchedMesh: Maximum item count reached.");const e={visible:!0,active:!0,geometryIndex:t};let s=null;this._availableInstanceIds.length>0?(this._availableInstanceIds.sort(fo),s=this._availableInstanceIds.shift(),this._instanceInfo[s]=e):(s=this._instanceInfo.length,this._instanceInfo.push(e));const i=this._matricesTexture;wo.identity().toArray(i.image.data,16*s),i.needsUpdate=!0;const r=this._colorsTexture;return r&&(Mo.toArray(r.image.data,4*s),r.needsUpdate=!0),this._visibilityChanged=!0,s}addGeometry(t,e=-1,s=-1){this._initializeGeometry(t),this._validateGeometry(t);const i={vertexStart:-1,vertexCount:-1,reservedVertexCount:-1,indexStart:-1,indexCount:-1,reservedIndexCount:-1,start:-1,count:-1,boundingBox:null,boundingSphere:null,active:!0},r=this._geometryInfo;i.vertexStart=this._nextVertexStart,i.reservedVertexCount=-1===e?t.getAttribute("position").count:e;const n=t.getIndex();if(null!==n&&(i.indexStart=this._nextIndexStart,i.reservedIndexCount=-1===s?n.count:s),-1!==i.indexStart&&i.indexStart+i.reservedIndexCount>this._maxIndexCount||i.vertexStart+i.reservedVertexCount>this._maxVertexCount)throw new Error("THREE.BatchedMesh: Reserved space request exceeds the maximum buffer size.");let a;return this._availableGeometryIds.length>0?(this._availableGeometryIds.sort(fo),a=this._availableGeometryIds.shift(),r[a]=i):(a=this._geometryCount,this._geometryCount++,r.push(i)),this.setGeometryAt(a,t),this._nextIndexStart=i.indexStart+i.reservedIndexCount,this._nextVertexStart=i.vertexStart+i.reservedVertexCount,a}setGeometryAt(t,e){if(t>=this._geometryCount)throw new Error("THREE.BatchedMesh: Maximum geometry count reached.");this._validateGeometry(e);const s=this.geometry,i=null!==s.getIndex(),r=s.getIndex(),n=e.getIndex(),a=this._geometryInfo[t];if(i&&n.count>a.reservedIndexCount||e.attributes.position.count>a.reservedVertexCount)throw new Error("THREE.BatchedMesh: Reserved space not large enough for provided geometry.");const o=a.vertexStart,h=a.reservedVertexCount;a.vertexCount=e.getAttribute("position").count;for(const t in s.attributes){const i=e.getAttribute(t),r=s.getAttribute(t);Po(i,r,o);const n=i.itemSize;for(let t=i.count,e=h;t=e.length||!1===e[t].active)return this;const s=this._instanceInfo;for(let e=0,i=s.length;ee).sort((t,e)=>s[t].vertexStart-s[e].vertexStart),r=this.geometry;for(let n=0,a=s.length;n=this._geometryCount)return null;const s=this.geometry,i=this._geometryInfo[t];if(null===i.boundingBox){const t=new Qr,e=s.index,r=s.attributes.position;for(let s=i.start,n=i.start+i.count;s=this._geometryCount)return null;const s=this.geometry,i=this._geometryInfo[t];if(null===i.boundingSphere){const e=new En;this.getBoundingBoxAt(t,Ao),Ao.getCenter(e.center);const r=s.index,n=s.attributes.position;let a=0;for(let t=i.start,s=i.start+i.count;tt.active);if(Math.max(...s.map(t=>t.vertexStart+t.reservedVertexCount))>t)throw new Error(`THREE.BatchedMesh: Geometry vertex values are being used outside the range ${e}. Cannot shrink further.`);if(this.geometry.index){if(Math.max(...s.map(t=>t.indexStart+t.reservedIndexCount))>e)throw new Error(`THREE.BatchedMesh: Geometry index values are being used outside the range ${e}. Cannot shrink further.`)}const i=this.geometry;i.dispose(),this._maxVertexCount=t,this._maxIndexCount=e,this._geometryInitialized&&(this._geometryInitialized=!1,this.geometry=new Wn,this._initializeGeometry(i));const r=this.geometry;i.index&&Ro(i.index.array,r.index.array);for(const t in i.attributes)Ro(i.attributes[t].array,r.attributes[t].array)}raycast(t,e){const s=this._instanceInfo,i=this._geometryInfo,r=this.matrixWorld,n=this.geometry;ko.material=this.material,ko.geometry.index=n.index,ko.geometry.attributes=n.attributes,null===ko.geometry.boundingBox&&(ko.geometry.boundingBox=new Qr),null===ko.geometry.boundingSphere&&(ko.geometry.boundingSphere=new En);for(let n=0,a=s.length;n({...t,boundingBox:null!==t.boundingBox?t.boundingBox.clone():null,boundingSphere:null!==t.boundingSphere?t.boundingSphere.clone():null})),this._instanceInfo=t._instanceInfo.map(t=>({...t})),this._availableInstanceIds=t._availableInstanceIds.slice(),this._availableGeometryIds=t._availableGeometryIds.slice(),this._nextIndexStart=t._nextIndexStart,this._nextVertexStart=t._nextVertexStart,this._geometryCount=t._geometryCount,this._maxInstanceCount=t._maxInstanceCount,this._maxVertexCount=t._maxVertexCount,this._maxIndexCount=t._maxIndexCount,this._geometryInitialized=t._geometryInitialized,this._multiDrawCounts=t._multiDrawCounts.slice(),this._multiDrawStarts=t._multiDrawStarts.slice(),this._indirectTexture=t._indirectTexture.clone(),this._indirectTexture.image.data=this._indirectTexture.image.data.slice(),this._matricesTexture=t._matricesTexture.clone(),this._matricesTexture.image.data=this._matricesTexture.image.data.slice(),null!==this._colorsTexture&&(this._colorsTexture=t._colorsTexture.clone(),this._colorsTexture.image.data=this._colorsTexture.image.data.slice()),this}dispose(){this.geometry.dispose(),this._matricesTexture.dispose(),this._matricesTexture=null,this._indirectTexture.dispose(),this._indirectTexture=null,null!==this._colorsTexture&&(this._colorsTexture.dispose(),this._colorsTexture=null)}onBeforeRender(t,e,s,i,r){if(!this._visibilityChanged&&!this.perObjectFrustumCulled&&!this.sortObjects)return;const n=i.getIndex();let a=null===n?1:n.array.BYTES_PER_ELEMENT,o=1;r.wireframe&&(o=2,a=i.attributes.position.count>65535?4:2);const h=this._instanceInfo,l=this._multiDrawStarts,c=this._multiDrawCounts,u=this._geometryInfo,d=this.perObjectFrustumCulled,p=this._indirectTexture,m=p.image.data,y=s.isArrayCamera?_o:So;d&&(s.isArrayCamera?y.setFromArrayCamera(s):(wo.multiplyMatrices(s.projectionMatrix,s.matrixWorldInverse).multiply(this.matrixWorld),y.setFromProjectionMatrix(wo,s.coordinateSystem,s.reversedDepth)));let g=0;if(this.sortObjects){wo.copy(this.matrixWorld).invert(),zo.setFromMatrixPosition(s.matrixWorld).applyMatrix4(wo),Co.set(0,0,-1).transformDirection(s.matrixWorld).transformDirection(wo);for(let t=0,e=h.length;t0){const s=t[e[0]];if(void 0!==s){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let t=0,e=s.length;ti)return;jo.applyMatrix4(t.matrixWorld);const h=e.ray.origin.distanceTo(jo);return he.far?void 0:{distance:h,point:Wo.clone().applyMatrix4(t.matrixWorld),index:a,face:null,faceIndex:null,barycoord:null,object:t}}const Ho=new Ti,Xo=new Ti;class Yo extends Jo{constructor(t,e){super(t,e),this.isLineSegments=!0,this.type="LineSegments"}computeLineDistances(){const t=this.geometry;if(null===t.index){const e=t.attributes.position,s=[];for(let t=0,i=e.count;t0){const s=t[e[0]];if(void 0!==s){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let t=0,e=s.length;tr.far)return;n.push({distance:h,distanceToRay:Math.sqrt(o),point:s,index:e,face:null,faceIndex:null,barycoord:null,object:a})}}class ih extends Ji{constructor(t,e,s,i,r=1006,n=1006,a,o,h){super(t,e,s,i,r,n,a,o,h),this.isVideoTexture=!0,this.generateMipmaps=!1,this._requestVideoFrameCallbackId=0;const l=this;"requestVideoFrameCallback"in t&&(this._requestVideoFrameCallbackId=t.requestVideoFrameCallback(function e(){l.needsUpdate=!0,l._requestVideoFrameCallbackId=t.requestVideoFrameCallback(e)}))}clone(){return new this.constructor(this.image).copy(this)}update(){const t=this.image;!1==="requestVideoFrameCallback"in t&&t.readyState>=t.HAVE_CURRENT_DATA&&(this.needsUpdate=!0)}dispose(){0!==this._requestVideoFrameCallbackId&&(this.source.data.cancelVideoFrameCallback(this._requestVideoFrameCallbackId),this._requestVideoFrameCallbackId=0),super.dispose()}}class rh extends ih{constructor(t,e,s,i,r,n,a,o){super({},t,e,s,i,r,n,a,o),this.isVideoFrameTexture=!0}update(){}clone(){return(new this.constructor).copy(this)}setFrame(t){this.image=t,this.needsUpdate=!0}}class nh extends Ji{constructor(t,e){super({width:t,height:e}),this.isFramebufferTexture=!0,this.magFilter=ft,this.minFilter=ft,this.generateMipmaps=!1,this.needsUpdate=!0}}class ah extends Ji{constructor(t,e,s,i,r,n,a,o,h,l,c,u){super(null,n,a,o,h,l,i,r,c,u),this.isCompressedTexture=!0,this.image={width:e,height:s},this.mipmaps=t,this.flipY=!1,this.generateMipmaps=!1}}class oh extends ah{constructor(t,e,s,i,r,n){super(t,e,s,r,n),this.isCompressedArrayTexture=!0,this.image.depth=i,this.wrapR=yt,this.layerUpdates=new Set}addLayerUpdate(t){this.layerUpdates.add(t)}clearLayerUpdates(){this.layerUpdates.clear()}}class hh extends ah{constructor(t,e,s){super(void 0,t[0].width,t[0].height,e,s,lt),this.isCompressedCubeTexture=!0,this.isCubeTexture=!0,this.image=t}}class lh extends Ji{constructor(t=[],e=301,s,i,r,n,a,o,h,l){super(t,e,s,i,r,n,a,o,h,l),this.isCubeTexture=!0,this.flipY=!1}get images(){return this.image}set images(t){this.image=t}}class ch extends Ji{constructor(t,e,s,i,r,n,a,o,h){super(t,e,s,i,r,n,a,o,h),this.isCanvasTexture=!0,this.needsUpdate=!0}}class uh extends Ji{constructor(t,e,s,i,r,n,a,o,h){super(t,e,s,i,r,n,a,o,h),this.isHTMLTexture=!0,this.generateMipmaps=!1,this.needsUpdate=!0;const l=t?t.parentNode:null;null!==l&&"requestPaint"in l&&(l.onpaint=()=>{this.needsUpdate=!0},l.requestPaint())}dispose(){const t=this.image?this.image.parentNode:null;null!==t&&"onpaint"in t&&(t.onpaint=null),super.dispose()}}class dh extends Ji{constructor(t,e,s=1014,i,r,n,a=1003,o=1003,h,l=1026,c=1){if(l!==Wt&&1027!==l)throw new Error("THREE.DepthTexture: format must be either THREE.DepthFormat or THREE.DepthStencilFormat");super({width:t,height:e,depth:c},i,r,n,a,o,l,s,h),this.isDepthTexture=!0,this.flipY=!1,this.generateMipmaps=!1,this.compareFunction=null}copy(t){return super.copy(t),this.source=new Di(Object.assign({},t.image)),this.compareFunction=t.compareFunction,this}toJSON(t){const e=super.toJSON(t);return null!==this.compareFunction&&(e.compareFunction=this.compareFunction),e}}class ph extends dh{constructor(t,e=1014,s=301,i,r,n=1003,a=1003,o,h=1026){const l={width:t,height:t,depth:1},c=[l,l,l,l,l,l];super(t,t,e,s,i,r,n,a,o,h),this.image=c,this.isCubeDepthTexture=!0,this.isCubeTexture=!0}get images(){return this.image}set images(t){this.image=t}}class mh extends Ji{constructor(t=null){super(),this.sourceTexture=t,this.isExternalTexture=!0}copy(t){return super.copy(t),this.sourceTexture=t.sourceTexture,this}}class yh extends Wn{constructor(t=1,e=1,s=1,i=1,r=1,n=1){super(),this.type="BoxGeometry",this.parameters={width:t,height:e,depth:s,widthSegments:i,heightSegments:r,depthSegments:n};const a=this;i=Math.floor(i),r=Math.floor(r),n=Math.floor(n);const o=[],h=[],l=[],c=[];let u=0,d=0;function p(t,e,s,i,r,n,p,m,y,g,f){const x=n/y,b=p/g,v=n/2,w=p/2,M=m/2,S=y+1,_=g+1;let A=0,T=0;const z=new Ti;for(let n=0;n<_;n++){const a=n*b-w;for(let o=0;o0?1:-1,l.push(z.x,z.y,z.z),c.push(o/y),c.push(1-n/g),A+=1}}for(let t=0;t0){const t=(f-1)*m;for(let e=0;e0||0!==i)&&(l.push(n,a,h),x+=3),(e>0||i!==r-1)&&(l.push(a,o,h),x+=3)}h.addGroup(g,x,0),g+=x}(),!1===n&&(t>0&&f(!0),e>0&&f(!1)),this.setIndex(l),this.setAttribute("position",new kn(c,3)),this.setAttribute("normal",new kn(u,3)),this.setAttribute("uv",new kn(d,2))}copy(t){return super.copy(t),this.parameters=Object.assign({},t.parameters),this}static fromJSON(t){return new xh(t.radiusTop,t.radiusBottom,t.height,t.radialSegments,t.heightSegments,t.openEnded,t.thetaStart,t.thetaLength)}}class bh extends xh{constructor(t=1,e=1,s=32,i=1,r=!1,n=0,a=2*Math.PI){super(0,t,e,s,i,r,n,a),this.type="ConeGeometry",this.parameters={radius:t,height:e,radialSegments:s,heightSegments:i,openEnded:r,thetaStart:n,thetaLength:a}}static fromJSON(t){return new bh(t.radius,t.height,t.radialSegments,t.heightSegments,t.openEnded,t.thetaStart,t.thetaLength)}}class vh extends Wn{constructor(t=[],e=[],s=1,i=0){super(),this.type="PolyhedronGeometry",this.parameters={vertices:t,indices:e,radius:s,detail:i};const r=[],n=[];function a(t,e,s,i){const r=i+1,n=[];for(let i=0;i<=r;i++){n[i]=[];const a=t.clone().lerp(s,i/r),o=e.clone().lerp(s,i/r),h=r-i;for(let t=0;t<=h;t++)n[i][t]=0===t&&i===r?a:a.clone().lerp(o,t/h)}for(let t=0;t.9&&a<.1&&(e<.2&&(n[t+0]+=1),s<.2&&(n[t+2]+=1),i<.2&&(n[t+4]+=1))}}()}(),this.setAttribute("position",new kn(r,3)),this.setAttribute("normal",new kn(r.slice(),3)),this.setAttribute("uv",new kn(n,2)),0===i?this.computeVertexNormals():this.normalizeNormals()}copy(t){return super.copy(t),this.parameters=Object.assign({},t.parameters),this}static fromJSON(t){return new vh(t.vertices,t.indices,t.radius,t.detail)}}class wh extends vh{constructor(t=1,e=0){const s=(1+Math.sqrt(5))/2,i=1/s;super([-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-i,-s,0,-i,s,0,i,-s,0,i,s,-i,-s,0,-i,s,0,i,-s,0,i,s,0,-s,0,-i,s,0,-i,-s,0,i,s,0,i],[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2,6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,11,19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9],t,e),this.type="DodecahedronGeometry",this.parameters={radius:t,detail:e}}static fromJSON(t){return new wh(t.radius,t.detail)}}const Mh=new Ti,Sh=new Ti,_h=new Ti,Ah=new $r;class Th extends Wn{constructor(t=null,e=1){if(super(),this.type="EdgesGeometry",this.parameters={geometry:t,thresholdAngle:e},null!==t){const s=4,i=Math.pow(10,s),r=Math.cos(yi*e),n=t.getIndex(),a=t.getAttribute("position"),o=n?n.count:a.count,h=[0,0,0],l=["a","b","c"],c=new Array(3),u={},d=[];for(let t=0;t0)){h=i;break}h=i-1}if(i=h,s[i]===n)return i/(r-1);const l=s[i];return(i+(n-l)/(s[i+1]-l))/(r-1)}getTangent(t,e){const s=1e-4;let i=t-s,r=t+s;i<0&&(i=0),r>1&&(r=1);const n=this.getPoint(i),a=this.getPoint(r),o=e||(n.isVector2?new _i:new Ti);return o.copy(a).sub(n).normalize(),o}getTangentAt(t,e){const s=this.getUtoTmapping(t);return this.getTangent(s,e)}computeFrenetFrames(t,e=!1){const s=new Ti,i=[],r=[],n=[],a=new Ti,o=new Qi;for(let e=0;e<=t;e++){const s=e/t;i[e]=this.getTangentAt(s,new Ti)}r[0]=new Ti,n[0]=new Ti;let h=Number.MAX_VALUE;const l=Math.abs(i[0].x),c=Math.abs(i[0].y),u=Math.abs(i[0].z);l<=h&&(h=l,s.set(1,0,0)),c<=h&&(h=c,s.set(0,1,0)),u<=h&&s.set(0,0,1),a.crossVectors(i[0],s).normalize(),r[0].crossVectors(i[0],a),n[0].crossVectors(i[0],r[0]);for(let e=1;e<=t;e++){if(r[e]=r[e-1].clone(),n[e]=n[e-1].clone(),a.crossVectors(i[e-1],i[e]),a.length()>Number.EPSILON){a.normalize();const t=Math.acos(xi(i[e-1].dot(i[e]),-1,1));r[e].applyMatrix4(o.makeRotationAxis(a,t))}n[e].crossVectors(i[e],r[e])}if(!0===e){let e=Math.acos(xi(r[0].dot(r[t]),-1,1));e/=t,i[0].dot(a.crossVectors(r[0],r[t]))>0&&(e=-e);for(let s=1;s<=t;s++)r[s].applyMatrix4(o.makeRotationAxis(i[s],e*s)),n[s].crossVectors(i[s],r[s])}return{tangents:i,normals:r,binormals:n}}clone(){return(new this.constructor).copy(this)}copy(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}toJSON(){const t={metadata:{version:4.7,type:"Curve",generator:"Curve.toJSON"}};return t.arcLengthDivisions=this.arcLengthDivisions,t.type=this.type,t}fromJSON(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}}class Ch extends zh{constructor(t=0,e=0,s=1,i=1,r=0,n=2*Math.PI,a=!1,o=0){super(),this.isEllipseCurve=!0,this.type="EllipseCurve",this.aX=t,this.aY=e,this.xRadius=s,this.yRadius=i,this.aStartAngle=r,this.aEndAngle=n,this.aClockwise=a,this.aRotation=o}getPoint(t,e=new _i){const s=e,i=2*Math.PI;let r=this.aEndAngle-this.aStartAngle;const n=Math.abs(r)i;)r-=i;r0?0:(Math.floor(Math.abs(h)/r)+1)*r:0===l&&h===r-1&&(h=r-2,l=1),this.closed||h>0?a=i[(h-1)%r]:(Oh.subVectors(i[0],i[1]).add(i[0]),a=Oh);const c=i[h%r],u=i[(h+1)%r];if(this.closed||h+2i.length-2?i.length-1:n+1],c=i[n>i.length-3?i.length-1:n+2];return s.set(Vh(a,o.x,h.x,l.x,c.x),Vh(a,o.y,h.y,l.y,c.y)),s}copy(t){super.copy(t),this.points=[];for(let e=0,s=t.points.length;e=s){const t=i[r]-s,n=this.curves[r],a=n.getLength(),o=0===a?0:1-t/a;return n.getPointAt(o,e)}r++}return null}getLength(){const t=this.getCurveLengths();return t[t.length-1]}updateArcLengths(){this.needsUpdate=!0,this.cacheLengths=null,this.getCurveLengths()}getCurveLengths(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;const t=[];let e=0;for(let s=0,i=this.curves.length;s1&&!e[e.length-1].equals(e[0])&&e.push(e[0]),e}copy(t){super.copy(t),this.curves=[];for(let e=0,s=t.curves.length;e0){const t=h.getPoint(0);t.equals(this.currentPoint)||this.lineTo(t.x,t.y)}this.curves.push(h);const l=h.getPoint(1);return this.currentPoint.copy(l),this}copy(t){return super.copy(t),this.currentPoint.copy(t.currentPoint),this}toJSON(){const t=super.toJSON();return t.currentPoint=this.currentPoint.toArray(),t}fromJSON(t){return super.fromJSON(t),this.currentPoint.fromArray(t.currentPoint),this}}class Gh extends Zh{constructor(t){super(t),this.uuid=fi(),this.type="Shape",this.holes=[]}getPointsHoles(t){const e=[];for(let s=0,i=this.holes.length;s80*s){o=t[0],h=t[1];let e=o,i=h;for(let n=s;ne&&(e=s),r>i&&(i=r)}l=Math.max(e-o,i-h),l=0!==l?32767/l:0}return tl(n,a,s,o,h,l,0),a}function Qh(t,e,s,i,r){let n;if(r===function(t,e,s,i){let r=0;for(let n=e,a=s-i;n0)for(let r=e;r=e;r-=i)n=vl(r/i|0,t[r],t[r+1],n);return n&&ml(n,n.next)&&(wl(n),n=n.next),n}function Kh(t,e){if(!t)return t;e||(e=t);let s,i=t;do{if(s=!1,i.steiner||!ml(i,i.next)&&0!==pl(i.prev,i,i.next))i=i.next;else{if(wl(i),i=e=i.prev,i===i.next)break;s=!0}}while(s||i!==e);return e}function tl(t,e,s,i,r,n,a){if(!t)return;!a&&n&&function(t,e,s,i){let r=t;do{0===r.z&&(r.z=hl(r.x,r.y,e,s,i)),r.prevZ=r.prev,r.nextZ=r.next,r=r.next}while(r!==t);r.prevZ.nextZ=null,r.prevZ=null,function(t){let e,s=1;do{let i,r=t;t=null;let n=null;for(e=0;r;){e++;let a=r,o=0;for(let t=0;t0||h>0&&a;)0!==o&&(0===h||!a||r.z<=a.z)?(i=r,r=r.nextZ,o--):(i=a,a=a.nextZ,h--),n?n.nextZ=i:t=i,i.prevZ=n,n=i;r=a}n.nextZ=null,s*=2}while(e>1)}(r)}(t,i,r,n);let o=t;for(;t.prev!==t.next;){const h=t.prev,l=t.next;if(n?sl(t,i,r,n):el(t))e.push(h.i,t.i,l.i),wl(t),t=l.next,o=l.next;else if((t=l)===o){a?1===a?tl(t=il(Kh(t),e),e,s,i,r,n,2):2===a&&rl(t,e,s,i,r,n):tl(Kh(t),e,s,i,r,n,1);break}}}function el(t){const e=t.prev,s=t,i=t.next;if(pl(e,s,i)>=0)return!1;const r=e.x,n=s.x,a=i.x,o=e.y,h=s.y,l=i.y,c=Math.min(r,n,a),u=Math.min(o,h,l),d=Math.max(r,n,a),p=Math.max(o,h,l);let m=i.next;for(;m!==e;){if(m.x>=c&&m.x<=d&&m.y>=u&&m.y<=p&&ul(r,o,n,h,a,l,m.x,m.y)&&pl(m.prev,m,m.next)>=0)return!1;m=m.next}return!0}function sl(t,e,s,i){const r=t.prev,n=t,a=t.next;if(pl(r,n,a)>=0)return!1;const o=r.x,h=n.x,l=a.x,c=r.y,u=n.y,d=a.y,p=Math.min(o,h,l),m=Math.min(c,u,d),y=Math.max(o,h,l),g=Math.max(c,u,d),f=hl(p,m,e,s,i),x=hl(y,g,e,s,i);let b=t.prevZ,v=t.nextZ;for(;b&&b.z>=f&&v&&v.z<=x;){if(b.x>=p&&b.x<=y&&b.y>=m&&b.y<=g&&b!==r&&b!==a&&ul(o,c,h,u,l,d,b.x,b.y)&&pl(b.prev,b,b.next)>=0)return!1;if(b=b.prevZ,v.x>=p&&v.x<=y&&v.y>=m&&v.y<=g&&v!==r&&v!==a&&ul(o,c,h,u,l,d,v.x,v.y)&&pl(v.prev,v,v.next)>=0)return!1;v=v.nextZ}for(;b&&b.z>=f;){if(b.x>=p&&b.x<=y&&b.y>=m&&b.y<=g&&b!==r&&b!==a&&ul(o,c,h,u,l,d,b.x,b.y)&&pl(b.prev,b,b.next)>=0)return!1;b=b.prevZ}for(;v&&v.z<=x;){if(v.x>=p&&v.x<=y&&v.y>=m&&v.y<=g&&v!==r&&v!==a&&ul(o,c,h,u,l,d,v.x,v.y)&&pl(v.prev,v,v.next)>=0)return!1;v=v.nextZ}return!0}function il(t,e){let s=t;do{const i=s.prev,r=s.next.next;!ml(i,r)&&yl(i,s,s.next,r)&&xl(i,r)&&xl(r,i)&&(e.push(i.i,s.i,r.i),wl(s),wl(s.next),s=t=r),s=s.next}while(s!==t);return Kh(s)}function rl(t,e,s,i,r,n){let a=t;do{let t=a.next.next;for(;t!==a.prev;){if(a.i!==t.i&&dl(a,t)){let o=bl(a,t);return a=Kh(a,a.next),o=Kh(o,o.next),tl(a,e,s,i,r,n,0),void tl(o,e,s,i,r,n,0)}t=t.next}a=a.next}while(a!==t)}function nl(t,e){let s=t.x-e.x;if(0===s&&(s=t.y-e.y,0===s)){s=(t.next.y-t.y)/(t.next.x-t.x)-(e.next.y-e.y)/(e.next.x-e.x)}return s}function al(t,e){const s=function(t,e){let s=e;const i=t.x,r=t.y;let n,a=-1/0;if(ml(t,s))return s;do{if(ml(t,s.next))return s.next;if(r<=s.y&&r>=s.next.y&&s.next.y!==s.y){const t=s.x+(r-s.y)*(s.next.x-s.x)/(s.next.y-s.y);if(t<=i&&t>a&&(a=t,n=s.x=s.x&&s.x>=h&&i!==s.x&&cl(rn.x||s.x===n.x&&ol(n,s)))&&(n=s,c=e)}s=s.next}while(s!==o);return n}(t,e);if(!s)return e;const i=bl(s,t);return Kh(i,i.next),Kh(s,s.next)}function ol(t,e){return pl(t.prev,t,e.prev)<0&&pl(e.next,t,t.next)<0}function hl(t,e,s,i,r){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=(t-s)*r|0)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=(e-i)*r|0)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function ll(t){let e=t,s=t;do{(e.x=(t-a)*(n-o)&&(t-a)*(i-o)>=(s-a)*(e-o)&&(s-a)*(n-o)>=(r-a)*(i-o)}function ul(t,e,s,i,r,n,a,o){return!(t===a&&e===o)&&cl(t,e,s,i,r,n,a,o)}function dl(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){let s=t;do{if(s.i!==t.i&&s.next.i!==t.i&&s.i!==e.i&&s.next.i!==e.i&&yl(s,s.next,t,e))return!0;s=s.next}while(s!==t);return!1}(t,e)&&(xl(t,e)&&xl(e,t)&&function(t,e){let s=t,i=!1;const r=(t.x+e.x)/2,n=(t.y+e.y)/2;do{s.y>n!=s.next.y>n&&s.next.y!==s.y&&r<(s.next.x-s.x)*(n-s.y)/(s.next.y-s.y)+s.x&&(i=!i),s=s.next}while(s!==t);return i}(t,e)&&(pl(t.prev,t,e.prev)||pl(t,e.prev,e))||ml(t,e)&&pl(t.prev,t,t.next)>0&&pl(e.prev,e,e.next)>0)}function pl(t,e,s){return(e.y-t.y)*(s.x-e.x)-(e.x-t.x)*(s.y-e.y)}function ml(t,e){return t.x===e.x&&t.y===e.y}function yl(t,e,s,i){const r=fl(pl(t,e,s)),n=fl(pl(t,e,i)),a=fl(pl(s,i,t)),o=fl(pl(s,i,e));return r!==n&&a!==o||(!(0!==r||!gl(t,s,e))||(!(0!==n||!gl(t,i,e))||(!(0!==a||!gl(s,t,i))||!(0!==o||!gl(s,e,i)))))}function gl(t,e,s){return e.x<=Math.max(t.x,s.x)&&e.x>=Math.min(t.x,s.x)&&e.y<=Math.max(t.y,s.y)&&e.y>=Math.min(t.y,s.y)}function fl(t){return t>0?1:t<0?-1:0}function xl(t,e){return pl(t.prev,t,t.next)<0?pl(t,e,t.next)>=0&&pl(t,t.prev,e)>=0:pl(t,e,t.prev)<0||pl(t,t.next,e)<0}function bl(t,e){const s=Ml(t.i,t.x,t.y),i=Ml(e.i,e.x,e.y),r=t.next,n=e.prev;return t.next=e,e.prev=t,s.next=r,r.prev=s,i.next=s,s.prev=i,n.next=i,i.prev=n,i}function vl(t,e,s,i){const r=Ml(t,e,s);return i?(r.next=i.next,r.prev=i,i.next.prev=r,i.next=r):(r.prev=r,r.next=r),r}function wl(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ)}function Ml(t,e,s){return{i:t,x:e,y:s,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}class Sl{static triangulate(t,e,s=2){return $h(t,e,s)}}class _l{static area(t){const e=t.length;let s=0;for(let i=e-1,r=0;r2&&t[e-1].equals(t[0])&&t.pop()}function Tl(t,e){for(let s=0;sNumber.EPSILON){const u=Math.sqrt(c),d=Math.sqrt(h*h+l*l),p=e.x-o/u,m=e.y+a/u,y=((s.x-l/d-p)*l-(s.y+h/d-m)*h)/(a*l-o*h);i=p+a*y-t.x,r=m+o*y-t.y;const g=i*i+r*r;if(g<=2)return new _i(i,r);n=Math.sqrt(g/2)}else{let t=!1;a>Number.EPSILON?h>Number.EPSILON&&(t=!0):a<-Number.EPSILON?h<-Number.EPSILON&&(t=!0):Math.sign(o)===Math.sign(l)&&(t=!0),t?(i=-o,r=a,n=Math.sqrt(c)):(i=a,r=o,n=Math.sqrt(c/2))}return new _i(i/n,r/n)}const k=[];for(let t=0,e=z.length,s=e-1,i=t+1;t=0;t--){const e=t/p,s=c*Math.cos(e*Math.PI/2),i=u*Math.sin(e*Math.PI/2)+d;for(let t=0,e=z.length;t=0;){const i=s;let r=s-1;r<0&&(r=t.length-1);for(let t=0,s=o+2*p;t0)&&d.push(e,r,h),(t!==s-1||o0&&(e.defines=this.defines),e.vertexShader=this.vertexShader,e.fragmentShader=this.fragmentShader,e.lights=this.lights,e.clipping=this.clipping;const s={};for(const t in this.extensions)!0===this.extensions[t]&&(s[t]=!0);return Object.keys(s).length>0&&(e.extensions=s),e}fromJSON(t,e){if(super.fromJSON(t,e),void 0!==t.uniforms)for(const s in t.uniforms){const i=t.uniforms[s];switch(this.uniforms[s]={},i.type){case"t":this.uniforms[s].value=e[i.value]||null;break;case"c":this.uniforms[s].value=(new Pr).setHex(i.value);break;case"v2":this.uniforms[s].value=(new _i).fromArray(i.value);break;case"v3":this.uniforms[s].value=(new Ti).fromArray(i.value);break;case"v4":this.uniforms[s].value=(new qi).fromArray(i.value);break;case"m3":this.uniforms[s].value=(new Ii).fromArray(i.value);break;case"m4":this.uniforms[s].value=(new Qi).fromArray(i.value);break;default:this.uniforms[s].value=i.value}}if(void 0!==t.defines&&(this.defines=t.defines),void 0!==t.vertexShader&&(this.vertexShader=t.vertexShader),void 0!==t.fragmentShader&&(this.fragmentShader=t.fragmentShader),void 0!==t.glslVersion&&(this.glslVersion=t.glslVersion),void 0!==t.extensions)for(const e in t.extensions)this.extensions[e]=t.extensions[e];return void 0!==t.lights&&(this.lights=t.lights),void 0!==t.clipping&&(this.clipping=t.clipping),this}}class Gl extends Zl{constructor(t){super(t),this.isRawShaderMaterial=!0,this.type="RawShaderMaterial"}}class $l extends Zn{constructor(t){super(),this.isMeshStandardMaterial=!0,this.type="MeshStandardMaterial",this.defines={STANDARD:""},this.color=new Pr(16777215),this.roughness=1,this.metalness=0,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Pr(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.roughnessMap=null,this.metalnessMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new hr,this.envMapIntensity=1,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.defines={STANDARD:""},this.color.copy(t.color),this.roughness=t.roughness,this.metalness=t.metalness,this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.roughnessMap=t.roughnessMap,this.metalnessMap=t.metalnessMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.envMapIntensity=t.envMapIntensity,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.flatShading=t.flatShading,this.fog=t.fog,this}}class Ql extends $l{constructor(t){super(),this.isMeshPhysicalMaterial=!0,this.defines={STANDARD:"",PHYSICAL:""},this.type="MeshPhysicalMaterial",this.anisotropyRotation=0,this.anisotropyMap=null,this.clearcoatMap=null,this.clearcoatRoughness=0,this.clearcoatRoughnessMap=null,this.clearcoatNormalScale=new _i(1,1),this.clearcoatNormalMap=null,this.ior=1.5,Object.defineProperty(this,"reflectivity",{get:function(){return xi(2.5*(this.ior-1)/(this.ior+1),0,1)},set:function(t){this.ior=(1+.4*t)/(1-.4*t)}}),this.iridescenceMap=null,this.iridescenceIOR=1.3,this.iridescenceThicknessRange=[100,400],this.iridescenceThicknessMap=null,this.sheenColor=new Pr(0),this.sheenColorMap=null,this.sheenRoughness=1,this.sheenRoughnessMap=null,this.transmissionMap=null,this.thickness=0,this.thicknessMap=null,this.attenuationDistance=1/0,this.attenuationColor=new Pr(1,1,1),this.specularIntensity=1,this.specularIntensityMap=null,this.specularColor=new Pr(1,1,1),this.specularColorMap=null,this._anisotropy=0,this._clearcoat=0,this._dispersion=0,this._iridescence=0,this._sheen=0,this._transmission=0,this.setValues(t)}get anisotropy(){return this._anisotropy}set anisotropy(t){this._anisotropy>0!=t>0&&this.version++,this._anisotropy=t}get clearcoat(){return this._clearcoat}set clearcoat(t){this._clearcoat>0!=t>0&&this.version++,this._clearcoat=t}get iridescence(){return this._iridescence}set iridescence(t){this._iridescence>0!=t>0&&this.version++,this._iridescence=t}get dispersion(){return this._dispersion}set dispersion(t){this._dispersion>0!=t>0&&this.version++,this._dispersion=t}get sheen(){return this._sheen}set sheen(t){this._sheen>0!=t>0&&this.version++,this._sheen=t}get transmission(){return this._transmission}set transmission(t){this._transmission>0!=t>0&&this.version++,this._transmission=t}copy(t){return super.copy(t),this.defines={STANDARD:"",PHYSICAL:""},this.anisotropy=t.anisotropy,this.anisotropyRotation=t.anisotropyRotation,this.anisotropyMap=t.anisotropyMap,this.clearcoat=t.clearcoat,this.clearcoatMap=t.clearcoatMap,this.clearcoatRoughness=t.clearcoatRoughness,this.clearcoatRoughnessMap=t.clearcoatRoughnessMap,this.clearcoatNormalMap=t.clearcoatNormalMap,this.clearcoatNormalScale.copy(t.clearcoatNormalScale),this.dispersion=t.dispersion,this.ior=t.ior,this.iridescence=t.iridescence,this.iridescenceMap=t.iridescenceMap,this.iridescenceIOR=t.iridescenceIOR,this.iridescenceThicknessRange=[...t.iridescenceThicknessRange],this.iridescenceThicknessMap=t.iridescenceThicknessMap,this.sheen=t.sheen,this.sheenColor.copy(t.sheenColor),this.sheenColorMap=t.sheenColorMap,this.sheenRoughness=t.sheenRoughness,this.sheenRoughnessMap=t.sheenRoughnessMap,this.transmission=t.transmission,this.transmissionMap=t.transmissionMap,this.thickness=t.thickness,this.thicknessMap=t.thicknessMap,this.attenuationDistance=t.attenuationDistance,this.attenuationColor.copy(t.attenuationColor),this.specularIntensity=t.specularIntensity,this.specularIntensityMap=t.specularIntensityMap,this.specularColor.copy(t.specularColor),this.specularColorMap=t.specularColorMap,this}}class Kl extends Zn{constructor(t){super(),this.isMeshPhongMaterial=!0,this.type="MeshPhongMaterial",this.color=new Pr(16777215),this.specular=new Pr(1118481),this.shininess=30,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Pr(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new hr,this.combine=0,this.reflectivity=1,this.envMapIntensity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.specular.copy(t.specular),this.shininess=t.shininess,this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.envMapIntensity=t.envMapIntensity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.flatShading=t.flatShading,this.fog=t.fog,this}}class tc extends Zn{constructor(t){super(),this.isMeshToonMaterial=!0,this.defines={TOON:""},this.type="MeshToonMaterial",this.color=new Pr(16777215),this.map=null,this.gradientMap=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Pr(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.alphaMap=null,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.gradientMap=t.gradientMap,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.alphaMap=t.alphaMap,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.fog=t.fog,this}}class ec extends Zn{constructor(t){super(),this.isMeshNormalMaterial=!0,this.type="MeshNormalMaterial",this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.flatShading=!1,this.setValues(t)}copy(t){return super.copy(t),this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.flatShading=t.flatShading,this}}class sc extends Zn{constructor(t){super(),this.isMeshLambertMaterial=!0,this.type="MeshLambertMaterial",this.color=new Pr(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Pr(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new hr,this.combine=0,this.reflectivity=1,this.envMapIntensity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.envMapIntensity=t.envMapIntensity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.flatShading=t.flatShading,this.fog=t.fog,this}}class ic extends Zn{constructor(t){super(),this.isMeshDepthMaterial=!0,this.type="MeshDepthMaterial",this.depthPacking=3200,this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(t)}copy(t){return super.copy(t),this.depthPacking=t.depthPacking,this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this}}class rc extends Zn{constructor(t){super(),this.isMeshDistanceMaterial=!0,this.type="MeshDistanceMaterial",this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.setValues(t)}copy(t){return super.copy(t),this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this}}class nc extends Zn{constructor(t){super(),this.isMeshMatcapMaterial=!0,this.defines={MATCAP:""},this.type="MeshMatcapMaterial",this.color=new Pr(16777215),this.matcap=null,this.map=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=0,this.normalScale=new _i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.alphaMap=null,this.wireframe=!1,this.wireframeLinewidth=1,this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.defines={MATCAP:""},this.color.copy(t.color),this.matcap=t.matcap,this.map=t.map,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.alphaMap=t.alphaMap,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.flatShading=t.flatShading,this.fog=t.fog,this}}class ac extends No{constructor(t){super(),this.isLineDashedMaterial=!0,this.type="LineDashedMaterial",this.scale=1,this.dashSize=3,this.gapSize=1,this.setValues(t)}copy(t){return super.copy(t),this.scale=t.scale,this.dashSize=t.dashSize,this.gapSize=t.gapSize,this}}function oc(t,e){return t&&t.constructor!==e?"number"==typeof e.BYTES_PER_ELEMENT?new e(t):Array.prototype.slice.call(t):t}function hc(t){const e=t.length,s=new Array(e);for(let t=0;t!==e;++t)s[t]=t;return s.sort(function(e,s){return t[e]-t[s]}),s}function lc(t,e,s){const i=t.length,r=new t.constructor(i);for(let n=0,a=0;a!==i;++n){const i=s[n]*e;for(let s=0;s!==e;++s)r[a++]=t[i+s]}return r}function cc(t,e,s,i){let r=1,n=t[0];for(;void 0!==n&&void 0===n[i];)n=t[r++];if(void 0===n)return;let a=n[i];if(void 0!==a)if(Array.isArray(a))do{a=n[i],void 0!==a&&(e.push(n.time),s.push(...a)),n=t[r++]}while(void 0!==n);else if(void 0!==a.toArray)do{a=n[i],void 0!==a&&(e.push(n.time),a.toArray(s,s.length)),n=t[r++]}while(void 0!==n);else do{a=n[i],void 0!==a&&(e.push(n.time),s.push(a)),n=t[r++]}while(void 0!==n)}class uc{static convertArray(t,e){return oc(t,e)}static isTypedArray(t){return $s(t)}static getKeyframeOrder(t){return hc(t)}static sortedArray(t,e,s){return lc(t,e,s)}static flattenJSON(t,e,s,i){cc(t,e,s,i)}static subclip(t,e,s,i,r=30){return function(t,e,s,i,r=30){const n=t.clone();n.name=e;const a=[];for(let t=0;t=i)){h.push(e.times[t]);for(let s=0;sn.tracks[t].times[0]&&(o=n.tracks[t].times[0]);for(let t=0;t=i.times[u]){const t=u*h+o,e=t+h-o;d=i.values.slice(t,e)}else{const t=i.createInterpolant(),e=o,s=h-o;t.evaluate(n),d=t.resultBuffer.slice(e,s)}"quaternion"===r&&(new Ai).fromArray(d).normalize().conjugate().toArray(d);const p=a.times.length;for(let t=0;t=r)){const a=e[1];t=r)break e}n=s,s=0;break s}break t}for(;s>>1;te;)--n;if(++n,0!==r||n!==i){r>=n&&(n=Math.max(n,1),r=n-1);const t=this.getValueSize();this.times=s.slice(r,n),this.values=this.values.slice(r*t,n*t)}return this}validate(){let t=!0;const e=this.getValueSize();e-Math.floor(e)!==0&&(oi("KeyframeTrack: Invalid value size in track.",this),t=!1);const s=this.times,i=this.values,r=s.length;0===r&&(oi("KeyframeTrack: Track is empty.",this),t=!1);let n=null;for(let e=0;e!==r;e++){const i=s[e];if("number"==typeof i&&isNaN(i)){oi("KeyframeTrack: Time is not a valid number.",this,e,i),t=!1;break}if(null!==n&&n>i){oi("KeyframeTrack: Out of order keys.",this,e,i,n),t=!1;break}n=i}if(void 0!==i&&$s(i))for(let e=0,s=i.length;e!==s;++e){const s=i[e];if(isNaN(s)){oi("KeyframeTrack: Value is not a valid number.",this,e,s),t=!1;break}}return t}optimize(){const t=this.times.slice(),e=this.values.slice(),s=this.getValueSize(),i=this.getInterpolation()===Le,r=t.length-1;let n=1;for(let a=1;a0){t[n]=t[r];for(let t=r*s,i=n*s,a=0;a!==s;++a)e[i+a]=e[t+a];++n}return n!==t.length?(this.times=t.slice(0,n),this.values=e.slice(0,n*s)):(this.times=t,this.values=e),this}clone(){const t=this.times.slice(),e=this.values.slice(),s=new(0,this.constructor)(this.name,t,e);return s.createInterpolant=this.createInterpolant,s}}fc.prototype.ValueTypeName="",fc.prototype.TimeBufferType=Float32Array,fc.prototype.ValueBufferType=Float32Array,fc.prototype.DefaultInterpolation=Ve;class xc extends fc{constructor(t,e,s){super(t,e,s)}}xc.prototype.ValueTypeName="bool",xc.prototype.ValueBufferType=Array,xc.prototype.DefaultInterpolation=Ne,xc.prototype.InterpolantFactoryMethodLinear=void 0,xc.prototype.InterpolantFactoryMethodSmooth=void 0;class bc extends fc{constructor(t,e,s,i){super(t,e,s,i)}}bc.prototype.ValueTypeName="color";class vc extends fc{constructor(t,e,s,i){super(t,e,s,i)}}vc.prototype.ValueTypeName="number";class wc extends dc{constructor(t,e,s,i){super(t,e,s,i)}interpolate_(t,e,s,i){const r=this.resultBuffer,n=this.sampleValues,a=this.valueSize,o=(s-e)/(i-e);let h=t*a;for(let t=h+a;h!==t;h+=4)Ai.slerpFlat(r,0,n,h-a,n,h,o);return r}}class Mc extends fc{constructor(t,e,s,i){super(t,e,s,i)}InterpolantFactoryMethodLinear(t){return new wc(this.times,this.values,this.getValueSize(),t)}}Mc.prototype.ValueTypeName="quaternion",Mc.prototype.InterpolantFactoryMethodSmooth=void 0;class Sc extends fc{constructor(t,e,s){super(t,e,s)}}Sc.prototype.ValueTypeName="string",Sc.prototype.ValueBufferType=Array,Sc.prototype.DefaultInterpolation=Ne,Sc.prototype.InterpolantFactoryMethodLinear=void 0,Sc.prototype.InterpolantFactoryMethodSmooth=void 0;class _c extends fc{constructor(t,e,s,i){super(t,e,s,i)}}_c.prototype.ValueTypeName="vector";class Ac{constructor(t="",e=-1,s=[],i=2500){this.name=t,this.tracks=s,this.duration=e,this.blendMode=i,this.uuid=fi(),this.userData={},this.duration<0&&this.resetDuration()}static parse(t){const e=[],s=t.tracks,i=1/(t.fps||1);for(let t=0,r=s.length;t!==r;++t)e.push(Tc(s[t]).scale(i));const r=new this(t.name,t.duration,e,t.blendMode);return r.uuid=t.uuid,r.userData=JSON.parse(t.userData||"{}"),r}static toJSON(t){const e=[],s=t.tracks,i={name:t.name,duration:t.duration,tracks:e,uuid:t.uuid,blendMode:t.blendMode,userData:JSON.stringify(t.userData)};for(let t=0,i=s.length;t!==i;++t)e.push(fc.toJSON(s[t]));return i}static CreateFromMorphTargetSequence(t,e,s,i){const r=e.length,n=[];for(let t=0;t1){const t=n[1];let e=i[t];e||(i[t]=e=[]),e.push(s)}}const n=[];for(const t in i)n.push(this.CreateFromMorphTargetSequence(t,i[t],e,s));return n}resetDuration(){let t=0;for(let e=0,s=this.tracks.length;e!==s;++e){const s=this.tracks[e];t=Math.max(t,s.times[s.times.length-1])}return this.duration=t,this}trim(){for(let t=0;t{e&&e(r),this.manager.itemEnd(t)},0);if(void 0!==Oc[t])return void Oc[t].push({onLoad:e,onProgress:s,onError:i});Oc[t]=[],Oc[t].push({onLoad:e,onProgress:s,onError:i});const n=new Request(t,{headers:new Headers(this.requestHeader),credentials:this.withCredentials?"include":"same-origin",signal:"function"==typeof AbortSignal.any?AbortSignal.any([this._abortController.signal,this.manager.abortController.signal]):this._abortController.signal}),a=this.mimeType,o=this.responseType;fetch(n).then(e=>{if(200===e.status||0===e.status){if(0===e.status&&ai("FileLoader: HTTP Status 0 received."),"undefined"==typeof ReadableStream||void 0===e.body||void 0===e.body.getReader)return e;const s=Oc[t],i=e.body.getReader(),r=e.headers.get("X-File-Size")||e.headers.get("Content-Length"),n=r?parseInt(r):0,a=0!==n;let o=0;const h=new ReadableStream({start(t){!function e(){i.read().then(({done:i,value:r})=>{if(i)t.close();else{o+=r.byteLength;const i=new ProgressEvent("progress",{lengthComputable:a,loaded:o,total:n});for(let t=0,e=s.length;t{t.error(e)})}()}});return new Response(h)}throw new Pc(`fetch for "${e.url}" responded with ${e.status}: ${e.statusText}`,e)}).then(t=>{switch(o){case"arraybuffer":return t.arrayBuffer();case"blob":return t.blob();case"document":return t.text().then(t=>(new DOMParser).parseFromString(t,a));case"json":return t.json();default:if(""===a)return t.text();{const e=/charset="?([^;"\s]*)"?/i.exec(a),s=e&&e[1]?e[1].toLowerCase():void 0,i=new TextDecoder(s);return t.arrayBuffer().then(t=>i.decode(t))}}}).then(e=>{zc.add(`file:${t}`,e);const s=Oc[t];delete Oc[t];for(let t=0,i=s.length;t{const s=Oc[t];if(void 0===s)throw this.manager.itemError(t),e;delete Oc[t];for(let t=0,i=s.length;t{this.manager.itemEnd(t)}),this.manager.itemStart(t)}setResponseType(t){return this.responseType=t,this}setMimeType(t){return this.mimeType=t,this}abort(){return this._abortController.abort(),this._abortController=new AbortController,this}}class Ec extends kc{constructor(t){super(t)}load(t,e,s,i){const r=this,n=new Rc(this.manager);n.setPath(this.path),n.setRequestHeader(this.requestHeader),n.setWithCredentials(this.withCredentials),n.load(t,function(s){try{e(r.parse(JSON.parse(s)))}catch(e){i?i(e):oi(e),r.manager.itemError(t)}},s,i)}parse(t){const e=[];for(let s=0;s0){const s=new Ic(e);r=new Lc(s),r.setCrossOrigin(this.crossOrigin);for(let e=0,s=t.length;e0){i=new Lc(this.manager),i.setCrossOrigin(this.crossOrigin);for(let e=0,i=t.length;e{let e=null,s=null;return void 0!==t.boundingBox&&(e=(new Qr).fromJSON(t.boundingBox)),void 0!==t.boundingSphere&&(s=(new En).fromJSON(t.boundingSphere)),{...t,boundingBox:e,boundingSphere:s}}),n._instanceInfo=t.instanceInfo,n._availableInstanceIds=t._availableInstanceIds,n._availableGeometryIds=t._availableGeometryIds,n._nextIndexStart=t.nextIndexStart,n._nextVertexStart=t.nextVertexStart,n._geometryCount=t.geometryCount,n._maxInstanceCount=t.maxInstanceCount,n._maxVertexCount=t.maxVertexCount,n._maxIndexCount=t.maxIndexCount,n._geometryInitialized=t.geometryInitialized,n._matricesTexture=c(t.matricesTexture.uuid),n._indirectTexture=c(t.indirectTexture.uuid),void 0!==t.colorsTexture&&(n._colorsTexture=c(t.colorsTexture.uuid)),void 0!==t.boundingSphere&&(n.boundingSphere=(new En).fromJSON(t.boundingSphere)),void 0!==t.boundingBox&&(n.boundingBox=(new Qr).fromJSON(t.boundingBox));break;case"LOD":n=new pa;break;case"Line":n=new Jo(h(t.geometry),l(t.material));break;case"LineLoop":n=new Zo(h(t.geometry),l(t.material));break;case"LineSegments":n=new Yo(h(t.geometry),l(t.material));break;case"PointCloud":case"Points":n=new eh(h(t.geometry),l(t.material));break;case"Sprite":n=new la(l(t.material));break;case"Group":n=new Tr;break;case"Bone":n=new Ha;break;default:n=new Ar}if(n.uuid=t.uuid,void 0!==t.name&&(n.name=t.name),void 0!==t.matrix?(n.matrix.fromArray(t.matrix),void 0!==t.matrixAutoUpdate&&(n.matrixAutoUpdate=t.matrixAutoUpdate),n.matrixAutoUpdate&&n.matrix.decompose(n.position,n.quaternion,n.scale)):(void 0!==t.position&&n.position.fromArray(t.position),void 0!==t.rotation&&n.rotation.fromArray(t.rotation),void 0!==t.quaternion&&n.quaternion.fromArray(t.quaternion),void 0!==t.scale&&n.scale.fromArray(t.scale)),void 0!==t.up&&n.up.fromArray(t.up),void 0!==t.pivot&&(n.pivot=(new Ti).fromArray(t.pivot)),void 0!==t.morphTargetDictionary&&(n.morphTargetDictionary=Object.assign({},t.morphTargetDictionary)),void 0!==t.morphTargetInfluences&&(n.morphTargetInfluences=t.morphTargetInfluences.slice()),void 0!==t.castShadow&&(n.castShadow=t.castShadow),void 0!==t.receiveShadow&&(n.receiveShadow=t.receiveShadow),t.shadow&&(void 0!==t.shadow.intensity&&(n.shadow.intensity=t.shadow.intensity),void 0!==t.shadow.bias&&(n.shadow.bias=t.shadow.bias),void 0!==t.shadow.normalBias&&(n.shadow.normalBias=t.shadow.normalBias),void 0!==t.shadow.radius&&(n.shadow.radius=t.shadow.radius),void 0!==t.shadow.mapSize&&n.shadow.mapSize.fromArray(t.shadow.mapSize),void 0!==t.shadow.camera&&(n.shadow.camera=this.parseObject(t.shadow.camera))),void 0!==t.visible&&(n.visible=t.visible),void 0!==t.frustumCulled&&(n.frustumCulled=t.frustumCulled),void 0!==t.renderOrder&&(n.renderOrder=t.renderOrder),void 0!==t.static&&(n.static=t.static),void 0!==t.userData&&(n.userData=t.userData),void 0!==t.layers&&(n.layers.mask=t.layers),void 0!==t.children){const a=t.children;for(let t=0;t{!0===Su.has(n)?(i&&i(Su.get(n)),r.manager.itemError(t),r.manager.itemEnd(t)):(e&&e(s),r.manager.itemEnd(t))}):void setTimeout(function(){e&&e(n),r.manager.itemEnd(t)},0);const a={};a.credentials="anonymous"===this.crossOrigin?"same-origin":"include",a.headers=this.requestHeader,a.signal="function"==typeof AbortSignal.any?AbortSignal.any([this._abortController.signal,this.manager.abortController.signal]):this._abortController.signal;const o=fetch(t,a).then(function(t){return t.blob()}).then(function(t){return createImageBitmap(t,Object.assign(r.options,{colorSpaceConversion:"none"}))}).then(function(s){zc.add(`image-bitmap:${t}`,s),e&&e(s),r.manager.itemEnd(t)}).catch(function(e){i&&i(e),Su.set(o,e),zc.remove(`image-bitmap:${t}`),r.manager.itemError(t),r.manager.itemEnd(t)});zc.add(`image-bitmap:${t}`,o),r.manager.itemStart(t)}abort(){return this._abortController.abort(),this._abortController=new AbortController,this}}let Au;class Tu{static getContext(){return void 0===Au&&(Au=new(window.AudioContext||window.webkitAudioContext)),Au}static setContext(t){Au=t}}class zu extends kc{constructor(t){super(t)}load(t,e,s,i){const r=this,n=new Rc(this.manager);function a(e){i?i(e):oi(e),r.manager.itemError(t)}n.setResponseType("arraybuffer"),n.setPath(this.path),n.setRequestHeader(this.requestHeader),n.setWithCredentials(this.withCredentials),n.load(t,function(s){try{const i=s.slice(0),n=Tu.getContext(),o=t+"#decode";r.manager.itemStart(o),n.decodeAudioData(i,function(t){e(t),r.manager.itemEnd(o)}).catch(function(t){a(t),r.manager.itemEnd(o)})}catch(t){a(t)}},s,i)}}const Cu=new Qi,Iu=new Qi,Bu=new Qi;class ku{constructor(){this.type="StereoCamera",this.aspect=1,this.eyeSep=.064,this.cameraL=new eu,this.cameraL.layers.enable(1),this.cameraL.matrixAutoUpdate=!1,this.cameraR=new eu,this.cameraR.layers.enable(2),this.cameraR.matrixAutoUpdate=!1,this._cache={focus:null,fov:null,aspect:null,near:null,far:null,zoom:null,eyeSep:null}}update(t){const e=this._cache;if(e.focus!==t.focus||e.fov!==t.fov||e.aspect!==t.aspect*this.aspect||e.near!==t.near||e.far!==t.far||e.zoom!==t.zoom||e.eyeSep!==this.eyeSep){e.focus=t.focus,e.fov=t.fov,e.aspect=t.aspect*this.aspect,e.near=t.near,e.far=t.far,e.zoom=t.zoom,e.eyeSep=this.eyeSep,Bu.copy(t.projectionMatrix);const s=e.eyeSep/2,i=s*e.near/e.focus,r=e.near*Math.tan(yi*e.fov*.5)/e.zoom;let n,a;Iu.elements[12]=-s,Cu.elements[12]=s,n=-r*e.aspect+i,a=r*e.aspect+i,Bu.elements[0]=2*e.near/(a-n),Bu.elements[8]=(a+n)/(a-n),this.cameraL.projectionMatrix.copy(Bu),n=-r*e.aspect-i,a=r*e.aspect-i,Bu.elements[0]=2*e.near/(a-n),Bu.elements[8]=(a+n)/(a-n),this.cameraR.projectionMatrix.copy(Bu)}this.cameraL.matrix.copy(t.matrixWorld).multiply(Iu),this.cameraL.matrixWorldNeedsUpdate=!0,this.cameraR.matrix.copy(t.matrixWorld).multiply(Cu),this.cameraR.matrixWorldNeedsUpdate=!0}}const Ou=-90;class Pu extends Ar{constructor(t,e,s){super(),this.type="CubeCamera",this.renderTarget=s,this.coordinateSystem=null,this.activeMipmapLevel=0;const i=new eu(Ou,1,t,e);i.layers=this.layers,this.add(i);const r=new eu(Ou,1,t,e);r.layers=this.layers,this.add(r);const n=new eu(Ou,1,t,e);n.layers=this.layers,this.add(n);const a=new eu(Ou,1,t,e);a.layers=this.layers,this.add(a);const o=new eu(Ou,1,t,e);o.layers=this.layers,this.add(o);const h=new eu(Ou,1,t,e);h.layers=this.layers,this.add(h)}updateCoordinateSystem(){const t=this.coordinateSystem,e=this.children.concat(),[s,i,r,n,a,o]=e;for(const t of e)this.remove(t);if(t===Ws)s.up.set(0,1,0),s.lookAt(1,0,0),i.up.set(0,1,0),i.lookAt(-1,0,0),r.up.set(0,0,-1),r.lookAt(0,1,0),n.up.set(0,0,1),n.lookAt(0,-1,0),a.up.set(0,1,0),a.lookAt(0,0,1),o.up.set(0,1,0),o.lookAt(0,0,-1);else{if(t!==Js)throw new Error("THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: "+t);s.up.set(0,-1,0),s.lookAt(-1,0,0),i.up.set(0,-1,0),i.lookAt(1,0,0),r.up.set(0,0,1),r.lookAt(0,1,0),n.up.set(0,0,-1),n.lookAt(0,-1,0),a.up.set(0,-1,0),a.lookAt(0,0,1),o.up.set(0,-1,0),o.lookAt(0,0,-1)}for(const t of e)this.add(t),t.updateMatrixWorld()}update(t,e){null===this.parent&&this.updateMatrixWorld();const{renderTarget:s,activeMipmapLevel:i}=this;this.coordinateSystem!==t.coordinateSystem&&(this.coordinateSystem=t.coordinateSystem,this.updateCoordinateSystem());const[r,n,a,o,h,l]=this.children,c=t.getRenderTarget(),u=t.getActiveCubeFace(),d=t.getActiveMipmapLevel(),p=t.xr.enabled;t.xr.enabled=!1;const m=s.texture.generateMipmaps;s.texture.generateMipmaps=!1;let y=!1;y=!0===t.isWebGLRenderer?t.state.buffers.depth.getReversed():t.reversedDepthBuffer,t.setRenderTarget(s,0,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,r),t.setRenderTarget(s,1,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,n),t.setRenderTarget(s,2,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,a),t.setRenderTarget(s,3,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,o),t.setRenderTarget(s,4,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,h),s.texture.generateMipmaps=m,t.setRenderTarget(s,5,i),y&&!1===t.autoClear&&t.clearDepth(),t.render(e,l),t.setRenderTarget(c,u,d),t.xr.enabled=p,s.texture.needsPMREMUpdate=!0}}class Ru extends eu{constructor(t=[]){super(),this.isArrayCamera=!0,this.isMultiViewCamera=!1,this.cameras=t}}class Eu{constructor(){this._previousTime=0,this._currentTime=0,this._startTime=performance.now(),this._delta=0,this._elapsed=0,this._timescale=1,this._document=null,this._pageVisibilityHandler=null}connect(t){this._document=t,void 0!==t.hidden&&(this._pageVisibilityHandler=Nu.bind(this),t.addEventListener("visibilitychange",this._pageVisibilityHandler,!1))}disconnect(){null!==this._pageVisibilityHandler&&(this._document.removeEventListener("visibilitychange",this._pageVisibilityHandler),this._pageVisibilityHandler=null),this._document=null}getDelta(){return this._delta/1e3}getElapsed(){return this._elapsed/1e3}getTimescale(){return this._timescale}setTimescale(t){return this._timescale=t,this}reset(){return this._currentTime=performance.now()-this._startTime,this}dispose(){this.disconnect()}update(t){return null!==this._pageVisibilityHandler&&!0===this._document.hidden?this._delta=0:(this._previousTime=this._currentTime,this._currentTime=(void 0!==t?t:performance.now())-this._startTime,this._delta=(this._currentTime-this._previousTime)*this._timescale,this._elapsed+=this._delta),this}}function Nu(){!1===this._document.hidden&&this.reset()}const Vu=new Ti,Lu=new Ai,Fu=new Ti,Du=new Ti,Uu=new Ti;class ju extends Ar{constructor(){super(),this.type="AudioListener",this.context=Tu.getContext(),this.gain=this.context.createGain(),this.gain.connect(this.context.destination),this.filter=null,this.timeDelta=0,this._timer=new Eu}getInput(){return this.gain}removeFilter(){return null!==this.filter&&(this.gain.disconnect(this.filter),this.filter.disconnect(this.context.destination),this.gain.connect(this.context.destination),this.filter=null),this}getFilter(){return this.filter}setFilter(t){return null!==this.filter?(this.gain.disconnect(this.filter),this.filter.disconnect(this.context.destination)):this.gain.disconnect(this.context.destination),this.filter=t,this.gain.connect(this.filter),this.filter.connect(this.context.destination),this}getMasterVolume(){return this.gain.gain.value}setMasterVolume(t){return this.gain.gain.setTargetAtTime(t,this.context.currentTime,.01),this}updateMatrixWorld(t){super.updateMatrixWorld(t),this._timer.update();const e=this.context.listener;if(this.timeDelta=this._timer.getDelta(),this.matrixWorld.decompose(Vu,Lu,Fu),Du.set(0,0,-1).applyQuaternion(Lu),Uu.set(0,1,0).applyQuaternion(Lu),e.positionX){const t=this.context.currentTime+this.timeDelta;e.positionX.linearRampToValueAtTime(Vu.x,t),e.positionY.linearRampToValueAtTime(Vu.y,t),e.positionZ.linearRampToValueAtTime(Vu.z,t),e.forwardX.linearRampToValueAtTime(Du.x,t),e.forwardY.linearRampToValueAtTime(Du.y,t),e.forwardZ.linearRampToValueAtTime(Du.z,t),e.upX.linearRampToValueAtTime(Uu.x,t),e.upY.linearRampToValueAtTime(Uu.y,t),e.upZ.linearRampToValueAtTime(Uu.z,t)}else e.setPosition(Vu.x,Vu.y,Vu.z),e.setOrientation(Du.x,Du.y,Du.z,Uu.x,Uu.y,Uu.z)}}class Wu extends Ar{constructor(t){super(),this.type="Audio",this.listener=t,this.context=t.context,this.gain=this.context.createGain(),this.gain.connect(t.getInput()),this.autoplay=!1,this.buffer=null,this.detune=0,this.loop=!1,this.loopStart=0,this.loopEnd=0,this.offset=0,this.duration=void 0,this.playbackRate=1,this.isPlaying=!1,this.hasPlaybackControl=!0,this.source=null,this.sourceType="empty",this._startedAt=0,this._progress=0,this._connected=!1,this.filters=[]}getOutput(){return this.gain}setNodeSource(t){return this.hasPlaybackControl=!1,this.sourceType="audioNode",this.source=t,this.connect(),this}setMediaElementSource(t){return this.hasPlaybackControl=!1,this.sourceType="mediaNode",this.source=this.context.createMediaElementSource(t),this.connect(),this}setMediaStreamSource(t){return this.hasPlaybackControl=!1,this.sourceType="mediaStreamNode",this.source=this.context.createMediaStreamSource(t),this.connect(),this}setBuffer(t){return this.buffer=t,this.sourceType="buffer",this.autoplay&&this.play(),this}play(t=0){if(!0===this.isPlaying)return void ai("Audio: Audio is already playing.");if(!1===this.hasPlaybackControl)return void ai("Audio: this Audio has no playback control.");this._startedAt=this.context.currentTime+t;const e=this.context.createBufferSource();return e.buffer=this.buffer,e.loop=this.loop,e.loopStart=this.loopStart,e.loopEnd=this.loopEnd,e.onended=this.onEnded.bind(this),e.start(this._startedAt,this._progress+this.offset,this.duration),this.isPlaying=!0,this.source=e,this.setDetune(this.detune),this.setPlaybackRate(this.playbackRate),this.connect()}pause(){if(!1!==this.hasPlaybackControl)return!0===this.isPlaying&&(this._progress+=Math.max(this.context.currentTime-this._startedAt,0)*this.playbackRate,!0===this.loop&&(this._progress=this._progress%(this.duration||this.buffer.duration)),this.source.stop(),this.source.onended=null,this.isPlaying=!1),this;ai("Audio: this Audio has no playback control.")}stop(t=0){if(!1!==this.hasPlaybackControl)return this._progress=0,null!==this.source&&(this.source.stop(this.context.currentTime+t),this.source.onended=null),this.isPlaying=!1,this;ai("Audio: this Audio has no playback control.")}connect(){if(this.filters.length>0){this.source.connect(this.filters[0]);for(let t=1,e=this.filters.length;t0){this.source.disconnect(this.filters[0]);for(let t=1,e=this.filters.length;t0&&this._mixBufferRegionAdditive(s,i,this._addIndex*e,1,e);for(let t=e,r=e+e;t!==r;++t)if(s[t]!==s[t+e]){a.setValue(s,i);break}}saveOriginalState(){const t=this.binding,e=this.buffer,s=this.valueSize,i=s*this._origIndex;t.getValue(e,i);for(let t=s,r=i;t!==r;++t)e[t]=e[i+t%s];this._setIdentity(),this.cumulativeWeight=0,this.cumulativeWeightAdditive=0}restoreOriginalState(){const t=3*this.valueSize;this.binding.setValue(this.buffer,t)}_setAdditiveIdentityNumeric(){const t=this._addIndex*this.valueSize,e=t+this.valueSize;for(let s=t;s=.5)for(let i=0;i!==r;++i)t[e+i]=t[s+i]}_slerp(t,e,s,i){Ai.slerpFlat(t,e,t,e,t,s,i)}_slerpAdditive(t,e,s,i,r){const n=this._workIndex*r;Ai.multiplyQuaternionsFlat(t,n,t,e,t,s),Ai.slerpFlat(t,e,t,e,t,n,i)}_lerp(t,e,s,i,r){const n=1-i;for(let a=0;a!==r;++a){const r=e+a;t[r]=t[r]*n+t[s+a]*i}}_lerpAdditive(t,e,s,i,r){for(let n=0;n!==r;++n){const r=e+n;t[r]=t[r]+t[s+n]*i}}}const $u="\\[\\]\\.:\\/",Qu=new RegExp("["+$u+"]","g"),Ku="[^"+$u+"]",td="[^"+$u.replace("\\.","")+"]",ed=new RegExp("^"+/((?:WC+[\/:])*)/.source.replace("WC",Ku)+/(WCOD+)?/.source.replace("WCOD",td)+/(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace("WC",Ku)+/\.(WC+)(?:\[(.+)\])?/.source.replace("WC",Ku)+"$"),sd=["material","materials","bones","map"];class id{constructor(t,e,s){this.path=e,this.parsedPath=s||id.parseTrackName(e),this.node=id.findNode(t,this.parsedPath.nodeName),this.rootNode=t,this.getValue=this._getValue_unbound,this.setValue=this._setValue_unbound}static create(t,e,s){return t&&t.isAnimationObjectGroup?new id.Composite(t,e,s):new id(t,e,s)}static sanitizeNodeName(t){return t.replace(/\s/g,"_").replace(Qu,"")}static parseTrackName(t){const e=ed.exec(t);if(null===e)throw new Error("THREE.PropertyBinding: Cannot parse trackName: "+t);const s={nodeName:e[2],objectName:e[3],objectIndex:e[4],propertyName:e[5],propertyIndex:e[6]},i=s.nodeName&&s.nodeName.lastIndexOf(".");if(void 0!==i&&-1!==i){const t=s.nodeName.substring(i+1);-1!==sd.indexOf(t)&&(s.nodeName=s.nodeName.substring(0,i),s.objectName=t)}if(null===s.propertyName||0===s.propertyName.length)throw new Error("THREE.PropertyBinding: can not parse propertyName from trackName: "+t);return s}static findNode(t,e){if(void 0===e||""===e||"."===e||-1===e||e===t.name||e===t.uuid)return t;if(t.skeleton){const s=t.skeleton.getBoneByName(e);if(void 0!==s)return s}if(t.children){const s=function(t){for(let i=0;i=r){const n=r++,l=t[n];e[l.uuid]=h,t[h]=l,e[o]=n,t[n]=a;for(let t=0,e=i;t!==e;++t){const e=s[t],i=e[n],r=e[h];e[h]=i,e[n]=r}}}this.nCachedObjects_=r}uncache(){const t=this._objects,e=this._indicesByUUID,s=this._bindings,i=s.length;let r=this.nCachedObjects_,n=t.length;for(let a=0,o=arguments.length;a!==o;++a){const o=arguments[a].uuid,h=e[o];if(void 0!==h)if(delete e[o],h0&&(e[a.uuid]=h),t[h]=a,t.pop();for(let t=0,e=i;t!==e;++t){const e=s[t];e[h]=e[r],e.pop()}}}this.nCachedObjects_=r}subscribe_(t,e){const s=this._bindingsIndicesByPath;let i=s[t];const r=this._bindings;if(void 0!==i)return r[i];const n=this._paths,a=this._parsedPaths,o=this._objects,h=o.length,l=this.nCachedObjects_,c=new Array(h);i=r.length,s[t]=i,n.push(t),a.push(e),r.push(c);for(let s=l,i=o.length;s!==i;++s){const i=o[s];c[s]=new id(i,t,e)}return c}unsubscribe_(t){const e=this._bindingsIndicesByPath,s=e[t];if(void 0!==s){const i=this._paths,r=this._parsedPaths,n=this._bindings,a=n.length-1,o=n[a];e[t[a]]=s,n[s]=o,n.pop(),r[s]=r[a],r.pop(),i[s]=i[a],i.pop()}}}class nd{constructor(t,e,s=null,i=e.blendMode){this._mixer=t,this._clip=e,this._localRoot=s,this.blendMode=i;const r=e.tracks,n=r.length,a=new Array(n),o={endingStart:De,endingEnd:De};for(let t=0;t!==n;++t){const e=r[t].createInterpolant(null);a[t]=e,e.settings=o}this._interpolantSettings=o,this._interpolants=a,this._propertyBindings=new Array(n),this._cacheIndex=null,this._byClipCacheIndex=null,this._timeScaleInterpolant=null,this._restoreTimeScale=null,this._weightInterpolant=null,this.loop=2201,this._loopCount=-1,this._startTime=null,this.time=0,this.timeScale=1,this._effectiveTimeScale=1,this.weight=1,this._effectiveWeight=1,this.repetitions=1/0,this.paused=!1,this.enabled=!0,this.clampWhenFinished=!1,this.zeroSlopeAtStart=!0,this.zeroSlopeAtEnd=!0}play(){return this._mixer._activateAction(this),this}stop(){return this._mixer._deactivateAction(this),this.reset()}reset(){return this.paused=!1,this.enabled=!0,this.time=0,this._loopCount=-1,this._startTime=null,this.stopFading().stopWarping()}isRunning(){return this.enabled&&!this.paused&&0!==this.timeScale&&null===this._startTime&&this._mixer._isActiveAction(this)}isScheduled(){return this._mixer._isActiveAction(this)}startAt(t){return this._startTime=t,this}setLoop(t,e){return this.loop=t,this.repetitions=e,this}setEffectiveWeight(t){return this.weight=t,this._effectiveWeight=this.enabled?t:0,this.stopFading()}getEffectiveWeight(){return this._effectiveWeight}fadeIn(t){return this._scheduleFading(t,0,1)}fadeOut(t){return this._scheduleFading(t,1,0)}crossFadeFrom(t,e,s=!1){if(t.fadeOut(e),this.fadeIn(e),!0===s){const s=this._clip.duration,i=t._clip.duration,r=i/s,n=s/i;t._restoreTimeScale=t.timeScale,this._restoreTimeScale=this.timeScale,t.warp(1,r,e),this.warp(n,1,e)}return this}crossFadeTo(t,e,s=!1){return t.crossFadeFrom(this,e,s)}stopFading(){const t=this._weightInterpolant;return null!==t&&(this._weightInterpolant=null,this._mixer._takeBackControlInterpolant(t)),this}setEffectiveTimeScale(t){return this.timeScale=t,this._effectiveTimeScale=this.paused?0:t,this.stopWarping()}getEffectiveTimeScale(){return this._effectiveTimeScale}setDuration(t){return this.timeScale=this._clip.duration/t,this.stopWarping()}syncWith(t){return this.time=t.time,this.timeScale=t.timeScale,this.stopWarping()}halt(t){return this.warp(this._effectiveTimeScale,0,t)}warp(t,e,s){const i=this._mixer,r=i.time,n=this.timeScale;let a=this._timeScaleInterpolant;null===a&&(a=i._lendControlInterpolant(),this._timeScaleInterpolant=a);const o=a.parameterPositions,h=a.sampleValues;return o[0]=r,o[1]=r+s,h[0]=t/n,h[1]=e/n,this}stopWarping(){const t=this._timeScaleInterpolant;return null!==t&&(this._timeScaleInterpolant=null,this._mixer._takeBackControlInterpolant(t)),this._restoreTimeScale=null,this}getMixer(){return this._mixer}getClip(){return this._clip}getRoot(){return this._localRoot||this._mixer._root}_update(t,e,s,i){if(!this.enabled)return void this._updateWeight(t);const r=this._startTime;if(null!==r){const i=(t-r)*s;i<0||0===s?e=0:(this._startTime=null,e=s*i)}e*=this._updateTimeScale(t);const n=this._updateTime(e),a=this._updateWeight(t);if(a>0){const t=this._interpolants,e=this._propertyBindings;if(this.blendMode===Je)for(let s=0,i=t.length;s!==i;++s)t[s].evaluate(n),e[s].accumulateAdditive(a);else for(let s=0,r=t.length;s!==r;++s)t[s].evaluate(n),e[s].accumulate(i,a)}}_updateWeight(t){let e=0;if(this.enabled){e=this.weight;const s=this._weightInterpolant;if(null!==s){const i=s.evaluate(t)[0];e*=i,t>s.parameterPositions[1]&&(this.stopFading(),0===i&&(this.enabled=!1))}}return this._effectiveWeight=e,e}_updateTimeScale(t){let e=0;if(!this.paused){e=this.timeScale;const s=this._timeScaleInterpolant;if(null!==s){e*=s.evaluate(t)[0],t>s.parameterPositions[1]&&(0===e?this.paused=!0:(null!==this._restoreTimeScale&&(e=this._restoreTimeScale),this.timeScale=e),this.stopWarping())}}return this._effectiveTimeScale=e,e}_updateTime(t){const e=this._clip.duration,s=this.loop;let i=this.time+t,r=this._loopCount;const n=2202===s;if(0===t)return-1===r||!n||1&~r?i:e-i;if(2200===s){-1===r&&(this._loopCount=0,this._setEndings(!0,!0,!1));t:{if(i>=e)i=e;else{if(!(i<0)){this.time=i;break t}i=0}this.clampWhenFinished?this.paused=!0:this.enabled=!1,this.time=i,this._mixer.dispatchEvent({type:"finished",action:this,direction:t<0?-1:1})}}else{if(-1===r&&(t>=0?(r=0,this._setEndings(!0,0===this.repetitions,n)):this._setEndings(0===this.repetitions,!0,n)),i>=e||i<0){const s=Math.floor(i/e);i-=e*s,r+=Math.abs(s);const a=this.repetitions-r;if(a<=0)this.clampWhenFinished?this.paused=!0:this.enabled=!1,i=t>0?e:0,this.time=i,this._mixer.dispatchEvent({type:"finished",action:this,direction:t>0?1:-1});else{if(1===a){const e=t<0;this._setEndings(e,!e,n)}else this._setEndings(!1,!1,n);this._loopCount=r,this.time=i,this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:s})}}else this._loopCount=r,this.time=i;if(n&&!(1&~r))return e-i}return i}_setEndings(t,e,s){const i=this._interpolantSettings;s?(i.endingStart=Ue,i.endingEnd=Ue):(i.endingStart=t?this.zeroSlopeAtStart?Ue:De:je,i.endingEnd=e?this.zeroSlopeAtEnd?Ue:De:je)}_scheduleFading(t,e,s){const i=this._mixer,r=i.time;let n=this._weightInterpolant;null===n&&(n=i._lendControlInterpolant(),this._weightInterpolant=n);const a=n.parameterPositions,o=n.sampleValues;return a[0]=r,o[0]=e,a[1]=r+t,o[1]=s,this}}const ad=new Float32Array(1);class od extends di{constructor(t){super(),this._root=t,this._initMemoryManager(),this._accuIndex=0,this.time=0,this.timeScale=1,"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}_bindAction(t,e){const s=t._localRoot||this._root,i=t._clip.tracks,r=i.length,n=t._propertyBindings,a=t._interpolants,o=s.uuid,h=this._bindingsByRootAndName;let l=h[o];void 0===l&&(l={},h[o]=l);for(let t=0;t!==r;++t){const r=i[t],h=r.name;let c=l[h];if(void 0!==c)++c.referenceCount,n[t]=c;else{if(c=n[t],void 0!==c){null===c._cacheIndex&&(++c.referenceCount,this._addInactiveBinding(c,o,h));continue}const i=e&&e._propertyBindings[t].binding.parsedPath;c=new Gu(id.create(s,h,i),r.ValueTypeName,r.getValueSize()),++c.referenceCount,this._addInactiveBinding(c,o,h),n[t]=c}a[t].resultBuffer=c.buffer}}_activateAction(t){if(!this._isActiveAction(t)){if(null===t._cacheIndex){const e=(t._localRoot||this._root).uuid,s=t._clip.uuid,i=this._actionsByClip[s];this._bindAction(t,i&&i.knownActions[0]),this._addInactiveAction(t,s,e)}const e=t._propertyBindings;for(let t=0,s=e.length;t!==s;++t){const s=e[t];0===s.useCount++&&(this._lendBinding(s),s.saveOriginalState())}this._lendAction(t)}}_deactivateAction(t){if(this._isActiveAction(t)){const e=t._propertyBindings;for(let t=0,s=e.length;t!==s;++t){const s=e[t];0===--s.useCount&&(s.restoreOriginalState(),this._takeBackBinding(s))}this._takeBackAction(t)}}_initMemoryManager(){this._actions=[],this._nActiveActions=0,this._actionsByClip={},this._bindings=[],this._nActiveBindings=0,this._bindingsByRootAndName={},this._controlInterpolants=[],this._nActiveControlInterpolants=0;const t=this;this.stats={actions:{get total(){return t._actions.length},get inUse(){return t._nActiveActions}},bindings:{get total(){return t._bindings.length},get inUse(){return t._nActiveBindings}},controlInterpolants:{get total(){return t._controlInterpolants.length},get inUse(){return t._nActiveControlInterpolants}}}}_isActiveAction(t){const e=t._cacheIndex;return null!==e&&e=0;--e)t[e].stop();return this}update(t){t*=this.timeScale;const e=this._actions,s=this._nActiveActions,i=this.time+=t,r=Math.sign(t),n=this._accuIndex^=1;for(let a=0;a!==s;++a){e[a]._update(i,t,r,n)}const a=this._bindings,o=this._nActiveBindings;for(let t=0;t!==o;++t)a[t].apply(n);return this}setTime(t){this.time=0;for(let t=0;t=this.min.x&&t.x<=this.max.x&&t.y>=this.min.y&&t.y<=this.max.y}containsBox(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y}getParameter(t,e){return e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y))}intersectsBox(t){return t.max.x>=this.min.x&&t.min.x<=this.max.x&&t.max.y>=this.min.y&&t.min.y<=this.max.y}clampPoint(t,e){return e.copy(t).clamp(this.min,this.max)}distanceToPoint(t){return this.clampPoint(t,Md).distanceTo(t)}intersect(t){return this.min.max(t.min),this.max.min(t.max),this.isEmpty()&&this.makeEmpty(),this}union(t){return this.min.min(t.min),this.max.max(t.max),this}translate(t){return this.min.add(t),this.max.add(t),this}equals(t){return t.min.equals(this.min)&&t.max.equals(this.max)}}const _d=new Ti,Ad=new Ti,Td=new Ti,zd=new Ti,Cd=new Ti,Id=new Ti,Bd=new Ti;class kd{constructor(t=new Ti,e=new Ti){this.start=t,this.end=e}set(t,e){return this.start.copy(t),this.end.copy(e),this}copy(t){return this.start.copy(t.start),this.end.copy(t.end),this}getCenter(t){return t.addVectors(this.start,this.end).multiplyScalar(.5)}delta(t){return t.subVectors(this.end,this.start)}distanceSq(){return this.start.distanceToSquared(this.end)}distance(){return this.start.distanceTo(this.end)}at(t,e){return this.delta(e).multiplyScalar(t).add(this.start)}closestPointToPointParameter(t,e){_d.subVectors(t,this.start),Ad.subVectors(this.end,this.start);const s=Ad.dot(Ad);if(0===s)return 0;let i=Ad.dot(_d)/s;return e&&(i=xi(i,0,1)),i}closestPointToPoint(t,e,s){const i=this.closestPointToPointParameter(t,e);return this.delta(s).multiplyScalar(i).add(this.start)}distanceSqToLine3(t,e=Id,s=Bd){const i=1e-8*1e-8;let r,n;const a=this.start,o=t.start,h=this.end,l=t.end;Td.subVectors(h,a),zd.subVectors(l,o),Cd.subVectors(a,o);const c=Td.dot(Td),u=zd.dot(zd),d=zd.dot(Cd);if(c<=i&&u<=i)return e.copy(a),s.copy(o),e.sub(s),e.dot(e);if(c<=i)r=0,n=d/u,n=xi(n,0,1);else{const t=Td.dot(Cd);if(u<=i)n=0,r=xi(-t/c,0,1);else{const e=Td.dot(zd),s=c*u-e*e;r=0!==s?xi((e*d-t*u)/s,0,1):0,n=(e*r+d)/u,n<0?(n=0,r=xi(-t/c,0,1)):n>1&&(n=1,r=xi((e-t)/c,0,1))}}return e.copy(a).addScaledVector(Td,r),s.copy(o).addScaledVector(zd,n),e.distanceToSquared(s)}applyMatrix4(t){return this.start.applyMatrix4(t),this.end.applyMatrix4(t),this}equals(t){return t.start.equals(this.start)&&t.end.equals(this.end)}clone(){return(new this.constructor).copy(this)}}const Od=new Ti;class Pd extends Ar{constructor(t,e){super(),this.light=t,this.matrixAutoUpdate=!1,this.color=e,this.type="SpotLightHelper";const s=new Wn,i=[0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,-1,0,1,0,0,0,0,1,1,0,0,0,0,-1,1];for(let t=0,e=1,s=32;t1)for(let s=0;s.99999)this.quaternion.set(0,0,0,1);else if(t.y<-.99999)this.quaternion.set(1,0,0,0);else{rp.set(t.z,0,-t.x).normalize();const e=Math.acos(t.y);this.quaternion.setFromAxisAngle(rp,e)}}setLength(t,e=.2*t,s=.2*e){this.line.scale.set(1,Math.max(1e-4,t-e),1),this.line.updateMatrix(),this.cone.scale.set(s,e,s),this.cone.position.y=t,this.cone.updateMatrix()}setColor(t){this.line.material.color.set(t),this.cone.material.color.set(t)}copy(t){return super.copy(t,!1),this.line.copy(t.line),this.cone.copy(t.cone),this}dispose(){this.line.geometry.dispose(),this.line.material.dispose(),this.cone.geometry.dispose(),this.cone.material.dispose()}}class hp extends Yo{constructor(t=1){const e=[0,0,0,t,0,0,0,0,0,0,t,0,0,0,0,0,0,t],s=new Wn;s.setAttribute("position",new kn(e,3)),s.setAttribute("color",new kn([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1],3));super(s,new No({vertexColors:!0,toneMapped:!1})),this.type="AxesHelper"}setColors(t,e,s){const i=new Pr,r=this.geometry.attributes.color.array;return i.set(t),i.toArray(r,0),i.toArray(r,3),i.set(e),i.toArray(r,6),i.toArray(r,9),i.set(s),i.toArray(r,12),i.toArray(r,15),this.geometry.attributes.color.needsUpdate=!0,this}dispose(){this.geometry.dispose(),this.material.dispose()}}class lp{constructor(){this.type="ShapePath",this.color=new Pr,this.subPaths=[],this.currentPath=null,this.userData={}}moveTo(t,e){return this.currentPath=new Zh,this.subPaths.push(this.currentPath),this.currentPath.moveTo(t,e),this}lineTo(t,e){return this.currentPath.lineTo(t,e),this}quadraticCurveTo(t,e,s,i){return this.currentPath.quadraticCurveTo(t,e,s,i),this}bezierCurveTo(t,e,s,i,r,n){return this.currentPath.bezierCurveTo(t,e,s,i,r,n),this}splineThru(t){return this.currentPath.splineThru(t),this}toShapes(){function t(t,e){let s=!1;const i=e.length;for(let r=0,n=i-1;rt.y!=a.y>t.y&&t.x<(a.x-i.x)*(t.y-i.y)/(a.y-i.y)+i.x&&(s=!s)}return s}function e(e,s){const i=s.getCenter(new _i);if(t(i,e))return i;const r=i.y,n=[],a=e.length;for(let t=0;tr!=i.y>r){const t=s.x+(r-s.y)*(i.x-s.x)/(i.y-s.y);n.push(t)}}return n.length>1&&(n.sort((t,e)=>t-e),i.x=(n[0]+n[1])/2),i}let s=this.userData.style&&this.userData.style.fillRule||"nonzero";"nonzero"!==s&&"evenodd"!==s&&(ai('Fill-rule "'+s+'" is not supported, falling back to "nonzero".'),s="nonzero");const i="nonzero"===s?t=>0!==t:t=>!!(1&t),r=[];for(const t of this.subPaths){const s=t.getPoints();if(s.length<3)continue;const i=_l.area(s);if(0===i)continue;const n=new Sd;for(let t=0;te.absArea-t.absArea);for(let e=0;e=0;i--){const e=r[i];if(e.boundingBox.containsPoint(s.interiorPoint)&&t(s.interiorPoint,e.points)){s.container=e.exclude?e.container:e,n=e.winding,s.winding+=n;break}}i(s.winding)===i(n)&&(s.exclude=!0)}for(const t of r)t.exclude||(t.role=null===t.container||"hole"===t.container.role?"outer":"hole");const n=[],a=new Map;for(const t of r){if(t.exclude||"outer"!==t.role)continue;const e=new Gh;e.curves=t.subPath.curves,n.push(e),a.set(t,e)}for(const t of r){if(t.exclude||"hole"!==t.role)continue;const e=a.get(t.container);if(!e)continue;const s=new Zh;s.curves=t.subPath.curves,e.holes.push(s)}return n}}class cp extends di{constructor(t,e=null){super(),this.object=t,this.domElement=e,this.enabled=!0,this.state=-1,this.keys={},this.mouseButtons={LEFT:null,MIDDLE:null,RIGHT:null},this.touches={ONE:null,TWO:null}}connect(t){void 0!==t?(null!==this.domElement&&this.disconnect(),this.domElement=t):ai("Controls: connect() now requires an element.")}disconnect(){}dispose(){}update(){}}function up(t,e,s,i){const r=function(t){switch(t){case zt:case Ct:return{byteLength:1,components:1};case Bt:case It:case Rt:return{byteLength:2,components:1};case Et:case Nt:return{byteLength:2,components:4};case Ot:case kt:case Pt:return{byteLength:4,components:1};case Lt:case Ft:return{byteLength:4,components:3}}throw new Error(`THREE.TextureUtils: Unknown texture type ${t}.`)}(i);switch(s){case 1021:return t*e;case qt:case Ht:return t*e/r.components*r.byteLength;case 1030:case 1031:return t*e*2/r.components*r.byteLength;case 1022:return t*e*3/r.components*r.byteLength;case jt:case 1033:return t*e*4/r.components*r.byteLength;case 33776:case 33777:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case 33778:case 33779:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case 35841:case 35843:return Math.max(t,16)*Math.max(e,8)/4;case 35840:case 35842:return Math.max(t,8)*Math.max(e,8)/2;case 36196:case 37492:case 37488:case 37489:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case 37496:case 37490:case 37491:case 37808:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case 37809:return Math.floor((t+4)/5)*Math.floor((e+3)/4)*16;case 37810:return Math.floor((t+4)/5)*Math.floor((e+4)/5)*16;case 37811:return Math.floor((t+5)/6)*Math.floor((e+4)/5)*16;case 37812:return Math.floor((t+5)/6)*Math.floor((e+5)/6)*16;case 37813:return Math.floor((t+7)/8)*Math.floor((e+4)/5)*16;case 37814:return Math.floor((t+7)/8)*Math.floor((e+5)/6)*16;case 37815:return Math.floor((t+7)/8)*Math.floor((e+7)/8)*16;case 37816:return Math.floor((t+9)/10)*Math.floor((e+4)/5)*16;case 37817:return Math.floor((t+9)/10)*Math.floor((e+5)/6)*16;case 37818:return Math.floor((t+9)/10)*Math.floor((e+7)/8)*16;case 37819:return Math.floor((t+9)/10)*Math.floor((e+9)/10)*16;case 37820:return Math.floor((t+11)/12)*Math.floor((e+9)/10)*16;case 37821:return Math.floor((t+11)/12)*Math.floor((e+11)/12)*16;case 36492:case 36494:case 36495:return Math.ceil(t/4)*Math.ceil(e/4)*16;case 36283:case 36284:return Math.ceil(t/4)*Math.ceil(e/4)*8;case 36285:case 36286:return Math.ceil(t/4)*Math.ceil(e/4)*16}throw new Error(`Unable to determine texture byte length for ${s} format.`)}class dp{static contain(t,e){return function(t,e){const s=t.image&&t.image.width?t.image.width/t.image.height:1;return s>e?(t.repeat.x=1,t.repeat.y=s/e,t.offset.x=0,t.offset.y=(1-t.repeat.y)/2):(t.repeat.x=e/s,t.repeat.y=1,t.offset.x=(1-t.repeat.x)/2,t.offset.y=0),t}(t,e)}static cover(t,e){return function(t,e){const s=t.image&&t.image.width?t.image.width/t.image.height:1;return s>e?(t.repeat.x=e/s,t.repeat.y=1,t.offset.x=(1-t.repeat.x)/2,t.offset.y=0):(t.repeat.x=1,t.repeat.y=s/e,t.offset.x=0,t.offset.y=(1-t.repeat.y)/2),t}(t,e)}static fill(t){return function(t){return t.repeat.x=1,t.repeat.y=1,t.offset.x=0,t.offset.y=0,t}(t)}static getByteLength(t,e,s,i){return up(t,e,s,i)}}"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register",{detail:{revision:t}})),"undefined"!=typeof window&&(window.__THREE__?ai("WARNING: Multiple instances of Three.js being imported."):window.__THREE__=t);export{st as ACESFilmicToneMapping,w as AddEquation,$ as AddOperation,Je as AdditiveAnimationBlendMode,g as AdditiveBlending,rt as AgXToneMapping,Dt as AlphaFormat,ks as AlwaysCompare,j as AlwaysDepth,Ss as AlwaysStencilFunc,lu as AmbientLight,nd as AnimationAction,Ac as AnimationClip,Ec as AnimationLoader,od as AnimationMixer,rd as AnimationObjectGroup,uc as AnimationUtils,Ih as ArcCurve,Ru as ArrayCamera,op as ArrowHelper,at as AttachedBindMode,Wu as Audio,Zu as AudioAnalyser,Tu as AudioContext,ju as AudioListener,zu as AudioLoader,hp as AxesHelper,d as BackSide,Ye as BasicDepthPacking,o as BasicShadowMap,Eo as BatchedMesh,gc as BezierInterpolant,Ha as Bone,xc as BooleanKeyframeTrack,Sd as Box2,Qr as Box3,sp as Box3Helper,yh as BoxGeometry,ep as BoxHelper,Mn as BufferAttribute,Wn as BufferGeometry,fu as BufferGeometryLoader,Ct as ByteType,zc as Cache,$c as Camera,Qd as CameraHelper,ch as CanvasTexture,gh as CapsuleGeometry,Nh as CatmullRomCurve3,et as CineonToneMapping,fh as CircleGeometry,yt as ClampToEdgeWrapping,xd as Clock,Pr as Color,bc as ColorKeyframeTrack,Ri as ColorManagement,Ys as Compatibility,oh as CompressedArrayTexture,hh as CompressedCubeTexture,ah as CompressedTexture,Nc as CompressedTextureLoader,bh as ConeGeometry,F as ConstantAlphaFactor,V as ConstantColorFactor,cp as Controls,Pu as CubeCamera,ph as CubeDepthTexture,lt as CubeReflectionMapping,ct as CubeRefractionMapping,lh as CubeTexture,Fc as CubeTextureLoader,pt as CubeUVReflectionMapping,Dh as CubicBezierCurve,Uh as CubicBezierCurve3,pc as CubicInterpolant,r as CullFaceBack,n as CullFaceFront,a as CullFaceFrontBack,i as CullFaceNone,zh as Curve,Yh as CurvePath,b as CustomBlending,it as CustomToneMapping,xh as CylinderGeometry,vd as Cylindrical,Gi as Data3DTexture,Yi as DataArrayTexture,Xa as DataTexture,Dc as DataTextureLoader,xn as DataUtils,ds as DecrementStencilOp,ms as DecrementWrapStencilOp,Bc as DefaultLoadingManager,Wt as DepthFormat,Jt as DepthStencilFormat,dh as DepthTexture,ot as DetachedBindMode,hu as DirectionalLight,Zd as DirectionalLightHelper,yc as DiscreteInterpolant,wh as DodecahedronGeometry,p as DoubleSide,O as DstAlphaFactor,R as DstColorFactor,Fs as DynamicCopyUsage,Ps as DynamicDrawUsage,Ns as DynamicReadUsage,Th as EdgesGeometry,Ch as EllipseCurve,Ts as EqualCompare,q as EqualDepth,xs as EqualStencilFunc,ut as EquirectangularReflectionMapping,dt as EquirectangularRefractionMapping,hr as Euler,di as EventDispatcher,mh as ExternalTexture,zl as ExtrudeGeometry,Rc as FileLoader,Bn as Float16BufferAttribute,kn as Float32BufferAttribute,Pt as FloatType,Nr as Fog,Er as FogExp2,nh as FramebufferTexture,u as FrontSide,mo as Frustum,go as FrustumArray,pd as GLBufferAttribute,Us as GLSL1,js as GLSL3,Cs as GreaterCompare,X as GreaterDepth,Bs as GreaterEqualCompare,H as GreaterEqualDepth,Ms as GreaterEqualStencilFunc,vs as GreaterStencilFunc,Jd as GridHelper,Tr as Group,uh as HTMLTexture,Rt as HalfFloatType,Wc as HemisphereLight,Wd as HemisphereLightHelper,Il as IcosahedronGeometry,_u as ImageBitmapLoader,Lc as ImageLoader,Li as ImageUtils,us as IncrementStencilOp,ps as IncrementWrapStencilOp,$a as InstancedBufferAttribute,gu as InstancedBufferGeometry,dd as InstancedInterleavedBuffer,no as InstancedMesh,Tn as Int16BufferAttribute,Cn as Int32BufferAttribute,Sn as Int8BufferAttribute,kt as IntType,Jn as InterleavedBuffer,Hn as InterleavedBufferAttribute,dc as Interpolant,Fe as InterpolateBezier,Ne as InterpolateDiscrete,Ve as InterpolateLinear,Le as InterpolateSmooth,Xs as InterpolationSamplingMode,Hs as InterpolationSamplingType,ys as InvertStencilOp,ls as KeepStencilOp,fc as KeyframeTrack,pa as LOD,Bl as LatheGeometry,lr as Layers,As as LessCompare,W as LessDepth,zs as LessEqualCompare,J as LessEqualDepth,bs as LessEqualStencilFunc,fs as LessStencilFunc,jc as Light,du as LightProbe,Jo as Line,kd as Line3,No as LineBasicMaterial,jh as LineCurve,Wh as LineCurve3,ac as LineDashedMaterial,Zo as LineLoop,Yo as LineSegments,Mt as LinearFilter,mc as LinearInterpolant,Tt as LinearMipMapLinearFilter,_t as LinearMipMapNearestFilter,At as LinearMipmapLinearFilter,St as LinearMipmapNearestFilter,ss as LinearSRGBColorSpace,K as LinearToneMapping,is as LinearTransfer,kc as Loader,yu as LoaderUtils,Ic as LoadingManager,Pe as LoopOnce,Ee as LoopPingPong,Re as LoopRepeat,e as MOUSE,Zn as Material,v as MaterialBlending,mu as MaterialLoader,Si as MathUtils,wd as Matrix2,Ii as Matrix3,Qi as Matrix4,A as MaxEquation,Ra as Mesh,Ma as MeshBasicMaterial,ic as MeshDepthMaterial,rc as MeshDistanceMaterial,sc as MeshLambertMaterial,nc as MeshMatcapMaterial,ec as MeshNormalMaterial,Kl as MeshPhongMaterial,Ql as MeshPhysicalMaterial,$l as MeshStandardMaterial,tc as MeshToonMaterial,_ as MinEquation,gt as MirroredRepeatWrapping,G as MixOperation,x as MultiplyBlending,Z as MultiplyOperation,ft as NearestFilter,wt as NearestMipMapLinearFilter,bt as NearestMipMapNearestFilter,vt as NearestMipmapLinearFilter,xt as NearestMipmapNearestFilter,nt as NeutralToneMapping,_s as NeverCompare,U as NeverDepth,gs as NeverStencilFunc,m as NoBlending,ts as NoColorSpace,ns as NoNormalPacking,Q as NoToneMapping,We as NormalAnimationBlendMode,y as NormalBlending,os as NormalGAPacking,as as NormalRGPacking,Is as NotEqualCompare,Y as NotEqualDepth,ws as NotEqualStencilFunc,vc as NumberKeyframeTrack,Ar as Object3D,bu as ObjectLoader,Ke as ObjectSpaceNormalMap,kl as OctahedronGeometry,z as OneFactor,D as OneMinusConstantAlphaFactor,L as OneMinusConstantColorFactor,P as OneMinusDstAlphaFactor,E as OneMinusDstColorFactor,k as OneMinusSrcAlphaFactor,I as OneMinusSrcColorFactor,au as OrthographicCamera,h as PCFShadowMap,l as PCFSoftShadowMap,Zh as Path,eu as PerspectiveCamera,lo as Plane,Ol as PlaneGeometry,ip as PlaneHelper,nu as PointLight,Fd as PointLightHelper,eh as Points,Go as PointsMaterial,qd as PolarGridHelper,vh as PolyhedronGeometry,Yu as PositionalAudio,id as PropertyBinding,Gu as PropertyMixer,Jh as QuadraticBezierCurve,qh as QuadraticBezierCurve3,Ai as Quaternion,Mc as QuaternionKeyframeTrack,wc as QuaternionLinearInterpolant,he as R11_EAC_Format,gi as RAD2DEG,ke as RED_GREEN_RGTC2_Format,Ie as RED_RGTC1_Format,t as REVISION,ce as RG11_EAC_Format,Ze as RGBADepthPacking,jt as RGBAFormat,Gt as RGBAIntegerFormat,Se as RGBA_ASTC_10x10_Format,ve as RGBA_ASTC_10x5_Format,we as RGBA_ASTC_10x6_Format,Me as RGBA_ASTC_10x8_Format,_e as RGBA_ASTC_12x10_Format,Ae as RGBA_ASTC_12x12_Format,de as RGBA_ASTC_4x4_Format,pe as RGBA_ASTC_5x4_Format,me as RGBA_ASTC_5x5_Format,ye as RGBA_ASTC_6x5_Format,ge as RGBA_ASTC_6x6_Format,fe as RGBA_ASTC_8x5_Format,xe as RGBA_ASTC_8x6_Format,be as RGBA_ASTC_8x8_Format,Te as RGBA_BPTC_Format,oe as RGBA_ETC2_EAC_Format,re as RGBA_PVRTC_2BPPV1_Format,ie as RGBA_PVRTC_4BPPV1_Format,Qt as RGBA_S3TC_DXT1_Format,Kt as RGBA_S3TC_DXT3_Format,te as RGBA_S3TC_DXT5_Format,Ge as RGBDepthPacking,Ut as RGBFormat,Zt as RGBIntegerFormat,ze as RGB_BPTC_SIGNED_Format,Ce as RGB_BPTC_UNSIGNED_Format,ne as RGB_ETC1_Format,ae as RGB_ETC2_Format,se as RGB_PVRTC_2BPPV1_Format,ee as RGB_PVRTC_4BPPV1_Format,$t as RGB_S3TC_DXT1_Format,$e as RGDepthPacking,Xt as RGFormat,Yt as RGIntegerFormat,Gl as RawShaderMaterial,wa as Ray,yd as Raycaster,cu as RectAreaLight,qt as RedFormat,Ht as RedIntegerFormat,tt as ReinhardToneMapping,Hi as RenderTarget,hd as RenderTarget3D,mt as RepeatWrapping,cs as ReplaceStencilOp,S as ReverseSubtractEquation,ui as ReversedDepthFuncs,Pl as RingGeometry,le as SIGNED_R11_EAC_Format,Oe as SIGNED_RED_GREEN_RGTC2_Format,Be as SIGNED_RED_RGTC1_Format,ue as SIGNED_RG11_EAC_Format,es as SRGBColorSpace,rs as SRGBTransfer,Vr as Scene,Zl as ShaderMaterial,Wl as ShadowMaterial,Gh as Shape,Rl as ShapeGeometry,lp as ShapePath,_l as ShapeUtils,It as ShortType,Ga as Skeleton,Vd as SkeletonHelper,qa as SkinnedMesh,Di as Source,En as Sphere,El as SphereGeometry,bd as Spherical,uu as SphericalHarmonics3,Hh as SplineCurve,iu as SpotLight,Pd as SpotLightHelper,la as Sprite,Gn as SpriteMaterial,B as SrcAlphaFactor,N as SrcAlphaSaturateFactor,C as SrcColorFactor,Ls as StaticCopyUsage,Os as StaticDrawUsage,Es as StaticReadUsage,ku as StereoCamera,Ds as StreamCopyUsage,Rs as StreamDrawUsage,Vs as StreamReadUsage,Sc as StringKeyframeTrack,M as SubtractEquation,f as SubtractiveBlending,s as TOUCH,Qe as TangentSpaceNormalMap,Nl as TetrahedronGeometry,Ji as Texture,Uc as TextureLoader,dp as TextureUtils,Eu as Timer,qs as TimestampQuery,Vl as TorusGeometry,Ll as TorusKnotGeometry,$r as Triangle,Xe as TriangleFanDrawMode,He as TriangleStripDrawMode,qe as TrianglesDrawMode,Fl as TubeGeometry,ht as UVMapping,zn as Uint16BufferAttribute,In as Uint32BufferAttribute,_n as Uint8BufferAttribute,An as Uint8ClampedBufferAttribute,ld as Uniform,ud as UniformsGroup,Yl as UniformsUtils,zt as UnsignedByteType,Ft as UnsignedInt101111Type,Vt as UnsignedInt248Type,Lt as UnsignedInt5999Type,Ot as UnsignedIntType,Et as UnsignedShort4444Type,Nt as UnsignedShort5551Type,Bt as UnsignedShortType,c as VSMShadowMap,_i as Vector2,Ti as Vector3,qi as Vector4,_c as VectorKeyframeTrack,rh as VideoFrameTexture,ih as VideoTexture,$i as WebGL3DRenderTarget,Zi as WebGLArrayRenderTarget,Ws as WebGLCoordinateSystem,Xi as WebGLRenderTarget,Js as WebGPUCoordinateSystem,Cr as WebXRController,Dl as WireframeGeometry,je as WrapAroundEnding,De as ZeroCurvatureEnding,T as ZeroFactor,Ue as ZeroSlopeEnding,hs as ZeroStencilOp,Jl as cloneUniforms,Ks as createCanvasElement,Qs as createElementNS,oi as error,up as getByteLength,ii as getConsoleFunction,Xl as getUnlitUniformColorSpace,$s as isTypedArray,ri as log,ql as mergeUniforms,ci as probeAsync,si as setConsoleFunction,ai as warn,hi as warnOnce,li as yieldToMain}; diff --git a/package.json b/package.json index 0ac5bb157ed092..4f5c8fc638e556 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "three", - "version": "0.184.0", + "version": "0.185.0", "description": "JavaScript 3D library", "type": "module", "main": "./build/three.cjs", diff --git a/src/constants.js b/src/constants.js index d6ea488f9e3604..1355a595424af3 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,4 +1,4 @@ -export const REVISION = '185dev'; +export const REVISION = '185'; /** * Represents mouse buttons and interaction types in context of controls. From 9cd4c716a87d3eb505a11b0260970247eb797812 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Thu, 25 Jun 2026 12:25:51 +0200 Subject: [PATCH 7/9] Update constants.js Bump revision. --- src/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants.js b/src/constants.js index 1355a595424af3..0017873a960b2c 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,4 +1,4 @@ -export const REVISION = '185'; +export const REVISION = '186dev'; /** * Represents mouse buttons and interaction types in context of controls. From a7d62abb6099603d70f8a9d78da01b8b1d917c8c Mon Sep 17 00:00:00 2001 From: Jack Lavigne Date: Thu, 25 Jun 2026 12:46:01 +0200 Subject: [PATCH 8/9] `Renderer`: Add `resetState` function (#33877) Co-authored-by: Michael Herzog --- src/renderers/common/Backend.js | 7 + src/renderers/common/Renderer.js | 16 +++ src/renderers/webgl-fallback/WebGLBackend.js | 9 ++ .../webgl-fallback/utils/WebGLState.js | 124 ++++++++++++++++++ 4 files changed, 156 insertions(+) diff --git a/src/renderers/common/Backend.js b/src/renderers/common/Backend.js index 867f222e3e55ac..12b344150f99f0 100644 --- a/src/renderers/common/Backend.js +++ b/src/renderers/common/Backend.js @@ -669,6 +669,13 @@ class Backend { */ setScissorTest( /*boolean*/ ) { } + /** + * Resets the backend's internal state. A no-op for backends without a state cache (e.g. WebGPU). + * + * @abstract + */ + resetState() { } + /** * Returns the clear color and alpha into a single * color object. diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 8dfc2ae055ad7b..41f9ab0c87198c 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -2153,6 +2153,22 @@ class Renderer { } + /** + * Resets the backend's internal state cache. Useful when the rendering context is shared with + * other libraries that change the state. A no-op for the WebGPU backend. + */ + resetState() { + + if ( this._initialized === false ) { + + throw new Error( 'THREE.Renderer: .resetState() called before the backend is initialized. Use "await renderer.init();" before using this method.' ); + + } + + this.backend.resetState(); + + } + /** * Returns the viewport definition. * diff --git a/src/renderers/webgl-fallback/WebGLBackend.js b/src/renderers/webgl-fallback/WebGLBackend.js index 68c2c6942b23a5..21721dab56243b 100644 --- a/src/renderers/webgl-fallback/WebGLBackend.js +++ b/src/renderers/webgl-fallback/WebGLBackend.js @@ -727,6 +727,15 @@ class WebGLBackend extends Backend { } + /** + * Restores the WebGL state to its default and invalidates the internal state cache. + */ + resetState() { + + this.state.reset(); + + } + /** * Returns the clear color and alpha into a single * color object. diff --git a/src/renderers/webgl-fallback/utils/WebGLState.js b/src/renderers/webgl-fallback/utils/WebGLState.js index adf1eae5944f91..eea31cd9c07479 100644 --- a/src/renderers/webgl-fallback/utils/WebGLState.js +++ b/src/renderers/webgl-fallback/utils/WebGLState.js @@ -56,6 +56,8 @@ class WebGLState { this.currentProgram = null; this.currentBlendingEnabled = false; this.currentBlending = null; + this.currentBlendEquation = null; + this.currentBlendEquationAlpha = null; this.currentBlendSrc = null; this.currentBlendDst = null; this.currentBlendSrcAlpha = null; @@ -1356,6 +1358,128 @@ class WebGLState { } + /** + * Restores the WebGL state to its default and clears the cache so subsequent renderings + * re-apply the required state. Useful when the WebGL context is shared with other libraries. + */ + reset() { + + const { gl } = this; + + // reset WebGL state + + gl.disable( gl.BLEND ); + gl.disable( gl.CULL_FACE ); + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.POLYGON_OFFSET_FILL ); + gl.disable( gl.SCISSOR_TEST ); + gl.disable( gl.STENCIL_TEST ); + gl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); + + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ONE, gl.ZERO ); + gl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO ); + gl.blendColor( 0, 0, 0, 0 ); + + gl.colorMask( true, true, true, true ); + gl.clearColor( 0, 0, 0, 0 ); + + gl.depthMask( true ); + gl.depthFunc( gl.LESS ); + gl.clearDepth( 1 ); + + gl.stencilMask( 0xffffffff ); + gl.stencilFunc( gl.ALWAYS, 0, 0xffffffff ); + gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP ); + gl.clearStencil( 0 ); + + gl.cullFace( gl.BACK ); + gl.frontFace( gl.CCW ); + + gl.polygonOffset( 0, 0 ); + + gl.activeTexture( gl.TEXTURE0 ); + + gl.bindFramebuffer( gl.FRAMEBUFFER, null ); + gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null ); + gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null ); + + gl.useProgram( null ); + + gl.lineWidth( 1 ); + + gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); + gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); + + gl.pixelStorei( gl.PACK_ALIGNMENT, 4 ); + gl.pixelStorei( gl.UNPACK_ALIGNMENT, 4 ); + gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false ); + gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); + gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.BROWSER_DEFAULT_WEBGL ); + gl.pixelStorei( gl.PACK_ROW_LENGTH, 0 ); + gl.pixelStorei( gl.PACK_SKIP_PIXELS, 0 ); + gl.pixelStorei( gl.PACK_SKIP_ROWS, 0 ); + gl.pixelStorei( gl.UNPACK_ROW_LENGTH, 0 ); + gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, 0 ); + gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, 0 ); + gl.pixelStorei( gl.UNPACK_SKIP_ROWS, 0 ); + gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, 0 ); + + this.resetVertexState(); + + // reset internal cache + + this.enabled = {}; + this.parameters = {}; + this.currentFlipSided = null; + this.currentCullFace = null; + this.currentProgram = null; + this.currentBlendingEnabled = false; + this.currentBlending = null; + this.currentBlendEquation = null; + this.currentBlendEquationAlpha = null; + this.currentBlendSrc = null; + this.currentBlendDst = null; + this.currentBlendSrcAlpha = null; + this.currentBlendDstAlpha = null; + this.currentPremultipledAlpha = null; + this.currentPolygonOffsetFactor = null; + this.currentPolygonOffsetUnits = null; + this.currentColorMask = null; + this.currentDepthFunc = null; + this.currentDepthMask = null; + this.currentStencilFunc = null; + this.currentStencilRef = null; + this.currentStencilFuncMask = null; + this.currentStencilFail = null; + this.currentStencilZFail = null; + this.currentStencilZPass = null; + this.currentStencilMask = null; + this.currentLineWidth = null; + this.currentClippingPlanes = 0; + + this.currentBoundFramebuffers = {}; + this.currentDrawbuffers = new WeakMap(); + + this.currentTextureSlot = null; + this.currentBoundTextures = {}; + this.currentBoundBufferBases = {}; + + this.currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); + this.currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); + + // re-apply reversed depth if used by the renderer + + this.currentDepthReversed = false; + + if ( this.backend.renderer.reversedDepthBuffer === true ) { + + this.setReversedDepth( true ); + + } + + } + } export default WebGLState; From bd942afdb3aae7fdcecf33ea34191fb43be56cc4 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Thu, 25 Jun 2026 12:46:42 +0200 Subject: [PATCH 9/9] Ray: New watertight intersectTriangle(). (#33661) --- src/core/Raycaster.js | 4 + src/math/Ray.js | 138 +++++++++++++++++++++----------- test/unit/src/math/Ray.tests.js | 32 ++++++++ 3 files changed, 129 insertions(+), 45 deletions(-) diff --git a/src/core/Raycaster.js b/src/core/Raycaster.js index f941e0b82b4c03..e5ed625bdfd7d9 100644 --- a/src/core/Raycaster.js +++ b/src/core/Raycaster.js @@ -185,6 +185,10 @@ class Raycaster { * be detected. To raycast against both faces of an object, you'll want to set {@link Material#side} * to `THREE.DoubleSide`. * + * Note that a ray hitting a triangle mesh exactly along an edge shared by two faces may be + * reported by both faces, resulting in two coincident intersections (identical point and + * distance) in the returned array. + * * @param {Object3D} object - The 3D object to check for intersection with the ray. * @param {boolean} [recursive=true] - If set to `true`, it also checks all descendants. * Otherwise it only checks intersection with the object. diff --git a/src/math/Ray.js b/src/math/Ray.js index dcd4234b9e4e09..8edd1a7497e6fd 100644 --- a/src/math/Ray.js +++ b/src/math/Ray.js @@ -5,10 +5,6 @@ const _segCenter = /*@__PURE__*/ new Vector3(); const _segDir = /*@__PURE__*/ new Vector3(); const _diff = /*@__PURE__*/ new Vector3(); -const _edge1 = /*@__PURE__*/ new Vector3(); -const _edge2 = /*@__PURE__*/ new Vector3(); -const _normal = /*@__PURE__*/ new Vector3(); - /** * A ray that emits from an origin in a certain direction. The class is used by * {@link Raycaster} to assist with raycasting. Raycasting is used for @@ -539,76 +535,128 @@ class Ray { */ intersectTriangle( a, b, c, backfaceCulling, target ) { - // Compute the offset origin, edges, and normal. + // Watertight ray/triangle intersection. Reference: Woop, Benthin, Wald, + // "Watertight Ray/Triangle Intersection", JCGT vol. 2 no. 1 (2013), Appendix A. + // https://jcgt.org/published/0002/01/05/ + + const origin = this.origin; + const direction = this.direction; + + const dx = direction.x; + const dy = direction.y; + const dz = direction.z; + + // triangle vertices relative to the ray origin + + const aox = a.x - origin.x, aoy = a.y - origin.y, aoz = a.z - origin.z; + const box = b.x - origin.x, boy = b.y - origin.y, boz = b.z - origin.z; + const cox = c.x - origin.x, coy = c.y - origin.y, coz = c.z - origin.z; + + // Use the dimension where the ray direction is maximal as the projection + // axis (kz) and read every component already permuted into (kx, ky, kz). + // kx and ky are swapped when the direction's kz component is negative, to + // preserve the winding order of triangles. + + const adx = Math.abs( dx ), ady = Math.abs( dy ), adz = Math.abs( dz ); + + let dkx, dky, dkz; + let akx, aky, akz, bkx, bky, bkz, ckx, cky, ckz; + + if ( adx >= ady && adx >= adz ) { - // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h + dkz = dx; akz = aox; bkz = box; ckz = cox; - _edge1.subVectors( b, a ); - _edge2.subVectors( c, a ); - _normal.crossVectors( _edge1, _edge2 ); + if ( dx >= 0 ) { - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - let DdN = this.direction.dot( _normal ); - let sign; + dkx = dy; dky = dz; + akx = aoy; aky = aoz; bkx = boy; bky = boz; ckx = coy; cky = coz; - if ( DdN > 0 ) { + } else { + + dkx = dz; dky = dy; + akx = aoz; aky = aoy; bkx = boz; bky = boy; ckx = coz; cky = coy; + + } + + } else if ( ady >= adz ) { - if ( backfaceCulling ) return null; - sign = 1; + dkz = dy; akz = aoy; bkz = boy; ckz = coy; - } else if ( DdN < 0 ) { + if ( dy >= 0 ) { - sign = - 1; - DdN = - DdN; + dkx = dz; dky = dx; + akx = aoz; aky = aox; bkx = boz; bky = box; ckx = coz; cky = cox; + + } else { + + dkx = dx; dky = dz; + akx = aox; aky = aoz; bkx = box; bky = boz; ckx = cox; cky = coz; + + } } else { - return null; + dkz = dz; akz = aoz; bkz = boz; ckz = coz; - } + if ( dz >= 0 ) { - _diff.subVectors( this.origin, a ); - const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); + dkx = dx; dky = dy; + akx = aox; aky = aoy; bkx = box; bky = boy; ckx = cox; cky = coy; - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { + } else { - return null; + dkx = dy; dky = dx; + akx = aoy; aky = aox; bkx = boy; bky = box; ckx = coy; cky = cox; + + } } - const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); + // a zero direction has no maximal axis and cannot intersect - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { + if ( dkz === 0 ) return null; - return null; + // shear constants that align the ray with the +kz axis - } + const sx = dkx / dkz, sy = dky / dkz, sz = 1 / dkz; - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { + // sheared and scaled vertices - return null; + const ax = akx - sx * akz, ay = aky - sy * akz; + const bx = bkx - sx * bkz, by = bky - sy * bkz; + const cx = ckx - sx * ckz, cy = cky - sy * ckz; - } + // scaled barycentric coordinates (signed edge functions); the shear makes a + // shared edge evaluate identically for both adjacent triangles, so the ray + // can never fall between them - // Line intersects triangle, check if ray does. - const QdN = - sign * _diff.dot( _normal ); + const u = cx * by - cy * bx; + const v = ax * cy - ay * cx; + const w = bx * ay - by * ax; - // t < 0, no intersection - if ( QdN < 0 ) { + if ( backfaceCulling ) { - return null; + if ( u < 0 || v < 0 || w < 0 ) return null; + + } else { + + if ( ( u < 0 || v < 0 || w < 0 ) && ( u > 0 || v > 0 || w > 0 ) ) return null; } - // Ray intersects triangle. - return this.at( QdN / DdN, target ); + const det = u + v + w; + + // ray is co-planar with the triangle + + if ( det === 0 ) return null; + + // scaled hit distance; t = tScaled / det must lie in front of the origin + + const tScaled = sz * ( u * akz + v * bkz + w * ckz ); + + if ( det > 0 ? tScaled < 0 : tScaled > 0 ) return null; + + return this.at( tScaled / det, target ); } diff --git a/test/unit/src/math/Ray.tests.js b/test/unit/src/math/Ray.tests.js index 177680104449ac..0a956ef71c56be 100644 --- a/test/unit/src/math/Ray.tests.js +++ b/test/unit/src/math/Ray.tests.js @@ -431,6 +431,38 @@ export default QUnit.module( 'Maths', () => { } ); + QUnit.test( 'intersectTriangle (watertight at shared edges)', ( assert ) => { + + // Two triangles forming a quad and sharing the diagonal edge from + // ( -2, -2, -2 ) to ( 2, -2, 2 ). A ray aimed exactly at the midpoint of + // that shared edge must be detected: a non-watertight test can let the ray + // slip through the seam between the triangles and miss both of them. + + const t1a = new Vector3( - 2, - 2, 2 ); + const t1b = new Vector3( - 2, - 2, - 2 ); + const t1c = new Vector3( 2, - 2, 2 ); + + const t2a = new Vector3( - 2, - 2, - 2 ); + const t2b = new Vector3( 2, - 2, - 2 ); + const t2c = new Vector3( 2, - 2, 2 ); + + const seam = new Vector3( 0, - 2, 0 ); // midpoint of the shared edge + const origin = new Vector3( - 4, - 9, 0.4 ); + const direction = new Vector3().subVectors( seam, origin ).normalize(); + const ray = new Ray( origin, direction ); + + const p1 = new Vector3(); + const p2 = new Vector3(); + const hit1 = ray.intersectTriangle( t1a, t1b, t1c, false, p1 ); + const hit2 = ray.intersectTriangle( t2a, t2b, t2c, false, p2 ); + + assert.ok( hit1 !== null || hit2 !== null, 'Ray hitting the shared edge is not dropped' ); + + const hit = hit1 !== null ? p1 : p2; + assert.ok( hit.distanceTo( seam ) <= eps, 'Intersection lies on the shared edge' ); + + } ); + QUnit.test( 'applyMatrix4', ( assert ) => { let a = new Ray( one3.clone(), new Vector3( 0, 0, 1 ) );