@fps-games/vfx 0.1.1 → 0.3.3

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 (250) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +41 -0
  3. package/dist/effects/coin-burst/index.js +6 -2
  4. package/dist/effects/comet-trail-flame/index.js +3 -1
  5. package/dist/effects/debris-smoke/index.js +1 -0
  6. package/dist/effects/electric-arc/index.js +1 -0
  7. package/dist/effects/energy-shield/index.js +1 -0
  8. package/dist/effects/explosion/index.js +3 -2
  9. package/dist/effects/hit-flash/index.js +12 -4
  10. package/dist/effects/laser-beam/index.js +14 -2
  11. package/dist/effects/lumber_vehicle_upgrade_aura/index.js +1 -0
  12. package/dist/effects/monster-hit/index.js +2 -1
  13. package/dist/effects/player-upgrade/index.js +5 -3
  14. package/dist/effects/pulse-laser-volley/index.js +14 -4
  15. package/dist/effects/thruster-flame/index.js +3 -1
  16. package/dist/effects/treasure-attention/index.d.ts +54 -0
  17. package/dist/effects/treasure-attention/index.js +535 -0
  18. package/dist/effects/ufo-thread-flame/index.js +1 -0
  19. package/dist/effects/vehicle-upgrade/index.js +4 -1
  20. package/dist/effects/weapon-trail/index.js +8 -3
  21. package/dist/effects/wood_crusher_sawdust/index.d.ts +4 -0
  22. package/dist/effects/wood_cutter_sawdust/index.d.ts +4 -0
  23. package/dist/index.js +85 -17
  24. package/dist/types.d.ts +80 -0
  25. package/index.json +19 -1
  26. package/package.json +9 -4
  27. package/packages/effects/treasure-attention/vfx-params.json +49 -0
  28. package/tooling/vfx-cli.mjs +8 -1
  29. package/docs/AUTHORING_EFFECTS.md +0 -297
  30. package/docs/VFX_LIBRARY_ARCHITECTURE_chatgpt.md +0 -1580
  31. package/docs/VFX_LIBRARY_EXECUTION_PLAN.md +0 -229
  32. package/docs/VFX_LIBRARY_PLAN.md +0 -546
  33. package/docs/vfx-ab/RESULTS.md +0 -35
  34. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/after.png +0 -0
  35. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/before.png +0 -0
  36. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/after.png +0 -0
  37. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/before.png +0 -0
  38. package/docs/vfx-integration_chatgpt.md +0 -462
  39. package/docs/weapon-trail-visualization.html +0 -241
  40. package/packages/EffectPackageService.ts +0 -240
  41. package/packages/SceneVfxService.ts +0 -96
  42. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/runtime/simpleMeshEffect.v1.js +0 -333
  43. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.frag.glsl +0 -25
  44. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.vert.glsl +0 -11
  45. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/textures/effect_jianbian.png +0 -0
  46. package/packages/build.mesh.yellow_build_upgrade_indicator/effect.json +0 -927
  47. package/packages/build.mesh.yellow_build_upgrade_indicator/media/contact-sheet.png +0 -0
  48. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.gif +0 -0
  49. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.mp4 +0 -0
  50. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.webm +0 -0
  51. package/packages/build.mesh.yellow_build_upgrade_indicator/media/thumbnail.png +0 -0
  52. package/packages/build.mesh.yellow_build_upgrade_indicator/metadata/description.json +0 -197
  53. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-effect.original.json +0 -895
  54. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-normalized-vfx-to-babylon-report.json +0 -241
  55. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-overrides.json +0 -542
  56. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-tuning-report.json +0 -69
  57. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/asset-map.json +0 -125
  58. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/lineage.json +0 -44
  59. package/packages/build.particle.golden_upgrade_starburst/assets/runtime/particleEmitter.v1.js +0 -210
  60. package/packages/build.particle.golden_upgrade_starburst/assets/textures/effect_guangdian.png +0 -0
  61. package/packages/build.particle.golden_upgrade_starburst/effect.json +0 -880
  62. package/packages/build.particle.golden_upgrade_starburst/media/contact-sheet.png +0 -0
  63. package/packages/build.particle.golden_upgrade_starburst/media/preview.gif +0 -0
  64. package/packages/build.particle.golden_upgrade_starburst/media/preview.mp4 +0 -0
  65. package/packages/build.particle.golden_upgrade_starburst/media/preview.webm +0 -0
  66. package/packages/build.particle.golden_upgrade_starburst/media/thumbnail.png +0 -0
  67. package/packages/build.particle.golden_upgrade_starburst/metadata/description.json +0 -161
  68. package/packages/build.particle.golden_upgrade_starburst/provenance/03-effect.original.json +0 -762
  69. package/packages/build.particle.golden_upgrade_starburst/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  70. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-overrides.json +0 -436
  71. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-tuning-report.json +0 -40
  72. package/packages/build.particle.golden_upgrade_starburst/provenance/asset-map.json +0 -115
  73. package/packages/build.particle.golden_upgrade_starburst/provenance/lineage.json +0 -40
  74. package/packages/build.particle.orange_upgrade_transform_sparks/assets/runtime/particleEmitter.v1.js +0 -210
  75. package/packages/build.particle.orange_upgrade_transform_sparks/assets/textures/b5b27ab1-e740-4398-b407-848fc2b2c897.png +0 -0
  76. package/packages/build.particle.orange_upgrade_transform_sparks/effect.json +0 -887
  77. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.gif +0 -0
  78. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.mp4 +0 -0
  79. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.webm +0 -0
  80. package/packages/build.particle.orange_upgrade_transform_sparks/media/thumbnail.png +0 -0
  81. package/packages/build.particle.orange_upgrade_transform_sparks/metadata/description.json +0 -179
  82. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-effect.original.json +0 -859
  83. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  84. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-overrides.json +0 -478
  85. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-tuning-report.json +0 -40
  86. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/asset-map.json +0 -114
  87. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/lineage.json +0 -41
  88. package/packages/build.particle.wood_chip_dust_burst/assets/runtime/particleEmitter.v1.js +0 -210
  89. package/packages/build.particle.wood_chip_dust_burst/assets/textures/effect_yanwu4.png +0 -0
  90. package/packages/build.particle.wood_chip_dust_burst/assets/textures/suipian3.png +0 -0
  91. package/packages/build.particle.wood_chip_dust_burst/effect.json +0 -1400
  92. package/packages/build.particle.wood_chip_dust_burst/media/contact-sheet.png +0 -0
  93. package/packages/build.particle.wood_chip_dust_burst/media/preview.gif +0 -0
  94. package/packages/build.particle.wood_chip_dust_burst/media/preview.mp4 +0 -0
  95. package/packages/build.particle.wood_chip_dust_burst/media/preview.webm +0 -0
  96. package/packages/build.particle.wood_chip_dust_burst/media/thumbnail.png +0 -0
  97. package/packages/build.particle.wood_chip_dust_burst/metadata/description.json +0 -173
  98. package/packages/build.particle.wood_chip_dust_burst/provenance/03-effect.original.json +0 -1277
  99. package/packages/build.particle.wood_chip_dust_burst/provenance/03-normalized-vfx-to-babylon-report.json +0 -329
  100. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-overrides.json +0 -749
  101. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-tuning-report.json +0 -60
  102. package/packages/build.particle.wood_chip_dust_burst/provenance/asset-map.json +0 -146
  103. package/packages/build.particle.wood_chip_dust_burst/provenance/lineage.json +0 -69
  104. package/packages/effects/building-upgrade/effect.json +0 -6
  105. package/packages/effects/building-upgrade/index.ts +0 -387
  106. package/packages/effects/character-entrance/effect.json +0 -6
  107. package/packages/effects/character-entrance/index.ts +0 -308
  108. package/packages/effects/coin-burst/index.ts +0 -43
  109. package/packages/effects/comet-trail-flame/assets/textures/trail_5x4.webp +0 -0
  110. package/packages/effects/comet-trail-flame/index.ts +0 -106
  111. package/packages/effects/conveyor-upgrade/effect.json +0 -6
  112. package/packages/effects/conveyor-upgrade/index.ts +0 -401
  113. package/packages/effects/debris-smoke/assets/models/rock_debris.glb +0 -0
  114. package/packages/effects/debris-smoke/assets/textures/smoke22.webp +0 -0
  115. package/packages/effects/debris-smoke/index.ts +0 -101
  116. package/packages/effects/door_open_gold_flakes/assets/runtime/goldFlakes.v1.js +0 -397
  117. package/packages/effects/door_open_gold_flakes/door_open_gold_flakes.json +0 -498
  118. package/packages/effects/door_open_gold_flakes/index.ts +0 -11
  119. package/packages/effects/electric-arc/assets/textures/spark_4x4.webp +0 -0
  120. package/packages/effects/electric-arc/index.ts +0 -62
  121. package/packages/effects/energy-shield/effect.json +0 -7
  122. package/packages/effects/energy-shield/index.ts +0 -431
  123. package/packages/effects/explosion/assets/textures/baozha29.webp +0 -0
  124. package/packages/effects/explosion/assets/textures/glow01.webp +0 -0
  125. package/packages/effects/explosion/assets/textures/lizi01.webp +0 -0
  126. package/packages/effects/explosion/assets/textures/xulie_lizibaozha02_5x5.webp +0 -0
  127. package/packages/effects/explosion/index.ts +0 -114
  128. package/packages/effects/ground-tile-upgrade/effect.json +0 -6
  129. package/packages/effects/ground-tile-upgrade/index.ts +0 -410
  130. package/packages/effects/hit-flash/index.ts +0 -69
  131. package/packages/effects/laser-beam/assets/textures/laser_glow.webp +0 -0
  132. package/packages/effects/laser-beam/assets/textures/laser_noise.webp +0 -0
  133. package/packages/effects/laser-beam/index.ts +0 -6
  134. package/packages/effects/laser-beam-factory.ts +0 -135
  135. package/packages/effects/lumber-cart-suction/effect.json +0 -6
  136. package/packages/effects/lumber-cart-suction/index.ts +0 -399
  137. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb +0 -0
  138. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/.active +0 -1
  139. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/1781589063890_upload.glb +0 -0
  140. package/packages/effects/lumber-debris-scatter/assets/runtime/solidParticleScatter.v1.js +0 -236
  141. package/packages/effects/lumber-debris-scatter/effect.json +0 -228
  142. package/packages/effects/lumber-debris-scatter/index.ts +0 -780
  143. package/packages/effects/lumber_vehicle_gear_spin/assets/runtime/gearSpin.v1.js +0 -202
  144. package/packages/effects/lumber_vehicle_gear_spin/index.ts +0 -11
  145. package/packages/effects/lumber_vehicle_gear_spin/lumber_vehicle_gear_spin.json +0 -338
  146. package/packages/effects/lumber_vehicle_upgrade_aura/assets/runtime/upgradeAura.v1.js +0 -353
  147. package/packages/effects/lumber_vehicle_upgrade_aura/index.ts +0 -11
  148. package/packages/effects/lumber_vehicle_upgrade_aura/lumber_vehicle_upgrade_aura.json +0 -365
  149. package/packages/effects/lumber_vehicle_wood_chips/assets/runtime/solidParticleScatter.v1.js +0 -310
  150. package/packages/effects/lumber_vehicle_wood_chips/index.ts +0 -11
  151. package/packages/effects/lumber_vehicle_wood_chips/lumber_vehicle_wood_chips.json +0 -335
  152. package/packages/effects/monster-hit/index.ts +0 -128
  153. package/packages/effects/player-upgrade/effect.json +0 -6
  154. package/packages/effects/player-upgrade/index.ts +0 -635
  155. package/packages/effects/pulse-laser-volley/assets/textures/bolt_guangshu.webp +0 -0
  156. package/packages/effects/pulse-laser-volley/index.ts +0 -204
  157. package/packages/effects/resource-collect-sparkles/effect.json +0 -6
  158. package/packages/effects/resource-collect-sparkles/index.ts +0 -254
  159. package/packages/effects/run-footstep-dust/assets/runtime/runFootstepDust.v1.js +0 -215
  160. package/packages/effects/run-footstep-dust/effect.json +0 -183
  161. package/packages/effects/run-footstep-dust/index.ts +0 -377
  162. package/packages/effects/space-shared.ts +0 -120
  163. package/packages/effects/thruster-flame/index.ts +0 -305
  164. package/packages/effects/tree-chop-dust/assets/textures/tree_chop_dust_atlas.png +0 -0
  165. package/packages/effects/tree-chop-dust/effect.json +0 -8
  166. package/packages/effects/tree-chop-dust/index.ts +0 -202
  167. package/packages/effects/tree-leaf-chop-burst/assets/runtime/particleEmitter.v1.js +0 -303
  168. package/packages/effects/tree-leaf-chop-burst/assets/textures/tree_leaf_chop_burst_leaf_atlas.png +0 -0
  169. package/packages/effects/tree-leaf-chop-burst/effect.json +0 -498
  170. package/packages/effects/tree-leaf-chop-burst/index.ts +0 -294
  171. package/packages/effects/tree_chop_dust_burst/assets/runtime/particleEmitter.v1.js +0 -318
  172. package/packages/effects/tree_chop_dust_burst/assets/textures/tree_chop_dust_burst_dust.png +0 -0
  173. package/packages/effects/tree_chop_dust_burst/index.ts +0 -13
  174. package/packages/effects/tree_chop_dust_burst/tree_chop_dust_burst.json +0 -412
  175. package/packages/effects/ufo-thread-flame/assets/textures/flame_flow.webp +0 -0
  176. package/packages/effects/ufo-thread-flame/assets/textures/flame_spike.webp +0 -0
  177. package/packages/effects/ufo-thread-flame/index.ts +0 -152
  178. package/packages/effects/vehicle-exhaust-smoke/assets/textures/vehicle_exhaust_smoke_atlas.png +0 -0
  179. package/packages/effects/vehicle-exhaust-smoke/effect.json +0 -8
  180. package/packages/effects/vehicle-exhaust-smoke/index.ts +0 -298
  181. package/packages/effects/vehicle-upgrade/effect.json +0 -6
  182. package/packages/effects/vehicle-upgrade/index.ts +0 -401
  183. package/packages/effects/weapon-trail/assets/textures/trail_wedge.png +0 -0
  184. package/packages/effects/weapon-trail/index.ts +0 -313
  185. package/packages/effects/wood-crusher-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  186. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  187. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  188. package/packages/effects/wood-crusher-sawdust/index.ts +0 -43
  189. package/packages/effects/wood-crusher-sawdust/wood_crusher_sawdust.json +0 -592
  190. package/packages/effects/wood-cutter-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  191. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  192. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  193. package/packages/effects/wood-cutter-sawdust/index.ts +0 -43
  194. package/packages/effects/wood-cutter-sawdust/wood_cutter_sawdust.json +0 -592
  195. package/packages/effects/wood-sawdust-effect-factory.ts +0 -194
  196. package/packages/effects/wood_crusher_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  197. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  198. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  199. package/packages/effects/wood_crusher_sawdust/index.ts +0 -29
  200. package/packages/effects/wood_crusher_sawdust/wood_crusher_sawdust.json +0 -592
  201. package/packages/effects/wood_cutter_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  202. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  203. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  204. package/packages/effects/wood_cutter_sawdust/index.ts +0 -29
  205. package/packages/effects/wood_cutter_sawdust/wood_cutter_sawdust.json +0 -592
  206. package/packages/effects/wood_peel_material_test/assets/runtime/woodPeelMaterialTest.v1.js +0 -196
  207. package/packages/effects/wood_peel_material_test/index.ts +0 -11
  208. package/packages/effects/wood_peel_material_test/wood_peel_material_test.json +0 -136
  209. package/packages/env.particle.forest_edge_fog/assets/runtime/particleEmitter.v1.js +0 -210
  210. package/packages/env.particle.forest_edge_fog/assets/textures/f12a23c4-b924-4322-a260-3d982428f1e8.png +0 -0
  211. package/packages/env.particle.forest_edge_fog/assets/textures/yuan.png +0 -0
  212. package/packages/env.particle.forest_edge_fog/effect.json +0 -854
  213. package/packages/env.particle.forest_edge_fog/media/preview.gif +0 -0
  214. package/packages/env.particle.forest_edge_fog/media/preview.mp4 +0 -0
  215. package/packages/env.particle.forest_edge_fog/media/preview.webm +0 -0
  216. package/packages/env.particle.forest_edge_fog/media/thumbnail.png +0 -0
  217. package/packages/env.particle.forest_edge_fog/metadata/description.json +0 -148
  218. package/packages/env.particle.forest_edge_fog/provenance/03-effect.original.json +0 -827
  219. package/packages/env.particle.forest_edge_fog/provenance/03-normalized-vfx-to-babylon-report.json +0 -196
  220. package/packages/env.particle.forest_edge_fog/provenance/04-preview-overrides.json +0 -432
  221. package/packages/env.particle.forest_edge_fog/provenance/04-preview-tuning-report.json +0 -28
  222. package/packages/env.particle.forest_edge_fog/provenance/asset-map.json +0 -124
  223. package/packages/env.particle.forest_edge_fog/provenance/lineage.json +0 -41
  224. package/packages/generation.ts +0 -14
  225. package/packages/index.ts +0 -14
  226. package/packages/legacy-particle-runtime.ts +0 -113
  227. package/packages/manifestRuntimePackage.ts +0 -519
  228. package/packages/registry.ts +0 -85
  229. package/packages/rendering.ts +0 -13
  230. package/packages/resource.particle.viscous_oil_suction/assets/runtime/top_disk_suction/particleEmitter.v1.js +0 -424
  231. package/packages/resource.particle.viscous_oil_suction/assets/textures/Glow ad.png +0 -0
  232. package/packages/resource.particle.viscous_oil_suction/effect.json +0 -1254
  233. package/packages/resource.particle.viscous_oil_suction/media/preview.gif +0 -0
  234. package/packages/resource.particle.viscous_oil_suction/media/preview.mp4 +0 -0
  235. package/packages/resource.particle.viscous_oil_suction/media/preview.webm +0 -0
  236. package/packages/resource.particle.viscous_oil_suction/media/thumbnail.png +0 -0
  237. package/packages/resource.particle.viscous_oil_suction/metadata/description.json +0 -122
  238. package/packages/resource.particle.viscous_oil_suction/provenance/03-effect.original.json +0 -1051
  239. package/packages/resource.particle.viscous_oil_suction/provenance/03-normalized-vfx-to-babylon-report.json +0 -200
  240. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-overrides.json +0 -581
  241. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-tuning-report.json +0 -28
  242. package/packages/resource.particle.viscous_oil_suction/provenance/asset-map.json +0 -111
  243. package/packages/resource.particle.viscous_oil_suction/provenance/lineage.json +0 -45
  244. package/packages/types.ts +0 -107
  245. package/tooling/check-dist-runtime.mjs +0 -166
  246. package/tooling/generate-index.mjs +0 -55
  247. package/tooling/test-guardrails.mjs +0 -157
  248. package/tooling/validate-boundary.mjs +0 -92
  249. package/tooling/validate-effects.mjs +0 -304
  250. package/tooling/validate.mjs +0 -53
@@ -1,305 +0,0 @@
1
- // 战机 shader 推进焰(移植自 babylon-laser index.html thrusterJet, 特效#14)。
2
- // 程序锥管 + 三段 GLSL(顶点径向舔动 + 片元 fBM 侵蚀/三段色温/马赫环 + 旋涡薄壳)+ 程序噪声纹理。
3
- // 纯自给:无外部贴图/glb。沿 root 局部 +Z 喷出(z=0 喷口、z=length 尾尖)。
4
- import { Mesh } from '@babylonjs/core/Meshes/mesh';
5
- import { VertexData } from '@babylonjs/core/Meshes/mesh.vertexData';
6
- import { ShaderMaterial } from '@babylonjs/core/Materials/shaderMaterial';
7
- import { Effect } from '@babylonjs/core/Materials/effect';
8
- import { RawTexture } from '@babylonjs/core/Materials/Textures/rawTexture';
9
- import { Texture } from '@babylonjs/core/Materials/Textures/texture';
10
- import { Constants } from '@babylonjs/core/Engines/constants';
11
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
12
- import type { Scene } from '@babylonjs/core/scene';
13
- import type { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
14
- import type { VfxColorValue, VfxEffectPackage, VfxParamValues } from '../../types';
15
- import { LIFETIME_DEBUG_PARAMS, LIFETIME_DEFAULTS, scheduleLifetime } from '../space-shared';
16
-
17
- type EngineMode = 'ion' | 'chem' | 'plasma';
18
-
19
- interface ThrusterFlameParams extends VfxParamValues {
20
- lifetimeMode: string; lifetime: number;
21
- throttle: number; // 推力 %(0..100)
22
- flow: number; // 流速 %(0..100)
23
- afterburner: number; // 加力 %(0..100)
24
- mode: EngineMode; // 引擎模式(运动预设:离子/化燃/等离子)
25
- rootColor: VfxColorValue;
26
- tipColor: VfxColorValue;
27
- coreColor: VfxColorValue;
28
- }
29
-
30
- // ── 引擎模式预设(运动/形状;颜色由 *Color 参数控制,默认取 ion) ──────────────────
31
- interface Preset { diamonds: number; diamondCount: number; lenMul: number; widthMul: number; speed: number; wobble: number; intensity: number; }
32
- const PRESETS: Record<EngineMode, Preset> = {
33
- ion: { diamonds: 0.18, diamondCount: 6.0, lenMul: 1.15, widthMul: 0.92, speed: 1.25, wobble: 0.055, intensity: 1.0 },
34
- chem: { diamonds: 0.95, diamondCount: 5.0, lenMul: 0.85, widthMul: 1.12, speed: 1.0, wobble: 0.075, intensity: 1.05 },
35
- plasma: { diamonds: 0.45, diamondCount: 7.0, lenMul: 1.0, widthMul: 1.0, speed: 1.5, wobble: 0.065, intensity: 1.05 },
36
- };
37
-
38
- const FLAME_LEN = 3.3;
39
- const FLAME_R = 0.235;
40
-
41
- const DEFAULT_PARAMS: ThrusterFlameParams = {
42
- ...LIFETIME_DEFAULTS,
43
- throttle: 60, flow: 50, afterburner: 0, mode: 'ion',
44
- rootColor: { r: 0.42, g: 0.80, b: 1.0 },
45
- tipColor: { r: 0.10, g: 0.30, b: 1.0 },
46
- coreColor: { r: 1.0, g: 1.0, b: 1.0 },
47
- };
48
-
49
- // ── GLSL(从 babylon-laser 原样移植,一字未改)──────────────────────────────────
50
- const FLAME_VERTEX = `precision highp float;
51
- attribute vec3 position;
52
- attribute vec2 uv;
53
- uniform mat4 worldViewProjection;
54
- uniform float time;
55
- uniform float uSpeed;
56
- uniform float uWobble;
57
- varying vec2 vUV;
58
- void main(){
59
- vUV = uv;
60
- float v = uv.y;
61
- float ang = uv.x * 6.2831853;
62
- vec3 p = position;
63
- float w = sin(v * 9.0 - time * uSpeed * 9.5 + sin(ang * 3.0 + time * 1.3) * 1.6)
64
- + 0.5 * sin(v * 17.0 - time * uSpeed * 14.5 + ang * 2.0);
65
- float amp = pow(v, 1.6) * uWobble;
66
- p.xy += vec2(cos(ang), sin(ang)) * w * amp;
67
- float sway = pow(v, 2.0) * uWobble * 1.3;
68
- p.x += sin(time * 2.1) * sway;
69
- p.y += cos(time * 1.6) * sway;
70
- p.z *= 1.0 + 0.05 * sin(time * 13.0 + ang) * v;
71
- gl_Position = worldViewProjection * vec4(p, 1.0);
72
- }`;
73
-
74
- const FLAME_FRAGMENT = `precision highp float;
75
- varying vec2 vUV;
76
- uniform sampler2D noiseTex;
77
- uniform float time;
78
- uniform float uSpeed;
79
- uniform float uIntensity;
80
- uniform vec3 uColorCore;
81
- uniform vec3 uColorRoot;
82
- uniform vec3 uColorTip;
83
- uniform float uDiamonds;
84
- uniform float uDiamondCount;
85
- void main(){
86
- float v = vUV.y;
87
- float u = vUV.x;
88
- float n1 = texture2D(noiseTex, vec2(u * 2.0, v * 1.8 - time * uSpeed * 2.8)).r;
89
- float n2 = texture2D(noiseTex, vec2(u * 4.0 + 0.37, v * 3.6 - time * uSpeed * 5.2)).g;
90
- float n = n1 * 0.62 + n2 * 0.38;
91
- float th = mix(0.13, 0.80, pow(v, 1.15));
92
- float body = smoothstep(th, th + 0.22, n);
93
- float tipFade = 1.0 - smoothstep(0.62, 1.0, v);
94
- float rootCore = pow(1.0 - v, 2.2);
95
- float dm = pow(abs(sin(v * uDiamondCount * 3.14159)), 16.0)
96
- * (1.0 - smoothstep(0.45, 0.78, v)) * uDiamonds;
97
- float heat = clamp(rootCore * 1.45 + n * 0.25 + dm * 0.85, 0.0, 1.0);
98
- vec3 col = mix(uColorRoot, uColorTip, smoothstep(0.18, 0.92, v));
99
- col = mix(col, uColorCore, smoothstep(0.65, 1.0, heat));
100
- float a = body * tipFade;
101
- a = max(a, rootCore * 0.92);
102
- float strength = (a + dm * 0.9) * uIntensity;
103
- gl_FragColor = vec4(col * strength, 1.0);
104
- }`;
105
-
106
- const SWIRL_FRAGMENT = `precision highp float;
107
- varying vec2 vUV;
108
- uniform sampler2D noiseTex;
109
- uniform float time;
110
- uniform float uSpeed;
111
- uniform float uIntensity;
112
- uniform vec3 uColorRoot;
113
- void main(){
114
- float v = vUV.y;
115
- float u = vUV.x;
116
- float n = texture2D(noiseTex,
117
- vec2(u * 1.5 + v * 1.4 - time * uSpeed * 2.0, v * 1.2 - time * uSpeed * 3.8)).r;
118
- float streak = smoothstep(0.55, 0.82, n);
119
- float fade = sin(v * 3.14159);
120
- float strength = streak * fade * uIntensity * 0.30;
121
- gl_FragColor = vec4(uColorRoot * strength, 1.0);
122
- }`;
123
-
124
- let shadersRegistered = false;
125
- function registerShaders(): void {
126
- if (shadersRegistered) return;
127
- Effect.ShadersStore['vfxThrusterFlameVertexShader'] = FLAME_VERTEX;
128
- Effect.ShadersStore['vfxThrusterFlameFragmentShader'] = FLAME_FRAGMENT;
129
- Effect.ShadersStore['vfxThrusterSwirlFragmentShader'] = SWIRL_FRAGMENT;
130
- shadersRegistered = true;
131
- }
132
-
133
- // ── 程序噪声纹理(可平铺 fBM,R/G 两套八度)──────────────────────────────────────
134
- function makeNoiseTexture(scene: Scene): RawTexture {
135
- const S = 256;
136
- const data = new Uint8Array(S * S * 4);
137
- type Lat = { vals: Float32Array; g: number };
138
- function lattice(seed: number, g: number): Lat {
139
- const vals = new Float32Array(g * g);
140
- let s = seed;
141
- for (let i = 0; i < vals.length; i++) { s = (s * 16807) % 2147483647; vals[i] = (s - 1) / 2147483646; }
142
- return { vals, g };
143
- }
144
- function sample(L: Lat, x: number, y: number): number {
145
- const g = L.g; const fx = x * g, fy = y * g;
146
- let x0 = Math.floor(fx), y0 = Math.floor(fy);
147
- const tx = fx - x0, ty = fy - y0;
148
- const sx = tx * tx * (3 - 2 * tx), sy = ty * ty * (3 - 2 * ty);
149
- const x1 = (x0 + 1) % g, y1 = (y0 + 1) % g; x0 = ((x0 % g) + g) % g; y0 = ((y0 % g) + g) % g;
150
- const a = L.vals[y0 * g + x0], b = L.vals[y0 * g + x1], c = L.vals[y1 * g + x0], d = L.vals[y1 * g + x1];
151
- return a + (b - a) * sx + (c - a) * sy + (a - b - c + d) * sx * sy;
152
- }
153
- function fbm(layers: Lat[], x: number, y: number): number {
154
- let v = 0, amp = 0.5, tot = 0;
155
- for (const L of layers) { v += sample(L, x, y) * amp; tot += amp; amp *= 0.5; }
156
- return v / tot;
157
- }
158
- const R = [lattice(1234, 4), lattice(5678, 8), lattice(91011, 16), lattice(1213, 32)];
159
- const G = [lattice(4321, 8), lattice(8765, 16), lattice(1110, 32), lattice(3141, 64)];
160
- for (let y = 0; y < S; y++) for (let x = 0; x < S; x++) {
161
- const u = x / S, vv = y / S; const o = (y * S + x) * 4;
162
- data[o] = (fbm(R, u, vv) * 255) | 0;
163
- data[o + 1] = (fbm(G, u, vv) * 255) | 0;
164
- data[o + 2] = 0; data[o + 3] = 255;
165
- }
166
- const tex = new RawTexture(data, S, S, Constants.TEXTUREFORMAT_RGBA, scene, false, false, Texture.BILINEAR_SAMPLINGMODE);
167
- tex.wrapU = tex.wrapV = Texture.WRAP_ADDRESSMODE;
168
- return tex;
169
- }
170
-
171
- // ── 喇叭锥管几何(沿 +Z,z=0 喷口、z=length 尾尖,无端盖)──────────────────────────
172
- function buildFlameMesh(name: string, scene: Scene, length: number, radius: number, rings: number, sides: number): Mesh {
173
- const positions: number[] = [], uvs: number[] = [], indices: number[] = [];
174
- for (let i = 0; i <= rings; i++) {
175
- const t = i / rings;
176
- const bulge = 1 + 0.30 * Math.sin(Math.PI * Math.min(t / 0.55, 1));
177
- const taper = t < 0.25 ? 1 : Math.pow(1 - (t - 0.25) / 0.75, 0.78);
178
- const r = radius * bulge * taper + 0.0005;
179
- for (let j = 0; j <= sides; j++) {
180
- const a = (j / sides) * 2 * Math.PI;
181
- positions.push(Math.cos(a) * r, Math.sin(a) * r, t * length);
182
- uvs.push(j / sides, t);
183
- }
184
- }
185
- for (let i = 0; i < rings; i++) for (let j = 0; j < sides; j++) {
186
- const a = i * (sides + 1) + j, b = a + 1, c = a + sides + 1, d = c + 1;
187
- indices.push(a, c, b, b, c, d);
188
- }
189
- const mesh = new Mesh(name, scene);
190
- const vd = new VertexData(); vd.positions = positions; vd.uvs = uvs; vd.indices = indices;
191
- vd.applyToMesh(mesh);
192
- mesh.alwaysSelectAsActiveMesh = true; // 顶点位移超出包围盒,关视锥剔除
193
- mesh.isPickable = false;
194
- return mesh;
195
- }
196
-
197
- function makeFXMaterial(name: string, scene: Scene, fragment: 'vfxThrusterFlame' | 'vfxThrusterSwirl', noiseTex: RawTexture): ShaderMaterial {
198
- const mat = new ShaderMaterial(name, scene, { vertex: 'vfxThrusterFlame', fragment }, {
199
- attributes: ['position', 'uv'],
200
- uniforms: ['worldViewProjection', 'time', 'uSpeed', 'uWobble', 'uIntensity', 'uColorCore', 'uColorRoot', 'uColorTip', 'uDiamonds', 'uDiamondCount'],
201
- samplers: ['noiseTex'],
202
- needAlphaBlending: true,
203
- });
204
- mat.setTexture('noiseTex', noiseTex);
205
- mat.alphaMode = Constants.ALPHA_ADD;
206
- mat.backFaceCulling = false;
207
- mat.disableDepthWrite = true;
208
- return mat;
209
- }
210
-
211
- function v3(c: VfxColorValue): Vector3 { return new Vector3(c.r, c.g, c.b); }
212
-
213
- // 把网格从场景里任何 GlowLayer 排除(ctx 不带 glow 引用,自查 scene.effectLayers)
214
- function excludeFromGlow(scene: Scene, mesh: AbstractMesh): void {
215
- for (const layer of scene.effectLayers ?? []) {
216
- const add = (layer as unknown as { addExcludedMesh?: (m: AbstractMesh) => void }).addExcludedMesh;
217
- if (typeof add === 'function') add.call(layer, mesh);
218
- }
219
- }
220
-
221
- export const thrusterFlameEffectPackage: VfxEffectPackage<ThrusterFlameParams> = {
222
- id: 'thruster-flame',
223
- nameZh: '战机尾焰',
224
- defaultParams: DEFAULT_PARAMS,
225
- debugAnchor: 'playerRight',
226
- debugParams: [
227
- ...LIFETIME_DEBUG_PARAMS,
228
- { key: 'throttle', label: '推力 %', kind: 'number', min: 0, max: 100, step: 1 },
229
- { key: 'flow', label: '流速 %', kind: 'number', min: 0, max: 100, step: 1 },
230
- { key: 'afterburner', label: '加力 %', kind: 'number', min: 0, max: 100, step: 1 },
231
- { key: 'mode', label: '引擎模式', kind: 'enum', options: [
232
- { value: 'ion', label: '离子(蓝)' }, { value: 'chem', label: '化燃(橙)' }, { value: 'plasma', label: '等离子(紫)' },
233
- ] },
234
- { key: 'rootColor', label: '根部色', kind: 'color' },
235
- { key: 'tipColor', label: '尾端色', kind: 'color' },
236
- { key: 'coreColor', label: '内核色', kind: 'color' },
237
- ],
238
-
239
- create({ scene, root, position, params }) {
240
- root.position.copyFrom(position);
241
- registerShaders();
242
- const preset = PRESETS[(params.mode as EngineMode)] ?? PRESETS.ion;
243
- const noiseTex = makeNoiseTexture(scene);
244
-
245
- const outer = buildFlameMesh('vfx_thruster_outer', scene, FLAME_LEN, FLAME_R, 30, 24);
246
- const core = buildFlameMesh('vfx_thruster_core', scene, FLAME_LEN * 0.68, FLAME_R * 0.5, 24, 18);
247
- const swirl = buildFlameMesh('vfx_thruster_swirl', scene, FLAME_LEN * 0.42, FLAME_R * 1.3, 14, 24);
248
- for (const m of [outer, core, swirl]) { m.parent = root; excludeFromGlow(scene, m); }
249
-
250
- const outerMat = makeFXMaterial('vfx_thruster_outerMat', scene, 'vfxThrusterFlame', noiseTex);
251
- const coreMat = makeFXMaterial('vfx_thruster_coreMat', scene, 'vfxThrusterFlame', noiseTex);
252
- const swirlMat = makeFXMaterial('vfx_thruster_swirlMat', scene, 'vfxThrusterSwirl', noiseTex);
253
- outer.material = outerMat; core.material = coreMat; swirl.material = swirlMat;
254
-
255
- // 颜色分配(coreMat 整体更靠 core/root;swirl 只用 root)
256
- const coreCol = v3(params.coreColor), rootCol = v3(params.rootColor), tipCol = v3(params.tipColor);
257
- outerMat.setVector3('uColorCore', coreCol); outerMat.setVector3('uColorRoot', rootCol); outerMat.setVector3('uColorTip', tipCol);
258
- coreMat.setVector3('uColorCore', coreCol); coreMat.setVector3('uColorRoot', coreCol); coreMat.setVector3('uColorTip', rootCol);
259
- swirlMat.setVector3('uColorRoot', rootCol);
260
- for (const mat of [outerMat, coreMat, swirlMat]) { mat.setFloat('uDiamondCount', preset.diamondCount); mat.setFloat('uDiamonds', 0); }
261
-
262
- // 缓动状态
263
- let t = 0, throttle = 0, flow = 0, abVis = 0;
264
- const targetThrottle = params.throttle / 100, targetFlow = params.flow / 100, targetAb = params.afterburner / 100;
265
-
266
- const observer = scene.onBeforeRenderObservable.add(() => {
267
- const dt = Math.min(scene.getEngine().getDeltaTime() / 1000, 0.05); t += dt;
268
- const k = 1 - Math.exp(-dt * 5);
269
- throttle += (targetThrottle - throttle) * k;
270
- flow += (targetFlow - flow) * k;
271
- abVis += (targetAb - abVis) * (1 - Math.exp(-dt * 7));
272
- const eff = throttle * (1 + abVis * 0.45);
273
- const flowMul = 0.2 + flow * 4.8;
274
-
275
- const lenS = (0.3 + eff * 1.0) * preset.lenMul;
276
- const widS = (0.8 + eff * 0.32) * preset.widthMul;
277
- const flick = 1 + 0.04 * Math.sin(t * 17.0) + 0.03 * Math.sin(t * 29.3);
278
- outer.scaling.set(widS, widS, lenS * flick);
279
- core.scaling.set(widS, widS, lenS * (1 + abVis * 0.25) * flick);
280
- swirl.scaling.set(widS * 1.05, widS * 1.05, Math.max(lenS * 0.85, 0.25));
281
-
282
- const inten = preset.intensity * (0.12 + eff * 1.05);
283
- const speed = preset.speed * flowMul;
284
- const wob = preset.wobble * (0.7 + eff * 0.6);
285
- for (const mat of [outerMat, coreMat, swirlMat]) {
286
- mat.setFloat('time', t); mat.setFloat('uSpeed', speed); mat.setFloat('uWobble', wob); mat.setFloat('uIntensity', inten);
287
- }
288
- outerMat.setFloat('uDiamonds', preset.diamonds * Math.min(1, eff * 1.2) * (1 + abVis * 0.8));
289
- });
290
-
291
- let disposed = false;
292
- let ltTimer = 0;
293
- const dispose = (): void => {
294
- if (disposed) return; disposed = true;
295
- if (ltTimer) window.clearTimeout(ltTimer);
296
- scene.onBeforeRenderObservable.remove(observer);
297
- outer.dispose(); core.dispose(); swirl.dispose();
298
- outerMat.dispose(); coreMat.dispose(); swirlMat.dispose();
299
- noiseTex.dispose();
300
- root.dispose();
301
- };
302
- ltTimer = scheduleLifetime(params, dispose);
303
- return { root, dispose };
304
- },
305
- };
@@ -1,8 +0,0 @@
1
- {
2
- "id": "tree-chop-dust",
3
- "nameZh": "砍树烟尘",
4
- "source": "runtime-generated",
5
- "assets": [
6
- "assets/textures/tree_chop_dust_atlas.png"
7
- ]
8
- }
@@ -1,202 +0,0 @@
1
- import { Color4 } from '@babylonjs/core/Maths/math.color';
2
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import { Sprite } from '@babylonjs/core/Sprites/sprite';
4
- import { SpriteManager } from '@babylonjs/core/Sprites/spriteManager';
5
- import type { Scene } from '@babylonjs/core/scene';
6
- import type { TransformNode } from '@babylonjs/core/Meshes/transformNode';
7
- import type { VfxColorValue, VfxEffectPackage, VfxParamValues } from '../../types';
8
- import { configureVfxSpriteManager } from '../../rendering';
9
- import manifest from './effect.json';
10
-
11
- interface TreeChopDustParams extends VfxParamValues {
12
- lifetime: number;
13
- scale: number;
14
- initialHeight: number;
15
- color: VfxColorValue;
16
- opacity: number;
17
- particleCount: number;
18
- emitRange: number;
19
- riseHeight: number;
20
- spread: number;
21
- dustSize: number;
22
- driftSpeed: number;
23
- gravity: number;
24
- }
25
-
26
- interface DustSpriteState {
27
- sprite: Sprite;
28
- velocity: Vector3;
29
- spin: number;
30
- width: number;
31
- height: number;
32
- lifetime: number;
33
- startDelay: number;
34
- }
35
-
36
- import DUST_TEXTURE_URL from './assets/textures/tree_chop_dust_atlas.png?url';
37
-
38
- const DEFAULT_PARAMS: TreeChopDustParams = {
39
- lifetime: 0.95,
40
- scale: 1,
41
- initialHeight: 1.05,
42
- color: { r: 0.33, g: 0.31, b: 0.28 },
43
- opacity: 0.82,
44
- particleCount: 28,
45
- emitRange: 0.3,
46
- riseHeight: 0.72,
47
- spread: 0.62,
48
- dustSize: 0.92,
49
- driftSpeed: 0.9,
50
- gravity: 0.18,
51
- };
52
-
53
- export const treeChopDustEffectPackage: VfxEffectPackage<TreeChopDustParams> = {
54
- id: 'tree-chop-dust',
55
- nameZh: '砍树烟尘',
56
- manifest,
57
- defaultParams: DEFAULT_PARAMS,
58
- debugAnchor: 'player',
59
- debugParams: [
60
- { key: 'lifetime', label: '生命周期', kind: 'lifetime', min: 0.08, max: 3, step: 0.01 },
61
- { key: 'scale', label: '整体缩放', kind: 'scale', min: 0.1, max: 6, step: 0.01 },
62
- { key: 'initialHeight', label: '初始高度', kind: 'number', min: 0, max: 3, step: 0.01 },
63
- { key: 'color', label: '烟尘颜色', kind: 'color' },
64
- { key: 'opacity', label: '透明度', kind: 'opacity', min: 0, max: 1, step: 0.01 },
65
- { key: 'particleCount', label: '粒子数量', kind: 'number', min: 1, max: 80, step: 1 },
66
- { key: 'emitRange', label: '发射范围', kind: 'number', min: 0, max: 2, step: 0.01 },
67
- { key: 'riseHeight', label: '上升高度', kind: 'number', min: 0, max: 3, step: 0.01 },
68
- { key: 'spread', label: '扩散幅度', kind: 'number', min: 0, max: 4, step: 0.01 },
69
- { key: 'dustSize', label: '烟尘大小', kind: 'number', min: 0.02, max: 2, step: 0.01 },
70
- { key: 'driftSpeed', label: '飘散速度', kind: 'number', min: 0, max: 4, step: 0.01 },
71
- { key: 'gravity', label: '下沉重力', kind: 'number', min: 0, max: 4, step: 0.01 },
72
- ],
73
- create({ scene, root, position, renderingGroupId, params }) {
74
- root.position.copyFrom(position).addInPlace(new Vector3(0, params.initialHeight, 0));
75
- const dust = createDustBurst(scene, root, params, renderingGroupId);
76
- let disposed = false;
77
- const disposeTimer = window.setTimeout(() => disposeAll(), (params.lifetime + 0.35) * 1000);
78
-
79
- return {
80
- root,
81
- dispose() {
82
- disposeAll();
83
- },
84
- };
85
-
86
- function disposeAll(): void {
87
- if (disposed) return;
88
- disposed = true;
89
- window.clearTimeout(disposeTimer);
90
- dust.dispose();
91
- root.dispose();
92
- }
93
- },
94
- };
95
-
96
- function createDustBurst(
97
- scene: Scene,
98
- root: TransformNode,
99
- params: TreeChopDustParams,
100
- renderingGroupId?: number,
101
- ): { dispose(): void } {
102
- const count = Math.max(1, Math.round(params.particleCount));
103
- const manager = new SpriteManager('tree-chop-dust.smoke', DUST_TEXTURE_URL, count, { width: 128, height: 128 }, scene);
104
- configureVfxSpriteManager(manager);
105
- if (renderingGroupId !== undefined) manager.renderingGroupId = renderingGroupId;
106
- manager.texture.hasAlpha = true;
107
- manager.fogEnabled = false;
108
-
109
- const effectScale = Math.max(0.05, params.scale);
110
- const origin = root.getAbsolutePosition().clone();
111
- const states: DustSpriteState[] = [];
112
-
113
- for (let index = 0; index < count; index += 1) {
114
- const sprite = new Sprite(`tree-chop-dust.puff.${index}`, manager);
115
- const angle = sequence01(index, 0) * Math.PI * 2;
116
- const radius = params.emitRange * effectScale * Math.sqrt(sequence01(index, 1));
117
- const startY = (sequence01(index, 2) - 0.5) * 0.08 * effectScale;
118
- const speed = params.driftSpeed * effectScale * lerp(0.55, 1.15, sequence01(index, 3));
119
- const size = Math.max(0.01, params.dustSize * effectScale * lerp(0.72, 1.35, sequence01(index, 4)));
120
-
121
- sprite.position = origin.add(new Vector3(Math.cos(angle) * radius, startY, Math.sin(angle) * radius));
122
- sprite.width = size;
123
- sprite.height = size * lerp(0.82, 1.16, sequence01(index, 5));
124
- sprite.angle = sequence01(index, 6) * Math.PI * 2;
125
- sprite.cellIndex = Math.floor(sequence01(index, 7) * 4) % 4;
126
- sprite.color = toColor4(params.color, 0);
127
-
128
- states.push({
129
- sprite,
130
- velocity: new Vector3(
131
- Math.cos(angle) * params.spread * speed,
132
- params.riseHeight * effectScale * lerp(0.18, 0.46, sequence01(index, 8)),
133
- Math.sin(angle) * params.spread * speed,
134
- ),
135
- spin: lerp(-1.4, 1.4, sequence01(index, 9)),
136
- width: sprite.width,
137
- height: sprite.height,
138
- lifetime: Math.max(0.05, params.lifetime * lerp(0.82, 1.14, sequence01(index, 10))),
139
- startDelay: index * 0.006,
140
- });
141
- }
142
-
143
- let age = 0;
144
- let disposed = false;
145
- const observer = scene.onBeforeRenderObservable.add(() => {
146
- if (disposed) return;
147
- const deltaSeconds = Math.min(0.05, scene.getEngine().getDeltaTime() / 1000);
148
- age += deltaSeconds;
149
- for (const state of states) {
150
- const localAge = age - state.startDelay;
151
- if (localAge < 0) {
152
- state.sprite.isVisible = false;
153
- continue;
154
- }
155
- const t = Math.min(1, localAge / state.lifetime);
156
- const fadeIn = Math.min(1, t / 0.12);
157
- const fadeOut = Math.max(0, 1 - Math.max(0, t - 0.38) / 0.62);
158
- const alpha = params.opacity * fadeIn * fadeOut;
159
- state.velocity.y -= params.gravity * deltaSeconds;
160
- state.sprite.position.addInPlace(state.velocity.scale(deltaSeconds));
161
- state.sprite.angle += state.spin * deltaSeconds;
162
- const grow = 1 + t * 1.55;
163
- state.sprite.width = state.width * grow;
164
- state.sprite.height = state.height * grow;
165
- state.sprite.color = toColor4(params.color, alpha);
166
- state.sprite.isVisible = alpha > 0.01;
167
- }
168
- if (age >= params.lifetime + 0.18) dispose();
169
- });
170
-
171
- function dispose(): void {
172
- if (disposed) return;
173
- disposed = true;
174
- scene.onBeforeRenderObservable.remove(observer);
175
- for (const state of states) state.sprite.dispose();
176
- manager.dispose();
177
- }
178
-
179
- return { dispose };
180
- }
181
-
182
- function toColor4(color: VfxColorValue, alpha: number): Color4 {
183
- return new Color4(clamp01(color.r), clamp01(color.g), clamp01(color.b), clamp01(alpha));
184
- }
185
-
186
- function lerp(min: number, max: number, t: number): number {
187
- return min + (max - min) * t;
188
- }
189
-
190
- function clamp01(value: number): number {
191
- return Math.min(1, Math.max(0, value));
192
- }
193
-
194
- function fract(value: number): number {
195
- return value - Math.floor(value);
196
- }
197
-
198
- function sequence01(index: number, axis: number): number {
199
- if (index === 0) return 0.5;
200
- const strides = [0.618033988749895, 0.414213562373095, 0.732050807568877, 0.324717957244746, 0.754877666246693, 0.569840290998053];
201
- return fract(0.5 + index * strides[axis % strides.length] + axis * 0.173205080756888);
202
- }