@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,204 +0,0 @@
1
- // 脉冲激光齐射(移植自 babylon-laser pulseSystem, 特效#12)。
2
- // ShaderMaterial 乘色范式(复刻 Unity Particle Add):贴图中性白+alpha,颜色全由 shader tint 决定。
3
- // 自包含库版:沿 root 局部 +Z 朝距离 targetDistance 的虚拟目标自动整排齐射光弹池 + 发射点白球。
4
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
5
- import { CreatePlane } from '@babylonjs/core/Meshes/Builders/planeBuilder';
6
- import { CreateSphere } from '@babylonjs/core/Meshes/Builders/sphereBuilder';
7
- import { ShaderMaterial } from '@babylonjs/core/Materials/shaderMaterial';
8
- import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
9
- import { Effect } from '@babylonjs/core/Materials/effect';
10
- import { Texture } from '@babylonjs/core/Materials/Textures/texture';
11
- import { Color3 } from '@babylonjs/core/Maths/math.color';
12
- import { Constants } from '@babylonjs/core/Engines/constants';
13
- import type { Mesh } from '@babylonjs/core/Meshes/mesh';
14
- import type { Scene } from '@babylonjs/core/scene';
15
- import type { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
16
- import type { VfxColorValue, VfxEffectPackage, VfxParamValues } from '../../types';
17
- import boltTextureUrl from './assets/textures/bolt_guangshu.webp?url';
18
- import { LIFETIME_DEBUG_PARAMS, LIFETIME_DEFAULTS, scheduleLifetime } from '../space-shared';
19
-
20
- interface PulseLaserParams extends VfxParamValues {
21
- lifetimeMode: string; lifetime: number;
22
- targetDistance: number;
23
- volleyInterval: number;
24
- boltSpeed: number;
25
- boltsPerVolley: number;
26
- boltSpacing: number;
27
- boltLen: number;
28
- boltColor: VfxColorValue;
29
- boltCore: number;
30
- boltCorePow: number;
31
- boltBright: number;
32
- }
33
-
34
- const DEFAULT_PARAMS: PulseLaserParams = {
35
- ...LIFETIME_DEFAULTS,
36
- targetDistance: 12,
37
- volleyInterval: 0.22,
38
- boltSpeed: 26,
39
- boltsPerVolley: 6,
40
- boltSpacing: 0.5,
41
- boltLen: 1.0,
42
- boltColor: { r: 0.071, g: 0.408, b: 1.0 }, // #1268ff
43
- boltCore: 0.3,
44
- boltCorePow: 3.0,
45
- boltBright: 1.3,
46
- };
47
-
48
- const BOLT_LEN = 2.0, BOLT_W = 0.34, POOL_SIZE = 60;
49
-
50
- const BOLT_VERTEX = `precision highp float;
51
- attribute vec3 position; attribute vec2 uv;
52
- uniform mat4 worldViewProjection;
53
- varying vec2 vUV;
54
- void main() { vUV = uv; gl_Position = worldViewProjection * vec4(position, 1.0); }`;
55
-
56
- // 乘色:col = mix(tint, white, pow(a,corePow)*coreStr) * bright; 配 ALPHA_ADD
57
- const BOLT_FRAGMENT = `precision highp float;
58
- varying vec2 vUV;
59
- uniform sampler2D tex;
60
- uniform vec3 tint;
61
- uniform float coreStr; uniform float corePow; uniform float bright;
62
- void main() {
63
- vec4 t = texture2D(tex, vUV);
64
- float core = pow(t.a, corePow) * coreStr;
65
- vec3 col = mix(tint, vec3(1.0), core) * bright;
66
- gl_FragColor = vec4(col, t.a);
67
- }`;
68
-
69
- let shadersRegistered = false;
70
- function registerShaders(): void {
71
- if (shadersRegistered) return;
72
- Effect.ShadersStore['vfxPulseBoltVertexShader'] = BOLT_VERTEX;
73
- Effect.ShadersStore['vfxPulseBoltFragmentShader'] = BOLT_FRAGMENT;
74
- shadersRegistered = true;
75
- }
76
-
77
- function colorVec(c: VfxColorValue): Color3 { return new Color3(c.r, c.g, c.b); }
78
-
79
- function excludeFromGlow(scene: Scene, mesh: AbstractMesh): void {
80
- for (const layer of scene.effectLayers ?? []) {
81
- const add = (layer as unknown as { addExcludedMesh?: (m: AbstractMesh) => void }).addExcludedMesh;
82
- if (typeof add === 'function') add.call(layer, mesh);
83
- }
84
- }
85
-
86
- interface Bolt { mesh: Mesh; left: number; }
87
-
88
- export const pulseLaserVolleyEffectPackage: VfxEffectPackage<PulseLaserParams> = {
89
- id: 'pulse-laser-volley',
90
- nameZh: '脉冲激光',
91
- defaultParams: DEFAULT_PARAMS,
92
- debugAnchor: 'playerRight',
93
- debugParams: [
94
- ...LIFETIME_DEBUG_PARAMS,
95
- { key: 'targetDistance', label: '目标距离', kind: 'number', min: 2, max: 40, step: 0.5 },
96
- { key: 'volleyInterval', label: '齐射间隔(s)', kind: 'number', min: 0.08, max: 1, step: 0.01 },
97
- { key: 'boltSpeed', label: '光弹速度', kind: 'number', min: 5, max: 60, step: 1 },
98
- { key: 'boltsPerVolley', label: '每排弹数', kind: 'number', min: 1, max: 12, step: 1 },
99
- { key: 'boltSpacing', label: '排内间距', kind: 'number', min: 0.2, max: 1.5, step: 0.05 },
100
- { key: 'boltLen', label: '光弹长度', kind: 'number', min: 0.3, max: 4, step: 0.05 },
101
- { key: 'boltColor', label: '光弹颜色', kind: 'color' },
102
- { key: 'boltCore', label: '白芯强度', kind: 'number', min: 0, max: 1, step: 0.01 },
103
- { key: 'boltCorePow', label: '白芯收窄', kind: 'number', min: 0.5, max: 8, step: 0.1 },
104
- { key: 'boltBright', label: '光弹亮度', kind: 'number', min: 0.2, max: 3, step: 0.05 },
105
- ],
106
-
107
- create({ scene, root, position, rotationY, params }) {
108
- root.position.copyFrom(position);
109
- root.rotation.y = rotationY;
110
- registerShaders();
111
-
112
- const boltTex = new Texture(boltTextureUrl, scene);
113
- boltTex.hasAlpha = true;
114
- const boltMat = new ShaderMaterial('vfx_pulse_boltMat', scene, { vertex: 'vfxPulseBolt', fragment: 'vfxPulseBolt' }, {
115
- attributes: ['position', 'uv'],
116
- uniforms: ['worldViewProjection', 'tint', 'coreStr', 'corePow', 'bright'],
117
- samplers: ['tex'], needAlphaBlending: true,
118
- });
119
- boltMat.setTexture('tex', boltTex);
120
- boltMat.backFaceCulling = false;
121
- boltMat.disableDepthWrite = true;
122
- boltMat.alphaMode = Constants.ALPHA_ADD;
123
- boltMat.setColor3('tint', colorVec(params.boltColor));
124
- boltMat.setFloat('coreStr', params.boltCore);
125
- boltMat.setFloat('corePow', params.boltCorePow);
126
- boltMat.setFloat('bright', params.boltBright);
127
-
128
- // 对象池:单面片,长轴沿局部 +Z,均为 root 子节点(在 root 局部空间飞行)
129
- const pool: Bolt[] = [];
130
- for (let i = 0; i < POOL_SIZE; i++) {
131
- const mesh = CreatePlane(`vfx_pulse_bolt${i}`, { width: BOLT_W, height: BOLT_LEN }, scene);
132
- mesh.rotation.x = Math.PI / 2;
133
- mesh.material = boltMat;
134
- mesh.isPickable = false;
135
- mesh.parent = root;
136
- mesh.setEnabled(false);
137
- excludeFromGlow(scene, mesh);
138
- pool.push({ mesh, left: 0 });
139
- }
140
-
141
- // 发射点白球(不排除辉光,吃 bloom)
142
- const muzzle = CreateSphere('vfx_pulse_muzzle', { diameter: 0.85, segments: 16 }, scene);
143
- const muzzleMat = new StandardMaterial('vfx_pulse_muzzleMat', scene);
144
- muzzleMat.emissiveColor = new Color3(1, 1, 1);
145
- muzzleMat.diffuseColor = Color3.Black();
146
- muzzleMat.disableLighting = true;
147
- muzzleMat.alpha = 0.95;
148
- muzzle.material = muzzleMat;
149
- muzzle.isPickable = false;
150
- muzzle.parent = root;
151
- muzzle.position.set(0, 0, 0);
152
- let muzzleFlash = 0;
153
-
154
- function fireVolley(): void {
155
- const n = Math.max(1, Math.round(params.boltsPerVolley));
156
- const dist = params.targetDistance;
157
- let fired = 0;
158
- for (const b of pool) {
159
- if (fired >= n) break;
160
- if (b.left > 0) continue;
161
- const off = (fired - (n - 1) / 2) * params.boltSpacing;
162
- b.mesh.position.set(off, 0, 0); // 沿局部 X 横向排开,从根部出发
163
- b.mesh.scaling.z = params.boltLen / BOLT_LEN;
164
- b.left = dist / params.boltSpeed;
165
- b.mesh.setEnabled(true);
166
- fired += 1;
167
- }
168
- muzzleFlash = 1;
169
- }
170
-
171
- let volleyTimer = 0;
172
- const observer = scene.onBeforeRenderObservable.add(() => {
173
- const dt = Math.min(scene.getEngine().getDeltaTime() / 1000, 0.05);
174
- volleyTimer += dt;
175
- if (volleyTimer >= params.volleyInterval) { volleyTimer = 0; fireVolley(); }
176
-
177
- muzzleFlash = Math.max(0, muzzleFlash - dt * 6);
178
- const s = 1 + muzzleFlash * 0.6;
179
- muzzle.scaling.set(s, s, s);
180
-
181
- const step = params.boltSpeed * dt;
182
- for (const b of pool) {
183
- if (b.left <= 0) continue;
184
- b.left -= dt;
185
- b.mesh.position.z += step; // 沿局部 +Z 匀速飞行
186
- if (b.left <= 0) b.mesh.setEnabled(false);
187
- }
188
- });
189
-
190
- let disposed = false;
191
- let ltTimer = 0;
192
- const dispose = (): void => {
193
- if (disposed) return; disposed = true;
194
- if (ltTimer) window.clearTimeout(ltTimer);
195
- scene.onBeforeRenderObservable.remove(observer);
196
- for (const b of pool) b.mesh.dispose();
197
- muzzle.dispose(); muzzleMat.dispose();
198
- boltMat.dispose(); boltTex.dispose();
199
- root.dispose();
200
- };
201
- ltTimer = scheduleLifetime(params, dispose);
202
- return { root, dispose };
203
- },
204
- };
@@ -1,6 +0,0 @@
1
- {
2
- "id": "resource-collect-sparkles",
3
- "nameZh": "资源持续收集圆点",
4
- "assets": [],
5
- "source": "procedural"
6
- }
@@ -1,254 +0,0 @@
1
- import { Color4 } from '@babylonjs/core/Maths/math.color';
2
- import { Matrix, Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import { DynamicTexture } from '@babylonjs/core/Materials/Textures/dynamicTexture';
4
- import { ParticleSystem } from '@babylonjs/core/Particles/particleSystem';
5
- import type { Particle } from '@babylonjs/core/Particles/particle';
6
- import type { Scene } from '@babylonjs/core/scene';
7
- import type { TransformNode } from '@babylonjs/core/Meshes/transformNode';
8
- import type { VfxColorValue, VfxEffectPackage, VfxParamValues } from '../../types';
9
- import { configureVfxParticleSystem } from '../../rendering';
10
- import manifest from './effect.json';
11
-
12
- interface ResourceCollectSparklesParams extends VfxParamValues {
13
- lifetime: number;
14
- emitMode: string;
15
- scale: number;
16
- initialHeight: number;
17
- color: VfxColorValue;
18
- opacity: number;
19
- particleCount: number;
20
- particleLife: number;
21
- minSize: number;
22
- maxSize: number;
23
- directionX: number;
24
- directionY: number;
25
- directionZ: number;
26
- riseSpeed: number;
27
- drift: number;
28
- glowStrength: number;
29
- }
30
-
31
- const DEFAULT_PARAMS: ResourceCollectSparklesParams = {
32
- lifetime: 1,
33
- emitMode: 'loop',
34
- scale: 1,
35
- initialHeight: 0.035,
36
- color: { r: 1, g: 0.82, b: 0.38 },
37
- opacity: 0.88,
38
- particleCount: 26,
39
- particleLife: 0.86,
40
- minSize: 0.045,
41
- maxSize: 0.12,
42
- directionX: 0,
43
- directionY: 1,
44
- directionZ: 0,
45
- riseSpeed: 0.58,
46
- drift: 0.08,
47
- glowStrength: 2.4,
48
- };
49
-
50
- export const resourceCollectSparklesEffectPackage: VfxEffectPackage<ResourceCollectSparklesParams> = {
51
- id: 'resource-collect-sparkles',
52
- nameZh: '资源持续收集圆点',
53
- manifest,
54
- defaultParams: DEFAULT_PARAMS,
55
- debugAnchor: 'playerRight',
56
- debugParams: [
57
- {
58
- key: 'emitMode',
59
- label: '发射模式',
60
- kind: 'enum',
61
- options: [
62
- { value: 'loop', label: '一直发射' },
63
- { value: 'timed', label: '自定义时间' },
64
- ],
65
- },
66
- { key: 'lifetime', label: '生命周期', kind: 'lifetime', min: 0.08, max: 8, step: 0.01 },
67
- { key: 'scale', label: '整体缩放', kind: 'scale', min: 0.05, max: 6, step: 0.01 },
68
- { key: 'initialHeight', label: '初始高度', kind: 'number', min: -0.5, max: 2, step: 0.01 },
69
- { key: 'color', label: '圆点颜色', kind: 'color' },
70
- { key: 'opacity', label: '透明度', kind: 'opacity', min: 0, max: 1, step: 0.01 },
71
- { key: 'particleCount', label: '粒子数量', kind: 'number', min: 0, max: 240, step: 1 },
72
- { key: 'particleLife', label: '单点生命', kind: 'number', min: 0.08, max: 4, step: 0.01 },
73
- { key: 'minSize', label: '最小大小', kind: 'number', min: 0.005, max: 1, step: 0.001 },
74
- { key: 'maxSize', label: '最大大小', kind: 'number', min: 0.005, max: 1, step: 0.001 },
75
- { key: 'directionX', label: '方向 X', kind: 'number', min: -1, max: 1, step: 0.01 },
76
- { key: 'directionY', label: '方向 Y', kind: 'number', min: -1, max: 1, step: 0.01 },
77
- { key: 'directionZ', label: '方向 Z', kind: 'number', min: -1, max: 1, step: 0.01 },
78
- { key: 'riseSpeed', label: '发射速度', kind: 'number', min: 0, max: 5, step: 0.01 },
79
- { key: 'drift', label: '方向扰动', kind: 'number', min: 0, max: 2, step: 0.01 },
80
- { key: 'glowStrength', label: '光芒强度', kind: 'number', min: 0.1, max: 8, step: 0.05 },
81
- ],
82
- create({ scene, root, position, params }) {
83
- root.position.copyFrom(position).addInPlace(new Vector3(0, params.initialHeight, 0));
84
- const system = createDotSystem(scene, root, params);
85
- let disposed = false;
86
- let stopTimer: number | null = null;
87
- let disposeTimer: number | null = null;
88
-
89
- system.start();
90
- if (readEmitMode(params.emitMode) === 'timed') {
91
- stopTimer = window.setTimeout(() => system.stop(), Math.max(0.01, params.lifetime) * 1000);
92
- disposeTimer = window.setTimeout(
93
- () => disposeAll(),
94
- Math.max(0.05, params.lifetime + params.particleLife + 0.12) * 1000,
95
- );
96
- }
97
-
98
- return {
99
- root,
100
- dispose() {
101
- disposeAll();
102
- },
103
- };
104
-
105
- function disposeAll(): void {
106
- if (disposed) return;
107
- disposed = true;
108
- if (stopTimer !== null) window.clearTimeout(stopTimer);
109
- if (disposeTimer !== null) window.clearTimeout(disposeTimer);
110
- system.stop();
111
- system.dispose(false);
112
- root.dispose();
113
- }
114
- },
115
- };
116
-
117
- function createDotSystem(scene: Scene, root: TransformNode, params: ResourceCollectSparklesParams): ParticleSystem {
118
- const scale = Math.max(0.01, params.scale);
119
- const particleCount = Math.max(0, Math.round(params.particleCount));
120
- const particleLife = Math.max(0.04, params.particleLife);
121
- const emitRate = particleCount / particleLife;
122
- const capacity = Math.max(16, Math.ceil(particleCount * 2.4 + 16));
123
- const system = new ParticleSystem('resource-collect-dots', capacity, scene);
124
- const minSize = Math.max(0.001, Math.min(params.minSize, params.maxSize) * scale);
125
- const maxSize = Math.max(minSize, Math.max(params.minSize, params.maxSize) * scale);
126
-
127
- configureVfxParticleSystem(system);
128
- system.particleTexture = getDotTexture(scene);
129
- system.emitter = root as any;
130
- system.emitRate = emitRate;
131
- system.updateSpeed = 1 / 60;
132
- system.minLifeTime = particleLife * 0.9;
133
- system.maxLifeTime = particleLife * 1.14;
134
- system.minSize = minSize;
135
- system.maxSize = maxSize;
136
- system.minAngularSpeed = 0;
137
- system.maxAngularSpeed = 0;
138
- system.minEmitPower = 1;
139
- system.maxEmitPower = 1;
140
- system.gravity = Vector3.Zero();
141
- system.blendMode = ParticleSystem.BLENDMODE_ADD;
142
- const glow = Math.max(0.1, params.glowStrength);
143
- system.color1 = tintColor(params.color, glow, 0.72 * params.opacity);
144
- system.color2 = tintColor(params.color, glow * 1.18, 0.9 * params.opacity);
145
- system.colorDead = tintColor(params.color, glow, 0);
146
- system.addColorGradient(0, tintColor(params.color, glow * 0.65, 0));
147
- system.addColorGradient(0.18, tintColor(params.color, glow * 1.28, 0.82 * params.opacity));
148
- system.addColorGradient(0.72, tintColor(params.color, glow, 0.68 * params.opacity));
149
- system.addColorGradient(1, tintColor(params.color, glow * 0.5, 0));
150
- system.addVelocityGradient(0, 0.92);
151
- system.addVelocityGradient(0.72, 1);
152
- system.addVelocityGradient(1, 0.42);
153
- system.addDragGradient(0, 0.08);
154
- system.addDragGradient(1, 0.45);
155
-
156
- let positionIndex = 0;
157
- let directionIndex = 4096;
158
- system.startPositionFunction = (worldMatrix: Matrix, positionToUpdate: Vector3, _particle: Particle, isLocal: boolean) => {
159
- const index = positionIndex;
160
- positionIndex += 1;
161
- const local = sampleDotPosition(index, scale);
162
- if (isLocal) positionToUpdate.copyFrom(local);
163
- else Vector3.TransformCoordinatesToRef(local, worldMatrix, positionToUpdate);
164
- };
165
- system.startDirectionFunction = (worldMatrix: Matrix, directionToUpdate: Vector3, _particle: Particle, isLocal: boolean) => {
166
- const index = directionIndex;
167
- directionIndex += 1;
168
- const baseDirection = getEmitDirection(params);
169
- const drift = Math.max(0, params.drift) * scale;
170
- const speed = Math.max(0, params.riseSpeed) * scale * lerp(0.78, 1.18, sequence01(index, 1));
171
- const direction = baseDirection
172
- .scale(speed)
173
- .addInPlace(new Vector3(
174
- (sequence01(index, 0) - 0.5) * drift,
175
- (sequence01(index, 3) - 0.5) * drift * 0.35,
176
- (sequence01(index, 2) - 0.5) * drift,
177
- ));
178
- if (isLocal) directionToUpdate.copyFrom(direction);
179
- else Vector3.TransformNormalToRef(direction, worldMatrix, directionToUpdate);
180
- };
181
-
182
- return system;
183
- }
184
-
185
- function readEmitMode(value: string): 'loop' | 'timed' {
186
- return value === 'timed' || value === '自定义时间' ? 'timed' : 'loop';
187
- }
188
-
189
- function getEmitDirection(params: ResourceCollectSparklesParams): Vector3 {
190
- const direction = new Vector3(params.directionX, params.directionY, params.directionZ);
191
- if (direction.lengthSquared() < 0.0001) direction.set(0, 1, 0);
192
- direction.normalize();
193
- return direction;
194
- }
195
-
196
- function sampleDotPosition(index: number, scale: number): Vector3 {
197
- const radius = Math.sqrt(sequence01(index, 0)) * 0.34 * scale;
198
- const angle = sequence01(index, 1) * Math.PI * 2;
199
- const y = sequence01(index, 2) * 0.018 * scale;
200
- return new Vector3(Math.cos(angle) * radius, y, Math.sin(angle) * radius);
201
- }
202
-
203
- function getDotTexture(scene: Scene): DynamicTexture {
204
- const key = '__vfx_resource_collect_dot_texture';
205
- const metadata = scene.metadata && typeof scene.metadata === 'object' ? scene.metadata as Record<string, unknown> : {};
206
- scene.metadata = metadata;
207
- const existing = metadata[key] as DynamicTexture | undefined;
208
- if (existing) return existing;
209
-
210
- const texture = new DynamicTexture('resource_collect_dot_texture', { width: 64, height: 64 }, scene, true);
211
- const context = texture.getContext() as unknown as CanvasRenderingContext2D;
212
- context.fillStyle = 'rgba(255, 255, 255, 0)';
213
- context.fillRect(0, 0, 64, 64);
214
- const dot = context.createRadialGradient(32, 32, 1, 32, 32, 30);
215
- dot.addColorStop(0, 'rgba(255, 255, 255, 1)');
216
- dot.addColorStop(0.64, 'rgba(255, 255, 255, 1)');
217
- dot.addColorStop(0.84, 'rgba(255, 255, 255, 0.82)');
218
- dot.addColorStop(1, 'rgba(255, 255, 255, 0)');
219
- context.fillStyle = dot;
220
- context.beginPath();
221
- context.arc(32, 32, 30, 0, Math.PI * 2);
222
- context.fill();
223
- texture.hasAlpha = true;
224
- texture.update(false);
225
- metadata[key] = texture;
226
- return texture;
227
- }
228
-
229
- function tintColor(color: VfxColorValue, brightness: number, alpha: number): Color4 {
230
- return new Color4(
231
- Math.max(0, color.r * brightness),
232
- Math.max(0, color.g * brightness),
233
- Math.max(0, color.b * brightness),
234
- clamp01(alpha),
235
- );
236
- }
237
-
238
- function lerp(min: number, max: number, t: number): number {
239
- return min + (max - min) * t;
240
- }
241
-
242
- function clamp01(value: number): number {
243
- return Math.min(1, Math.max(0, value));
244
- }
245
-
246
- function fract(value: number): number {
247
- return value - Math.floor(value);
248
- }
249
-
250
- function sequence01(index: number, axis: number): number {
251
- if (index === 0) return 0.5;
252
- const strides = [0.618033988749895, 0.414213562373095, 0.732050807568877, 0.324717957244746, 0.754877666246693, 0.569840290998053];
253
- return fract(0.5 + index * strides[axis % strides.length] + axis * 0.173205080756888);
254
- }
@@ -1,215 +0,0 @@
1
- export const runtimeId = "runFootstepDust/v1";
2
-
3
- export const runtimeContract = {
4
- runtimeId,
5
- kind: "runFootstepDust",
6
- fields: {
7
- count: "Particles emitted per burst.",
8
- frequency: "Dust burst frequency in bursts per second.",
9
- particleColor: "Base particle tint as a #rrggbb color.",
10
- spread: "Dust spawn jitter behind the character.",
11
- effectScale: "Overall spatial scale for spawn spacing, particle size, speed, and gravity.",
12
- },
13
- };
14
-
15
- function num(value, fallback = 0) {
16
- const next = Number(value);
17
- return Number.isFinite(next) ? next : fallback;
18
- }
19
-
20
- function fract(value) {
21
- return value - Math.floor(value);
22
- }
23
-
24
- function sequence01(index, axis) {
25
- if (index === 0) return 0.5;
26
- const strides = [0.618033988749895, 0.414213562373095, 0.732050807568877, 0.324717957244746, 0.754877666246693, 0.569840290998053];
27
- return fract(0.5 + index * strides[axis % strides.length] + axis * 0.173205080756888);
28
- }
29
-
30
- function readVec(BABYLON, value, fallback = new BABYLON.Vector3(1, 0, 0)) {
31
- if (!value) return fallback;
32
- return new BABYLON.Vector3(num(value.x, fallback.x), num(value.y, fallback.y), num(value.z, fallback.z));
33
- }
34
-
35
- function readColor(BABYLON, value, fallback = new BABYLON.Color4(0.78, 0.7, 0.54, 1)) {
36
- if (typeof value === "string") {
37
- const match = /^#?([0-9a-f]{6})$/i.exec(value);
38
- if (match) {
39
- const hex = match[1];
40
- return new BABYLON.Color4(
41
- parseInt(hex.slice(0, 2), 16) / 255,
42
- parseInt(hex.slice(2, 4), 16) / 255,
43
- parseInt(hex.slice(4, 6), 16) / 255,
44
- 1,
45
- );
46
- }
47
- }
48
- if (value && typeof value === "object") {
49
- return new BABYLON.Color4(num(value.r, fallback.r), num(value.g, fallback.g), num(value.b, fallback.b), num(value.a, 1));
50
- }
51
- return fallback;
52
- }
53
-
54
- function tintColor(BABYLON, color, brightness, alpha) {
55
- return new BABYLON.Color4(
56
- Math.min(1, Math.max(0, color.r * brightness)),
57
- Math.min(1, Math.max(0, color.g * brightness)),
58
- Math.min(1, Math.max(0, color.b * brightness)),
59
- alpha,
60
- );
61
- }
62
-
63
- function makeTexture(BABYLON, scene) {
64
- const texture = new BABYLON.DynamicTexture("runFootstepDustTexture", { width: 128, height: 128 }, scene, true);
65
- const context = texture.getContext();
66
- context.clearRect(0, 0, 128, 128);
67
- for (const puff of [
68
- { x: 48, y: 69, radius: 34, alpha: 0.72 },
69
- { x: 70, y: 60, radius: 31, alpha: 0.64 },
70
- { x: 86, y: 76, radius: 27, alpha: 0.5 },
71
- ]) {
72
- const shadow = context.createRadialGradient(puff.x, puff.y + 2, puff.radius * 0.22, puff.x, puff.y + 2, puff.radius + 5);
73
- shadow.addColorStop(0, `rgba(86, 78, 64, ${puff.alpha * 0.22})`);
74
- shadow.addColorStop(1, "rgba(86, 78, 64, 0)");
75
- context.fillStyle = shadow;
76
- context.beginPath();
77
- context.arc(puff.x, puff.y + 2, puff.radius + 5, 0, Math.PI * 2);
78
- context.fill();
79
- }
80
- for (const puff of [
81
- { x: 48, y: 69, radius: 34, alpha: 0.72 },
82
- { x: 70, y: 60, radius: 31, alpha: 0.64 },
83
- { x: 86, y: 76, radius: 27, alpha: 0.5 },
84
- ]) {
85
- const core = context.createRadialGradient(puff.x, puff.y, puff.radius * 0.12, puff.x, puff.y, puff.radius);
86
- core.addColorStop(0, `rgba(255, 255, 255, ${puff.alpha})`);
87
- core.addColorStop(0.58, `rgba(235, 229, 210, ${puff.alpha * 0.52})`);
88
- core.addColorStop(1, "rgba(220, 212, 188, 0)");
89
- context.fillStyle = core;
90
- context.beginPath();
91
- context.arc(puff.x, puff.y, puff.radius, 0, Math.PI * 2);
92
- context.fill();
93
- }
94
- texture.hasAlpha = true;
95
- texture.update(false);
96
- return texture;
97
- }
98
-
99
- export async function createRunFootstepDustRuntime({ BABYLON, scene, emitter, shouldStart = false }) {
100
- if (!BABYLON) throw new Error("createRunFootstepDustRuntime requires BABYLON.");
101
- if (!scene) throw new Error("createRunFootstepDustRuntime requires scene.");
102
- if (!emitter) throw new Error("createRunFootstepDustRuntime requires emitter.");
103
-
104
- const particlesPerStep = Math.max(0, Math.round(num(emitter.count ?? emitter.particleCount, 6)));
105
- const effectScale = Math.max(0.01, num(emitter.effectScale, 1));
106
- const spread = Math.max(0, num(emitter.spread, 0.3)) * effectScale;
107
- const frequency = Math.max(0, num(emitter.frequency ?? emitter.stepFrequency, 4.5));
108
- const lifeTime = Math.max(0.05, num(emitter.lifeTime ?? emitter.particleLifeTime, 0.42));
109
- const particleSize = Math.max(0.001, num(emitter.particleSize, 0.18)) * effectScale;
110
- const speed = Math.max(0, num(emitter.speed, 0.56)) * effectScale;
111
- const trailOffset = Math.max(0, num(emitter.trailOffset, 0.22)) * effectScale;
112
- const dustColor = readColor(BABYLON, emitter.particleColor);
113
- const capacity = Math.max(48, Math.round(num(emitter.capacity, Math.ceil(particlesPerStep * Math.max(6, frequency * lifeTime * 3)))));
114
- const movementDirection = readVec(BABYLON, emitter.emissionDirection, new BABYLON.Vector3(1, 0, 0));
115
- movementDirection.y = 0;
116
- if (movementDirection.lengthSquared() < 0.0001) movementDirection.set(1, 0, 0);
117
- movementDirection.normalize();
118
- const sideDirection = new BABYLON.Vector3(-movementDirection.z, 0, movementDirection.x);
119
- const backwardDirection = movementDirection.scale(-1);
120
- const system = new BABYLON.ParticleSystem(emitter.name || "run_footstep_dust", capacity, scene);
121
-
122
- system.particleTexture = makeTexture(BABYLON, scene);
123
- system.emitter = readVec(BABYLON, emitter.origin, BABYLON.Vector3.Zero());
124
- system.emitRate = 0;
125
- system.manualEmitCount = 0;
126
- system.updateSpeed = 1 / 60;
127
- system.minLifeTime = lifeTime;
128
- system.maxLifeTime = lifeTime;
129
- system.minSize = particleSize;
130
- system.maxSize = particleSize;
131
- system.minEmitPower = speed;
132
- system.maxEmitPower = speed;
133
- system.minAngularSpeed = 0;
134
- system.maxAngularSpeed = 0;
135
- system.gravity = readVec(BABYLON, emitter.gravity, new BABYLON.Vector3(0, -0.08, 0)).scale(effectScale);
136
- system.blendMode = BABYLON.ParticleSystem.BLENDMODE_STANDARD;
137
- system.color1 = tintColor(BABYLON, dustColor, 1.04, 0.58);
138
- system.color2 = tintColor(BABYLON, dustColor, 0.78, 0.4);
139
- system.colorDead = tintColor(BABYLON, dustColor, 0.52, 0);
140
- system.addColorGradient(0, tintColor(BABYLON, dustColor, 1.04, 0));
141
- system.addColorGradient(0.12, tintColor(BABYLON, dustColor, 1.04, 0.6));
142
- system.addColorGradient(0.58, tintColor(BABYLON, dustColor, 0.86, 0.42));
143
- system.addColorGradient(1, tintColor(BABYLON, dustColor, 0.58, 0));
144
- system.addSizeGradient(0, particleSize * 0.5);
145
- system.addSizeGradient(0.28, particleSize * 1.18);
146
- system.addSizeGradient(0.72, particleSize * 1.65);
147
- system.addSizeGradient(1, particleSize * 1.95);
148
- system.addVelocityGradient(0, 1);
149
- system.addVelocityGradient(0.35, 0.34);
150
- system.addVelocityGradient(1, 0.03);
151
- system.addDragGradient(0, 0.85);
152
- system.addDragGradient(1, 1.9);
153
-
154
- let positionIndex = 0;
155
- let directionIndex = 0;
156
- system.startPositionFunction = (worldMatrix, positionToUpdate, _particle, isLocal) => {
157
- const index = positionIndex;
158
- positionIndex += 1;
159
- const sideJitter = (sequence01(index, 1) - 0.5) * spread * 0.55;
160
- const backwardJitter = sequence01(index, 2) * spread * 0.75;
161
- const lift = sequence01(index, 3) * 0.012 * effectScale;
162
- const x = backwardDirection.x * (trailOffset + backwardJitter) + sideDirection.x * sideJitter;
163
- const y = lift;
164
- const z = backwardDirection.z * (trailOffset + backwardJitter) + sideDirection.z * sideJitter;
165
- if (isLocal) positionToUpdate.set(x, y, z);
166
- else BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, worldMatrix, positionToUpdate);
167
- };
168
- system.startDirectionFunction = (worldMatrix, directionToUpdate, _particle, isLocal) => {
169
- const index = directionIndex;
170
- directionIndex += 1;
171
- const backward = backwardDirection.scale(0.28 + sequence01(index, 4) * 0.26);
172
- const sideways = sideDirection.scale((sequence01(index, 5) - 0.5) * 0.28);
173
- const x = backward.x + sideways.x;
174
- const y = 0.08 + sequence01(index, 6) * 0.1;
175
- const z = backward.z + sideways.z;
176
- if (isLocal) directionToUpdate.set(x, y, z);
177
- else BABYLON.Vector3.TransformNormalFromFloatsToRef(x, y, z, worldMatrix, directionToUpdate);
178
- };
179
-
180
- let observer = null;
181
- let timeToNextStep = 0;
182
- const interval = frequency > 0 ? 1 / frequency : Number.POSITIVE_INFINITY;
183
- const start = () => {
184
- if (observer) return;
185
- system.start();
186
- observer = scene.onBeforeRenderObservable.add(() => {
187
- if (particlesPerStep <= 0 || !Number.isFinite(interval)) return;
188
- timeToNextStep -= scene.getEngine().getDeltaTime() / 1000;
189
- while (timeToNextStep <= 0) {
190
- system.manualEmitCount += particlesPerStep;
191
- timeToNextStep += interval;
192
- }
193
- });
194
- };
195
-
196
- const preview = {
197
- start,
198
- stop: () => {
199
- if (observer) scene.onBeforeRenderObservable.remove(observer);
200
- observer = null;
201
- system.stop();
202
- },
203
- dispose: () => {
204
- if (observer) scene.onBeforeRenderObservable.remove(observer);
205
- observer = null;
206
- system.particleTexture?.dispose();
207
- system.dispose();
208
- },
209
- isStarted: () => Boolean(observer),
210
- duration: Math.max(1, lifeTime + 1 / Math.max(0.001, frequency)),
211
- system,
212
- };
213
- if (shouldStart) preview.start();
214
- return preview;
215
- }