three-base 2.0.137 → 2.0.142

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 (307) hide show
  1. package/DirectionalLight.d.ts +6 -6
  2. package/DirectionalLight.js +68 -68
  3. package/DirectionalLight.ts +70 -0
  4. package/EventMap.d.ts +103 -0
  5. package/EventMap.js +17 -0
  6. package/EventMap.ts +139 -0
  7. package/EventMesh.d.ts +18 -18
  8. package/EventMesh.js +59 -59
  9. package/EventMesh.ts +60 -0
  10. package/ExtrudeMesh.d.ts +112 -112
  11. package/ExtrudeMesh.js +342 -366
  12. package/ExtrudeMesh.ts +553 -0
  13. package/FPScontrol.d.ts +511 -78
  14. package/FPScontrol.js +1799 -617
  15. package/FPScontrol.ts +1855 -0
  16. package/Fire/Fire.d.ts +28 -28
  17. package/Fire/Fire.js +80 -79
  18. package/Fire/Fire.ts +89 -0
  19. package/Fire/{FireGromtry.d.ts → FireGeometry.d.ts} +9 -9
  20. package/Fire/{FireGromtry.js → FireGeometry.js} +40 -40
  21. package/Fire/FireGeometry.ts +40 -0
  22. package/Fire/FireMaterial.d.ts +28 -28
  23. package/Fire/FireMaterial.js +32 -32
  24. package/Fire/FireMaterial.ts +89 -0
  25. package/Fire/index.d.ts +2 -1
  26. package/Fire/index.js +2 -1
  27. package/Fire/index.ts +2 -0
  28. package/GLTFDracoLoader.d.ts +71 -0
  29. package/GLTFDracoLoader.js +266 -0
  30. package/GLTFDracoLoader.ts +430 -0
  31. package/Grass/Grass.d.ts +8 -8
  32. package/Grass/Grass.js +38 -38
  33. package/Grass/Grass.ts +38 -0
  34. package/Grass/index.d.ts +2 -1
  35. package/Grass/index.js +2 -1
  36. package/Grass/index.ts +2 -0
  37. package/Ground1/Ground1.d.ts +10 -11
  38. package/Ground1/Ground1.js +95 -91
  39. package/Ground1/Ground1.ts +114 -0
  40. package/Ground1/index.d.ts +1 -1
  41. package/Ground1/index.js +1 -1
  42. package/Ground1/index.ts +1 -0
  43. package/MeshBase.d.ts +36 -35
  44. package/MeshBase.js +247 -223
  45. package/MeshBase.ts +253 -0
  46. package/Mirror.d.ts +25 -25
  47. package/Mirror.js +143 -139
  48. package/Mirror.ts +203 -0
  49. package/MktMesh/MktMesh.d.ts +291 -0
  50. package/MktMesh/MktMesh.js +1045 -0
  51. package/MktMesh/MktMesh.ts +1075 -0
  52. package/MktMesh/assetUrl.d.ts +35 -0
  53. package/MktMesh/assetUrl.js +69 -0
  54. package/MktMesh/assetUrl.ts +69 -0
  55. package/MktMesh/mktMappingShader.d.ts +12 -0
  56. package/MktMesh/mktMappingShader.js +139 -0
  57. package/MktMesh/mktMappingShader.ts +139 -0
  58. package/MktMesh/mktShader.d.ts +263 -0
  59. package/MktMesh/mktShader.js +1248 -0
  60. package/MktMesh/mktShader.ts +1344 -0
  61. package/MktMesh/panoBitmapTexture.d.ts +25 -0
  62. package/MktMesh/panoBitmapTexture.js +52 -0
  63. package/MktMesh/panoBitmapTexture.ts +54 -0
  64. package/MktMesh/panoSlotBind.d.ts +29 -0
  65. package/MktMesh/panoSlotBind.js +106 -0
  66. package/MktMesh/panoSlotBind.ts +153 -0
  67. package/MktMesh/panoSlotRoster.d.ts +22 -0
  68. package/MktMesh/panoSlotRoster.js +169 -0
  69. package/MktMesh/panoSlotRoster.ts +168 -0
  70. package/MktMesh/panoTileStitcher.d.ts +119 -0
  71. package/MktMesh/panoTileStitcher.js +533 -0
  72. package/MktMesh/panoTileStitcher.ts +616 -0
  73. package/MktMesh/types.d.ts +75 -0
  74. package/MktMesh/types.js +1 -0
  75. package/MktMesh/types.ts +87 -0
  76. package/MktMesh/wallCapTessellate.d.ts +33 -0
  77. package/MktMesh/wallCapTessellate.js +97 -0
  78. package/MktMesh/wallCapTessellate.ts +68 -0
  79. package/PipeMesh/PathGeometry.d.ts +44 -44
  80. package/PipeMesh/PathGeometry.js +307 -300
  81. package/PipeMesh/PathGeometry.ts +343 -0
  82. package/PipeMesh/PathPoint.d.ts +17 -17
  83. package/PipeMesh/PathPoint.js +31 -31
  84. package/PipeMesh/PathPoint.ts +36 -0
  85. package/PipeMesh/PathPointList.d.ts +31 -31
  86. package/PipeMesh/PathPointList.js +209 -208
  87. package/PipeMesh/PathPointList.ts +227 -0
  88. package/PipeMesh/PathTubeGeometry.d.ts +25 -25
  89. package/PipeMesh/PathTubeGeometry.js +130 -130
  90. package/PipeMesh/PathTubeGeometry.ts +146 -0
  91. package/PipeMesh/PipeMesh.d.ts +25 -25
  92. package/PipeMesh/PipeMesh.js +63 -63
  93. package/PipeMesh/PipeMesh.ts +76 -0
  94. package/PipeMesh/index.d.ts +1 -1
  95. package/PipeMesh/index.js +1 -1
  96. package/PipeMesh/index.ts +1 -0
  97. package/PlaneModal1/PlaneModal1.d.ts +34 -31
  98. package/PlaneModal1/PlaneModal1.js +109 -105
  99. package/PlaneModal1/PlaneModal1.ts +138 -0
  100. package/PlaneModal1/index.d.ts +1 -1
  101. package/PlaneModal1/index.js +1 -1
  102. package/PlaneModal1/index.ts +1 -0
  103. package/PlaneText.d.ts +27 -39
  104. package/PlaneText.js +35 -30
  105. package/PlaneText.ts +55 -0
  106. package/PlaneWithHtml.d.ts +13 -11
  107. package/PlaneWithHtml.js +64 -63
  108. package/PlaneWithHtml.ts +70 -0
  109. package/PointLight/PointLight.d.ts +13 -14
  110. package/PointLight/PointLight.js +64 -70
  111. package/PointLight/PointLight.ts +67 -0
  112. package/PointLight/index.d.ts +1 -1
  113. package/PointLight/index.js +1 -1
  114. package/PointLight/index.ts +1 -0
  115. package/README.md +110 -79
  116. package/Sky/Sky.d.ts +9 -9
  117. package/Sky/Sky.js +22 -22
  118. package/Sky/Sky.ts +22 -0
  119. package/Sky/index.d.ts +1 -1
  120. package/Sky/index.js +1 -1
  121. package/Sky/index.ts +1 -0
  122. package/SpotLight.d.ts +6 -6
  123. package/SpotLight.js +72 -72
  124. package/SpotLight.ts +74 -0
  125. package/SpriteModal1/SpriteModal1.d.ts +33 -30
  126. package/SpriteModal1/SpriteModal1.js +116 -110
  127. package/SpriteModal1/SpriteModal1.ts +146 -0
  128. package/SpriteModal1/index.d.ts +1 -1
  129. package/SpriteModal1/index.js +1 -1
  130. package/SpriteModal1/index.ts +1 -0
  131. package/SpriteText.d.ts +46 -45
  132. package/SpriteText.js +176 -163
  133. package/SpriteText.ts +205 -0
  134. package/SpriteText2.d.ts +48 -47
  135. package/SpriteText2.js +223 -198
  136. package/SpriteText2.ts +258 -0
  137. package/SpriteText3.d.ts +86 -0
  138. package/SpriteText3.js +519 -0
  139. package/SpriteText3.ts +599 -0
  140. package/SpriteWithHtml.d.ts +9 -7
  141. package/SpriteWithHtml.js +44 -44
  142. package/SpriteWithHtml.ts +50 -0
  143. package/Text3D/Text3D.d.ts +20 -20
  144. package/Text3D/Text3D.js +58 -62
  145. package/Text3D/Text3D.ts +77 -0
  146. package/Text3D/fontController.d.ts +60 -59
  147. package/Text3D/fontController.js +166 -199
  148. package/Text3D/fontController.ts +209 -0
  149. package/Text3D/index.d.ts +1 -1
  150. package/Text3D/index.js +1 -1
  151. package/Text3D/index.ts +1 -0
  152. package/ThreeBase.d.ts +248 -111
  153. package/ThreeBase.js +1202 -656
  154. package/ThreeBase.ts +1314 -0
  155. package/ThreeMouse.d.ts +54 -40
  156. package/ThreeMouse.js +453 -407
  157. package/ThreeMouse.ts +472 -0
  158. package/Transition.d.ts +45 -40
  159. package/Transition.js +209 -210
  160. package/Transition.ts +242 -0
  161. package/Water/Water.d.ts +5 -5
  162. package/Water/Water.js +44 -26
  163. package/Water/Water.ts +45 -0
  164. package/Water/index.d.ts +2 -0
  165. package/Water/index.js +2 -0
  166. package/Water/index.ts +2 -0
  167. package/WaterFlow/FloatingDuck.d.ts +88 -0
  168. package/WaterFlow/FloatingDuck.js +307 -0
  169. package/WaterFlow/FloatingDuck.ts +445 -0
  170. package/WaterFlow/GPUFluidSimulation.d.ts +56 -0
  171. package/WaterFlow/GPUFluidSimulation.js +785 -0
  172. package/WaterFlow/GPUFluidSimulation.ts +827 -0
  173. package/WaterFlow/ReservoirSystem.d.ts +106 -0
  174. package/WaterFlow/ReservoirSystem.js +621 -0
  175. package/WaterFlow/ReservoirSystem.ts +754 -0
  176. package/WaterFlow/SPHField.d.ts +35 -0
  177. package/WaterFlow/SPHField.js +204 -0
  178. package/WaterFlow/SPHField.ts +228 -0
  179. package/WaterFlow/ScreenSpaceFluidPass.d.ts +69 -0
  180. package/WaterFlow/ScreenSpaceFluidPass.js +736 -0
  181. package/WaterFlow/ScreenSpaceFluidPass.ts +789 -0
  182. package/WaterFlow/ShallowWaterSurface.d.ts +45 -0
  183. package/WaterFlow/ShallowWaterSurface.js +441 -0
  184. package/WaterFlow/ShallowWaterSurface.ts +510 -0
  185. package/WaterFlow/WaterFlow.d.ts +87 -0
  186. package/WaterFlow/WaterFlow.js +171 -0
  187. package/WaterFlow/WaterFlow.ts +219 -0
  188. package/WaterFlow/types.d.ts +161 -0
  189. package/WaterFlow/types.js +1 -0
  190. package/WaterFlow/types.ts +189 -0
  191. package/WaterFlow/waterFlowPass.d.ts +3 -0
  192. package/WaterFlow/waterFlowPass.js +20 -0
  193. package/WaterFlow/waterFlowPass.ts +40 -0
  194. package/collisionOBB.d.ts +42 -0
  195. package/collisionOBB.js +112 -0
  196. package/collisionOBB.ts +108 -0
  197. package/declare.d.ts +23 -165
  198. package/index.d.ts +1 -1
  199. package/index.js +1 -2
  200. package/index.ts +1 -0
  201. package/instanced/InstancedBatch.d.ts +57 -0
  202. package/instanced/InstancedBatch.js +348 -0
  203. package/instanced/InstancedBatch.ts +406 -0
  204. package/instanced/InstancedChild.d.ts +174 -0
  205. package/instanced/InstancedChild.js +264 -0
  206. package/instanced/InstancedChild.ts +327 -0
  207. package/instanced/InstancedGroup.d.ts +43 -0
  208. package/instanced/InstancedGroup.js +126 -0
  209. package/instanced/InstancedGroup.ts +137 -0
  210. package/{InstancedMeshBase.d.ts → instanced/InstancedMeshBase.d.ts} +38 -39
  211. package/{InstancedMeshBase.js → instanced/InstancedMeshBase.js} +282 -293
  212. package/instanced/InstancedMeshBase.ts +292 -0
  213. package/instanced/InstancedTemplate.d.ts +46 -0
  214. package/instanced/InstancedTemplate.js +125 -0
  215. package/instanced/InstancedTemplate.ts +188 -0
  216. package/keyControls.d.ts +30 -0
  217. package/{keyContorls.js → keyControls.js} +101 -98
  218. package/keyControls.ts +107 -0
  219. package/lensflare/LensFlare.d.ts +39 -0
  220. package/lensflare/LensFlare.js +373 -0
  221. package/lensflare/LensFlare.ts +448 -0
  222. package/lensflare/lensflare0.png +0 -0
  223. package/lensflare/lensflare3.png +0 -0
  224. package/lensflare/lensflare_custom.png +0 -0
  225. package/lensflare/lensflare_hex_ghost.png +0 -0
  226. package/lensflare/lensflare_hex_main.png +0 -0
  227. package/package.json +99 -25
  228. package/pass/bloom_pass.d.ts +2 -2
  229. package/pass/bloom_pass.js +188 -101
  230. package/pass/bloom_pass.ts +243 -0
  231. package/pass/cover_pass.d.ts +2 -2
  232. package/pass/cover_pass.js +122 -99
  233. package/pass/cover_pass.ts +182 -0
  234. package/pass/debugDepthState.d.ts +20 -0
  235. package/pass/debugDepthState.js +9 -0
  236. package/pass/debugDepthState.ts +22 -0
  237. package/pass/depthTestBias.d.ts +12 -0
  238. package/pass/depthTestBias.js +12 -0
  239. package/pass/depthTestBias.ts +12 -0
  240. package/pass/effectUserData.d.ts +27 -0
  241. package/pass/effectUserData.js +1 -0
  242. package/pass/effectUserData.ts +46 -0
  243. package/pass/frosted_glass_pass.d.ts +2 -2
  244. package/pass/frosted_glass_pass.js +167 -186
  245. package/pass/frosted_glass_pass.ts +288 -0
  246. package/pass/fsQuad.d.ts +9 -9
  247. package/pass/fsQuad.js +28 -28
  248. package/pass/fsQuad.ts +34 -0
  249. package/pass/glow_pass.d.ts +2 -2
  250. package/pass/glow_pass.js +449 -233
  251. package/pass/glow_pass.ts +654 -0
  252. package/pass/screen_bloom_pass.d.ts +11 -0
  253. package/pass/screen_bloom_pass.js +235 -0
  254. package/pass/screen_bloom_pass.ts +263 -0
  255. package/pass/ssao_pass.d.ts +88 -13
  256. package/pass/ssao_pass.js +802 -366
  257. package/pass/ssao_pass.ts +918 -0
  258. package/pass/stroke_pass.d.ts +2 -2
  259. package/pass/stroke_pass.js +329 -258
  260. package/pass/stroke_pass.ts +638 -0
  261. package/shaders/FXAAShaderMaterial.d.ts +3 -3
  262. package/shaders/FXAAShaderMaterial.js +23 -66
  263. package/shaders/FXAAShaderMaterial.ts +201 -0
  264. package/shaders/baseDepthVisShader.d.ts +11 -0
  265. package/shaders/baseDepthVisShader.js +36 -0
  266. package/shaders/baseDepthVisShader.ts +36 -0
  267. package/shaders/baseTextureToRenderShader.d.ts +2 -2
  268. package/shaders/baseTextureToRenderShader.js +55 -60
  269. package/shaders/baseTextureToRenderShader.ts +232 -0
  270. package/shaders/copyShader.d.ts +3 -3
  271. package/shaders/copyShader.js +13 -14
  272. package/shaders/copyShader.ts +32 -0
  273. package/shaders/debugDepthCompareShader.d.ts +14 -0
  274. package/shaders/debugDepthCompareShader.js +49 -0
  275. package/shaders/debugDepthCompareShader.ts +50 -0
  276. package/shaders/debugDepthJudgeShader.d.ts +18 -0
  277. package/shaders/debugDepthJudgeShader.js +52 -0
  278. package/shaders/debugDepthJudgeShader.ts +54 -0
  279. package/shaders/debugExcludedShader.d.ts +13 -0
  280. package/shaders/debugExcludedShader.js +46 -0
  281. package/shaders/debugExcludedShader.ts +48 -0
  282. package/spriteTextBg.d.ts +1 -0
  283. package/spriteTextBg.js +20 -0
  284. package/spriteTextBg.ts +20 -0
  285. package/threeLoader.d.ts +35 -34
  286. package/threeLoader.js +518 -527
  287. package/threeLoader.ts +525 -0
  288. package/threeUtils.d.ts +53 -52
  289. package/threeUtils.js +329 -330
  290. package/threeUtils.ts +373 -0
  291. package/types.d.ts +2 -0
  292. package/types.js +6 -0
  293. package/types.ts +8 -0
  294. package/ApngTexture.d.ts +0 -13
  295. package/ApngTexture.js +0 -61
  296. package/ApngTextureManage.d.ts +0 -18
  297. package/ApngTextureManage.js +0 -87
  298. package/KeyFrameAnimation.d.ts +0 -25
  299. package/KeyFrameAnimation.js +0 -78
  300. package/keyContorls.d.ts +0 -25
  301. package/lib/draco/draco_decoder.js +0 -34
  302. package/lib/draco/draco_decoder.wasm +0 -0
  303. package/lib/draco/draco_wasm_wrapper.js +0 -117
  304. package/pass/emissive_ao_pass.d.ts +0 -13
  305. package/pass/emissive_ao_pass.js +0 -370
  306. package/shaders.d.ts +0 -5
  307. package/shaders.js +0 -93
@@ -0,0 +1,621 @@
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
+ }