three-base 2.0.172 → 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 (115) hide show
  1. package/FPScontrol.d.ts +15 -24
  2. package/FPScontrol.js +63 -87
  3. package/FPScontrol.ts +64 -88
  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 +3 -2
  10. package/MktMesh/MktMesh.js +27 -8
  11. package/MktMesh/MktMesh.ts +24 -8
  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/PipeMesh/PipeJoint.d.ts +22 -0
  25. package/PipeMesh/PipeJoint.js +33 -0
  26. package/PipeMesh/PipeJoint.ts +38 -0
  27. package/PipeMesh/PipeMesh.d.ts +94 -22
  28. package/PipeMesh/PipeMesh.js +192 -53
  29. package/PipeMesh/PipeMesh.ts +223 -76
  30. package/PipeMesh/PipeStraight.d.ts +19 -0
  31. package/PipeMesh/PipeStraight.js +34 -0
  32. package/PipeMesh/PipeStraight.ts +45 -0
  33. package/PipeMesh/axisSnap.d.ts +52 -0
  34. package/PipeMesh/axisSnap.js +52 -0
  35. package/PipeMesh/axisSnap.ts +93 -0
  36. package/PipeMesh/directRoute.d.ts +85 -0
  37. package/PipeMesh/directRoute.js +126 -0
  38. package/PipeMesh/directRoute.ts +190 -0
  39. package/PipeMesh/index.d.ts +6 -0
  40. package/PipeMesh/index.js +6 -0
  41. package/PipeMesh/index.ts +7 -1
  42. package/PipeMesh/pipeMaterial.d.ts +16 -0
  43. package/PipeMesh/pipeMaterial.js +23 -0
  44. package/PipeMesh/pipeMaterial.ts +26 -0
  45. package/ThreeBase.d.ts +1 -39
  46. package/ThreeBase.js +38 -147
  47. package/ThreeBase.ts +1176 -1302
  48. package/instanced/InstancedBatch.d.ts +14 -1
  49. package/instanced/InstancedBatch.js +53 -3
  50. package/instanced/InstancedBatch.ts +62 -3
  51. package/instanced/InstancedChild.d.ts +24 -0
  52. package/instanced/InstancedChild.js +40 -0
  53. package/instanced/InstancedChild.ts +41 -0
  54. package/instanced/InstancedGroup.d.ts +48 -0
  55. package/instanced/InstancedGroup.js +45 -1
  56. package/instanced/InstancedGroup.ts +85 -0
  57. package/package.json +2 -7
  58. package/pass/debugDepthState.d.ts +16 -8
  59. package/pass/debugDepthState.js +4 -0
  60. package/pass/debugDepthState.ts +27 -9
  61. package/pass/glow_pass.js +11 -94
  62. package/pass/glow_pass.ts +11 -97
  63. package/pass/ssao_pass.d.ts +9 -23
  64. package/pass/ssao_pass.js +41 -128
  65. package/pass/ssao_pass.ts +36 -166
  66. package/pass/stroke_pass.js +3 -0
  67. package/pass/stroke_pass.ts +4 -0
  68. package/shaders/debugDepthCompareShader.d.ts +1 -1
  69. package/shaders/debugDepthCompareShader.js +1 -1
  70. package/shaders/debugDepthCompareShader.ts +1 -1
  71. package/shaders/debugDepthJudgeShader.d.ts +1 -1
  72. package/shaders/debugDepthJudgeShader.js +1 -1
  73. package/shaders/debugDepthJudgeShader.ts +1 -1
  74. package/PipeMesh/PathGeometry.d.ts +0 -44
  75. package/PipeMesh/PathGeometry.js +0 -307
  76. package/PipeMesh/PathGeometry.ts +0 -343
  77. package/PipeMesh/PathPoint.d.ts +0 -17
  78. package/PipeMesh/PathPoint.js +0 -31
  79. package/PipeMesh/PathPoint.ts +0 -36
  80. package/PipeMesh/PathPointList.d.ts +0 -31
  81. package/PipeMesh/PathPointList.js +0 -209
  82. package/PipeMesh/PathPointList.ts +0 -227
  83. package/PipeMesh/PathTubeGeometry.d.ts +0 -25
  84. package/PipeMesh/PathTubeGeometry.js +0 -130
  85. package/PipeMesh/PathTubeGeometry.ts +0 -146
  86. package/WaterFlow/FloatingDuck.d.ts +0 -88
  87. package/WaterFlow/FloatingDuck.js +0 -307
  88. package/WaterFlow/FloatingDuck.ts +0 -445
  89. package/WaterFlow/GPUFluidSimulation.d.ts +0 -56
  90. package/WaterFlow/GPUFluidSimulation.js +0 -785
  91. package/WaterFlow/GPUFluidSimulation.ts +0 -827
  92. package/WaterFlow/ReservoirSystem.d.ts +0 -106
  93. package/WaterFlow/ReservoirSystem.js +0 -621
  94. package/WaterFlow/ReservoirSystem.ts +0 -754
  95. package/WaterFlow/SPHField.d.ts +0 -35
  96. package/WaterFlow/SPHField.js +0 -204
  97. package/WaterFlow/SPHField.ts +0 -228
  98. package/WaterFlow/ScreenSpaceFluidPass.d.ts +0 -69
  99. package/WaterFlow/ScreenSpaceFluidPass.js +0 -736
  100. package/WaterFlow/ScreenSpaceFluidPass.ts +0 -789
  101. package/WaterFlow/ShallowWaterSurface.d.ts +0 -45
  102. package/WaterFlow/ShallowWaterSurface.js +0 -441
  103. package/WaterFlow/ShallowWaterSurface.ts +0 -510
  104. package/WaterFlow/WaterFlow.d.ts +0 -87
  105. package/WaterFlow/WaterFlow.js +0 -171
  106. package/WaterFlow/WaterFlow.ts +0 -219
  107. package/WaterFlow/types.d.ts +0 -161
  108. package/WaterFlow/types.js +0 -1
  109. package/WaterFlow/types.ts +0 -189
  110. package/WaterFlow/waterFlowPass.d.ts +0 -3
  111. package/WaterFlow/waterFlowPass.js +0 -20
  112. package/WaterFlow/waterFlowPass.ts +0 -40
  113. package/shaders/debugExcludedShader.d.ts +0 -15
  114. package/shaders/debugExcludedShader.js +0 -48
  115. package/shaders/debugExcludedShader.ts +0 -50
@@ -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
- }