@jgengine/shell 0.4.0 → 0.6.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 (62) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +27 -0
  3. package/dist/GamePlayerShell.js +19 -9
  4. package/dist/demo/demoGame.js +10 -2
  5. package/dist/demo/environmentShowcase.d.ts +2 -0
  6. package/dist/demo/environmentShowcase.js +15 -0
  7. package/dist/environment/EnvironmentScene.d.ts +5 -0
  8. package/dist/environment/EnvironmentScene.js +75 -0
  9. package/dist/environment/index.d.ts +1 -0
  10. package/dist/environment/index.js +1 -0
  11. package/dist/environment.d.ts +1 -0
  12. package/dist/environment.js +1 -0
  13. package/dist/registry.d.ts +4 -2
  14. package/dist/structures/GeneratedBuilding.d.ts +44 -0
  15. package/dist/structures/GeneratedBuilding.js +86 -0
  16. package/dist/terrain/GrassField.d.ts +21 -0
  17. package/dist/terrain/GrassField.js +31 -0
  18. package/dist/terrain/ProceduralGround.d.ts +9 -0
  19. package/dist/terrain/ProceduralGround.js +17 -0
  20. package/dist/terrain/grassGeometry.d.ts +27 -0
  21. package/dist/terrain/grassGeometry.js +77 -0
  22. package/dist/terrain/grassMaterial.d.ts +33 -0
  23. package/dist/terrain/grassMaterial.js +108 -0
  24. package/dist/terrain/index.d.ts +7 -0
  25. package/dist/terrain/index.js +7 -0
  26. package/dist/terrain/random.d.ts +4 -0
  27. package/dist/terrain/random.js +27 -0
  28. package/dist/terrain/terrainMath.d.ts +34 -0
  29. package/dist/terrain/terrainMath.js +92 -0
  30. package/dist/terrain.d.ts +1 -0
  31. package/dist/terrain.js +1 -0
  32. package/dist/water/Ocean.d.ts +6 -0
  33. package/dist/water/Ocean.js +30 -0
  34. package/dist/water/OceanConfig.d.ts +90 -0
  35. package/dist/water/OceanConfig.js +131 -0
  36. package/dist/water/OceanMaterial.d.ts +54 -0
  37. package/dist/water/OceanMaterial.js +51 -0
  38. package/dist/water/OceanShader.d.ts +2 -0
  39. package/dist/water/OceanShader.js +78 -0
  40. package/dist/water/index.d.ts +3 -0
  41. package/dist/water/index.js +3 -0
  42. package/dist/water.d.ts +1 -0
  43. package/dist/water.js +1 -0
  44. package/dist/weather/LightningStrike.d.ts +17 -0
  45. package/dist/weather/LightningStrike.js +130 -0
  46. package/dist/weather/RainField.d.ts +20 -0
  47. package/dist/weather/RainField.js +107 -0
  48. package/dist/weather/SnowField.d.ts +19 -0
  49. package/dist/weather/SnowField.js +108 -0
  50. package/dist/weather/WeatherLayer.d.ts +17 -0
  51. package/dist/weather/WeatherLayer.js +16 -0
  52. package/dist/weather/index.d.ts +5 -0
  53. package/dist/weather/index.js +5 -0
  54. package/dist/weather/weatherGeometry.d.ts +2 -0
  55. package/dist/weather/weatherGeometry.js +14 -0
  56. package/dist/weather/weatherMath.d.ts +7 -0
  57. package/dist/weather/weatherMath.js +30 -0
  58. package/dist/weather/weatherUniforms.d.ts +18 -0
  59. package/dist/weather/weatherUniforms.js +34 -0
  60. package/dist/world/InstancedBodies.d.ts +17 -0
  61. package/dist/world/InstancedBodies.js +84 -0
  62. package/package.json +12 -5
@@ -0,0 +1,54 @@
1
+ import * as THREE from "three";
2
+ import { type ResolvedOceanConfig } from "./OceanConfig.js";
3
+ export interface OceanMaterialUniforms {
4
+ uTime: {
5
+ value: number;
6
+ };
7
+ uWaveDirections: {
8
+ value: THREE.Vector2[];
9
+ };
10
+ uWaveParams: {
11
+ value: THREE.Vector4[];
12
+ };
13
+ uChoppiness: {
14
+ value: number;
15
+ };
16
+ uShallowColor: {
17
+ value: THREE.Color;
18
+ };
19
+ uDeepColor: {
20
+ value: THREE.Color;
21
+ };
22
+ uCrestColor: {
23
+ value: THREE.Color;
24
+ };
25
+ uFoamColor: {
26
+ value: THREE.Color;
27
+ };
28
+ uOpacity: {
29
+ value: number;
30
+ };
31
+ uFresnelStrength: {
32
+ value: number;
33
+ };
34
+ uHorizonBlend: {
35
+ value: number;
36
+ };
37
+ uFoamThreshold: {
38
+ value: number;
39
+ };
40
+ uFoamSoftness: {
41
+ value: number;
42
+ };
43
+ uFoamIntensity: {
44
+ value: number;
45
+ };
46
+ uFoamCoverage: {
47
+ value: number;
48
+ };
49
+ }
50
+ export type OceanShaderMaterial = THREE.ShaderMaterial & {
51
+ uniforms: OceanMaterialUniforms;
52
+ };
53
+ export declare function createOceanMaterial(config: ResolvedOceanConfig): OceanShaderMaterial;
54
+ export declare function syncOceanMaterial(material: OceanShaderMaterial, config: ResolvedOceanConfig, elapsedSeconds: number): void;
@@ -0,0 +1,51 @@
1
+ import * as THREE from "three";
2
+ import { buildOceanWaveUniforms } from "./OceanConfig.js";
3
+ import { oceanFragmentShader, oceanVertexShader } from "./OceanShader.js";
4
+ export function createOceanMaterial(config) {
5
+ const material = new THREE.ShaderMaterial({
6
+ vertexShader: oceanVertexShader,
7
+ fragmentShader: oceanFragmentShader,
8
+ side: THREE.DoubleSide,
9
+ transparent: config.color.opacity < 1,
10
+ depthWrite: config.color.opacity >= 0.98,
11
+ uniforms: {
12
+ uTime: { value: 0 },
13
+ uWaveDirections: { value: [] },
14
+ uWaveParams: { value: [] },
15
+ uChoppiness: { value: config.choppiness },
16
+ uShallowColor: { value: new THREE.Color(config.color.shallow) },
17
+ uDeepColor: { value: new THREE.Color(config.color.deep) },
18
+ uCrestColor: { value: new THREE.Color(config.color.crest) },
19
+ uFoamColor: { value: new THREE.Color(config.color.foam) },
20
+ uOpacity: { value: config.color.opacity },
21
+ uFresnelStrength: { value: config.color.fresnelStrength },
22
+ uHorizonBlend: { value: config.color.horizonBlend },
23
+ uFoamThreshold: { value: config.foam.crestThreshold },
24
+ uFoamSoftness: { value: config.foam.softness },
25
+ uFoamIntensity: { value: config.foam.intensity },
26
+ uFoamCoverage: { value: config.foam.coverage },
27
+ },
28
+ });
29
+ syncOceanMaterial(material, config, 0);
30
+ return material;
31
+ }
32
+ export function syncOceanMaterial(material, config, elapsedSeconds) {
33
+ const waveUniforms = buildOceanWaveUniforms(config);
34
+ material.uniforms.uTime.value = elapsedSeconds;
35
+ material.uniforms.uWaveDirections.value = waveUniforms.directions;
36
+ material.uniforms.uWaveParams.value = waveUniforms.params;
37
+ material.uniforms.uChoppiness.value = config.choppiness;
38
+ material.uniforms.uShallowColor.value.set(config.color.shallow);
39
+ material.uniforms.uDeepColor.value.set(config.color.deep);
40
+ material.uniforms.uCrestColor.value.set(config.color.crest);
41
+ material.uniforms.uFoamColor.value.set(config.color.foam);
42
+ material.uniforms.uOpacity.value = config.color.opacity;
43
+ material.uniforms.uFresnelStrength.value = config.color.fresnelStrength;
44
+ material.uniforms.uHorizonBlend.value = config.color.horizonBlend;
45
+ material.uniforms.uFoamThreshold.value = config.foam.crestThreshold;
46
+ material.uniforms.uFoamSoftness.value = config.foam.softness;
47
+ material.uniforms.uFoamIntensity.value = config.foam.intensity;
48
+ material.uniforms.uFoamCoverage.value = config.foam.coverage;
49
+ material.transparent = config.color.opacity < 1;
50
+ material.depthWrite = config.color.opacity >= 0.98;
51
+ }
@@ -0,0 +1,2 @@
1
+ export declare const oceanVertexShader = "\nuniform float uTime;\nuniform vec2 uWaveDirections[6];\nuniform vec4 uWaveParams[6];\nuniform float uChoppiness;\nuniform float uFoamThreshold;\nuniform float uFoamSoftness;\nuniform float uFoamCoverage;\n\nvarying vec3 vWorldPosition;\nvarying vec3 vNormal;\nvarying float vCrest;\nvarying float vWaveHeight;\n\nvoid main() {\n vec3 displaced = position;\n vec3 tangent = vec3(1.0, 0.0, 0.0);\n vec3 bitangent = vec3(0.0, 0.0, 1.0);\n float crest = 0.0;\n\n for (int i = 0; i < 6; i++) {\n vec2 direction = normalize(uWaveDirections[i]);\n vec4 wave = uWaveParams[i];\n float k = wave.x;\n float amplitude = wave.y;\n float steepness = wave.z;\n float omega = wave.w;\n float phase = k * dot(direction, position.xz) - omega * uTime;\n float sine = sin(phase);\n float cosine = cos(phase);\n float horizontal = steepness * amplitude * uChoppiness;\n displaced.x += horizontal * direction.x * cosine;\n displaced.z += horizontal * direction.y * cosine;\n displaced.y += amplitude * sine;\n float common = horizontal * k * sine;\n tangent += vec3(-common * direction.x * direction.x, amplitude * k * direction.x * cosine, -common * direction.x * direction.y);\n bitangent += vec3(-common * direction.x * direction.y, amplitude * k * direction.y * cosine, -common * direction.y * direction.y);\n crest = max(crest, smoothstep(uFoamThreshold, uFoamThreshold + uFoamSoftness, sine * steepness + uFoamCoverage * 0.35));\n }\n\n vNormal = normalize(cross(bitangent, tangent));\n vCrest = crest;\n vWaveHeight = displaced.y;\n vec4 worldPosition = modelMatrix * vec4(displaced, 1.0);\n vWorldPosition = worldPosition.xyz;\n gl_Position = projectionMatrix * viewMatrix * worldPosition;\n}\n";
2
+ export declare const oceanFragmentShader = "\nuniform vec3 uShallowColor;\nuniform vec3 uDeepColor;\nuniform vec3 uCrestColor;\nuniform vec3 uFoamColor;\nuniform float uOpacity;\nuniform float uFresnelStrength;\nuniform float uHorizonBlend;\nuniform float uFoamIntensity;\n\nvarying vec3 vWorldPosition;\nvarying vec3 vNormal;\nvarying float vCrest;\nvarying float vWaveHeight;\n\nvoid main() {\n vec3 normal = normalize(vNormal);\n vec3 viewDirection = normalize(cameraPosition - vWorldPosition);\n float fresnel = pow(1.0 - max(dot(normal, viewDirection), 0.0), 4.5);\n float depthMix = smoothstep(-1.8, 1.6, vWaveHeight);\n vec3 waterColor = mix(uDeepColor, uShallowColor, depthMix);\n waterColor = mix(waterColor, uCrestColor, smoothstep(0.15, 1.2, vWaveHeight) * 0.28);\n waterColor = mix(waterColor, vec3(0.86, 0.95, 1.0), fresnel * uFresnelStrength);\n float horizon = smoothstep(0.0, 1.0, 1.0 - abs(normal.y));\n waterColor = mix(waterColor, uShallowColor, horizon * uHorizonBlend);\n float foam = clamp(vCrest * uFoamIntensity, 0.0, 1.0);\n vec3 color = mix(waterColor, uFoamColor, foam);\n gl_FragColor = vec4(color, uOpacity);\n}\n";
@@ -0,0 +1,78 @@
1
+ export const oceanVertexShader = `
2
+ uniform float uTime;
3
+ uniform vec2 uWaveDirections[6];
4
+ uniform vec4 uWaveParams[6];
5
+ uniform float uChoppiness;
6
+ uniform float uFoamThreshold;
7
+ uniform float uFoamSoftness;
8
+ uniform float uFoamCoverage;
9
+
10
+ varying vec3 vWorldPosition;
11
+ varying vec3 vNormal;
12
+ varying float vCrest;
13
+ varying float vWaveHeight;
14
+
15
+ void main() {
16
+ vec3 displaced = position;
17
+ vec3 tangent = vec3(1.0, 0.0, 0.0);
18
+ vec3 bitangent = vec3(0.0, 0.0, 1.0);
19
+ float crest = 0.0;
20
+
21
+ for (int i = 0; i < 6; i++) {
22
+ vec2 direction = normalize(uWaveDirections[i]);
23
+ vec4 wave = uWaveParams[i];
24
+ float k = wave.x;
25
+ float amplitude = wave.y;
26
+ float steepness = wave.z;
27
+ float omega = wave.w;
28
+ float phase = k * dot(direction, position.xz) - omega * uTime;
29
+ float sine = sin(phase);
30
+ float cosine = cos(phase);
31
+ float horizontal = steepness * amplitude * uChoppiness;
32
+ displaced.x += horizontal * direction.x * cosine;
33
+ displaced.z += horizontal * direction.y * cosine;
34
+ displaced.y += amplitude * sine;
35
+ float common = horizontal * k * sine;
36
+ tangent += vec3(-common * direction.x * direction.x, amplitude * k * direction.x * cosine, -common * direction.x * direction.y);
37
+ bitangent += vec3(-common * direction.x * direction.y, amplitude * k * direction.y * cosine, -common * direction.y * direction.y);
38
+ crest = max(crest, smoothstep(uFoamThreshold, uFoamThreshold + uFoamSoftness, sine * steepness + uFoamCoverage * 0.35));
39
+ }
40
+
41
+ vNormal = normalize(cross(bitangent, tangent));
42
+ vCrest = crest;
43
+ vWaveHeight = displaced.y;
44
+ vec4 worldPosition = modelMatrix * vec4(displaced, 1.0);
45
+ vWorldPosition = worldPosition.xyz;
46
+ gl_Position = projectionMatrix * viewMatrix * worldPosition;
47
+ }
48
+ `;
49
+ export const oceanFragmentShader = `
50
+ uniform vec3 uShallowColor;
51
+ uniform vec3 uDeepColor;
52
+ uniform vec3 uCrestColor;
53
+ uniform vec3 uFoamColor;
54
+ uniform float uOpacity;
55
+ uniform float uFresnelStrength;
56
+ uniform float uHorizonBlend;
57
+ uniform float uFoamIntensity;
58
+
59
+ varying vec3 vWorldPosition;
60
+ varying vec3 vNormal;
61
+ varying float vCrest;
62
+ varying float vWaveHeight;
63
+
64
+ void main() {
65
+ vec3 normal = normalize(vNormal);
66
+ vec3 viewDirection = normalize(cameraPosition - vWorldPosition);
67
+ float fresnel = pow(1.0 - max(dot(normal, viewDirection), 0.0), 4.5);
68
+ float depthMix = smoothstep(-1.8, 1.6, vWaveHeight);
69
+ vec3 waterColor = mix(uDeepColor, uShallowColor, depthMix);
70
+ waterColor = mix(waterColor, uCrestColor, smoothstep(0.15, 1.2, vWaveHeight) * 0.28);
71
+ waterColor = mix(waterColor, vec3(0.86, 0.95, 1.0), fresnel * uFresnelStrength);
72
+ float horizon = smoothstep(0.0, 1.0, 1.0 - abs(normal.y));
73
+ waterColor = mix(waterColor, uShallowColor, horizon * uHorizonBlend);
74
+ float foam = clamp(vCrest * uFoamIntensity, 0.0, 1.0);
75
+ vec3 color = mix(waterColor, uFoamColor, foam);
76
+ gl_FragColor = vec4(color, uOpacity);
77
+ }
78
+ `;
@@ -0,0 +1,3 @@
1
+ export { Ocean, type OceanProps } from "./Ocean.js";
2
+ export { DEFAULT_OCEAN_CONFIG, MAX_OCEAN_WAVES, OCEAN_QUALITY_PRESETS, buildOceanWaveUniforms, createOceanConfig, type OceanColorConfig, type OceanConfig, type OceanDirectionVector, type OceanFoamConfig, type OceanQualityPreset, type OceanWaveConfig, type OceanWaveDirection, type ResolvedOceanColorConfig, type ResolvedOceanConfig, type ResolvedOceanFoamConfig, type ResolvedOceanWaveConfig, } from "./OceanConfig.js";
3
+ export { createOceanMaterial, syncOceanMaterial, type OceanMaterialUniforms, type OceanShaderMaterial, } from "./OceanMaterial.js";
@@ -0,0 +1,3 @@
1
+ export { Ocean } from "./Ocean.js";
2
+ export { DEFAULT_OCEAN_CONFIG, MAX_OCEAN_WAVES, OCEAN_QUALITY_PRESETS, buildOceanWaveUniforms, createOceanConfig, } from "./OceanConfig.js";
3
+ export { createOceanMaterial, syncOceanMaterial, } from "./OceanMaterial.js";
@@ -0,0 +1 @@
1
+ export * from "./water/index.js";
package/dist/water.js ADDED
@@ -0,0 +1 @@
1
+ export * from "./water/index.js";
@@ -0,0 +1,17 @@
1
+ import * as THREE from "three";
2
+ import type { WeatherVector } from "./weatherUniforms.js";
3
+ export interface LightningStrikeProps {
4
+ origin: WeatherVector;
5
+ target: WeatherVector;
6
+ strikeKey?: string | number;
7
+ seed?: number;
8
+ visible?: boolean;
9
+ duration?: number;
10
+ color?: THREE.ColorRepresentation;
11
+ glow?: number;
12
+ branches?: number;
13
+ jaggedness?: number;
14
+ impactLight?: number;
15
+ renderOrder?: number;
16
+ }
17
+ export declare function LightningStrike({ origin, target, strikeKey, seed, visible, duration, color, glow, branches, jaggedness, impactLight, renderOrder, }: LightningStrikeProps): import("react").JSX.Element;
@@ -0,0 +1,130 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef } from "react";
3
+ import { useFrame } from "@react-three/fiber";
4
+ import * as THREE from "three";
5
+ const DEFAULT_COLOR = "#bdd4ff";
6
+ function createRandom(seed) {
7
+ let state = seed >>> 0;
8
+ return () => {
9
+ state = (state * 1664525 + 1013904223) >>> 0;
10
+ return state / 4294967296;
11
+ };
12
+ }
13
+ function hashStrikeKey(value) {
14
+ const text = String(value);
15
+ let hash = 2166136261;
16
+ for (let index = 0; index < text.length; index += 1) {
17
+ hash ^= text.charCodeAt(index);
18
+ hash = Math.imul(hash, 16777619);
19
+ }
20
+ return hash >>> 0;
21
+ }
22
+ function vectorFromTuple(value) {
23
+ return new THREE.Vector3(value[0], value[1], value[2]);
24
+ }
25
+ function perpendicularVector(direction, random) {
26
+ const axis = Math.abs(direction.y) > 0.82 ? new THREE.Vector3(1, 0, 0) : new THREE.Vector3(0, 1, 0);
27
+ return new THREE.Vector3().crossVectors(direction, axis).normalize().applyAxisAngle(direction, random() * Math.PI * 2);
28
+ }
29
+ function createLightningSegments(origin, target, seed, branches, jaggedness) {
30
+ const random = createRandom(seed);
31
+ const start = vectorFromTuple(origin);
32
+ const end = vectorFromTuple(target);
33
+ const direction = new THREE.Vector3().subVectors(end, start).normalize();
34
+ const length = start.distanceTo(end);
35
+ const points = [];
36
+ const pointCount = 14;
37
+ for (let index = 0; index < pointCount; index += 1) {
38
+ const ratio = index / (pointCount - 1);
39
+ const point = start.clone().lerp(end, ratio);
40
+ if (index > 0 && index < pointCount - 1) {
41
+ point.addScaledVector(perpendicularVector(direction, random), (random() - 0.5) * jaggedness * length);
42
+ }
43
+ points.push(point);
44
+ }
45
+ const segments = [];
46
+ for (let index = 0; index < points.length - 1; index += 1) {
47
+ segments.push({ from: points[index], to: points[index + 1] });
48
+ }
49
+ const branchCount = Math.max(0, Math.floor(branches));
50
+ for (let index = 0; index < branchCount; index += 1) {
51
+ const sourceIndex = 2 + Math.floor(random() * Math.max(1, points.length - 5));
52
+ const source = points[sourceIndex];
53
+ const branchDirection = direction
54
+ .clone()
55
+ .addScaledVector(perpendicularVector(direction, random), 0.75 + random() * 0.8)
56
+ .normalize();
57
+ const branchLength = length * (0.1 + random() * 0.22);
58
+ const middle = source.clone().addScaledVector(branchDirection, branchLength * 0.48);
59
+ const tip = source.clone().addScaledVector(branchDirection, branchLength);
60
+ middle.addScaledVector(perpendicularVector(branchDirection, random), (random() - 0.5) * jaggedness * length * 0.35);
61
+ segments.push({ from: source.clone(), to: middle });
62
+ segments.push({ from: middle, to: tip });
63
+ }
64
+ return segments;
65
+ }
66
+ function writeLightningGeometry(geometry, segments) {
67
+ const positions = new Float32Array(segments.length * 6);
68
+ for (let index = 0; index < segments.length; index += 1) {
69
+ const segment = segments[index];
70
+ positions[index * 6] = segment.from.x;
71
+ positions[index * 6 + 1] = segment.from.y;
72
+ positions[index * 6 + 2] = segment.from.z;
73
+ positions[index * 6 + 3] = segment.to.x;
74
+ positions[index * 6 + 4] = segment.to.y;
75
+ positions[index * 6 + 5] = segment.to.z;
76
+ }
77
+ geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
78
+ geometry.computeBoundingSphere();
79
+ }
80
+ export function LightningStrike({ origin, target, strikeKey = 0, seed = 451, visible = true, duration = 0.18, color = DEFAULT_COLOR, glow = 2.4, branches = 5, jaggedness = 0.08, impactLight = 26, renderOrder = 20, }) {
81
+ const lifeRef = useRef(0);
82
+ const lightRef = useRef(null);
83
+ const geometry = useMemo(() => new THREE.BufferGeometry(), []);
84
+ const material = useMemo(() => new THREE.LineBasicMaterial({
85
+ color,
86
+ transparent: true,
87
+ opacity: 0,
88
+ blending: THREE.AdditiveBlending,
89
+ depthWrite: false,
90
+ }), [color]);
91
+ const line = useMemo(() => {
92
+ const next = new THREE.LineSegments(geometry, material);
93
+ next.frustumCulled = false;
94
+ next.renderOrder = renderOrder;
95
+ return next;
96
+ }, [geometry, material, renderOrder]);
97
+ useEffect(() => {
98
+ material.color.set(color);
99
+ }, [color, material]);
100
+ useEffect(() => {
101
+ if (!visible) {
102
+ lifeRef.current = 0;
103
+ material.opacity = 0;
104
+ if (lightRef.current !== null)
105
+ lightRef.current.intensity = 0;
106
+ return;
107
+ }
108
+ writeLightningGeometry(geometry, createLightningSegments(origin, target, seed ^ hashStrikeKey(strikeKey), branches, jaggedness));
109
+ lifeRef.current = duration;
110
+ }, [branches, duration, geometry, jaggedness, material, origin, seed, strikeKey, target, visible]);
111
+ useFrame((_state, delta) => {
112
+ if (!visible || lifeRef.current <= 0) {
113
+ material.opacity = 0;
114
+ if (lightRef.current !== null)
115
+ lightRef.current.intensity = 0;
116
+ return;
117
+ }
118
+ lifeRef.current = Math.max(0, lifeRef.current - delta);
119
+ const amount = duration <= 0 ? 0 : lifeRef.current / duration;
120
+ const flicker = 0.62 + Math.random() * 0.38;
121
+ material.opacity = amount * glow * flicker;
122
+ if (lightRef.current !== null)
123
+ lightRef.current.intensity = amount * impactLight * flicker;
124
+ });
125
+ useEffect(() => () => {
126
+ geometry.dispose();
127
+ material.dispose();
128
+ }, [geometry, material]);
129
+ return (_jsxs(_Fragment, { children: [_jsx("primitive", { object: line }), _jsx("pointLight", { ref: lightRef, position: [target[0], target[1], target[2]], color: color, intensity: 0, distance: 56, decay: 2 })] }));
130
+ }
@@ -0,0 +1,20 @@
1
+ import * as THREE from "three";
2
+ import { type WeatherVector } from "./weatherUniforms.js";
3
+ export interface RainFieldProps {
4
+ count?: number;
5
+ density?: number;
6
+ volume?: WeatherVector;
7
+ wind?: WeatherVector;
8
+ origin?: WeatherVector;
9
+ followCamera?: boolean;
10
+ speed?: number;
11
+ length?: number;
12
+ width?: number;
13
+ opacity?: number;
14
+ color?: THREE.ColorRepresentation;
15
+ lightning?: number;
16
+ timeScale?: number;
17
+ seed?: number;
18
+ renderOrder?: number;
19
+ }
20
+ export declare function RainField({ count, density, volume, wind, origin, followCamera, speed, length, width, opacity, color, lightning, timeScale, seed, renderOrder, }: RainFieldProps): import("react").JSX.Element;
@@ -0,0 +1,107 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo } from "react";
3
+ import { useFrame, useThree } from "@react-three/fiber";
4
+ import * as THREE from "three";
5
+ import { createWeatherQuadGeometry } from "./weatherGeometry.js";
6
+ import { resolveWeatherInstanceCount } from "./weatherMath.js";
7
+ import { useWeatherUniformSet } from "./weatherUniforms.js";
8
+ const DEFAULT_VOLUME = [56, 42, 56];
9
+ const DEFAULT_ORIGIN = [0, 0, 0];
10
+ const DEFAULT_RAIN_COLOR = "#b8c4d8";
11
+ export function RainField({ count = 8000, density = 0.45, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 22, length = 1.35, width = 0.018, opacity = 0.48, color = DEFAULT_RAIN_COLOR, lightning, timeScale, seed = 11939, renderOrder = 10, }) {
12
+ const { camera } = useThree();
13
+ const shared = useWeatherUniformSet({ wind, lightning, timeScale });
14
+ const geometry = useMemo(() => createWeatherQuadGeometry(count, seed), [count, seed]);
15
+ const material = useMemo(() => {
16
+ const uniforms = {
17
+ uTime: shared.time,
18
+ uWind: shared.wind,
19
+ uLightning: shared.lightning,
20
+ uAnchor: { value: new THREE.Vector3() },
21
+ uVolume: { value: new THREE.Vector3() },
22
+ uSpeed: { value: speed },
23
+ uLength: { value: length },
24
+ uWidth: { value: width },
25
+ uOpacity: { value: opacity },
26
+ uColor: { value: new THREE.Color(color) },
27
+ };
28
+ return new THREE.ShaderMaterial({
29
+ uniforms,
30
+ transparent: true,
31
+ depthWrite: false,
32
+ blending: THREE.NormalBlending,
33
+ vertexShader: `
34
+ uniform float uTime;
35
+ uniform vec3 uWind;
36
+ uniform vec3 uAnchor;
37
+ uniform vec3 uVolume;
38
+ uniform float uSpeed;
39
+ uniform float uLength;
40
+ uniform float uWidth;
41
+
42
+ attribute vec3 aSpawn;
43
+ attribute float aDrift;
44
+
45
+ varying vec2 vUv;
46
+ varying float vDrift;
47
+
48
+ void main() {
49
+ vUv = uv;
50
+ vDrift = aDrift;
51
+
52
+ vec3 origin = uAnchor - vec3(uVolume.x * 0.5, uVolume.y * 0.85, uVolume.z * 0.5);
53
+ float speed = uSpeed * (0.72 + 0.56 * aDrift);
54
+ vec3 velocity = vec3(uWind.x, -speed, uWind.z);
55
+ vec3 local = aSpawn * uVolume + velocity * uTime;
56
+ vec3 worldCenter = mod(local - origin, uVolume) + origin;
57
+ vec3 direction = normalize(velocity);
58
+ vec3 cameraRay = normalize(cameraPosition - worldCenter);
59
+ vec3 sideRaw = cross(direction, cameraRay);
60
+ vec3 side = length(sideRaw) < 0.001 ? vec3(1.0, 0.0, 0.0) : normalize(sideRaw);
61
+ float streak = uLength * (0.68 + 0.64 * aDrift);
62
+ vec3 world = worldCenter + side * position.x * uWidth + direction * position.y * streak;
63
+
64
+ gl_Position = projectionMatrix * viewMatrix * vec4(world, 1.0);
65
+ }
66
+ `,
67
+ fragmentShader: `
68
+ uniform float uOpacity;
69
+ uniform vec3 uColor;
70
+ uniform float uLightning;
71
+
72
+ varying vec2 vUv;
73
+ varying float vDrift;
74
+
75
+ void main() {
76
+ float across = smoothstep(0.0, 0.45, vUv.x) * smoothstep(1.0, 0.55, vUv.x);
77
+ float along = smoothstep(0.0, 0.28, vUv.y) * smoothstep(1.0, 0.58, vUv.y);
78
+ float alpha = across * along * uOpacity * (0.55 + 0.45 * vDrift);
79
+ if (alpha < 0.001) discard;
80
+ vec3 litColor = uColor * (1.0 + uLightning * 2.25);
81
+ gl_FragColor = vec4(litColor, alpha);
82
+ }
83
+ `,
84
+ });
85
+ }, [color, length, opacity, shared, speed, width]);
86
+ useFrame(() => {
87
+ const anchor = followCamera ? camera.position : null;
88
+ const uniforms = material.uniforms;
89
+ const target = uniforms.uAnchor.value;
90
+ if (anchor !== null)
91
+ target.copy(anchor);
92
+ else
93
+ target.set(origin[0], origin[1], origin[2]);
94
+ uniforms.uVolume.value.set(volume[0], volume[1], volume[2]);
95
+ uniforms.uSpeed.value = speed;
96
+ uniforms.uLength.value = length;
97
+ uniforms.uWidth.value = width;
98
+ uniforms.uOpacity.value = opacity;
99
+ uniforms.uColor.value.set(color);
100
+ geometry.instanceCount = resolveWeatherInstanceCount(count, density);
101
+ });
102
+ useEffect(() => () => {
103
+ geometry.dispose();
104
+ material.dispose();
105
+ }, [geometry, material]);
106
+ return _jsx("mesh", { geometry: geometry, material: material, frustumCulled: false, renderOrder: renderOrder });
107
+ }
@@ -0,0 +1,19 @@
1
+ import * as THREE from "three";
2
+ import { type WeatherVector } from "./weatherUniforms.js";
3
+ export interface SnowFieldProps {
4
+ count?: number;
5
+ density?: number;
6
+ volume?: WeatherVector;
7
+ wind?: WeatherVector;
8
+ origin?: WeatherVector;
9
+ followCamera?: boolean;
10
+ speed?: number;
11
+ size?: number;
12
+ sway?: number;
13
+ opacity?: number;
14
+ color?: THREE.ColorRepresentation;
15
+ timeScale?: number;
16
+ seed?: number;
17
+ renderOrder?: number;
18
+ }
19
+ export declare function SnowField({ count, density, volume, wind, origin, followCamera, speed, size, sway, opacity, color, timeScale, seed, renderOrder, }: SnowFieldProps): import("react").JSX.Element;
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo } from "react";
3
+ import { useFrame, useThree } from "@react-three/fiber";
4
+ import * as THREE from "three";
5
+ import { createWeatherQuadGeometry } from "./weatherGeometry.js";
6
+ import { resolveWeatherInstanceCount } from "./weatherMath.js";
7
+ import { useWeatherUniformSet } from "./weatherUniforms.js";
8
+ const DEFAULT_VOLUME = [52, 38, 52];
9
+ const DEFAULT_ORIGIN = [0, 0, 0];
10
+ const DEFAULT_SNOW_COLOR = "#ffffff";
11
+ export function SnowField({ count = 6000, density = 0.5, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 3.2, size = 0.11, sway = 0.62, opacity = 0.86, color = DEFAULT_SNOW_COLOR, timeScale, seed = 72931, renderOrder = 11, }) {
12
+ const { camera } = useThree();
13
+ const shared = useWeatherUniformSet({ wind, timeScale });
14
+ const geometry = useMemo(() => createWeatherQuadGeometry(count, seed), [count, seed]);
15
+ const material = useMemo(() => {
16
+ const uniforms = {
17
+ uTime: shared.time,
18
+ uWind: shared.wind,
19
+ uAnchor: { value: new THREE.Vector3() },
20
+ uVolume: { value: new THREE.Vector3() },
21
+ uSpeed: { value: speed },
22
+ uSize: { value: size },
23
+ uSway: { value: sway },
24
+ uOpacity: { value: opacity },
25
+ uColor: { value: new THREE.Color(color) },
26
+ };
27
+ return new THREE.ShaderMaterial({
28
+ uniforms,
29
+ transparent: true,
30
+ depthWrite: false,
31
+ blending: THREE.NormalBlending,
32
+ vertexShader: `
33
+ uniform float uTime;
34
+ uniform vec3 uWind;
35
+ uniform vec3 uAnchor;
36
+ uniform vec3 uVolume;
37
+ uniform float uSpeed;
38
+ uniform float uSize;
39
+ uniform float uSway;
40
+
41
+ attribute vec3 aSpawn;
42
+ attribute float aDrift;
43
+
44
+ varying vec2 vUv;
45
+ varying float vDrift;
46
+
47
+ void main() {
48
+ vUv = uv;
49
+ vDrift = aDrift;
50
+
51
+ vec3 origin = uAnchor - vec3(uVolume.x * 0.5, uVolume.y * 0.42, uVolume.z * 0.5);
52
+ float phase = aDrift * 6.28318530718;
53
+ float fallSpeed = uSpeed * (0.62 + 0.76 * aDrift);
54
+ vec3 wander = vec3(
55
+ sin(uTime * 0.72 + phase) + sin(uTime * 1.53 + phase * 1.7) * 0.32,
56
+ 0.0,
57
+ cos(uTime * 0.61 + phase) + cos(uTime * 1.27 + phase * 1.3) * 0.32
58
+ ) * uSway * (0.45 + 0.55 * aDrift);
59
+ vec3 local = aSpawn * uVolume + vec3(uWind.x, -fallSpeed, uWind.z) * uTime + wander;
60
+ vec3 worldCenter = mod(local - origin, uVolume) + origin;
61
+ float flakeSize = uSize * (0.48 + 1.08 * aDrift);
62
+ vec3 right = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);
63
+ vec3 up = vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);
64
+ vec3 world = worldCenter + right * position.x * flakeSize + up * position.y * flakeSize;
65
+
66
+ gl_Position = projectionMatrix * viewMatrix * vec4(world, 1.0);
67
+ }
68
+ `,
69
+ fragmentShader: `
70
+ uniform float uOpacity;
71
+ uniform vec3 uColor;
72
+
73
+ varying vec2 vUv;
74
+ varying float vDrift;
75
+
76
+ void main() {
77
+ float distanceFromCenter = length(vUv - 0.5) * 2.0;
78
+ float disc = smoothstep(1.0, 0.12, distanceFromCenter);
79
+ float core = smoothstep(0.54, 0.0, distanceFromCenter) * 0.42;
80
+ float alpha = (disc + core) * uOpacity * (0.56 + 0.44 * vDrift);
81
+ if (alpha < 0.001) discard;
82
+ gl_FragColor = vec4(uColor, alpha);
83
+ }
84
+ `,
85
+ });
86
+ }, [color, opacity, shared, size, speed, sway]);
87
+ useFrame(() => {
88
+ const anchor = followCamera ? camera.position : null;
89
+ const uniforms = material.uniforms;
90
+ const target = uniforms.uAnchor.value;
91
+ if (anchor !== null)
92
+ target.copy(anchor);
93
+ else
94
+ target.set(origin[0], origin[1], origin[2]);
95
+ uniforms.uVolume.value.set(volume[0], volume[1], volume[2]);
96
+ uniforms.uSpeed.value = speed;
97
+ uniforms.uSize.value = size;
98
+ uniforms.uSway.value = sway;
99
+ uniforms.uOpacity.value = opacity;
100
+ uniforms.uColor.value.set(color);
101
+ geometry.instanceCount = resolveWeatherInstanceCount(count, density);
102
+ });
103
+ useEffect(() => () => {
104
+ geometry.dispose();
105
+ material.dispose();
106
+ }, [geometry, material]);
107
+ return _jsx("mesh", { geometry: geometry, material: material, frustumCulled: false, renderOrder: renderOrder });
108
+ }
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from "react";
2
+ import { type RainFieldProps } from "./RainField.js";
3
+ import { type SnowFieldProps } from "./SnowField.js";
4
+ import { type WeatherVector } from "./weatherUniforms.js";
5
+ export type WeatherLayerMode = "clear" | "rain" | "snow" | "mixed";
6
+ export interface WeatherLayerProps {
7
+ mode?: WeatherLayerMode;
8
+ intensity?: number;
9
+ wind?: WeatherVector;
10
+ lightning?: number;
11
+ timeScale?: number;
12
+ rain?: Omit<RainFieldProps, "wind" | "lightning" | "timeScale"> | false;
13
+ snow?: Omit<SnowFieldProps, "wind" | "timeScale"> | false;
14
+ enabled?: boolean;
15
+ children?: ReactNode;
16
+ }
17
+ export declare function WeatherLayer({ mode, intensity, wind, lightning, timeScale, rain, snow, enabled, children, }: WeatherLayerProps): import("react").JSX.Element | null;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { RainField } from "./RainField.js";
3
+ import { SnowField } from "./SnowField.js";
4
+ import { WeatherUniformProvider } from "./weatherUniforms.js";
5
+ function resolveLayerDensity(value, fallback, intensity) {
6
+ return (value ?? fallback) * intensity;
7
+ }
8
+ export function WeatherLayer({ mode = "clear", intensity = 1, wind, lightning, timeScale, rain, snow, enabled = true, children, }) {
9
+ if (!enabled)
10
+ return null;
11
+ const rainProps = rain === false ? null : (rain ?? {});
12
+ const snowProps = snow === false ? null : (snow ?? {});
13
+ const showRain = rainProps !== null && (mode === "rain" || mode === "mixed");
14
+ const showSnow = snowProps !== null && (mode === "snow" || mode === "mixed");
15
+ return (_jsxs(WeatherUniformProvider, { wind: wind, lightning: lightning, timeScale: timeScale, children: [showRain ? (_jsx(RainField, { ...rainProps, density: resolveLayerDensity(rainProps.density, 0.45, intensity) })) : null, showSnow ? (_jsx(SnowField, { ...snowProps, density: resolveLayerDensity(snowProps.density, 0.5, intensity) })) : null, children] }));
16
+ }
@@ -0,0 +1,5 @@
1
+ export { LightningStrike, type LightningStrikeProps } from "./LightningStrike.js";
2
+ export { RainField, type RainFieldProps } from "./RainField.js";
3
+ export { SnowField, type SnowFieldProps } from "./SnowField.js";
4
+ export { WeatherLayer, type WeatherLayerMode, type WeatherLayerProps } from "./WeatherLayer.js";
5
+ export { createWeatherUniformSet, WeatherUniformProvider, useWeatherUniformSet, type WeatherUniformOptions, type WeatherUniformSet, type WeatherVector, } from "./weatherUniforms.js";
@@ -0,0 +1,5 @@
1
+ export { LightningStrike } from "./LightningStrike.js";
2
+ export { RainField } from "./RainField.js";
3
+ export { SnowField } from "./SnowField.js";
4
+ export { WeatherLayer } from "./WeatherLayer.js";
5
+ export { createWeatherUniformSet, WeatherUniformProvider, useWeatherUniformSet, } from "./weatherUniforms.js";
@@ -0,0 +1,2 @@
1
+ import * as THREE from "three";
2
+ export declare function createWeatherQuadGeometry(maxCount: number, seed: number): THREE.InstancedBufferGeometry;