three-base 2.0.169 → 2.0.175

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 (124) hide show
  1. package/FPScontrol.d.ts +55 -30
  2. package/FPScontrol.js +278 -225
  3. package/FPScontrol.ts +177 -127
  4. package/Ground1/Ground1.d.ts +4 -2
  5. package/Ground1/Ground1.js +77 -57
  6. package/Ground1/Ground1.ts +77 -57
  7. package/Mirror.js +0 -4
  8. package/Mirror.ts +0 -4
  9. package/MktMesh/MktMesh.d.ts +8 -3
  10. package/MktMesh/MktMesh.js +43 -14
  11. package/MktMesh/MktMesh.ts +40 -14
  12. package/MktMesh/mktMappingShader.d.ts +6 -5
  13. package/MktMesh/mktMappingShader.js +26 -19
  14. package/MktMesh/mktMappingShader.ts +28 -19
  15. package/MktMesh/mktShader.d.ts +127 -58
  16. package/MktMesh/mktShader.js +192 -117
  17. package/MktMesh/mktShader.ts +251 -127
  18. package/MktMesh/panoSlotRoster.d.ts +2 -2
  19. package/MktMesh/panoSlotRoster.js +9 -7
  20. package/MktMesh/panoSlotRoster.ts +9 -7
  21. package/MktMesh/panoTileStitcher.d.ts +20 -13
  22. package/MktMesh/panoTileStitcher.js +97 -76
  23. package/MktMesh/panoTileStitcher.ts +119 -90
  24. package/MktMesh/types.d.ts +5 -1
  25. package/MktMesh/types.ts +5 -1
  26. package/PipeMesh/PipeJoint.d.ts +22 -0
  27. package/PipeMesh/PipeJoint.js +33 -0
  28. package/PipeMesh/PipeJoint.ts +38 -0
  29. package/PipeMesh/PipeMesh.d.ts +94 -22
  30. package/PipeMesh/PipeMesh.js +192 -53
  31. package/PipeMesh/PipeMesh.ts +223 -76
  32. package/PipeMesh/PipeStraight.d.ts +19 -0
  33. package/PipeMesh/PipeStraight.js +34 -0
  34. package/PipeMesh/PipeStraight.ts +45 -0
  35. package/PipeMesh/axisSnap.d.ts +52 -0
  36. package/PipeMesh/axisSnap.js +52 -0
  37. package/PipeMesh/axisSnap.ts +93 -0
  38. package/PipeMesh/directRoute.d.ts +85 -0
  39. package/PipeMesh/directRoute.js +126 -0
  40. package/PipeMesh/directRoute.ts +190 -0
  41. package/PipeMesh/index.d.ts +6 -0
  42. package/PipeMesh/index.js +6 -0
  43. package/PipeMesh/index.ts +7 -1
  44. package/PipeMesh/pipeMaterial.d.ts +16 -0
  45. package/PipeMesh/pipeMaterial.js +23 -0
  46. package/PipeMesh/pipeMaterial.ts +26 -0
  47. package/ThreeBase.d.ts +2 -49
  48. package/ThreeBase.js +43 -164
  49. package/ThreeBase.ts +1176 -1314
  50. package/instanced/InstancedBatch.d.ts +14 -1
  51. package/instanced/InstancedBatch.js +53 -3
  52. package/instanced/InstancedBatch.ts +62 -3
  53. package/instanced/InstancedChild.d.ts +24 -0
  54. package/instanced/InstancedChild.js +40 -0
  55. package/instanced/InstancedChild.ts +41 -0
  56. package/instanced/InstancedGroup.d.ts +48 -0
  57. package/instanced/InstancedGroup.js +45 -1
  58. package/instanced/InstancedGroup.ts +85 -0
  59. package/package.json +2 -7
  60. package/pass/bloom_pass.js +7 -14
  61. package/pass/bloom_pass.ts +7 -16
  62. package/pass/cover_pass.js +7 -15
  63. package/pass/cover_pass.ts +7 -17
  64. package/pass/debugDepthState.d.ts +16 -8
  65. package/pass/debugDepthState.js +4 -0
  66. package/pass/debugDepthState.ts +27 -9
  67. package/pass/glow_pass.js +19 -110
  68. package/pass/glow_pass.ts +19 -115
  69. package/pass/ssao_pass.d.ts +14 -59
  70. package/pass/ssao_pass.js +66 -171
  71. package/pass/ssao_pass.ts +63 -207
  72. package/pass/stroke_pass.js +13 -16
  73. package/pass/stroke_pass.ts +14 -18
  74. package/shaders/baseDepthVisShader.d.ts +1 -1
  75. package/shaders/baseDepthVisShader.js +1 -1
  76. package/shaders/baseDepthVisShader.ts +1 -1
  77. package/shaders/debugDepthCompareShader.d.ts +2 -2
  78. package/shaders/debugDepthCompareShader.js +2 -2
  79. package/shaders/debugDepthCompareShader.ts +2 -2
  80. package/shaders/debugDepthJudgeShader.d.ts +1 -1
  81. package/shaders/debugDepthJudgeShader.js +1 -1
  82. package/shaders/debugDepthJudgeShader.ts +1 -1
  83. package/PipeMesh/PathGeometry.d.ts +0 -44
  84. package/PipeMesh/PathGeometry.js +0 -307
  85. package/PipeMesh/PathGeometry.ts +0 -343
  86. package/PipeMesh/PathPoint.d.ts +0 -17
  87. package/PipeMesh/PathPoint.js +0 -31
  88. package/PipeMesh/PathPoint.ts +0 -36
  89. package/PipeMesh/PathPointList.d.ts +0 -31
  90. package/PipeMesh/PathPointList.js +0 -209
  91. package/PipeMesh/PathPointList.ts +0 -227
  92. package/PipeMesh/PathTubeGeometry.d.ts +0 -25
  93. package/PipeMesh/PathTubeGeometry.js +0 -130
  94. package/PipeMesh/PathTubeGeometry.ts +0 -146
  95. package/WaterFlow/FloatingDuck.d.ts +0 -88
  96. package/WaterFlow/FloatingDuck.js +0 -307
  97. package/WaterFlow/FloatingDuck.ts +0 -445
  98. package/WaterFlow/GPUFluidSimulation.d.ts +0 -56
  99. package/WaterFlow/GPUFluidSimulation.js +0 -785
  100. package/WaterFlow/GPUFluidSimulation.ts +0 -827
  101. package/WaterFlow/ReservoirSystem.d.ts +0 -106
  102. package/WaterFlow/ReservoirSystem.js +0 -621
  103. package/WaterFlow/ReservoirSystem.ts +0 -754
  104. package/WaterFlow/SPHField.d.ts +0 -35
  105. package/WaterFlow/SPHField.js +0 -204
  106. package/WaterFlow/SPHField.ts +0 -228
  107. package/WaterFlow/ScreenSpaceFluidPass.d.ts +0 -69
  108. package/WaterFlow/ScreenSpaceFluidPass.js +0 -736
  109. package/WaterFlow/ScreenSpaceFluidPass.ts +0 -789
  110. package/WaterFlow/ShallowWaterSurface.d.ts +0 -45
  111. package/WaterFlow/ShallowWaterSurface.js +0 -441
  112. package/WaterFlow/ShallowWaterSurface.ts +0 -510
  113. package/WaterFlow/WaterFlow.d.ts +0 -87
  114. package/WaterFlow/WaterFlow.js +0 -171
  115. package/WaterFlow/WaterFlow.ts +0 -219
  116. package/WaterFlow/types.d.ts +0 -161
  117. package/WaterFlow/types.js +0 -1
  118. package/WaterFlow/types.ts +0 -189
  119. package/WaterFlow/waterFlowPass.d.ts +0 -3
  120. package/WaterFlow/waterFlowPass.js +0 -20
  121. package/WaterFlow/waterFlowPass.ts +0 -40
  122. package/shaders/debugExcludedShader.d.ts +0 -13
  123. package/shaders/debugExcludedShader.js +0 -46
  124. package/shaders/debugExcludedShader.ts +0 -48
@@ -1,621 +0,0 @@
1
- import * as THREE from 'three';
2
- import { ShallowWaterSurface } from './ShallowWaterSurface';
3
- export class ReservoirSystem {
4
- /** 几何口径(全部由构造参数推导,构造后不变)。 */
5
- length;
6
- width;
7
- bottomY;
8
- rimY;
9
- innerBottomY;
10
- maxWaterY;
11
- innerLength;
12
- innerWidth;
13
- surfaceArea;
14
- capacity;
15
- /** 水量与溢流状态(每帧变化)。 */
16
- volume;
17
- overflowRate;
18
- motionOverflowRate;
19
- overflowIntensity;
20
- active;
21
- particleFluidMode;
22
- /** 容器晃动(xz 两个分量复用 Vector2 的 x/y)。 */
23
- motionPreviousPosition;
24
- motionVelocity;
25
- motionAcceleration;
26
- sloshOffset;
27
- sloshVelocity;
28
- motionWaveImpulse;
29
- /** 池体节点与拾取体。 */
30
- renderer;
31
- group;
32
- hitMeshes;
33
- rimFoamMaterial;
34
- rimFoamGroup;
35
- waterMaterial;
36
- waterBody;
37
- /** 浅水波面;非WebGL2 或缺浮点扩展时为 null(此时只保留水体盒)。 */
38
- surface;
39
- overflowMaterial;
40
- overflowGroup;
41
- /** 溢流粒子(GPU points)的缓冲与状态。 */
42
- dropCount;
43
- dropPositions;
44
- dropVelocities;
45
- dropLife;
46
- dropMaxLife;
47
- dropBounces;
48
- dropSizes;
49
- dropAlpha;
50
- dropCursor;
51
- dropAccumulator;
52
- dropMaterial;
53
- spillPoints;
54
- constructor(renderer, { flowTexture, length = 1.72, width = 1.28, bottomY = -0.13, rimY = 1.14, centerX = 1.82, centerZ = 0, waveWidth = 192, waveHeight = 128, } = {}) {
55
- this.renderer = renderer;
56
- this.length = length;
57
- this.width = width;
58
- this.bottomY = bottomY;
59
- this.rimY = rimY;
60
- this.innerBottomY = bottomY + 0.11;
61
- this.maxWaterY = rimY - 0.024;
62
- this.innerLength = length - 0.14;
63
- this.innerWidth = width - 0.14;
64
- this.surfaceArea = this.innerLength * this.innerWidth;
65
- this.capacity = this.surfaceArea * (this.maxWaterY - this.innerBottomY);
66
- this.volume = 0;
67
- this.overflowRate = 0;
68
- this.motionOverflowRate = 0;
69
- this.overflowIntensity = 0;
70
- this.active = false;
71
- this.motionPreviousPosition = new THREE.Vector2(centerX, centerZ);
72
- this.motionVelocity = new THREE.Vector2();
73
- this.motionAcceleration = new THREE.Vector2();
74
- this.sloshOffset = new THREE.Vector2();
75
- this.sloshVelocity = new THREE.Vector2();
76
- this.motionWaveImpulse = new THREE.Vector2();
77
- this.particleFluidMode = false;
78
- this.group = new THREE.Group();
79
- this.group.position.set(centerX, 0, centerZ);
80
- this.hitMeshes = [];
81
- const steel = new THREE.MeshStandardMaterial({ color: 0x71878d, roughness: 0.2, metalness: 0.92 });
82
- const darkSteel = new THREE.MeshStandardMaterial({ color: 0x172a30, roughness: 0.36, metalness: 0.78 });
83
- const glass = new THREE.MeshPhysicalMaterial({
84
- color: 0x9edbe2,
85
- metalness: 0,
86
- roughness: 0.08,
87
- transmission: 0.9,
88
- transparent: true,
89
- opacity: 0.19,
90
- thickness: 0.04,
91
- ior: 1.48,
92
- clearcoat: 1,
93
- clearcoatRoughness: 0.04,
94
- depthWrite: false,
95
- side: THREE.DoubleSide,
96
- });
97
- const wallThickness = 0.035;
98
- const tankHeight = rimY - bottomY;
99
- const frontBackGeometry = new THREE.BoxGeometry(length, tankHeight, wallThickness);
100
- const sideGeometry = new THREE.BoxGeometry(wallThickness, tankHeight, width);
101
- [-1, 1].forEach((side) => {
102
- const wall = new THREE.Mesh(frontBackGeometry, glass);
103
- wall.position.set(0, bottomY + tankHeight * 0.5, side * width * 0.5);
104
- wall.renderOrder = 6;
105
- this.group.add(wall);
106
- this.hitMeshes.push(wall);
107
- });
108
- [-1, 1].forEach((side) => {
109
- const wall = new THREE.Mesh(sideGeometry, glass);
110
- wall.position.set(side * length * 0.5, bottomY + tankHeight * 0.5, 0);
111
- wall.renderOrder = 6;
112
- this.group.add(wall);
113
- this.hitMeshes.push(wall);
114
- });
115
- const base = new THREE.Mesh(new THREE.BoxGeometry(length + 0.08, 0.09, width + 0.08), darkSteel);
116
- base.position.y = bottomY + 0.025;
117
- base.castShadow = true;
118
- base.receiveShadow = true;
119
- this.group.add(base);
120
- this.hitMeshes.push(base);
121
- const rail = 0.045;
122
- const verticalGeometry = new THREE.BoxGeometry(rail, tankHeight + 0.08, rail);
123
- for (const x of [-1, 1]) {
124
- for (const z of [-1, 1]) {
125
- const post = new THREE.Mesh(verticalGeometry, steel);
126
- post.position.set(x * length * 0.5, bottomY + tankHeight * 0.5, z * width * 0.5);
127
- post.castShadow = true;
128
- this.group.add(post);
129
- }
130
- }
131
- const railXGeometry = new THREE.BoxGeometry(length + 0.08, rail, rail);
132
- const railZGeometry = new THREE.BoxGeometry(rail, rail, width + 0.08);
133
- for (const y of [bottomY + 0.035, rimY]) {
134
- for (const z of [-1, 1]) {
135
- const edge = new THREE.Mesh(railXGeometry, steel);
136
- edge.position.set(0, y, z * width * 0.5);
137
- edge.castShadow = true;
138
- this.group.add(edge);
139
- }
140
- for (const x of [-1, 1]) {
141
- const edge = new THREE.Mesh(railZGeometry, steel);
142
- edge.position.set(x * length * 0.5, y, 0);
143
- edge.castShadow = true;
144
- this.group.add(edge);
145
- }
146
- }
147
- this.rimFoamMaterial = new THREE.MeshBasicMaterial({
148
- color: 0xbdeff4,
149
- transparent: true,
150
- opacity: 0,
151
- depthWrite: false,
152
- blending: THREE.AdditiveBlending,
153
- });
154
- this.rimFoamGroup = new THREE.Group();
155
- const foamX = new THREE.BoxGeometry(this.innerLength * 0.96, 0.012, 0.025);
156
- const foamZ = new THREE.BoxGeometry(0.025, 0.012, this.innerWidth * 0.96);
157
- for (const z of [-1, 1]) {
158
- const foam = new THREE.Mesh(foamX, this.rimFoamMaterial);
159
- foam.position.set(0, this.maxWaterY + 0.014, z * this.innerWidth * 0.5);
160
- this.rimFoamGroup.add(foam);
161
- }
162
- for (const x of [-1, 1]) {
163
- const foam = new THREE.Mesh(foamZ, this.rimFoamMaterial);
164
- foam.position.set(x * this.innerLength * 0.5, this.maxWaterY + 0.014, 0);
165
- this.rimFoamGroup.add(foam);
166
- }
167
- this.group.add(this.rimFoamGroup);
168
- this.waterMaterial = new THREE.ShaderMaterial({
169
- uniforms: {
170
- uTime: { value: 0 },
171
- uFlowMap: { value: flowTexture },
172
- uOpacity: { value: 0.42 },
173
- },
174
- vertexShader: /* glsl */ `
175
- varying vec2 vUv;
176
- varying vec3 vWorldPosition;
177
- varying vec3 vWorldNormal;
178
- void main() {
179
- vUv = uv;
180
- vec4 world = modelMatrix * vec4(position, 1.0);
181
- vWorldPosition = world.xyz;
182
- vWorldNormal = normalize(mat3(modelMatrix) * normal);
183
- gl_Position = projectionMatrix * viewMatrix * world;
184
- }
185
- `,
186
- fragmentShader: /* glsl */ `
187
- uniform float uTime;
188
- uniform float uOpacity;
189
- uniform sampler2D uFlowMap;
190
- varying vec2 vUv;
191
- varying vec3 vWorldPosition;
192
- varying vec3 vWorldNormal;
193
- void main() {
194
- vec3 viewDir = normalize(cameraPosition - vWorldPosition);
195
- float fresnel = 0.02 + 0.98 * pow(1.0 - abs(dot(viewDir, normalize(vWorldNormal))), 5.0);
196
- vec3 detail = texture2D(uFlowMap, vUv * vec2(1.6, 2.8) + vec2(0.0, uTime * 0.018)).rgb;
197
- float depthTint = smoothstep(-0.2, 1.2, vWorldPosition.y);
198
- vec3 color = mix(vec3(0.008, 0.12, 0.16), vec3(0.035, 0.34, 0.4), depthTint);
199
- color += detail * 0.035 + vec3(0.2, 0.62, 0.68) * fresnel * 0.42;
200
- gl_FragColor = vec4(color, uOpacity + fresnel * 0.18);
201
- }
202
- `,
203
- transparent: true,
204
- depthWrite: false,
205
- side: THREE.DoubleSide,
206
- });
207
- this.waterBody = new THREE.Mesh(new THREE.BoxGeometry(this.innerLength, 1, this.innerWidth), this.waterMaterial);
208
- this.waterBody.renderOrder = 3;
209
- this.group.add(this.waterBody);
210
- this.surface = renderer.capabilities.isWebGL2 && renderer.extensions.has('EXT_color_buffer_float')
211
- ? new ShallowWaterSurface(renderer, {
212
- width: waveWidth,
213
- height: waveHeight,
214
- basinMinX: centerX - this.innerLength * 0.5,
215
- basinMaxX: centerX + this.innerLength * 0.5,
216
- basinHalfZ: this.innerWidth * 0.5,
217
- basinCenterZ: centerZ,
218
- waterY: this.innerBottomY,
219
- cpuSampling: true,
220
- })
221
- : null;
222
- if (this.surface)
223
- this.surface.mesh.renderOrder = 5;
224
- this.overflowMaterial = new THREE.ShaderMaterial({
225
- uniforms: {
226
- uTime: { value: 0 },
227
- uOverflow: { value: 0 },
228
- uFlowMap: { value: flowTexture },
229
- },
230
- vertexShader: /* glsl */ `
231
- uniform float uTime;
232
- uniform float uOverflow;
233
- varying vec2 vUv;
234
- void main() {
235
- vUv = uv;
236
- vec3 p = position;
237
- float fall = 1.0 - uv.y;
238
- p.x *= mix(0.7, 1.0, uv.y);
239
- p.z += sin(uv.x * 29.0 + uTime * 5.0) * 0.012 * fall * uOverflow;
240
- p.y -= sin(uv.x * 17.0 + uTime * 3.7) * 0.016 * fall;
241
- gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);
242
- }
243
- `,
244
- fragmentShader: /* glsl */ `
245
- uniform float uTime;
246
- uniform float uOverflow;
247
- uniform sampler2D uFlowMap;
248
- varying vec2 vUv;
249
- void main() {
250
- vec2 flowUvA = vec2(vUv.x * 5.7 + uTime * 0.035, vUv.y * 7.5 + uTime * 0.82);
251
- vec2 flowUvB = vec2(vUv.x * 9.3 - uTime * 0.028, vUv.y * 4.2 + uTime * 0.57);
252
- float noiseA = dot(texture2D(uFlowMap, flowUvA).rgb, vec3(0.2, 0.58, 0.22));
253
- float noiseB = dot(texture2D(uFlowMap, flowUvB).rgb, vec3(0.35, 0.5, 0.15));
254
- float noise = mix(noiseA, noiseB, 0.46);
255
- float rivuletField = 0.5 + 0.5 * sin(vUv.x * 71.0 + noise * 9.0 + sin(vUv.y * 13.0) * 2.2);
256
- float rivulets = smoothstep(0.54 - uOverflow * 0.08, 0.86, rivuletField);
257
- float edge = smoothstep(0.0, 0.13, vUv.x) * smoothstep(1.0, 0.87, vUv.x);
258
- float verticalFade = smoothstep(0.0, 0.12, vUv.y);
259
- float breakup = smoothstep(0.27, 0.76, noise + vUv.y * 0.16);
260
- float alpha = edge * verticalFade * rivulets * breakup * (0.2 + uOverflow * 0.62);
261
- if (alpha < 0.025 || uOverflow < 0.01) discard;
262
- vec3 color = mix(vec3(0.045, 0.32, 0.38), vec3(0.58, 0.92, 0.97), noise * 0.3);
263
- gl_FragColor = vec4(color, alpha);
264
- }
265
- `,
266
- transparent: true,
267
- depthWrite: false,
268
- side: THREE.DoubleSide,
269
- });
270
- this.overflowGroup = new THREE.Group();
271
- const sheetHeight = 0.62;
272
- const makeSheet = (sheetWidth, x, z, rotationY = 0) => {
273
- const sheet = new THREE.Mesh(new THREE.PlaneGeometry(sheetWidth, sheetHeight, 48, 24), this.overflowMaterial);
274
- sheet.position.set(x, rimY - sheetHeight * 0.5 - 0.012, z);
275
- sheet.rotation.y = rotationY;
276
- sheet.renderOrder = 7;
277
- this.overflowGroup.add(sheet);
278
- };
279
- makeSheet(length * 0.78, 0, -width * 0.5 - 0.026);
280
- makeSheet(length * 0.78, 0, width * 0.5 + 0.026);
281
- makeSheet(width * 0.72, -length * 0.5 - 0.026, 0, Math.PI * 0.5);
282
- makeSheet(width * 0.72, length * 0.5 + 0.026, 0, Math.PI * 0.5);
283
- this.group.add(this.overflowGroup);
284
- this.dropCount = waveWidth <= 112 ? 650 : 1400;
285
- this.dropPositions = new Float32Array(this.dropCount * 3);
286
- this.dropVelocities = new Float32Array(this.dropCount * 3);
287
- this.dropLife = new Float32Array(this.dropCount);
288
- this.dropMaxLife = new Float32Array(this.dropCount);
289
- this.dropBounces = new Uint8Array(this.dropCount);
290
- this.dropSizes = new Float32Array(this.dropCount);
291
- this.dropAlpha = new Float32Array(this.dropCount);
292
- this.dropCursor = 0;
293
- this.dropAccumulator = 0;
294
- this.dropPositions.fill(-100);
295
- const dropGeometry = new THREE.BufferGeometry();
296
- const positionAttribute = new THREE.BufferAttribute(this.dropPositions, 3);
297
- positionAttribute.setUsage(THREE.DynamicDrawUsage);
298
- const alphaAttribute = new THREE.BufferAttribute(this.dropAlpha, 1);
299
- alphaAttribute.setUsage(THREE.DynamicDrawUsage);
300
- const sizeAttribute = new THREE.BufferAttribute(this.dropSizes, 1);
301
- sizeAttribute.setUsage(THREE.DynamicDrawUsage);
302
- dropGeometry.setAttribute('position', positionAttribute);
303
- dropGeometry.setAttribute('aAlpha', alphaAttribute);
304
- dropGeometry.setAttribute('aSize', sizeAttribute);
305
- this.dropMaterial = new THREE.ShaderMaterial({
306
- uniforms: {
307
- uPixelRatio: { value: Math.min(devicePixelRatio, 2) },
308
- },
309
- vertexShader: /* glsl */ `
310
- attribute float aAlpha;
311
- attribute float aSize;
312
- uniform float uPixelRatio;
313
- varying float vAlpha;
314
- void main() {
315
- vec4 mv = modelViewMatrix * vec4(position, 1.0);
316
- gl_Position = aAlpha > 0.001 ? projectionMatrix * mv : vec4(2.0, 2.0, 2.0, 1.0);
317
- gl_PointSize = min(7.5, aSize * uPixelRatio * (5.0 / max(2.0, -mv.z)));
318
- vAlpha = aAlpha;
319
- }
320
- `,
321
- fragmentShader: /* glsl */ `
322
- varying float vAlpha;
323
- void main() {
324
- float d = length(gl_PointCoord - 0.5);
325
- if (d > 0.5) discard;
326
- float core = smoothstep(0.5, 0.08, d);
327
- float glint = 1.0 - smoothstep(0.015, 0.13, distance(gl_PointCoord, vec2(0.34, 0.3)));
328
- float alpha = core * vAlpha;
329
- gl_FragColor = vec4(vec3(0.42, 0.87, 0.96) + glint * 0.45, alpha);
330
- }
331
- `,
332
- transparent: true,
333
- depthWrite: false,
334
- blending: THREE.AdditiveBlending,
335
- toneMapped: false,
336
- });
337
- this.spillPoints = new THREE.Points(dropGeometry, this.dropMaterial);
338
- this.spillPoints.frustumCulled = false;
339
- this.spillPoints.renderOrder = 8;
340
- this.reset();
341
- this.setActive(false);
342
- }
343
- setActive(active) {
344
- this.active = active;
345
- this.group.visible = active;
346
- if (this.surface)
347
- this.surface.mesh.visible = active && this.volume > 0.0005 && !this.particleFluidMode;
348
- if (active)
349
- this.motionPreviousPosition.set(this.centerX, this.centerZ);
350
- }
351
- setParticleFluidMode(enabled) {
352
- this.particleFluidMode = Boolean(enabled);
353
- this.updateGeometry();
354
- }
355
- moveTo(x, z) {
356
- this.group.position.x = x;
357
- this.group.position.z = z;
358
- this.updateGeometry();
359
- }
360
- reset(fillRatio = 0) {
361
- this.volume = this.capacity * THREE.MathUtils.clamp(fillRatio, 0, 1);
362
- this.overflowRate = 0;
363
- this.motionOverflowRate = 0;
364
- this.overflowIntensity = 0;
365
- this.clearSpillParticles();
366
- this.surface?.clear();
367
- this.motionPreviousPosition.set(this.centerX, this.centerZ);
368
- this.motionVelocity.set(0, 0);
369
- this.motionAcceleration.set(0, 0);
370
- this.sloshOffset.set(0, 0);
371
- this.sloshVelocity.set(0, 0);
372
- this.motionWaveImpulse.set(0, 0);
373
- this.surface?.setContainerMotion(0, 0, 0, 0);
374
- this.updateGeometry();
375
- }
376
- updateContainerMotion(delta) {
377
- const safeDelta = Math.max(delta, 1 / 240);
378
- const currentX = this.centerX;
379
- const currentZ = this.centerZ;
380
- const rawVelocityX = THREE.MathUtils.clamp((currentX - this.motionPreviousPosition.x) / safeDelta, -3.4, 3.4);
381
- const rawVelocityZ = THREE.MathUtils.clamp((currentZ - this.motionPreviousPosition.y) / safeDelta, -3.4, 3.4);
382
- const previousVelocityX = this.motionVelocity.x;
383
- const previousVelocityZ = this.motionVelocity.y;
384
- const velocityResponse = 1 - Math.exp(-safeDelta * 18);
385
- this.motionVelocity.x = THREE.MathUtils.lerp(previousVelocityX, rawVelocityX, velocityResponse);
386
- this.motionVelocity.y = THREE.MathUtils.lerp(previousVelocityZ, rawVelocityZ, velocityResponse);
387
- this.motionAcceleration.x = THREE.MathUtils.clamp((this.motionVelocity.x - previousVelocityX) / safeDelta, -22, 22);
388
- this.motionAcceleration.y = THREE.MathUtils.clamp((this.motionVelocity.y - previousVelocityZ) / safeDelta, -22, 22);
389
- this.motionPreviousPosition.set(currentX, currentZ);
390
- const omega = 3.65;
391
- const damping = 0.28;
392
- const targetX = THREE.MathUtils.clamp(-this.motionAcceleration.x / 9.81 * this.innerLength * 0.5, -0.18, 0.18);
393
- const targetZ = THREE.MathUtils.clamp(-this.motionAcceleration.y / 9.81 * this.innerWidth * 0.5, -0.15, 0.15);
394
- const dampingForce = 2 * damping * omega;
395
- this.sloshVelocity.x += ((targetX - this.sloshOffset.x) * omega * omega - this.sloshVelocity.x * dampingForce) * safeDelta;
396
- this.sloshVelocity.y += ((targetZ - this.sloshOffset.y) * omega * omega - this.sloshVelocity.y * dampingForce) * safeDelta;
397
- this.sloshOffset.addScaledVector(this.sloshVelocity, safeDelta);
398
- this.sloshOffset.x = THREE.MathUtils.clamp(this.sloshOffset.x, -0.16, 0.16);
399
- this.sloshOffset.y = THREE.MathUtils.clamp(this.sloshOffset.y, -0.13, 0.13);
400
- this.motionWaveImpulse.set(-this.motionAcceleration.x * safeDelta * 0.00015, -this.motionAcceleration.y * safeDelta * 0.00015);
401
- if (!this.active || this.volume < 0.0005) {
402
- this.sloshOffset.multiplyScalar(Math.exp(-safeDelta * 5));
403
- this.sloshVelocity.multiplyScalar(Math.exp(-safeDelta * 5));
404
- this.motionWaveImpulse.set(0, 0);
405
- }
406
- this.surface?.setContainerMotion(this.sloshOffset.x, this.sloshOffset.y, this.motionWaveImpulse.x, this.motionWaveImpulse.y);
407
- }
408
- clearSpillParticles() {
409
- if (!this.dropLife)
410
- return;
411
- this.dropLife.fill(0);
412
- this.dropAlpha.fill(0);
413
- this.dropPositions.fill(-100);
414
- this.dropAccumulator = 0;
415
- if (this.spillPoints) {
416
- this.spillPoints.geometry.attributes.position.needsUpdate = true;
417
- this.spillPoints.geometry.attributes.aAlpha.needsUpdate = true;
418
- }
419
- }
420
- spawnSpillParticle() {
421
- let index = -1;
422
- for (let attempt = 0; attempt < this.dropCount; attempt++) {
423
- const candidate = (this.dropCursor + attempt) % this.dropCount;
424
- if (this.dropLife[candidate] <= 0) {
425
- index = candidate;
426
- this.dropCursor = (candidate + 1) % this.dropCount;
427
- break;
428
- }
429
- }
430
- if (index < 0)
431
- return;
432
- let edge = Math.floor(Math.random() * 4);
433
- if (this.motionOverflowRate > 0.002) {
434
- if (Math.abs(this.sloshOffset.x) > Math.abs(this.sloshOffset.y)) {
435
- edge = this.sloshOffset.x >= 0 ? 3 : 2;
436
- }
437
- else {
438
- edge = this.sloshOffset.y >= 0 ? 0 : 1;
439
- }
440
- }
441
- const along = Math.random() - 0.5;
442
- const speedOut = 0.1 + Math.random() * 0.24 + this.overflowIntensity * 0.22;
443
- const velocityOffset = index * 3;
444
- const positionOffset = index * 3;
445
- let x = this.centerX;
446
- let z = this.centerZ;
447
- let vx = (Math.random() - 0.5) * 0.08;
448
- let vz = (Math.random() - 0.5) * 0.08;
449
- if (edge === 0) {
450
- x += along * this.innerLength * 0.92;
451
- z += this.innerWidth * 0.5 + 0.015;
452
- vz += speedOut;
453
- }
454
- else if (edge === 1) {
455
- x += along * this.innerLength * 0.92;
456
- z -= this.innerWidth * 0.5 + 0.015;
457
- vz -= speedOut;
458
- }
459
- else if (edge === 2) {
460
- x -= this.innerLength * 0.5 + 0.015;
461
- z += along * this.innerWidth * 0.9;
462
- vx -= speedOut;
463
- }
464
- else {
465
- x += this.innerLength * 0.5 + 0.015;
466
- z += along * this.innerWidth * 0.9;
467
- vx += speedOut;
468
- }
469
- this.dropPositions[positionOffset] = x;
470
- this.dropPositions[positionOffset + 1] = this.maxWaterY + 0.012 + Math.random() * 0.018;
471
- this.dropPositions[positionOffset + 2] = z;
472
- this.dropVelocities[velocityOffset] = vx;
473
- this.dropVelocities[velocityOffset + 1] = 0.03 + Math.random() * 0.19;
474
- this.dropVelocities[velocityOffset + 2] = vz;
475
- this.dropLife[index] = 1.25 + Math.random() * 0.8;
476
- this.dropMaxLife[index] = this.dropLife[index];
477
- this.dropBounces[index] = 0;
478
- this.dropSizes[index] = 2.2 + Math.random() * 3.3;
479
- this.dropAlpha[index] = 0.75 + Math.random() * 0.25;
480
- }
481
- updateSpillParticles(delta) {
482
- if (this.active && this.overflowRate > 0.0001) {
483
- const spawnRate = Math.min(this.dropCount * 0.86, 140 + this.overflowRate * 2600);
484
- this.dropAccumulator += spawnRate * delta;
485
- const spawnCount = Math.min(48, Math.floor(this.dropAccumulator));
486
- this.dropAccumulator -= spawnCount;
487
- for (let i = 0; i < spawnCount; i++)
488
- this.spawnSpillParticle();
489
- }
490
- const floorY = -0.17;
491
- const drag = Math.exp(-0.18 * delta);
492
- for (let i = 0; i < this.dropCount; i++) {
493
- if (this.dropLife[i] <= 0)
494
- continue;
495
- const offset = i * 3;
496
- this.dropLife[i] -= delta;
497
- this.dropVelocities[offset] *= drag;
498
- this.dropVelocities[offset + 1] = this.dropVelocities[offset + 1] * drag - 9.81 * delta;
499
- this.dropVelocities[offset + 2] *= drag;
500
- this.dropPositions[offset] += this.dropVelocities[offset] * delta;
501
- this.dropPositions[offset + 1] += this.dropVelocities[offset + 1] * delta;
502
- this.dropPositions[offset + 2] += this.dropVelocities[offset + 2] * delta;
503
- if (this.dropPositions[offset + 1] <= floorY && this.dropVelocities[offset + 1] < 0) {
504
- if (this.dropBounces[i] === 0) {
505
- const impact = -this.dropVelocities[offset + 1];
506
- const angle = Math.random() * Math.PI * 2;
507
- const radial = impact * (0.08 + Math.random() * 0.11);
508
- this.dropPositions[offset + 1] = floorY + 0.006;
509
- this.dropVelocities[offset] = Math.cos(angle) * radial;
510
- this.dropVelocities[offset + 1] = impact * (0.17 + Math.random() * 0.12);
511
- this.dropVelocities[offset + 2] = Math.sin(angle) * radial;
512
- this.dropBounces[i] = 1;
513
- this.dropLife[i] = Math.min(this.dropLife[i], 0.48 + Math.random() * 0.18);
514
- this.dropSizes[i] *= 0.72;
515
- }
516
- else {
517
- this.dropLife[i] = 0;
518
- }
519
- }
520
- if (this.dropLife[i] <= 0) {
521
- this.dropAlpha[i] = 0;
522
- this.dropPositions[offset + 1] = -100;
523
- }
524
- else {
525
- const lifeRatio = this.dropLife[i] / Math.max(this.dropMaxLife[i], 0.001);
526
- this.dropAlpha[i] = THREE.MathUtils.clamp(lifeRatio * 1.35, 0, 1);
527
- }
528
- }
529
- this.spillPoints.geometry.attributes.position.needsUpdate = true;
530
- this.spillPoints.geometry.attributes.aAlpha.needsUpdate = true;
531
- this.spillPoints.geometry.attributes.aSize.needsUpdate = true;
532
- }
533
- updateGeometry() {
534
- const waterHeight = Math.max(0.001, this.waterY - this.innerBottomY);
535
- this.waterBody.scale.y = waterHeight;
536
- this.waterBody.position.y = this.innerBottomY + waterHeight * 0.5;
537
- this.waterBody.visible = this.active && this.volume > 0.0005 && !this.particleFluidMode;
538
- if (this.surface) {
539
- this.surface.setBounds({
540
- basinMinX: this.minX,
541
- basinMaxX: this.maxX,
542
- basinHalfZ: this.innerWidth * 0.5,
543
- basinCenterZ: this.centerZ,
544
- waterY: this.waterY,
545
- });
546
- this.surface.mesh.visible = this.active && this.volume > 0.0005 && !this.particleFluidMode;
547
- }
548
- }
549
- update(delta, elapsed, { flowRate, captureFraction = 0, impactX, impactZ = 0, impactEnergy = 0, emitting = false, }) {
550
- this.updateContainerMotion(delta);
551
- const effectiveCapture = this.active && emitting ? THREE.MathUtils.clamp(captureFraction, 0, 1) : 0;
552
- const captured = effectiveCapture > 0.001;
553
- const incomingRate = flowRate * effectiveCapture;
554
- const incomingVolume = incomingRate * delta;
555
- const available = Math.max(0, this.capacity - this.volume);
556
- const stored = Math.min(available, incomingVolume);
557
- const overflowVolume = Math.max(0, incomingVolume - stored);
558
- this.volume = Math.min(this.capacity, this.volume + stored);
559
- const freeboard = Math.max(0, this.maxWaterY - this.waterY);
560
- const crestRise = Math.max(Math.abs(this.sloshOffset.x), Math.abs(this.sloshOffset.y));
561
- const overtoppingHead = Math.max(0, crestRise - freeboard);
562
- this.motionOverflowRate = this.active
563
- ? Math.min(0.38, overtoppingHead * this.surfaceArea * 1.25)
564
- : 0;
565
- const motionOverflowVolume = Math.min(this.volume, this.motionOverflowRate * delta);
566
- this.volume = Math.max(0, this.volume - motionOverflowVolume);
567
- this.overflowRate = (delta > 0 ? overflowVolume / delta : 0) + this.motionOverflowRate;
568
- const targetOverflow = THREE.MathUtils.clamp(this.overflowRate / 0.16, 0, 1);
569
- this.overflowIntensity = THREE.MathUtils.lerp(this.overflowIntensity, targetOverflow, 1 - Math.exp(-delta * (targetOverflow > this.overflowIntensity ? 5.5 : 2.2)));
570
- this.updateGeometry();
571
- this.waterMaterial.uniforms.uTime.value = elapsed;
572
- this.overflowMaterial.uniforms.uTime.value = elapsed;
573
- this.overflowMaterial.uniforms.uOverflow.value = this.overflowIntensity;
574
- this.overflowGroup.visible = this.active && this.overflowIntensity > 0.008;
575
- this.rimFoamMaterial.opacity = this.overflowIntensity * 0.38;
576
- this.rimFoamGroup.visible = this.active && this.overflowIntensity > 0.025;
577
- this.updateSpillParticles(delta);
578
- if (this.active) {
579
- this.surface?.update(delta, elapsed, impactX, impactEnergy, captured && emitting, impactZ);
580
- }
581
- return {
582
- captured,
583
- captureFraction: effectiveCapture,
584
- incomingRate,
585
- missedRate: Math.max(0, flowRate - incomingRate),
586
- overflowRate: this.overflowRate,
587
- };
588
- }
589
- setPixelRatio(pixelRatio) {
590
- this.dropMaterial.uniforms.uPixelRatio.value = Math.min(pixelRatio, 2);
591
- }
592
- get fillRatio() {
593
- return this.capacity > 0 ? this.volume / this.capacity : 0;
594
- }
595
- get waterY() {
596
- return THREE.MathUtils.lerp(this.innerBottomY, this.maxWaterY, this.fillRatio);
597
- }
598
- get centerX() {
599
- return this.group.position.x;
600
- }
601
- get centerZ() {
602
- return this.group.position.z;
603
- }
604
- get minX() {
605
- return this.centerX - this.innerLength * 0.5;
606
- }
607
- get maxX() {
608
- return this.centerX + this.innerLength * 0.5;
609
- }
610
- get collisionBounds() {
611
- return {
612
- minX: this.minX,
613
- maxX: this.maxX,
614
- halfZ: this.innerWidth * 0.5,
615
- centerZ: this.centerZ,
616
- waterY: this.waterY,
617
- wallTop: this.rimY,
618
- containerFloorY: this.innerBottomY,
619
- };
620
- }
621
- }