three-environment-shadows 0.0.0-stage → 1.0.0

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.
Files changed (4) hide show
  1. package/README.md +23 -2
  2. package/index.d.ts +112 -0
  3. package/index.js +7 -0
  4. package/package.json +60 -4
package/README.md CHANGED
@@ -1,3 +1,24 @@
1
- # Temporary Holding Version
1
+ # three-environment-shadows
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ```js
4
+ import * as Three from 'three';
5
+ import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js';
6
+ import { EnvironmentLightExtractor, EnvironmentShadowLights } from 'three-environment-shadows';
7
+
8
+ renderer.shadowMap.enabled = true;
9
+
10
+ const hdri = await new UltraHDRLoader().loadAsync('./environment.jpg');
11
+ hdri.mapping = Three.EquirectangularReflectionMapping;
12
+
13
+ const result = await EnvironmentLightExtractor.extract(hdri);
14
+
15
+ scene.background = hdri;
16
+ scene.environment = result.texture;
17
+
18
+ const lights = new EnvironmentShadowLights(result.lights);
19
+ lights.bounds.set(new Three.Vector3(0, 1, 0), 6);
20
+ scene.add(lights);
21
+
22
+ mesh.castShadow = true;
23
+ mesh.receiveShadow = true;
24
+ ```
package/index.d.ts ADDED
@@ -0,0 +1,112 @@
1
+ import {
2
+ Color,
3
+ DataTexture,
4
+ DirectionalLight,
5
+ Group,
6
+ Object3D,
7
+ Sphere,
8
+ Texture,
9
+ Vector3,
10
+ } from 'three';
11
+
12
+ /**
13
+ * One part of an extracted light.
14
+ */
15
+ export interface LightPart {
16
+ /** Unit vector from the scene towards the part, in the space of the environment map. */
17
+ direction: Vector3;
18
+ /** The color, normalized so its largest component is `1`. */
19
+ color: Color;
20
+ /** The irradiance, usable as `DirectionalLight.intensity`. */
21
+ intensity: number;
22
+ /** Half-angle (in radians) of the cone the part emits from. */
23
+ coneAngle: number;
24
+ }
25
+
26
+ /**
27
+ * A light source that was found in an environment map.
28
+ */
29
+ export interface ExtractedLight extends LightPart {
30
+ /** Fraction of the total environment energy emitted by this light. */
31
+ energyFraction: number;
32
+ /**
33
+ * `partitions[ n - 1 ]` divides the light into `n` parts (median cut). The energies of the
34
+ * parts add up to the energy of the light. `partitions[ 0 ]` holds the light as one part.
35
+ */
36
+ partitions: LightPart[][];
37
+ }
38
+
39
+ export interface ExtractionResult {
40
+ /** The extracted lights, strongest first. */
41
+ lights: ExtractedLight[];
42
+ /** `0` if the environment emits equally in all directions, `1` if all light comes from one direction. */
43
+ directionality: number;
44
+ /** Fraction of the total environment energy emitted by the extracted lights. */
45
+ dominance: number;
46
+ /** The radiance (luminance) above which texels are part of a light. */
47
+ threshold: number;
48
+ /** A copy of the environment map without the extracted lights, or `null`. */
49
+ texture: DataTexture | null;
50
+ }
51
+
52
+ export interface ExtractOptions {
53
+ /** The maximum number of lights. Default `2`. */
54
+ maxLights?: number;
55
+ /** Texels brighter than this factor times the mean radiance are part of a light. Default `8`. */
56
+ threshold?: number;
57
+ /** The minimum fraction of the total energy a light must emit. Default `0.05`. */
58
+ minEnergy?: number;
59
+ /** The minimum elevation (in radians) of a light, in the space of the environment map. Default `-Math.PI / 2`. */
60
+ minElevation?: number;
61
+ /** The maximum number of parts per light, see `ExtractedLight.partitions`. Default `4`. */
62
+ samples?: number;
63
+ /** The width of the grid used to group bright texels into regions. Default `512`. */
64
+ resolution?: number;
65
+ /** Whether to create a copy of the environment map without the lights. Default `true`. */
66
+ removeLights?: boolean;
67
+ }
68
+
69
+ /**
70
+ * Finds the dominant light sources of an equirectangular environment map.
71
+ */
72
+ export class EnvironmentLightExtractor {
73
+ static extract(texture: Texture, options?: ExtractOptions): Promise<ExtractionResult>;
74
+ }
75
+
76
+ export interface EnvironmentShadowLightsOptions {
77
+ /** The maximum number of shadow casting lights per source. Default `4`. */
78
+ samples?: number;
79
+ /** The maximum number of shadow maps of all sources. Default `4`. */
80
+ maxShadowMaps?: number;
81
+ /** The shadow map size of each light. Default `1024`. */
82
+ mapSize?: number;
83
+ /** The typical distance between shadow casters and receivers, relative to the bounds radius. Default `0.1`. */
84
+ relativeOccluderDistance?: number;
85
+ }
86
+
87
+ /**
88
+ * Casts shadows from the light sources of an environment map.
89
+ */
90
+ export class EnvironmentShadowLights extends Group {
91
+ constructor(sources?: ExtractedLight[], options?: EnvironmentShadowLightsOptions);
92
+
93
+ readonly isEnvironmentShadowLights: true;
94
+
95
+ samples: number;
96
+ maxShadowMaps: number;
97
+ mapSize: number;
98
+ relativeOccluderDistance: number;
99
+
100
+ /** The area that receives shadows, in world space. */
101
+ bounds: Sphere;
102
+
103
+ /** The target of all lights, at the center of the bounds. */
104
+ readonly target: Object3D;
105
+
106
+ /** The lights. The shadow casting lights come first. */
107
+ readonly lights: DirectionalLight[];
108
+
109
+ setLights(sources: ExtractedLight[]): this;
110
+
111
+ dispose(): void;
112
+ }
package/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import { Color, RGBAFormat, RGBFormat, FloatType, HalfFloatType, UnsignedByteType, SRGBColorSpace, LinearSRGBColorSpace, NoColorSpace, DataUtils, DataTexture, Vector3, Group, Sphere, Object3D, DirectionalLight } from 'three';
2
+
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
+
5
+ const _direction=new Vector3,_position=new Vector3;class EnvironmentShadowLights extends Group{constructor(t=[],i={}){super(),this.isEnvironmentShadowLights=!0,this.type="EnvironmentShadowLights",this.samples=void 0!==i.samples?i.samples:4,this.maxShadowMaps=void 0!==i.maxShadowMaps?i.maxShadowMaps:4,this.mapSize=void 0!==i.mapSize?i.mapSize:1024,this.relativeOccluderDistance=void 0!==i.relativeOccluderDistance?i.relativeOccluderDistance:.1,this.bounds=new Sphere(new Vector3,10),this.target=new Object3D,this.target.matrixAutoUpdate=!1,this.target.matrixWorldAutoUpdate=!1,this.add(this.target),this.lights=[],this._sources=[],this._parts=[],this.setLights(t);}setLights(t){this._removeLights(),this._sources=t.slice();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 a=0;a<t.length&&e>0;a++){const o=t[a];if(0===s[a]||i(o.coneAngle)<=1)continue;const r=Math.min(Math.min(this.samples,o.partitions.length)-1,e);s[a]+=r,e-=r;}for(let e=0;e<t.length;e++){const a=s[e]>0,o=t[e].partitions[Math.max(1,s[e])-1];for(const t of o){const s=new DirectionalLight(t.color,t.intensity);s.matrixAutoUpdate=!1,s.matrixWorldAutoUpdate=!1,s.target=this.target,a&&(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);}}return this}updateMatrixWorld(t){super.updateMatrixWorld(t);let i=this;for(;null!==i.parent;)i=i.parent;const s=i.isScene?i.environmentRotation:null,e=i.isScene?i.environmentIntensity:1,{center:a,radius:o}=this.bounds;this.target.position.copy(a),this.target.matrix.makeTranslation(a),this.target.matrixWorld.copy(this.target.matrix);const r=2*o/this.mapSize;for(let t=0;t<this.lights.length;t++){const i=this.lights[t],n=this._parts[t];if(_direction.copy(n.direction),null!==s&&_direction.applyEuler(s),_position.copy(_direction).multiplyScalar(2*o).add(a),i.position.copy(_position),i.matrix.makeTranslation(_position),i.matrixWorld.copy(i.matrix),i.intensity=n.intensity*e,i.castShadow){const t=i.shadow.camera;t.left=-o,t.right=o,t.top=o,t.bottom=-o,t.near=o,t.far=3*o,t.updateProjectionMatrix(),i.shadow.normalBias=r;}}}copy(t,i=!0){if(super.copy(t,!1),this.samples=t.samples,this.maxShadowMaps=t.maxShadowMaps,this.mapSize=t.mapSize,this.relativeOccluderDistance=t.relativeOccluderDistance,this.bounds.copy(t.bounds),this.setLights(t._sources),i)for(const i of t.children)i===t.target||t.lights.includes(i)||this.add(i.clone());return this}dispose(){this._removeLights();}_removeLights(){for(const t of this.lights)this.remove(t),t.dispose();this.lights.length=0,this._parts.length=0;}}
6
+
7
+ export { EnvironmentLightExtractor, EnvironmentShadowLights };
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "three-environment-shadows",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Use HDR / EXR / UltraHDR environment maps as source of light and shadows in Three.js.",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "import": "./index.js"
12
+ }
13
+ },
14
+ "sideEffects": false,
15
+ "files": [
16
+ "index.js",
17
+ "index.d.ts",
18
+ "README.md"
19
+ ],
20
+ "keywords": [
21
+ "threejs",
22
+ "three",
23
+ "shadows",
24
+ "environment",
25
+ "envmap",
26
+ "hdr",
27
+ "ibl",
28
+ "lighting",
29
+ "webgl",
30
+ "webgpu"
31
+ ],
32
+ "scripts": {
33
+ "build": "rollup --config ./rollup.config.mjs && cd build && npm pack",
34
+ "build:examples": "cd examples && npm run build",
35
+ "dev": "cd examples && npm run dev",
36
+ "format": "prettier --write src test types",
37
+ "test": "vitest run && tsc",
38
+ "test:build": "TEST_BUILD=1 vitest run",
39
+ "release": "npm run build && npm run test:build && cd ./build && npm publish"
40
+ },
41
+ "author": {
42
+ "name": "mjurczyk",
43
+ "url": "https://discourse.threejs.org/u/mjurczyk"
44
+ },
45
+ "license": "MIT",
46
+ "peerDependencies": {
47
+ "three": ">=0.163.0"
48
+ },
49
+ "devDependencies": {
50
+ "@lopatnov/rollup-plugin-uglify": "^2.1.5",
51
+ "@types/three": "^0.187.0",
52
+ "prettier": "^3.9.0",
53
+ "rollup": "^2.79.1",
54
+ "rollup-plugin-copy": "^3.4.0",
55
+ "rollup-plugin-delete": "^2.0.0",
56
+ "rollup-plugin-peer-deps-external": "^2.2.4",
57
+ "terser": "^5.36.0",
58
+ "three": "^0.187.0",
59
+ "typescript": "^5.9.0",
60
+ "vitest": "^5.1.0"
61
+ }
62
+ }