three-environment-shadows 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/index.d.ts +35 -7
- package/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
# three-environment-shadows
|
|
2
2
|
|
|
3
|
+
Use environment as a source of light and _shadows_ in your Three.js scene.
|
|
4
|
+
Extracts light sources from environment map automatically - no need for any more of the manually set PointLights and SunLights 🤲
|
|
5
|
+
|
|
3
6
|
```js
|
|
4
7
|
import * as Three from 'three';
|
|
5
8
|
import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js';
|
|
6
|
-
import {
|
|
9
|
+
import { EnvironmentShadowLights } from 'three-environment-shadows';
|
|
7
10
|
|
|
8
11
|
renderer.shadowMap.enabled = true;
|
|
9
12
|
|
|
10
13
|
const hdri = await new UltraHDRLoader().loadAsync('./environment.jpg');
|
|
11
14
|
hdri.mapping = Three.EquirectangularReflectionMapping;
|
|
12
15
|
|
|
13
|
-
const result = await EnvironmentLightExtractor.extract(hdri);
|
|
14
|
-
|
|
15
16
|
scene.background = hdri;
|
|
16
|
-
scene.environment =
|
|
17
|
+
scene.environment = hdri;
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
scene.add(lights);
|
|
19
|
+
const shadows = new EnvironmentShadowLights(scene);
|
|
20
|
+
shadows.bounds.set(new Three.Vector3(0, 1, 0), 6);
|
|
21
21
|
|
|
22
22
|
mesh.castShadow = true;
|
|
23
23
|
mesh.receiveShadow = true;
|
package/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
DirectionalLight,
|
|
5
5
|
Group,
|
|
6
6
|
Object3D,
|
|
7
|
+
Object3DEventMap,
|
|
8
|
+
Scene,
|
|
7
9
|
Sphere,
|
|
8
10
|
Texture,
|
|
9
11
|
Vector3,
|
|
@@ -74,9 +76,19 @@ export class EnvironmentLightExtractor {
|
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
export interface EnvironmentShadowLightsOptions {
|
|
77
|
-
/** The maximum number of
|
|
79
|
+
/** The maximum number of lights. Default `2`. */
|
|
80
|
+
maxLights?: number;
|
|
81
|
+
/** Texels brighter than this factor times the mean radiance are part of a light. Default `8`. */
|
|
82
|
+
threshold?: number;
|
|
83
|
+
/** The minimum fraction of the total energy a light must emit. Default `0.05`. */
|
|
84
|
+
minEnergy?: number;
|
|
85
|
+
/** The minimum elevation (in radians) of a light, in the space of the environment map. Default `-Math.PI / 2`. */
|
|
86
|
+
minElevation?: number;
|
|
87
|
+
/** The width of the grid used to group bright texels into regions. Default `512`. */
|
|
88
|
+
resolution?: number;
|
|
89
|
+
/** The maximum number of shadow casting lights per light source. Default `4`. */
|
|
78
90
|
samples?: number;
|
|
79
|
-
/** The maximum number of shadow maps of all sources. Default `4`. */
|
|
91
|
+
/** The maximum number of shadow maps of all light sources. Default `4`. */
|
|
80
92
|
maxShadowMaps?: number;
|
|
81
93
|
/** The shadow map size of each light. Default `1024`. */
|
|
82
94
|
mapSize?: number;
|
|
@@ -84,29 +96,45 @@ export interface EnvironmentShadowLightsOptions {
|
|
|
84
96
|
relativeOccluderDistance?: number;
|
|
85
97
|
}
|
|
86
98
|
|
|
99
|
+
export interface EnvironmentShadowLightsEventMap extends Object3DEventMap {
|
|
100
|
+
/** The light sources changed, see `EnvironmentShadowLights.result`. */
|
|
101
|
+
change: {};
|
|
102
|
+
}
|
|
103
|
+
|
|
87
104
|
/**
|
|
88
|
-
* Casts shadows from the light sources of
|
|
105
|
+
* Casts shadows from the light sources of `scene.environment`.
|
|
89
106
|
*/
|
|
90
|
-
export class EnvironmentShadowLights extends Group {
|
|
91
|
-
|
|
107
|
+
export class EnvironmentShadowLights extends Group<EnvironmentShadowLightsEventMap> {
|
|
108
|
+
/** Constructs the group and adds it to the scene. */
|
|
109
|
+
constructor(scene?: Scene | null, options?: EnvironmentShadowLightsOptions);
|
|
92
110
|
|
|
93
111
|
readonly isEnvironmentShadowLights: true;
|
|
94
112
|
|
|
113
|
+
maxLights: number;
|
|
114
|
+
threshold: number;
|
|
115
|
+
minEnergy: number;
|
|
116
|
+
minElevation: number;
|
|
117
|
+
resolution: number;
|
|
95
118
|
samples: number;
|
|
96
119
|
maxShadowMaps: number;
|
|
97
120
|
mapSize: number;
|
|
98
121
|
relativeOccluderDistance: number;
|
|
99
122
|
|
|
123
|
+
/** Set to `true` to find the light sources again, e.g. after a change of the options. */
|
|
124
|
+
needsUpdate: boolean;
|
|
125
|
+
|
|
100
126
|
/** The area that receives shadows, in world space. */
|
|
101
127
|
bounds: Sphere;
|
|
102
128
|
|
|
129
|
+
/** The light sources of the environment map, or `null` before they are found. */
|
|
130
|
+
readonly result: Omit<ExtractionResult, 'texture'> | null;
|
|
131
|
+
|
|
103
132
|
/** The target of all lights, at the center of the bounds. */
|
|
104
133
|
readonly target: Object3D;
|
|
105
134
|
|
|
106
135
|
/** The lights. The shadow casting lights come first. */
|
|
107
136
|
readonly lights: DirectionalLight[];
|
|
108
137
|
|
|
109
|
-
|
|
110
|
-
|
|
138
|
+
/** Puts the original environment map back, frees the GPU-related resources and removes the group. */
|
|
111
139
|
dispose(): void;
|
|
112
140
|
}
|
package/index.js
CHANGED
|
@@ -2,6 +2,6 @@ import { Color, RGBAFormat, RGBFormat, FloatType, HalfFloatType, UnsignedByteTyp
|
|
|
2
2
|
|
|
3
3
|
const _color=new Color,_rgb={r:0,g:0,b:0};class EnvironmentLightExtractor{static async extract(t,e={}){const{maxLights:n=2,threshold:o=8,minEnergy:r=.05,minElevation:a=-Math.PI/2,samples:i=4,resolution:l=512,removeLights:c=!0}=e,s=readSource(t),h=await runAnalysis({data:s.data,width:s.width,height:s.height,channels:s.channels,encoding:s.encoding,decode:s.decode,flipY:t.flipY,maxLights:n,thresholdFactor:o,minEnergy:r,minElevation:a,maxParts:i,resolution:l});h.clipped&&console.warn("THREE.EnvironmentLightExtractor: Light texels are clipped in the 8-bit texture. Light intensities are too low.");const f=t=>({direction:(new Vector3).fromArray(t.direction),color:(new Color).fromArray(t.color),intensity:t.intensity,coneAngle:t.coneAngle}),p=h.lights.map(t=>{const e=t.partitions.map(t=>t.map(f));return {...f(t),energyFraction:t.energyFraction,partitions:e}});let g=null;return c&&(g=s.createTexture(h.data),removeSelectedLights(s,g.image.data,h)),{lights:p,directionality:h.directionality,dominance:h.dominance,threshold:h.threshold,texture:g}}}function runAnalysis(t){if("undefined"==typeof Worker)return Promise.resolve(analyze(t));const e=`const analyze = ${analyze.toString()};\n\n self.onmessage = (event) => {\n const output = analyze(event.data);\n self.postMessage(output, [output.data.buffer, output.cellSelected.buffer]);\n };`,n=URL.createObjectURL(new Blob([e],{type:"application/javascript"}));return new Promise((e,o)=>{const r=new Worker(n),a=()=>{r.terminate(),URL.revokeObjectURL(n);};r.onmessage=t=>{a(),e(t.data);},r.onerror=t=>{a(),o(new Error("THREE.EnvironmentLightExtractor: "+t.message));},r.postMessage(t,[t.data.buffer]);})}function analyze(t){const{data:e,width:n,height:o,channels:r,flipY:a,maxLights:i,thresholdFactor:l,minEnergy:c,minElevation:s,maxParts:h,resolution:f}=t;let p=t.decode;if("half"===t.encoding){p=new Float32Array(65536);for(let t=0;t<65536;t++){const e=t>>10&31,n=1023&t;let o;o=0===e?n*Math.pow(2,-24):31===e?0===n?1/0:NaN:(1+n/1024)*Math.pow(2,e-15),p[t]=32768&t?-o:o;}}const g="byte"===t.encoding,d=[0,0,0];function u(t,o){const a=(o*n+t)*r;return null===p?(d[0]=e[a],d[1]=e[a+1],d[2]=e[a+2]):(d[0]=p[e[a]],d[1]=p[e[a+1]],d[2]=p[e[a+2]]),.2126*d[0]+.7152*d[1]+.0722*d[2]}const m=new Float64Array(n),y=new Float64Array(n);for(let t=0;t<n;t++){const e=2*((t+.5)/n-.5)*Math.PI;m[t]=Math.sin(e),y[t]=Math.cos(e);}const M=new Float64Array(o),w=new Float64Array(o),x=new Float64Array(o);for(let t=0;t<o;t++){const e=a?1-t/o:t/o,r=a?1-(t+1)/o:(t+1)/o,i=((e+r)/2-.5)*Math.PI;M[t]=Math.sin(i),w[t]=Math.cos(i),x[t]=2*Math.PI/n*Math.abs(Math.sin((r-.5)*Math.PI)-Math.sin((e-.5)*Math.PI));}const E=Math.max(1,Math.min(n,f)),F=Math.max(1,Math.round(E*o/n)),b=E*F,T=new Int32Array(n),S=new Int32Array(o);for(let t=0;t<n;t++)T[t]=Math.floor(t*E/n);for(let t=0;t<o;t++)S[t]=Math.floor(t*F/o);const R={data:e,directionality:0,dominance:0,threshold:0,gridWidth:E,gridHeight:F,cellSelected:new Uint8Array(b),lights:[],clipped:!1},A=new Float32Array(b);let L=0,v=0,B=0,H=0;for(let t=0;t<o;t++){const e=x[t],o=S[t]*E;for(let r=0;r<n;r++){const n=u(r,t);if(!(n>0&&n<1/0))continue;const a=n*e;L+=a,v+=a*w[t]*y[r],B+=a*M[t],H+=a*w[t]*m[r];const i=o+T[r];n>A[i]&&(A[i]=n);}}if(0===L)return R;R.directionality=Math.hypot(v,B,H)/L;const U=l*L/(4*Math.PI);R.threshold=U;const G=new Int32Array(b).fill(-1),C=[],I=[],_=Math.floor(E/2);for(let t=0;t<b;t++){if(G[t]>=0||A[t]<=U)continue;const e=[];for(G[t]=C.length,I.push(t);I.length>0;){const t=I.pop(),n=t%E,o=(t-n)/E;e.push(t);for(let t=-1;t<=1;t++)for(let e=-1;e<=1;e++){if(0===e&&0===t)continue;let r=n+e,a=o+t;(a<0||a>=F)&&(a=a<0?0:F-1,r+=_),r=(r%E+E)%E;const i=a*E+r;G[i]>=0||A[i]<=U||(G[i]=C.length,I.push(i));}}C.push(e);}const D=new Int32Array(b).fill(-1);let P=0;for(const t of C)for(const e of t)D[e]=P++;const z=new Float64Array(7*P),k=new Uint8Array(P);for(let t=0;t<o;t++){const o=x[t],a=S[t]*E;for(let i=0;i<n;i++){const l=D[a+T[i]];if(l<0)continue;const c=u(i,t);if(!(c>U&&c<1/0))continue;const s=1-U/c,h=c*s*o,f=7*l;if(z[f]+=d[0]*s*o,z[f+1]+=d[1]*s*o,z[f+2]+=d[2]*s*o,z[f+3]+=h,z[f+4]+=h*w[t]*y[i],z[f+5]+=h*M[t],z[f+6]+=h*w[t]*m[i],g){const o=(t*n+i)*r;255!==e[o]&&255!==e[o+1]&&255!==e[o+2]||(k[l]=1);}}}function j(t){const e={r:0,g:0,b:0,energy:0,x:0,y:0,z:0};for(const n of t){const t=7*D[n];e.r+=z[t],e.g+=z[t+1],e.b+=z[t+2],e.energy+=z[t+3],e.x+=z[t+4],e.y+=z[t+5],e.z+=z[t+6];}return e}function O(t,e){const{r:n,g:o,b:r,energy:a,x:i,y:l,z:c}=j(t),s=Math.hypot(i,l,c),h=Math.max(n,o,r),f=s/a;return {direction:s>0?[i/s,l/s,c/s]:e,color:[n/h,o/h,r/h],intensity:h,coneAngle:Math.acos(Math.min(1,Math.max(-1,2*f-1)))}}function Y(t){const{x:e,y:n,z:o}=j(t),r=Math.hypot(e,n,o),a=[e/r,n/r,o/r],i=Math.abs(a[1])<.99?[0,1,0]:[1,0,0],l=[i[1]*a[2]-i[2]*a[1],i[2]*a[0]-i[0]*a[2],i[0]*a[1]-i[1]*a[0]],c=Math.hypot(l[0],l[1],l[2]);l[0]/=c,l[1]/=c,l[2]/=c;const s=[a[1]*l[2]-a[2]*l[1],a[2]*l[0]-a[0]*l[2],a[0]*l[1]-a[1]*l[0]],h=[];let f=0,p=0,g=0;for(const e of t){const t=7*D[e],n=z[t+3],o=Math.hypot(z[t+4],z[t+5],z[t+6]),r=z[t+4]/o,a=z[t+5]/o,i=z[t+6]/o,c=r*l[0]+a*l[1]+i*l[2],d=r*s[0]+a*s[1]+i*s[2];h.push({cell:e,e:n,px:c,py:d}),f+=n,p+=n*c,g+=n*d;}p/=f,g/=f;let d=0,u=0,m=0;for(const t of h){const e=t.px-p,n=t.py-g;d+=t.e*e*e,u+=t.e*n*n,m+=t.e*e*n;}const y=.5*Math.atan2(2*m,d-u),M=Math.cos(y),w=Math.sin(y);for(const t of h)t.projection=t.px*M+t.py*w;h.sort((t,e)=>t.projection-e.projection);let x=0,E=0;for(;x<h.length-1&&E+h[x].e<=f/2;)E+=h[x++].e;return x=Math.min(h.length-1,Math.max(1,x)),[h.slice(0,x).map(t=>t.cell),h.slice(x).map(t=>t.cell)]}function N(t,e){const n=[[t]];for(;n.length<h;){const t=n[n.length-1];let e=-1,o=0;for(let n=0;n<t.length;n++){if(t[n].length<2)continue;const{energy:r,x:a,y:i,z:l}=j(t[n]),c=r-Math.hypot(a,i,l);c>o&&(e=n,o=c);}if(e<0)break;const r=t.slice();r.splice(e,1,...Y(t[e])),n.push(r);}return n.map(t=>t.map(t=>O(t,e)))}const W=C.map(t=>j(t).energy),q=C.map((t,e)=>e).sort((t,e)=>W[e]-W[t]),V=Math.sin(s);for(const t of q){if(R.lights.length>=i)break;const e=W[t]/L;if(e<c)break;const n=C[t],o=O(n,[0,1,0]);if(!(o.direction[1]<V)){o.energyFraction=e,o.partitions=N(n,o.direction),R.lights.push(o),R.dominance+=e;for(const t of n)R.cellSelected[t]=1,1===k[D[t]]&&(R.clipped=!0);}}return R}function removeSelectedLights(t,e,n){const{width:o,height:r}=t,{gridWidth:a,gridHeight:i,cellSelected:l,threshold:c}=n,s=t.reader(e),h=t.writer(e);for(let t=0;t<i;t++){const e=Math.ceil(t*r/i),n=Math.ceil((t+1)*r/i);for(let r=0;r<a;r++){if(0===l[t*a+r])continue;const i=Math.ceil(r*o/a),f=Math.ceil((r+1)*o/a);for(let t=e;t<n;t++)for(let e=i;e<f;e++){s(e,t,_rgb);const n=.2126*_rgb.r+.7152*_rgb.g+.0722*_rgb.b;if(!(n>c&&n<1/0))continue;const o=c/n;h(e,t,_rgb.r*o,_rgb.g*o,_rgb.b*o);}}}}function readSource(t){const e=t.image;if(t.isCubeTexture||t.isCompressedTexture||t.isRenderTargetTexture||t.isData3DTexture||t.isDataArrayTexture)throw new Error("THREE.EnvironmentLightExtractor: Only equirectangular data or image textures are supported.");if(null==e)throw new Error("THREE.EnvironmentLightExtractor: The texture has no image data.");let n,o,r,a,i,l;if(void 0!==e.data){if(o=e.width,r=e.height,n=e.data.slice(),i=t.type,l=t.format,l===RGBAFormat)a=4;else {if(l!==RGBFormat)throw new Error("THREE.EnvironmentLightExtractor: Only RGB and RGBA textures are supported.");a=3;}if(i!==FloatType&&i!==HalfFloatType&&i!==UnsignedByteType)throw new Error("THREE.EnvironmentLightExtractor: Only float, half float and unsigned byte textures are supported.")}else o=e.width,r=e.height,n=readImage(e,o,r),a=4,i=UnsignedByteType,l=RGBAFormat;let c=t.colorSpace;c!==SRGBColorSpace&&c!==LinearSRGBColorSpace&&c!==NoColorSpace&&(console.warn("THREE.EnvironmentLightExtractor: Unsupported color space. Texels are read as linear."),c=LinearSRGBColorSpace);const s=c===SRGBColorSpace;let h=null;if(i===UnsignedByteType){h=new Float32Array(256);for(let t=0;t<256;t++)h[t]=s?_color.setRGB(t/255,0,0,SRGBColorSpace).r:t/255;}return {data:n,width:o,height:r,channels:a,encoding:i===FloatType?"float":i===HalfFloatType?"half":"byte",decode:h,reader:function(t){return i===FloatType?(e,n,r)=>{const i=(n*o+e)*a;r.r=t[i],r.g=t[i+1],r.b=t[i+2];}:i===HalfFloatType?(e,n,r)=>{const i=(n*o+e)*a;r.r=DataUtils.fromHalfFloat(t[i]),r.g=DataUtils.fromHalfFloat(t[i+1]),r.b=DataUtils.fromHalfFloat(t[i+2]);}:(e,n,r)=>{const i=(n*o+e)*a;r.r=h[t[i]],r.g=h[t[i+1]],r.b=h[t[i+2]];}},writer:function(t){return i===FloatType?(e,n,r,i,l)=>{const c=(n*o+e)*a;t[c]=r,t[c+1]=i,t[c+2]=l;}:i===HalfFloatType?(e,n,r,i,l)=>{const c=(n*o+e)*a;t[c]=DataUtils.toHalfFloat(r),t[c+1]=DataUtils.toHalfFloat(i),t[c+2]=DataUtils.toHalfFloat(l);}:(e,n,r,i,l)=>{const c=(n*o+e)*a;s&&(_color.setRGB(r,i,l).getRGB(_rgb,SRGBColorSpace),r=_rgb.r,i=_rgb.g,l=_rgb.b),t[c]=Math.round(255*Math.min(1,r)),t[c+1]=Math.round(255*Math.min(1,i)),t[c+2]=Math.round(255*Math.min(1,l));}},createTexture:function(e){const n=new DataTexture(e,o,r,l,i);return n.mapping=t.mapping,n.wrapS=t.wrapS,n.wrapT=t.wrapT,n.magFilter=t.magFilter,n.minFilter=t.minFilter,n.anisotropy=t.anisotropy,n.generateMipmaps=t.generateMipmaps,n.unpackAlignment=t.unpackAlignment,n.colorSpace=t.colorSpace,n.flipY=t.flipY,n.needsUpdate=!0,n}}}function readImage(t,e,n){const o=Math.min(e,4096),r=Math.min(n,4096);let a;"undefined"!=typeof document?(a=document.createElement("canvas"),a.width=o,a.height=r):a=new OffscreenCanvas(o,r);const i=a.getContext("2d",{willReadFrequently:!0}),l=new Uint8Array(e*n*4);for(let a=0;a<n;a+=r)for(let c=0;c<e;c+=o){const s=Math.min(o,e-c),h=Math.min(r,n-a);i.clearRect(0,0,s,h),i.drawImage(t,c,a,s,h,0,0,s,h);const f=i.getImageData(0,0,s,h).data;for(let t=0;t<h;t++)l.set(f.subarray(t*s*4,(t+1)*s*4),4*((a+t)*e+c));}return l}
|
|
4
4
|
|
|
5
|
-
const _direction=new Vector3,_position=new Vector3;class EnvironmentShadowLights extends Group{constructor(t=
|
|
5
|
+
const _direction=new Vector3,_position=new Vector3,getRoot=t=>{let i=t;for(;null!==i.parent;)i=i.parent;return i},isLoaded=t=>{const i=t.image;return null!=i&&(!!Array.isArray(i)||i.width>0&&i.height>0&&(void 0===i.data||i.data.length>0))};class EnvironmentShadowLights extends Group{constructor(t=null,i={}){super(),this.isEnvironmentShadowLights=!0,this.type="EnvironmentShadowLights",this.maxLights=i.maxLights??2,this.threshold=i.threshold??8,this.minEnergy=i.minEnergy??.05,this.minElevation=i.minElevation??-Math.PI/2,this.resolution=i.resolution??512,this.samples=i.samples??4,this.maxShadowMaps=i.maxShadowMaps??4,this.mapSize=i.mapSize??1024,this.relativeOccluderDistance=i.relativeOccluderDistance??.1,this.needsUpdate=!1,this.bounds=new Sphere(new Vector3,10),this.result=null,this.target=new Object3D,this.target.matrixAutoUpdate=!1,this.target.matrixWorldAutoUpdate=!1,this.add(this.target),this.lights=[],this._parts=[],this._source=null,this._sourceVersion=-1,this._residual=null,this._scene=null,this._request=null,this.addEventListener("removed",()=>this._restoreEnvironment()),null!==t&&t.add(this);}updateMatrixWorld(t){super.updateMatrixWorld(t);const i=getRoot(this);i.isScene&&this._syncEnvironment(i);const s=i.isScene?i.environmentRotation:null,e=i.isScene?i.environmentIntensity:1,{center:n,radius:r}=this.bounds;this.target.position.copy(n),this.target.matrix.makeTranslation(n),this.target.matrixWorld.copy(this.target.matrix);const o=2*r/this.mapSize;for(let t=0;t<this.lights.length;t++){const i=this.lights[t],h=this._parts[t];if(_direction.copy(h.direction),null!==s&&_direction.applyEuler(s),_position.copy(_direction).multiplyScalar(2*r).add(n),i.position.copy(_position),i.matrix.makeTranslation(_position),i.matrixWorld.copy(i.matrix),i.intensity=h.intensity*e,i.castShadow){const t=i.shadow.camera;t.left=-r,t.right=r,t.top=r,t.bottom=-r,t.near=r,t.far=3*r,t.updateProjectionMatrix(),i.shadow.normalBias=o;}}}copy(t,i=!0){if(super.copy(t,!1),this.maxLights=t.maxLights,this.threshold=t.threshold,this.minEnergy=t.minEnergy,this.minElevation=t.minElevation,this.resolution=t.resolution,this.samples=t.samples,this.maxShadowMaps=t.maxShadowMaps,this.mapSize=t.mapSize,this.relativeOccluderDistance=t.relativeOccluderDistance,this.bounds.copy(t.bounds),i)for(const i of t.children)i===t.target||t.lights.includes(i)||this.add(i.clone());return this}dispose(){this._restoreEnvironment(),this._clear(),this.removeFromParent();}_syncEnvironment(t){let i=t.environment;if(null!==i&&i===this._residual){if(!this.needsUpdate&&this._source.version===this._sourceVersion)return;i=this._source;}else if(null!==i&&i===this._source&&null!==this._residual&&!this.needsUpdate&&i.version===this._sourceVersion)return t.environment=this._residual,void(this._scene=t);const s=this._request;i===this._source||null!==s&&i===s.texture||this._clear(),null!==i&&isLoaded(i)&&(null===s||s.texture!==i||s.version!==i.version||this.needsUpdate)&&this._extract(t,i);}_extract(t,i){const s={texture:i,version:i.version};this.needsUpdate=!1,this._request=s,EnvironmentLightExtractor.extract(i,{maxLights:this.maxLights,threshold:this.threshold,minEnergy:this.minEnergy,minElevation:this.minElevation,samples:this.samples,resolution:this.resolution}).then(i=>{this._request===s?(this._request=null,this._apply(t,s,i)):i.texture.dispose();},t=>{this._request===s&&console.warn(t.message);});}_apply(t,i,s){const e=this._residual,n=t.environment;this._source=i.texture,this._sourceVersion=i.version,this._residual=s.texture,this.result={lights:s.lights,directionality:s.directionality,dominance:s.dominance,threshold:s.threshold},this._setLights(s.lights),getRoot(this)!==t||n!==this._source&&n!==e||(t.environment=this._residual,this._scene=t),null!==e&&e.dispose(),this.dispatchEvent({type:"change"});}_restoreEnvironment(){const t=this._scene;null!==t&&t.environment===this._residual&&(t.environment=this._source),this._scene=null;}_clear(){this._request=null,null!==this._residual&&this._residual.dispose(),this._source=null,this._sourceVersion=-1,this._residual=null,this._scene=null,null===this.result&&0===this.lights.length||(this.result=null,this._setLights([]),this.dispatchEvent({type:"change"}));}_setLights(t){for(const t of this.lights)this.remove(t),t.dispose();this.lights.length=0,this._parts.length=0;const i=t=>Math.tan(t)*this.relativeOccluderDistance*this.mapSize/2,s=t.map(()=>0);let e=this.maxShadowMaps;for(let i=0;i<t.length&&e>0;i++)s[i]=1,e--;for(let n=0;n<t.length&&e>0;n++){const r=t[n];if(0===s[n]||i(r.coneAngle)<=1)continue;const o=Math.min(Math.min(this.samples,r.partitions.length)-1,e);s[n]+=o,e-=o;}for(let e=0;e<t.length;e++){const n=s[e]>0,r=t[e].partitions[Math.max(1,s[e])-1];for(const t of r){const s=new DirectionalLight(t.color,t.intensity);s.matrixAutoUpdate=!1,s.matrixWorldAutoUpdate=!1,s.target=this.target,n&&(s.castShadow=!0,s.shadow.mapSize.setScalar(this.mapSize),s.shadow.radius=Math.min(8,Math.max(1,i(t.coneAngle)))),this.lights.push(s),this._parts.push(t),this.add(s);}}}}
|
|
6
6
|
|
|
7
7
|
export { EnvironmentLightExtractor, EnvironmentShadowLights };
|