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/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" ] -} +} 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" 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/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; diff --git a/examples/jsm/tsl/display/ImportanceSampledEnvironment.js b/examples/jsm/tsl/display/ImportanceSampledEnvironment.js new file mode 100644 index 00000000000000..1a5260bbede3ff --- /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 {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 {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 ) } ) { + + 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 {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, + 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 {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, + 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..a1443f922b359b --- /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 {Node} c + * @returns {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 {Node} c + * @returns {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 {Node} rgb + * @param {Node} flickerSuppression + * @returns {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 {Node} tapOffset + * @param {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 {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 {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 {TextureNode} beautyNode + * @param {TextureNode} depthNode + * @param {TextureNode} normalNode + * @param {TextureNode} velocityNode + * @param {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|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 {TextureNode} beautyNode + * @param {TextureNode} depthNode + * @param {TextureNode} normalNode + * @param {TextureNode} velocityNode + * @param {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..d5ddee5b8351d2 --- /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 {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 00000000000000..a656e532c2f97e Binary files /dev/null and b/examples/screenshots/webgpu_postprocessing_ssr_denoise.jpg differ 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/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..0017873a960b2c 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,4 +1,4 @@ -export const REVISION = '185dev'; +export const REVISION = '186dev'; /** * Represents mouse buttons and interaction types in context of controls. 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/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; 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', 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 ) );