@jgengine/shell 0.5.0 → 0.7.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 (130) hide show
  1. package/CHANGELOG.md +191 -2
  2. package/README.md +27 -0
  3. package/dist/GamePlayerShell.js +224 -43
  4. package/dist/audio/AudioComponents.d.ts +12 -0
  5. package/dist/audio/AudioComponents.js +53 -0
  6. package/dist/audio/audioEngine.d.ts +23 -0
  7. package/dist/audio/audioEngine.js +110 -0
  8. package/dist/camera/GameCameraRig.d.ts +11 -0
  9. package/dist/camera/GameCameraRig.js +42 -0
  10. package/dist/camera/GameOrbitCamera.d.ts +3 -1
  11. package/dist/camera/GameOrbitCamera.js +4 -2
  12. package/dist/camera/cameraRigs.d.ts +22 -0
  13. package/dist/camera/cameraRigs.js +485 -0
  14. package/dist/camera/index.d.ts +5 -0
  15. package/dist/camera/index.js +5 -0
  16. package/dist/camera/rigMath.d.ts +130 -0
  17. package/dist/camera/rigMath.js +338 -0
  18. package/dist/camera/shakeChannel.d.ts +29 -0
  19. package/dist/camera/shakeChannel.js +38 -0
  20. package/dist/demo/builderDemo.d.ts +2 -0
  21. package/dist/demo/builderDemo.js +189 -0
  22. package/dist/demo/demoGame.js +10 -2
  23. package/dist/demo/environmentShowcase.d.ts +2 -0
  24. package/dist/demo/environmentShowcase.js +15 -0
  25. package/dist/demo/mapDemo.d.ts +2 -0
  26. package/dist/demo/mapDemo.js +206 -0
  27. package/dist/demo/pointerDemo.d.ts +2 -0
  28. package/dist/demo/pointerDemo.js +151 -0
  29. package/dist/demo/sensorShowcase.d.ts +2 -0
  30. package/dist/demo/sensorShowcase.js +131 -0
  31. package/dist/demo/survivalDemo.d.ts +2 -0
  32. package/dist/demo/survivalDemo.js +291 -0
  33. package/dist/environment/EnvironmentScene.d.ts +5 -0
  34. package/dist/environment/EnvironmentScene.js +75 -0
  35. package/dist/environment/index.d.ts +1 -0
  36. package/dist/environment/index.js +1 -0
  37. package/dist/environment.d.ts +1 -0
  38. package/dist/environment.js +1 -0
  39. package/dist/map/MapMarkerBeacons.d.ts +12 -0
  40. package/dist/map/MapMarkerBeacons.js +16 -0
  41. package/dist/map/index.d.ts +2 -0
  42. package/dist/map/index.js +2 -0
  43. package/dist/map/terrainMap.d.ts +23 -0
  44. package/dist/map/terrainMap.js +79 -0
  45. package/dist/pointer/PointerOverlays.d.ts +12 -0
  46. package/dist/pointer/PointerOverlays.js +17 -0
  47. package/dist/pointer/PointerProbe.d.ts +4 -0
  48. package/dist/pointer/PointerProbe.js +27 -0
  49. package/dist/pointer/pointerService.d.ts +24 -0
  50. package/dist/pointer/pointerService.js +68 -0
  51. package/dist/registry.d.ts +4 -2
  52. package/dist/replay/useSessionRecorder.d.ts +14 -0
  53. package/dist/replay/useSessionRecorder.js +22 -0
  54. package/dist/structures/GeneratedBuilding.d.ts +44 -0
  55. package/dist/structures/GeneratedBuilding.js +86 -0
  56. package/dist/structures/PlacementGhost.d.ts +8 -0
  57. package/dist/structures/PlacementGhost.js +11 -0
  58. package/dist/structures/index.d.ts +2 -0
  59. package/dist/structures/index.js +2 -0
  60. package/dist/structures.d.ts +1 -0
  61. package/dist/structures.js +1 -0
  62. package/dist/terrain/CarvedTerrain.d.ts +22 -0
  63. package/dist/terrain/CarvedTerrain.js +19 -0
  64. package/dist/terrain/EditableGround.d.ts +11 -0
  65. package/dist/terrain/EditableGround.js +38 -0
  66. package/dist/terrain/GrassField.d.ts +21 -0
  67. package/dist/terrain/GrassField.js +31 -0
  68. package/dist/terrain/ProceduralGround.d.ts +9 -0
  69. package/dist/terrain/ProceduralGround.js +17 -0
  70. package/dist/terrain/TerraformBrushCursor.d.ts +8 -0
  71. package/dist/terrain/TerraformBrushCursor.js +13 -0
  72. package/dist/terrain/grassGeometry.d.ts +27 -0
  73. package/dist/terrain/grassGeometry.js +77 -0
  74. package/dist/terrain/grassMaterial.d.ts +33 -0
  75. package/dist/terrain/grassMaterial.js +108 -0
  76. package/dist/terrain/index.d.ts +10 -0
  77. package/dist/terrain/index.js +10 -0
  78. package/dist/terrain/random.d.ts +4 -0
  79. package/dist/terrain/random.js +27 -0
  80. package/dist/terrain/terrainMath.d.ts +47 -0
  81. package/dist/terrain/terrainMath.js +172 -0
  82. package/dist/terrain.d.ts +1 -0
  83. package/dist/terrain.js +1 -0
  84. package/dist/vision/FrustumSensorHud.d.ts +21 -0
  85. package/dist/vision/FrustumSensorHud.js +67 -0
  86. package/dist/vision/HiddenStateProbeHud.d.ts +11 -0
  87. package/dist/vision/HiddenStateProbeHud.js +11 -0
  88. package/dist/vision/RevealVision.d.ts +28 -0
  89. package/dist/vision/RevealVision.js +53 -0
  90. package/dist/water/Ocean.d.ts +6 -0
  91. package/dist/water/Ocean.js +30 -0
  92. package/dist/water/OceanConfig.d.ts +90 -0
  93. package/dist/water/OceanConfig.js +131 -0
  94. package/dist/water/OceanMaterial.d.ts +54 -0
  95. package/dist/water/OceanMaterial.js +51 -0
  96. package/dist/water/OceanShader.d.ts +2 -0
  97. package/dist/water/OceanShader.js +78 -0
  98. package/dist/water/index.d.ts +3 -0
  99. package/dist/water/index.js +3 -0
  100. package/dist/water.d.ts +1 -0
  101. package/dist/water.js +1 -0
  102. package/dist/weather/FireSpreadLayer.d.ts +14 -0
  103. package/dist/weather/FireSpreadLayer.js +68 -0
  104. package/dist/weather/LightningStrike.d.ts +17 -0
  105. package/dist/weather/LightningStrike.js +130 -0
  106. package/dist/weather/RainField.d.ts +20 -0
  107. package/dist/weather/RainField.js +107 -0
  108. package/dist/weather/SnowField.d.ts +19 -0
  109. package/dist/weather/SnowField.js +108 -0
  110. package/dist/weather/WeatherLayer.d.ts +17 -0
  111. package/dist/weather/WeatherLayer.js +16 -0
  112. package/dist/weather/index.d.ts +6 -0
  113. package/dist/weather/index.js +6 -0
  114. package/dist/weather/weatherGeometry.d.ts +2 -0
  115. package/dist/weather/weatherGeometry.js +14 -0
  116. package/dist/weather/weatherMath.d.ts +7 -0
  117. package/dist/weather/weatherMath.js +30 -0
  118. package/dist/weather/weatherUniforms.d.ts +18 -0
  119. package/dist/weather/weatherUniforms.js +34 -0
  120. package/dist/world/InstancedBodies.d.ts +17 -0
  121. package/dist/world/InstancedBodies.js +84 -0
  122. package/dist/world/InstancedJoints.d.ts +14 -0
  123. package/dist/world/InstancedJoints.js +32 -0
  124. package/dist/world/WorldHud.d.ts +2 -0
  125. package/dist/world/WorldHud.js +70 -12
  126. package/dist/world/WorldItems.d.ts +5 -0
  127. package/dist/world/WorldItems.js +31 -0
  128. package/dist/world/floatTextStyle.d.ts +14 -0
  129. package/dist/world/floatTextStyle.js +39 -0
  130. package/package.json +12 -4
@@ -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,6 @@
1
+ export { FireSpreadLayer, type FireSpreadLayerProps } from "./FireSpreadLayer.js";
2
+ export { LightningStrike, type LightningStrikeProps } from "./LightningStrike.js";
3
+ export { RainField, type RainFieldProps } from "./RainField.js";
4
+ export { SnowField, type SnowFieldProps } from "./SnowField.js";
5
+ export { WeatherLayer, type WeatherLayerMode, type WeatherLayerProps } from "./WeatherLayer.js";
6
+ export { createWeatherUniformSet, WeatherUniformProvider, useWeatherUniformSet, type WeatherUniformOptions, type WeatherUniformSet, type WeatherVector, } from "./weatherUniforms.js";
@@ -0,0 +1,6 @@
1
+ export { FireSpreadLayer } from "./FireSpreadLayer.js";
2
+ export { LightningStrike } from "./LightningStrike.js";
3
+ export { RainField } from "./RainField.js";
4
+ export { SnowField } from "./SnowField.js";
5
+ export { WeatherLayer } from "./WeatherLayer.js";
6
+ 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;
@@ -0,0 +1,14 @@
1
+ import * as THREE from "three";
2
+ import { createWeatherSeedAttributes } from "./weatherMath.js";
3
+ export function createWeatherQuadGeometry(maxCount, seed) {
4
+ const count = Math.max(0, Math.floor(maxCount));
5
+ const geometry = new THREE.InstancedBufferGeometry();
6
+ geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array([-0.5, -0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0, -0.5, 0.5, 0]), 3));
7
+ geometry.setAttribute("uv", new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2));
8
+ geometry.setIndex([0, 1, 2, 0, 2, 3]);
9
+ const attributes = createWeatherSeedAttributes(count, seed);
10
+ geometry.setAttribute("aSpawn", new THREE.InstancedBufferAttribute(attributes.spawn, 3));
11
+ geometry.setAttribute("aDrift", new THREE.InstancedBufferAttribute(attributes.drift, 1));
12
+ geometry.instanceCount = count;
13
+ return geometry;
14
+ }
@@ -0,0 +1,7 @@
1
+ export declare function clampWeatherRatio(value: number): number;
2
+ export declare function resolveWeatherInstanceCount(maxCount: number, density: number): number;
3
+ export interface WeatherSeedAttributes {
4
+ spawn: Float32Array;
5
+ drift: Float32Array;
6
+ }
7
+ export declare function createWeatherSeedAttributes(maxCount: number, seed: number): WeatherSeedAttributes;
@@ -0,0 +1,30 @@
1
+ export function clampWeatherRatio(value) {
2
+ if (!Number.isFinite(value))
3
+ return 0;
4
+ if (value <= 0)
5
+ return 0;
6
+ if (value >= 1)
7
+ return 1;
8
+ return value;
9
+ }
10
+ export function resolveWeatherInstanceCount(maxCount, density) {
11
+ const safeMax = Math.max(0, Math.floor(maxCount));
12
+ return Math.floor(safeMax * clampWeatherRatio(density));
13
+ }
14
+ export function createWeatherSeedAttributes(maxCount, seed) {
15
+ const count = Math.max(0, Math.floor(maxCount));
16
+ const spawn = new Float32Array(count * 3);
17
+ const drift = new Float32Array(count);
18
+ let state = seed >>> 0;
19
+ const next = () => {
20
+ state = (state * 1664525 + 1013904223) >>> 0;
21
+ return state / 4294967296;
22
+ };
23
+ for (let index = 0; index < count; index += 1) {
24
+ spawn[index * 3] = next();
25
+ spawn[index * 3 + 1] = next();
26
+ spawn[index * 3 + 2] = next();
27
+ drift[index] = next();
28
+ }
29
+ return { spawn, drift };
30
+ }
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from "react";
2
+ import * as THREE from "three";
3
+ export type WeatherVector = readonly [number, number, number];
4
+ export interface WeatherUniformSet {
5
+ time: THREE.IUniform<number>;
6
+ wind: THREE.IUniform<THREE.Vector3>;
7
+ lightning: THREE.IUniform<number>;
8
+ }
9
+ export interface WeatherUniformOptions {
10
+ wind?: WeatherVector;
11
+ lightning?: number;
12
+ timeScale?: number;
13
+ }
14
+ export declare function createWeatherUniformSet(options?: WeatherUniformOptions): WeatherUniformSet;
15
+ export declare function WeatherUniformProvider({ children, ...options }: WeatherUniformOptions & {
16
+ children: ReactNode;
17
+ }): import("react").JSX.Element;
18
+ export declare function useWeatherUniformSet(options?: WeatherUniformOptions): WeatherUniformSet;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useMemo } from "react";
3
+ import { useFrame } from "@react-three/fiber";
4
+ import * as THREE from "three";
5
+ const WeatherUniformContext = createContext(null);
6
+ export function createWeatherUniformSet(options = {}) {
7
+ const wind = options.wind ?? [0, 0, 0];
8
+ return {
9
+ time: { value: 0 },
10
+ wind: { value: new THREE.Vector3(wind[0], wind[1], wind[2]) },
11
+ lightning: { value: options.lightning ?? 0 },
12
+ };
13
+ }
14
+ function useUniformTicker(uniforms, options, enabled = true) {
15
+ useFrame((_state, delta) => {
16
+ if (!enabled)
17
+ return;
18
+ const wind = options.wind ?? [0, 0, 0];
19
+ uniforms.time.value += delta * (options.timeScale ?? 1);
20
+ uniforms.wind.value.set(wind[0], wind[1], wind[2]);
21
+ uniforms.lightning.value = options.lightning ?? 0;
22
+ });
23
+ }
24
+ export function WeatherUniformProvider({ children, ...options }) {
25
+ const uniforms = useMemo(() => createWeatherUniformSet(options), []);
26
+ useUniformTicker(uniforms, options);
27
+ return _jsx(WeatherUniformContext.Provider, { value: uniforms, children: children });
28
+ }
29
+ export function useWeatherUniformSet(options = {}) {
30
+ const shared = useContext(WeatherUniformContext);
31
+ const local = useMemo(() => createWeatherUniformSet(options), []);
32
+ useUniformTicker(local, options, shared === null);
33
+ return shared ?? local;
34
+ }
@@ -0,0 +1,17 @@
1
+ import type { PhysicsWorld } from "@jgengine/core/physics/physicsWorld";
2
+ export interface InstancedBodiesProps {
3
+ /** Physics world whose SoA buffers are streamed straight into the instance matrix. */
4
+ world: PhysicsWorld;
5
+ /** When true, color each instance by simulation state (sleeping / awake / contact) instead of its base color. */
6
+ debugTint?: boolean;
7
+ /** Per-body base RGB (length ≥ capacity·3), used when debugTint is off. Defaults to flat gray. */
8
+ baseColors?: Float32Array;
9
+ /** Bump to force a color re-upload after the world is rebuilt (e.g. reset). */
10
+ epoch?: number;
11
+ }
12
+ /**
13
+ * Renders a PhysicsWorld's box bodies as a single InstancedMesh — one draw call per batch.
14
+ * Transforms are written directly into `instanceMatrix.array` each frame (bodies never touch
15
+ * the per-entity React path). Reusable by any game: hand it a physics world and go.
16
+ */
17
+ export declare function InstancedBodies({ world, debugTint, baseColors, epoch }: InstancedBodiesProps): import("react").JSX.Element;