spoint 0.1.653 → 0.1.654

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.
@@ -35,208 +35,10 @@
35
35
  import * as THREE from 'three'
36
36
  import { InstancedMesh2 } from '@three.ez/instanced-mesh'
37
37
  import { createGrassDecal } from '/src/terrain/GrassDecal.js'
38
-
39
- // Streak quad: a thin vertical billboard-ish quad (not a full 3D cylinder -- rain streaks read as a
40
- // 2D motion-blurred line from any practical viewing angle, matching the cheap-shading discipline
41
- // Grass.js documents for its own Lambert-lite material). Built once, shared across all instances.
42
- function makeStreakGeo() {
43
- const w = 0.012, h = 0.55 // half-width negligible, tall thin quad in local Y (falls along -Y)
44
- const pos = new Float32Array([
45
- -w, 0, 0, w, 0, 0, w, -h, 0,
46
- -w, 0, 0, w, -h, 0, -w, -h, 0,
47
- ])
48
- const uv = new Float32Array([0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0])
49
- const g = new THREE.BufferGeometry()
50
- g.setAttribute('position', new THREE.BufferAttribute(pos, 3))
51
- g.setAttribute('uv', new THREE.BufferAttribute(uv, 2))
52
- g.computeBoundingSphere(); g.computeBoundingBox()
53
- return g
54
- }
55
-
56
- // Splash ring: a small flat quad billboard, GPU-expanded+faded in the shader from droplet-recycle time
57
- // (see makeSplashMaterial) rather than real ring geometry -- cheaper than a torus/segmented-ring mesh
58
- // for a sub-half-second cosmetic pulse.
59
- function makeSplashGeo() {
60
- const s = 0.5
61
- const pos = new Float32Array([-s, 0, -s, s, 0, -s, s, 0, s, -s, 0, -s, s, 0, s, -s, 0, s])
62
- const uv = new Float32Array([0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1])
63
- const g = new THREE.BufferGeometry()
64
- g.setAttribute('position', new THREE.BufferAttribute(pos, 3))
65
- g.setAttribute('uv', new THREE.BufferAttribute(uv, 2))
66
- g.computeBoundingSphere(); g.computeBoundingBox()
67
- return g
68
- }
69
-
70
- // Rain streak material: per-instance fall-streak alpha, faces the camera around the vertical axis only
71
- // (billboarded in Y, matching how real rain is seen -- always "hanging" vertically regardless of view
72
- // yaw) via a per-instance yaw baked into the instance matrix at spawn (see _respawnDroplet), refreshed
73
- // only when the camera's OWN yaw changes enough to matter (see update()'s _lastCamYaw gate) rather than
74
- // every frame -- rain streaks are thin enough that sub-degree billboard error is imperceptible.
75
- function makeRainMaterial() {
76
- const material = new THREE.ShaderMaterial({
77
- transparent: true, depthWrite: false, side: THREE.DoubleSide,
78
- uniforms: { uColor: { value: new THREE.Color(0.72, 0.78, 0.86) }, uOpacity: { value: 0.35 } },
79
- vertexShader: `
80
- varying vec2 vUv;
81
- varying float vFade;
82
- void main() {
83
- vUv = uv;
84
- // fade the quad edges (uv.x) so the streak reads as a soft line, not a hard-edged rectangle
85
- vFade = 1.0 - abs(uv.x * 2.0 - 1.0);
86
- vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(position, 1.0);
87
- gl_Position = projectionMatrix * mvPosition;
88
- }
89
- `,
90
- fragmentShader: `
91
- uniform vec3 uColor; uniform float uOpacity;
92
- varying vec2 vUv; varying float vFade;
93
- void main() {
94
- float streak = smoothstep(0.0, 0.15, vUv.y) * smoothstep(1.0, 0.85, vUv.y);
95
- float a = uOpacity * vFade * (0.3 + 0.7 * streak);
96
- if (a < 0.01) discard;
97
- gl_FragColor = vec4(uColor, a);
98
- }
99
- `,
100
- })
101
- material.customProgramCacheKey = () => 'weather-rain-streak'
102
- return material
103
- }
104
-
105
- // Splash material: a soft radial ring that expands + fades over its own per-instance lifetime
106
- // (uSplashTime holds the SHARED clock value at spawn, per-instance via initUniformsPerInstance --
107
- // elapsed = uTime - spawnTime, matching Grass.js's windPhase-style per-instance uniform pattern).
108
- function makeSplashMaterial() {
109
- const material = new THREE.ShaderMaterial({
110
- transparent: true, depthWrite: false, side: THREE.DoubleSide,
111
- uniforms: { uTime: { value: 0 }, uColor: { value: new THREE.Color(0.8, 0.85, 0.92) }, uLifeS: { value: 0.4 } },
112
- // instancedmesh2-instanceindex-undeclared-identifier-vegetation-shader: initUniformsPerInstance's
113
- // (see the call below) windPhase/spawnTime-style texel-fetch injection needs the instanceIndex
114
- // vertex attribute in scope, normally free via THREE's own '#include <batching_pars_vertex>' but
115
- // absent from this hand-written ShaderMaterial -- same class of real live GL compile failure as
116
- // Grass.js/SSAO.js/Vegetation.js's addShadowLOD sites (ERROR 0:83 'instanceIndex' : undeclared
117
- // identifier, caught live via a WebGL2RenderingContext.prototype.compileShader monkeypatch, real
118
- // booted server + weather splash particles streamed in during real gameplay, PORT=8250).
119
- // '#include <instanced_pars_vertex>' declares instanceIndex + getInstancedMatrix(); the raw
120
- // instanceMatrix attribute is a dummy zero-length buffer under InstancedMesh2's always-on
121
- // USE_INSTANCING_INDIRECT mode (the real per-instance matrix lives in matricesTexture), so the
122
- // pre-existing raw instanceMatrix read below was also silently wrong -- fixed by locally shadowing
123
- // it with the real computed matrix.
124
- vertexShader: `
125
- uniform float uTime, uLifeS;
126
- varying vec2 vUv; varying float vAlpha;
127
- #include <instanced_pars_vertex>
128
- void main() {
129
- #ifdef USE_INSTANCING_INDIRECT
130
- mat4 instanceMatrix = getInstancedMatrix();
131
- #endif
132
- vUv = uv;
133
- float age = clamp((uTime - spawnTime) / uLifeS, 0.0, 1.0);
134
- vAlpha = (1.0 - age) * step(0.0, spawnTime);
135
- float scale = mix(0.15, 1.0, age);
136
- vec3 p = position * scale;
137
- vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(p, 1.0);
138
- gl_Position = projectionMatrix * mvPosition;
139
- }
140
- `,
141
- fragmentShader: `
142
- uniform vec3 uColor;
143
- varying vec2 vUv; varying float vAlpha;
144
- void main() {
145
- float d = distance(vUv, vec2(0.5));
146
- float ring = smoothstep(0.5, 0.38, d) - smoothstep(0.38, 0.28, d);
147
- float a = ring * vAlpha * 0.5;
148
- if (a < 0.01) discard;
149
- gl_FragColor = vec4(uColor, a);
150
- }
151
- `,
152
- })
153
- material.customProgramCacheKey = () => 'weather-splash-ring'
154
- return material
155
- }
156
-
157
- // Snow flake quad: a small flat square (not a thin streak like rain -- snow falls slowly enough to read
158
- // as a soft round dot/blob, not a motion-blurred line), billboarded FULLY toward the camera (both yaw
159
- // AND pitch, unlike rain's yaw-only vertical hang) since a flat square only reads correctly face-on.
160
- function makeFlakeGeo() {
161
- const s = 0.05
162
- const pos = new Float32Array([-s, -s, 0, s, -s, 0, s, s, 0, -s, -s, 0, s, s, 0, -s, s, 0])
163
- const uv = new Float32Array([0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1])
164
- const g = new THREE.BufferGeometry()
165
- g.setAttribute('position', new THREE.BufferAttribute(pos, 3))
166
- g.setAttribute('uv', new THREE.BufferAttribute(uv, 2))
167
- g.computeBoundingSphere(); g.computeBoundingBox()
168
- return g
169
- }
170
-
171
- // Snow flake material: soft radial falloff (round dot, not a hard-edged square), full camera-facing
172
- // billboard done via the instance quaternion (set once per frame, see update() -- unlike rain's cheap
173
- // yaw-only refresh, full billboarding is unavoidable for a flat square viewed from any pitch).
174
- function makeSnowMaterial() {
175
- const material = new THREE.ShaderMaterial({
176
- transparent: true, depthWrite: false, side: THREE.DoubleSide,
177
- uniforms: { uColor: { value: new THREE.Color(0.95, 0.97, 1.0) }, uOpacity: { value: 0.8 } },
178
- vertexShader: `
179
- varying vec2 vUv;
180
- void main() {
181
- vUv = uv;
182
- vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(position, 1.0);
183
- gl_Position = projectionMatrix * mvPosition;
184
- }
185
- `,
186
- fragmentShader: `
187
- uniform vec3 uColor; uniform float uOpacity;
188
- varying vec2 vUv;
189
- void main() {
190
- float d = distance(vUv, vec2(0.5));
191
- float a = uOpacity * smoothstep(0.5, 0.15, d);
192
- if (a < 0.01) discard;
193
- gl_FragColor = vec4(uColor, a);
194
- }
195
- `,
196
- })
197
- material.customProgramCacheKey = () => 'weather-snow-flake'
198
- return material
199
- }
200
-
201
- // Far billboard-sheet tier material: identical visual language to the near-tier material it mirrors
202
- // (rain streak or snow flake) but with a distance-based fade baked in (uFadeNear/uFadeFar, per-instance
203
- // distance computed in the vertex shader from view-space Z) so the far sheet's own outer edge (where its
204
- // own box wrap would otherwise produce a visible "wall" of particles popping in/out) fades smoothly
205
- // instead of hard-cutting -- the one extra bit of shader work this cheap tier needs since it deliberately
206
- // has no per-instance CPU-side fade bookkeeping (see the tier-shape comment above: far tier is Y-fall +
207
- // wrap ONLY, no per-particle state beyond position).
208
- function makeFarSheetMaterial(baseColor, opacity, roundDot) {
209
- const material = new THREE.ShaderMaterial({
210
- transparent: true, depthWrite: false, side: THREE.DoubleSide,
211
- uniforms: {
212
- uColor: { value: baseColor.clone() }, uOpacity: { value: opacity },
213
- uFadeNear: { value: 40 }, uFadeFar: { value: 90 },
214
- },
215
- vertexShader: `
216
- varying vec2 vUv; varying float vDist;
217
- void main() {
218
- vUv = uv;
219
- vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(position, 1.0);
220
- vDist = -mvPosition.z;
221
- gl_Position = projectionMatrix * mvPosition;
222
- }
223
- `,
224
- fragmentShader: `
225
- uniform vec3 uColor; uniform float uOpacity, uFadeNear, uFadeFar;
226
- varying vec2 vUv; varying float vDist;
227
- void main() {
228
- float shape = ${roundDot ? 'smoothstep(0.5, 0.15, distance(vUv, vec2(0.5)))' : '(smoothstep(0.0, 0.15, vUv.y) * smoothstep(1.0, 0.85, vUv.y) * (1.0 - abs(vUv.x * 2.0 - 1.0)))'};
229
- float fadeIn = smoothstep(uFadeNear, uFadeNear + 8.0, vDist);
230
- float fadeOut = 1.0 - smoothstep(uFadeFar - 10.0, uFadeFar, vDist);
231
- float a = uOpacity * shape * fadeIn * fadeOut;
232
- if (a < 0.01) discard;
233
- gl_FragColor = vec4(uColor, a);
234
- }
235
- `,
236
- })
237
- material.customProgramCacheKey = () => `weather-far-sheet-${roundDot ? 'snow' : 'rain'}`
238
- return material
239
- }
38
+ import {
39
+ makeStreakGeo, makeSplashGeo, makeRainMaterial, makeSplashMaterial,
40
+ makeFlakeGeo, makeSnowMaterial, makeFarSheetMaterial
41
+ } from './WeatherMaterials.js'
240
42
 
241
43
  const _q = new THREE.Quaternion(), _upY = new THREE.Vector3(0, 1, 0)
242
44
  const _camPos = new THREE.Vector3(), _camQuat = new THREE.Quaternion()
@@ -0,0 +1,208 @@
1
+ // Pure geometry/material factories for Weather.js's rain/snow/splash/far-sheet InstancedMesh2 tiers.
2
+ // No per-instance simulation state -- these build shared geometry buffers and ShaderMaterials once,
3
+ // consumed by createWeather()'s stateful update loop in Weather.js itself.
4
+
5
+ import * as THREE from 'three'
6
+
7
+ // Streak quad: a thin vertical billboard-ish quad (not a full 3D cylinder -- rain streaks read as a
8
+ // 2D motion-blurred line from any practical viewing angle, matching the cheap-shading discipline
9
+ // Grass.js documents for its own Lambert-lite material). Built once, shared across all instances.
10
+ export function makeStreakGeo() {
11
+ const w = 0.012, h = 0.55 // half-width negligible, tall thin quad in local Y (falls along -Y)
12
+ const pos = new Float32Array([
13
+ -w, 0, 0, w, 0, 0, w, -h, 0,
14
+ -w, 0, 0, w, -h, 0, -w, -h, 0,
15
+ ])
16
+ const uv = new Float32Array([0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0])
17
+ const g = new THREE.BufferGeometry()
18
+ g.setAttribute('position', new THREE.BufferAttribute(pos, 3))
19
+ g.setAttribute('uv', new THREE.BufferAttribute(uv, 2))
20
+ g.computeBoundingSphere(); g.computeBoundingBox()
21
+ return g
22
+ }
23
+
24
+ // Splash ring: a small flat quad billboard, GPU-expanded+faded in the shader from droplet-recycle time
25
+ // (see makeSplashMaterial) rather than real ring geometry -- cheaper than a torus/segmented-ring mesh
26
+ // for a sub-half-second cosmetic pulse.
27
+ export function makeSplashGeo() {
28
+ const s = 0.5
29
+ const pos = new Float32Array([-s, 0, -s, s, 0, -s, s, 0, s, -s, 0, -s, s, 0, s, -s, 0, s])
30
+ const uv = new Float32Array([0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1])
31
+ const g = new THREE.BufferGeometry()
32
+ g.setAttribute('position', new THREE.BufferAttribute(pos, 3))
33
+ g.setAttribute('uv', new THREE.BufferAttribute(uv, 2))
34
+ g.computeBoundingSphere(); g.computeBoundingBox()
35
+ return g
36
+ }
37
+
38
+ // Rain streak material: per-instance fall-streak alpha, faces the camera around the vertical axis only
39
+ // (billboarded in Y, matching how real rain is seen -- always "hanging" vertically regardless of view
40
+ // yaw) via a per-instance yaw baked into the instance matrix at spawn (see Weather.js's
41
+ // _respawnDroplet), refreshed only when the camera's OWN yaw changes enough to matter (see update()'s
42
+ // _lastCamYaw gate) rather than every frame -- rain streaks are thin enough that sub-degree billboard
43
+ // error is imperceptible.
44
+ export function makeRainMaterial() {
45
+ const material = new THREE.ShaderMaterial({
46
+ transparent: true, depthWrite: false, side: THREE.DoubleSide,
47
+ uniforms: { uColor: { value: new THREE.Color(0.72, 0.78, 0.86) }, uOpacity: { value: 0.35 } },
48
+ vertexShader: `
49
+ varying vec2 vUv;
50
+ varying float vFade;
51
+ void main() {
52
+ vUv = uv;
53
+ // fade the quad edges (uv.x) so the streak reads as a soft line, not a hard-edged rectangle
54
+ vFade = 1.0 - abs(uv.x * 2.0 - 1.0);
55
+ vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(position, 1.0);
56
+ gl_Position = projectionMatrix * mvPosition;
57
+ }
58
+ `,
59
+ fragmentShader: `
60
+ uniform vec3 uColor; uniform float uOpacity;
61
+ varying vec2 vUv; varying float vFade;
62
+ void main() {
63
+ float streak = smoothstep(0.0, 0.15, vUv.y) * smoothstep(1.0, 0.85, vUv.y);
64
+ float a = uOpacity * vFade * (0.3 + 0.7 * streak);
65
+ if (a < 0.01) discard;
66
+ gl_FragColor = vec4(uColor, a);
67
+ }
68
+ `,
69
+ })
70
+ material.customProgramCacheKey = () => 'weather-rain-streak'
71
+ return material
72
+ }
73
+
74
+ // Splash material: a soft radial ring that expands + fades over its own per-instance lifetime
75
+ // (uSplashTime holds the SHARED clock value at spawn, per-instance via initUniformsPerInstance --
76
+ // elapsed = uTime - spawnTime, matching Grass.js's windPhase-style per-instance uniform pattern).
77
+ export function makeSplashMaterial() {
78
+ const material = new THREE.ShaderMaterial({
79
+ transparent: true, depthWrite: false, side: THREE.DoubleSide,
80
+ uniforms: { uTime: { value: 0 }, uColor: { value: new THREE.Color(0.8, 0.85, 0.92) }, uLifeS: { value: 0.4 } },
81
+ // instancedmesh2-instanceindex-undeclared-identifier-vegetation-shader: initUniformsPerInstance's
82
+ // (see the call below) windPhase/spawnTime-style texel-fetch injection needs the instanceIndex
83
+ // vertex attribute in scope, normally free via THREE's own '#include <batching_pars_vertex>' but
84
+ // absent from this hand-written ShaderMaterial -- same class of real live GL compile failure as
85
+ // Grass.js/SSAO.js/Vegetation.js's addShadowLOD sites (ERROR 0:83 'instanceIndex' : undeclared
86
+ // identifier, caught live via a WebGL2RenderingContext.prototype.compileShader monkeypatch, real
87
+ // booted server + weather splash particles streamed in during real gameplay, PORT=8250).
88
+ // '#include <instanced_pars_vertex>' declares instanceIndex + getInstancedMatrix(); the raw
89
+ // instanceMatrix attribute is a dummy zero-length buffer under InstancedMesh2's always-on
90
+ // USE_INSTANCING_INDIRECT mode (the real per-instance matrix lives in matricesTexture), so the
91
+ // pre-existing raw instanceMatrix read below was also silently wrong -- fixed by locally shadowing
92
+ // it with the real computed matrix.
93
+ vertexShader: `
94
+ uniform float uTime, uLifeS;
95
+ varying vec2 vUv; varying float vAlpha;
96
+ #include <instanced_pars_vertex>
97
+ void main() {
98
+ #ifdef USE_INSTANCING_INDIRECT
99
+ mat4 instanceMatrix = getInstancedMatrix();
100
+ #endif
101
+ vUv = uv;
102
+ float age = clamp((uTime - spawnTime) / uLifeS, 0.0, 1.0);
103
+ vAlpha = (1.0 - age) * step(0.0, spawnTime);
104
+ float scale = mix(0.15, 1.0, age);
105
+ vec3 p = position * scale;
106
+ vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(p, 1.0);
107
+ gl_Position = projectionMatrix * mvPosition;
108
+ }
109
+ `,
110
+ fragmentShader: `
111
+ uniform vec3 uColor;
112
+ varying vec2 vUv; varying float vAlpha;
113
+ void main() {
114
+ float d = distance(vUv, vec2(0.5));
115
+ float ring = smoothstep(0.5, 0.38, d) - smoothstep(0.38, 0.28, d);
116
+ float a = ring * vAlpha * 0.5;
117
+ if (a < 0.01) discard;
118
+ gl_FragColor = vec4(uColor, a);
119
+ }
120
+ `,
121
+ })
122
+ material.customProgramCacheKey = () => 'weather-splash-ring'
123
+ return material
124
+ }
125
+
126
+ // Snow flake quad: a small flat square (not a thin streak like rain -- snow falls slowly enough to read
127
+ // as a soft round dot/blob, not a motion-blurred line), billboarded FULLY toward the camera (both yaw
128
+ // AND pitch, unlike rain's yaw-only vertical hang) since a flat square only reads correctly face-on.
129
+ export function makeFlakeGeo() {
130
+ const s = 0.05
131
+ const pos = new Float32Array([-s, -s, 0, s, -s, 0, s, s, 0, -s, -s, 0, s, s, 0, -s, s, 0])
132
+ const uv = new Float32Array([0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1])
133
+ const g = new THREE.BufferGeometry()
134
+ g.setAttribute('position', new THREE.BufferAttribute(pos, 3))
135
+ g.setAttribute('uv', new THREE.BufferAttribute(uv, 2))
136
+ g.computeBoundingSphere(); g.computeBoundingBox()
137
+ return g
138
+ }
139
+
140
+ // Snow flake material: soft radial falloff (round dot, not a hard-edged square), full camera-facing
141
+ // billboard done via the instance quaternion (set once per frame, see Weather.js's update() -- unlike
142
+ // rain's cheap yaw-only refresh, full billboarding is unavoidable for a flat square viewed from any pitch).
143
+ export function makeSnowMaterial() {
144
+ const material = new THREE.ShaderMaterial({
145
+ transparent: true, depthWrite: false, side: THREE.DoubleSide,
146
+ uniforms: { uColor: { value: new THREE.Color(0.95, 0.97, 1.0) }, uOpacity: { value: 0.8 } },
147
+ vertexShader: `
148
+ varying vec2 vUv;
149
+ void main() {
150
+ vUv = uv;
151
+ vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(position, 1.0);
152
+ gl_Position = projectionMatrix * mvPosition;
153
+ }
154
+ `,
155
+ fragmentShader: `
156
+ uniform vec3 uColor; uniform float uOpacity;
157
+ varying vec2 vUv;
158
+ void main() {
159
+ float d = distance(vUv, vec2(0.5));
160
+ float a = uOpacity * smoothstep(0.5, 0.15, d);
161
+ if (a < 0.01) discard;
162
+ gl_FragColor = vec4(uColor, a);
163
+ }
164
+ `,
165
+ })
166
+ material.customProgramCacheKey = () => 'weather-snow-flake'
167
+ return material
168
+ }
169
+
170
+ // Far billboard-sheet tier material: identical visual language to the near-tier material it mirrors
171
+ // (rain streak or snow flake) but with a distance-based fade baked in (uFadeNear/uFadeFar, per-instance
172
+ // distance computed in the vertex shader from view-space Z) so the far sheet's own outer edge (where its
173
+ // own box wrap would otherwise produce a visible "wall" of particles popping in/out) fades smoothly
174
+ // instead of hard-cutting -- the one extra bit of shader work this cheap tier needs since it deliberately
175
+ // has no per-instance CPU-side fade bookkeeping (see Weather.js's tier-shape comment: far tier is
176
+ // Y-fall + wrap ONLY, no per-particle state beyond position).
177
+ export function makeFarSheetMaterial(baseColor, opacity, roundDot) {
178
+ const material = new THREE.ShaderMaterial({
179
+ transparent: true, depthWrite: false, side: THREE.DoubleSide,
180
+ uniforms: {
181
+ uColor: { value: baseColor.clone() }, uOpacity: { value: opacity },
182
+ uFadeNear: { value: 40 }, uFadeFar: { value: 90 },
183
+ },
184
+ vertexShader: `
185
+ varying vec2 vUv; varying float vDist;
186
+ void main() {
187
+ vUv = uv;
188
+ vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(position, 1.0);
189
+ vDist = -mvPosition.z;
190
+ gl_Position = projectionMatrix * mvPosition;
191
+ }
192
+ `,
193
+ fragmentShader: `
194
+ uniform vec3 uColor; uniform float uOpacity, uFadeNear, uFadeFar;
195
+ varying vec2 vUv; varying float vDist;
196
+ void main() {
197
+ float shape = ${roundDot ? 'smoothstep(0.5, 0.15, distance(vUv, vec2(0.5)))' : '(smoothstep(0.0, 0.15, vUv.y) * smoothstep(1.0, 0.85, vUv.y) * (1.0 - abs(vUv.x * 2.0 - 1.0)))'};
198
+ float fadeIn = smoothstep(uFadeNear, uFadeNear + 8.0, vDist);
199
+ float fadeOut = 1.0 - smoothstep(uFadeFar - 10.0, uFadeFar, vDist);
200
+ float a = uOpacity * shape * fadeIn * fadeOut;
201
+ if (a < 0.01) discard;
202
+ gl_FragColor = vec4(uColor, a);
203
+ }
204
+ `,
205
+ })
206
+ material.customProgramCacheKey = () => `weather-far-sheet-${roundDot ? 'snow' : 'rain'}`
207
+ return material
208
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoint",
3
- "version": "0.1.653",
3
+ "version": "0.1.654",
4
4
  "description": "Physics and netcode SDK for multiplayer game servers",
5
5
  "type": "module",
6
6
  "workspaces": [