@fps-games/vfx 0.3.2 → 0.3.4

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 (234) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +5 -5
  3. package/dist/effects/treasure-attention/index.d.ts +1 -1
  4. package/package.json +6 -5
  5. package/tooling/vfx-cli.mjs +31 -22
  6. package/tooling/vfx-package-resolution.mjs +8 -8
  7. package/docs/AUTHORING_EFFECTS.md +0 -297
  8. package/docs/INTEGRATION.md +0 -194
  9. package/docs/VFX_LIBRARY_ARCHITECTURE_chatgpt.md +0 -1580
  10. package/docs/VFX_LIBRARY_EXECUTION_PLAN.md +0 -229
  11. package/docs/VFX_LIBRARY_PLAN.md +0 -546
  12. package/docs/hit-flash-red-method.md +0 -155
  13. package/docs/treasure_attention_fx_babylonjs_guide.md +0 -901
  14. package/docs/vfx-ab/RESULTS.md +0 -35
  15. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/after.png +0 -0
  16. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/before.png +0 -0
  17. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/after.png +0 -0
  18. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/before.png +0 -0
  19. package/docs/vfx-integration_chatgpt.md +0 -462
  20. package/docs/vfx-placement-design.md +0 -164
  21. package/docs/weapon-trail-visualization.html +0 -241
  22. package/packages/EffectPackageService.ts +0 -346
  23. package/packages/SceneVfxService.ts +0 -96
  24. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/runtime/simpleMeshEffect.v1.js +0 -333
  25. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.frag.glsl +0 -25
  26. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.vert.glsl +0 -11
  27. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/textures/effect_jianbian.png +0 -0
  28. package/packages/build.mesh.yellow_build_upgrade_indicator/effect.json +0 -927
  29. package/packages/build.mesh.yellow_build_upgrade_indicator/media/contact-sheet.png +0 -0
  30. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.gif +0 -0
  31. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.mp4 +0 -0
  32. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.webm +0 -0
  33. package/packages/build.mesh.yellow_build_upgrade_indicator/media/thumbnail.png +0 -0
  34. package/packages/build.mesh.yellow_build_upgrade_indicator/metadata/description.json +0 -197
  35. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-effect.original.json +0 -895
  36. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-normalized-vfx-to-babylon-report.json +0 -241
  37. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-overrides.json +0 -542
  38. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-tuning-report.json +0 -69
  39. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/asset-map.json +0 -125
  40. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/lineage.json +0 -44
  41. package/packages/build.particle.golden_upgrade_starburst/assets/runtime/particleEmitter.v1.js +0 -210
  42. package/packages/build.particle.golden_upgrade_starburst/assets/textures/effect_guangdian.png +0 -0
  43. package/packages/build.particle.golden_upgrade_starburst/effect.json +0 -880
  44. package/packages/build.particle.golden_upgrade_starburst/media/contact-sheet.png +0 -0
  45. package/packages/build.particle.golden_upgrade_starburst/media/preview.gif +0 -0
  46. package/packages/build.particle.golden_upgrade_starburst/media/preview.mp4 +0 -0
  47. package/packages/build.particle.golden_upgrade_starburst/media/preview.webm +0 -0
  48. package/packages/build.particle.golden_upgrade_starburst/media/thumbnail.png +0 -0
  49. package/packages/build.particle.golden_upgrade_starburst/metadata/description.json +0 -161
  50. package/packages/build.particle.golden_upgrade_starburst/provenance/03-effect.original.json +0 -762
  51. package/packages/build.particle.golden_upgrade_starburst/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  52. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-overrides.json +0 -436
  53. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-tuning-report.json +0 -40
  54. package/packages/build.particle.golden_upgrade_starburst/provenance/asset-map.json +0 -115
  55. package/packages/build.particle.golden_upgrade_starburst/provenance/lineage.json +0 -40
  56. package/packages/build.particle.orange_upgrade_transform_sparks/assets/runtime/particleEmitter.v1.js +0 -210
  57. package/packages/build.particle.orange_upgrade_transform_sparks/assets/textures/b5b27ab1-e740-4398-b407-848fc2b2c897.png +0 -0
  58. package/packages/build.particle.orange_upgrade_transform_sparks/effect.json +0 -887
  59. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.gif +0 -0
  60. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.mp4 +0 -0
  61. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.webm +0 -0
  62. package/packages/build.particle.orange_upgrade_transform_sparks/media/thumbnail.png +0 -0
  63. package/packages/build.particle.orange_upgrade_transform_sparks/metadata/description.json +0 -179
  64. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-effect.original.json +0 -859
  65. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  66. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-overrides.json +0 -478
  67. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-tuning-report.json +0 -40
  68. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/asset-map.json +0 -114
  69. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/lineage.json +0 -41
  70. package/packages/build.particle.wood_chip_dust_burst/assets/runtime/particleEmitter.v1.js +0 -210
  71. package/packages/build.particle.wood_chip_dust_burst/assets/textures/effect_yanwu4.png +0 -0
  72. package/packages/build.particle.wood_chip_dust_burst/assets/textures/suipian3.png +0 -0
  73. package/packages/build.particle.wood_chip_dust_burst/effect.json +0 -1400
  74. package/packages/build.particle.wood_chip_dust_burst/media/contact-sheet.png +0 -0
  75. package/packages/build.particle.wood_chip_dust_burst/media/preview.gif +0 -0
  76. package/packages/build.particle.wood_chip_dust_burst/media/preview.mp4 +0 -0
  77. package/packages/build.particle.wood_chip_dust_burst/media/preview.webm +0 -0
  78. package/packages/build.particle.wood_chip_dust_burst/media/thumbnail.png +0 -0
  79. package/packages/build.particle.wood_chip_dust_burst/metadata/description.json +0 -173
  80. package/packages/build.particle.wood_chip_dust_burst/provenance/03-effect.original.json +0 -1277
  81. package/packages/build.particle.wood_chip_dust_burst/provenance/03-normalized-vfx-to-babylon-report.json +0 -329
  82. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-overrides.json +0 -749
  83. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-tuning-report.json +0 -60
  84. package/packages/build.particle.wood_chip_dust_burst/provenance/asset-map.json +0 -146
  85. package/packages/build.particle.wood_chip_dust_burst/provenance/lineage.json +0 -69
  86. package/packages/effects/building-upgrade/effect.json +0 -6
  87. package/packages/effects/building-upgrade/index.ts +0 -387
  88. package/packages/effects/character-entrance/effect.json +0 -6
  89. package/packages/effects/character-entrance/index.ts +0 -308
  90. package/packages/effects/coin-burst/index.ts +0 -48
  91. package/packages/effects/comet-trail-flame/assets/textures/trail_5x4.webp +0 -0
  92. package/packages/effects/comet-trail-flame/index.ts +0 -107
  93. package/packages/effects/conveyor-upgrade/effect.json +0 -6
  94. package/packages/effects/conveyor-upgrade/index.ts +0 -401
  95. package/packages/effects/debris-smoke/assets/models/rock_debris.glb +0 -0
  96. package/packages/effects/debris-smoke/assets/textures/smoke22.webp +0 -0
  97. package/packages/effects/debris-smoke/index.ts +0 -102
  98. package/packages/effects/door_open_gold_flakes/assets/runtime/goldFlakes.v1.js +0 -397
  99. package/packages/effects/door_open_gold_flakes/door_open_gold_flakes.json +0 -498
  100. package/packages/effects/door_open_gold_flakes/index.ts +0 -11
  101. package/packages/effects/electric-arc/assets/textures/spark_4x4.webp +0 -0
  102. package/packages/effects/electric-arc/index.ts +0 -63
  103. package/packages/effects/energy-shield/effect.json +0 -7
  104. package/packages/effects/energy-shield/index.ts +0 -432
  105. package/packages/effects/explosion/assets/textures/baozha29.webp +0 -0
  106. package/packages/effects/explosion/assets/textures/glow01.webp +0 -0
  107. package/packages/effects/explosion/assets/textures/lizi01.webp +0 -0
  108. package/packages/effects/explosion/assets/textures/xulie_lizibaozha02_5x5.webp +0 -0
  109. package/packages/effects/explosion/index.ts +0 -115
  110. package/packages/effects/ground-tile-upgrade/effect.json +0 -6
  111. package/packages/effects/ground-tile-upgrade/index.ts +0 -410
  112. package/packages/effects/hit-flash/index.ts +0 -77
  113. package/packages/effects/laser-beam/assets/textures/laser_glow.webp +0 -0
  114. package/packages/effects/laser-beam/assets/textures/laser_noise.webp +0 -0
  115. package/packages/effects/laser-beam/index.ts +0 -6
  116. package/packages/effects/laser-beam-factory.ts +0 -146
  117. package/packages/effects/lumber-cart-suction/effect.json +0 -6
  118. package/packages/effects/lumber-cart-suction/index.ts +0 -399
  119. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb +0 -0
  120. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/.active +0 -1
  121. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/1781589063890_upload.glb +0 -0
  122. package/packages/effects/lumber-debris-scatter/assets/runtime/solidParticleScatter.v1.js +0 -236
  123. package/packages/effects/lumber-debris-scatter/effect.json +0 -228
  124. package/packages/effects/lumber-debris-scatter/index.ts +0 -780
  125. package/packages/effects/lumber_vehicle_gear_spin/assets/runtime/gearSpin.v1.js +0 -202
  126. package/packages/effects/lumber_vehicle_gear_spin/index.ts +0 -11
  127. package/packages/effects/lumber_vehicle_gear_spin/lumber_vehicle_gear_spin.json +0 -338
  128. package/packages/effects/lumber_vehicle_upgrade_aura/assets/runtime/upgradeAura.v1.js +0 -353
  129. package/packages/effects/lumber_vehicle_upgrade_aura/index.ts +0 -13
  130. package/packages/effects/lumber_vehicle_upgrade_aura/lumber_vehicle_upgrade_aura.json +0 -365
  131. package/packages/effects/lumber_vehicle_wood_chips/assets/runtime/solidParticleScatter.v1.js +0 -310
  132. package/packages/effects/lumber_vehicle_wood_chips/index.ts +0 -11
  133. package/packages/effects/lumber_vehicle_wood_chips/lumber_vehicle_wood_chips.json +0 -335
  134. package/packages/effects/monster-hit/index.ts +0 -129
  135. package/packages/effects/player-upgrade/effect.json +0 -6
  136. package/packages/effects/player-upgrade/index.ts +0 -640
  137. package/packages/effects/pulse-laser-volley/assets/textures/bolt_guangshu.webp +0 -0
  138. package/packages/effects/pulse-laser-volley/index.ts +0 -215
  139. package/packages/effects/resource-collect-sparkles/effect.json +0 -6
  140. package/packages/effects/resource-collect-sparkles/index.ts +0 -254
  141. package/packages/effects/run-footstep-dust/assets/runtime/runFootstepDust.v1.js +0 -215
  142. package/packages/effects/run-footstep-dust/effect.json +0 -183
  143. package/packages/effects/run-footstep-dust/index.ts +0 -377
  144. package/packages/effects/space-shared.ts +0 -120
  145. package/packages/effects/thruster-flame/index.ts +0 -306
  146. package/packages/effects/treasure-attention/effect.json +0 -7
  147. package/packages/effects/treasure-attention/index.ts +0 -630
  148. package/packages/effects/tree-chop-dust/assets/textures/tree_chop_dust_atlas.png +0 -0
  149. package/packages/effects/tree-chop-dust/effect.json +0 -8
  150. package/packages/effects/tree-chop-dust/index.ts +0 -202
  151. package/packages/effects/tree-leaf-chop-burst/assets/runtime/particleEmitter.v1.js +0 -303
  152. package/packages/effects/tree-leaf-chop-burst/assets/textures/tree_leaf_chop_burst_leaf_atlas.png +0 -0
  153. package/packages/effects/tree-leaf-chop-burst/effect.json +0 -498
  154. package/packages/effects/tree-leaf-chop-burst/index.ts +0 -294
  155. package/packages/effects/tree_chop_dust_burst/assets/runtime/particleEmitter.v1.js +0 -318
  156. package/packages/effects/tree_chop_dust_burst/assets/textures/tree_chop_dust_burst_dust.png +0 -0
  157. package/packages/effects/tree_chop_dust_burst/index.ts +0 -13
  158. package/packages/effects/tree_chop_dust_burst/tree_chop_dust_burst.json +0 -412
  159. package/packages/effects/ufo-thread-flame/assets/textures/flame_flow.webp +0 -0
  160. package/packages/effects/ufo-thread-flame/assets/textures/flame_spike.webp +0 -0
  161. package/packages/effects/ufo-thread-flame/index.ts +0 -153
  162. package/packages/effects/vehicle-exhaust-smoke/assets/textures/vehicle_exhaust_smoke_atlas.png +0 -0
  163. package/packages/effects/vehicle-exhaust-smoke/effect.json +0 -8
  164. package/packages/effects/vehicle-exhaust-smoke/index.ts +0 -298
  165. package/packages/effects/vehicle-upgrade/effect.json +0 -6
  166. package/packages/effects/vehicle-upgrade/index.ts +0 -404
  167. package/packages/effects/weapon-trail/assets/textures/trail_wedge.png +0 -0
  168. package/packages/effects/weapon-trail/index.ts +0 -317
  169. package/packages/effects/wood-crusher-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  170. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  171. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  172. package/packages/effects/wood-crusher-sawdust/index.ts +0 -43
  173. package/packages/effects/wood-crusher-sawdust/wood_crusher_sawdust.json +0 -592
  174. package/packages/effects/wood-cutter-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  175. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  176. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  177. package/packages/effects/wood-cutter-sawdust/index.ts +0 -43
  178. package/packages/effects/wood-cutter-sawdust/wood_cutter_sawdust.json +0 -592
  179. package/packages/effects/wood-sawdust-effect-factory.ts +0 -194
  180. package/packages/effects/wood_crusher_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  181. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  182. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  183. package/packages/effects/wood_crusher_sawdust/index.ts +0 -29
  184. package/packages/effects/wood_crusher_sawdust/wood_crusher_sawdust.json +0 -592
  185. package/packages/effects/wood_cutter_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  186. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  187. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  188. package/packages/effects/wood_cutter_sawdust/index.ts +0 -29
  189. package/packages/effects/wood_cutter_sawdust/wood_cutter_sawdust.json +0 -592
  190. package/packages/effects/wood_peel_material_test/assets/runtime/woodPeelMaterialTest.v1.js +0 -196
  191. package/packages/effects/wood_peel_material_test/index.ts +0 -11
  192. package/packages/effects/wood_peel_material_test/wood_peel_material_test.json +0 -136
  193. package/packages/env.particle.forest_edge_fog/assets/runtime/particleEmitter.v1.js +0 -210
  194. package/packages/env.particle.forest_edge_fog/assets/textures/f12a23c4-b924-4322-a260-3d982428f1e8.png +0 -0
  195. package/packages/env.particle.forest_edge_fog/assets/textures/yuan.png +0 -0
  196. package/packages/env.particle.forest_edge_fog/effect.json +0 -854
  197. package/packages/env.particle.forest_edge_fog/media/preview.gif +0 -0
  198. package/packages/env.particle.forest_edge_fog/media/preview.mp4 +0 -0
  199. package/packages/env.particle.forest_edge_fog/media/preview.webm +0 -0
  200. package/packages/env.particle.forest_edge_fog/media/thumbnail.png +0 -0
  201. package/packages/env.particle.forest_edge_fog/metadata/description.json +0 -148
  202. package/packages/env.particle.forest_edge_fog/provenance/03-effect.original.json +0 -827
  203. package/packages/env.particle.forest_edge_fog/provenance/03-normalized-vfx-to-babylon-report.json +0 -196
  204. package/packages/env.particle.forest_edge_fog/provenance/04-preview-overrides.json +0 -432
  205. package/packages/env.particle.forest_edge_fog/provenance/04-preview-tuning-report.json +0 -28
  206. package/packages/env.particle.forest_edge_fog/provenance/asset-map.json +0 -124
  207. package/packages/env.particle.forest_edge_fog/provenance/lineage.json +0 -41
  208. package/packages/generation.ts +0 -14
  209. package/packages/index.ts +0 -14
  210. package/packages/legacy-particle-runtime.ts +0 -113
  211. package/packages/manifestRuntimePackage.ts +0 -519
  212. package/packages/registry.ts +0 -85
  213. package/packages/rendering.ts +0 -13
  214. package/packages/resource.particle.viscous_oil_suction/assets/runtime/top_disk_suction/particleEmitter.v1.js +0 -424
  215. package/packages/resource.particle.viscous_oil_suction/assets/textures/Glow ad.png +0 -0
  216. package/packages/resource.particle.viscous_oil_suction/effect.json +0 -1254
  217. package/packages/resource.particle.viscous_oil_suction/media/preview.gif +0 -0
  218. package/packages/resource.particle.viscous_oil_suction/media/preview.mp4 +0 -0
  219. package/packages/resource.particle.viscous_oil_suction/media/preview.webm +0 -0
  220. package/packages/resource.particle.viscous_oil_suction/media/thumbnail.png +0 -0
  221. package/packages/resource.particle.viscous_oil_suction/metadata/description.json +0 -122
  222. package/packages/resource.particle.viscous_oil_suction/provenance/03-effect.original.json +0 -1051
  223. package/packages/resource.particle.viscous_oil_suction/provenance/03-normalized-vfx-to-babylon-report.json +0 -200
  224. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-overrides.json +0 -581
  225. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-tuning-report.json +0 -28
  226. package/packages/resource.particle.viscous_oil_suction/provenance/asset-map.json +0 -111
  227. package/packages/resource.particle.viscous_oil_suction/provenance/lineage.json +0 -45
  228. package/packages/types.ts +0 -195
  229. package/tooling/check-dist-runtime.mjs +0 -166
  230. package/tooling/generate-index.mjs +0 -55
  231. package/tooling/test-guardrails.mjs +0 -157
  232. package/tooling/validate-boundary.mjs +0 -92
  233. package/tooling/validate-effects.mjs +0 -304
  234. package/tooling/validate.mjs +0 -53
@@ -1,215 +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 { Vector3 } from '@babylonjs/core/Maths/math.vector';
13
- import { Space } from '@babylonjs/core/Maths/math.axis';
14
- import { Constants } from '@babylonjs/core/Engines/constants';
15
- import type { Mesh } from '@babylonjs/core/Meshes/mesh';
16
- import type { Scene } from '@babylonjs/core/scene';
17
- import type { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
18
- import type { VfxColorValue, VfxEffectPackage, VfxParamValues } from '../../types';
19
- import boltTextureUrl from './assets/textures/bolt_guangshu.webp?url';
20
- import { LIFETIME_DEBUG_PARAMS, LIFETIME_DEFAULTS, scheduleLifetime } from '../space-shared';
21
-
22
- interface PulseLaserParams extends VfxParamValues {
23
- lifetimeMode: string; lifetime: number;
24
- targetDistance: number;
25
- volleyInterval: number;
26
- boltSpeed: number;
27
- boltsPerVolley: number;
28
- boltSpacing: number;
29
- boltLen: number;
30
- boltColor: VfxColorValue;
31
- boltCore: number;
32
- boltCorePow: number;
33
- boltBright: number;
34
- }
35
-
36
- const DEFAULT_PARAMS: PulseLaserParams = {
37
- ...LIFETIME_DEFAULTS,
38
- targetDistance: 12,
39
- volleyInterval: 0.22,
40
- boltSpeed: 26,
41
- boltsPerVolley: 6,
42
- boltSpacing: 0.5,
43
- boltLen: 1.0,
44
- boltColor: { r: 0.071, g: 0.408, b: 1.0 }, // #1268ff
45
- boltCore: 0.3,
46
- boltCorePow: 3.0,
47
- boltBright: 1.3,
48
- };
49
-
50
- const BOLT_LEN = 2.0, BOLT_W = 0.34, POOL_SIZE = 60;
51
-
52
- const BOLT_VERTEX = `precision highp float;
53
- attribute vec3 position; attribute vec2 uv;
54
- uniform mat4 worldViewProjection;
55
- varying vec2 vUV;
56
- void main() { vUV = uv; gl_Position = worldViewProjection * vec4(position, 1.0); }`;
57
-
58
- // 乘色:col = mix(tint, white, pow(a,corePow)*coreStr) * bright; 配 ALPHA_ADD
59
- const BOLT_FRAGMENT = `precision highp float;
60
- varying vec2 vUV;
61
- uniform sampler2D tex;
62
- uniform vec3 tint;
63
- uniform float coreStr; uniform float corePow; uniform float bright;
64
- void main() {
65
- vec4 t = texture2D(tex, vUV);
66
- float core = pow(t.a, corePow) * coreStr;
67
- vec3 col = mix(tint, vec3(1.0), core) * bright;
68
- gl_FragColor = vec4(col, t.a);
69
- }`;
70
-
71
- let shadersRegistered = false;
72
- function registerShaders(): void {
73
- if (shadersRegistered) return;
74
- Effect.ShadersStore['vfxPulseBoltVertexShader'] = BOLT_VERTEX;
75
- Effect.ShadersStore['vfxPulseBoltFragmentShader'] = BOLT_FRAGMENT;
76
- shadersRegistered = true;
77
- }
78
-
79
- function colorVec(c: VfxColorValue): Color3 { return new Color3(c.r, c.g, c.b); }
80
-
81
- function excludeFromGlow(scene: Scene, mesh: AbstractMesh): void {
82
- for (const layer of scene.effectLayers ?? []) {
83
- const add = (layer as unknown as { addExcludedMesh?: (m: AbstractMesh) => void }).addExcludedMesh;
84
- if (typeof add === 'function') add.call(layer, mesh);
85
- }
86
- }
87
-
88
- interface Bolt { mesh: Mesh; left: number; }
89
-
90
- export const pulseLaserVolleyEffectPackage: VfxEffectPackage<PulseLaserParams> = {
91
- id: 'pulse-laser-volley',
92
- nameZh: '脉冲激光',
93
- placement: 'socket',
94
- optionalInputs: ['aim'],
95
- defaultParams: DEFAULT_PARAMS,
96
- debugAnchor: 'playerRight',
97
- debugParams: [
98
- ...LIFETIME_DEBUG_PARAMS,
99
- { key: 'targetDistance', label: '目标距离', kind: 'number', min: 2, max: 40, step: 0.5 },
100
- { key: 'volleyInterval', label: '齐射间隔(s)', kind: 'number', min: 0.08, max: 1, step: 0.01 },
101
- { key: 'boltSpeed', label: '光弹速度', kind: 'number', min: 5, max: 60, step: 1 },
102
- { key: 'boltsPerVolley', label: '每排弹数', kind: 'number', min: 1, max: 12, step: 1 },
103
- { key: 'boltSpacing', label: '排内间距', kind: 'number', min: 0.2, max: 1.5, step: 0.05 },
104
- { key: 'boltLen', label: '光弹长度', kind: 'number', min: 0.3, max: 4, step: 0.05 },
105
- { key: 'boltColor', label: '光弹颜色', kind: 'color' },
106
- { key: 'boltCore', label: '白芯强度', kind: 'number', min: 0, max: 1, step: 0.01 },
107
- { key: 'boltCorePow', label: '白芯收窄', kind: 'number', min: 0.5, max: 8, step: 0.1 },
108
- { key: 'boltBright', label: '光弹亮度', kind: 'number', min: 0.2, max: 3, step: 0.05 },
109
- ],
110
-
111
- create({ scene, root, position, rotationY, params, inputs }) {
112
- root.position.copyFrom(position);
113
- root.rotation.y = rotationY;
114
- registerShaders();
115
- // 有 inputs.aim:每帧朝目标(Space.WORLD,root 挂在炮口)+ 齐射射程=到目标距离;无 aim 回退挂点朝向 + params.targetDistance。
116
- const effectiveDist = (): number => {
117
- if (!inputs?.aim) return params.targetDistance;
118
- const tgt = inputs.aim();
119
- root.lookAt(tgt, 0, 0, 0, Space.WORLD);
120
- return Vector3.Distance(root.getAbsolutePosition(), tgt);
121
- };
122
-
123
- const boltTex = new Texture(boltTextureUrl, scene);
124
- boltTex.hasAlpha = true;
125
- const boltMat = new ShaderMaterial('vfx_pulse_boltMat', scene, { vertex: 'vfxPulseBolt', fragment: 'vfxPulseBolt' }, {
126
- attributes: ['position', 'uv'],
127
- uniforms: ['worldViewProjection', 'tint', 'coreStr', 'corePow', 'bright'],
128
- samplers: ['tex'], needAlphaBlending: true,
129
- });
130
- boltMat.setTexture('tex', boltTex);
131
- boltMat.backFaceCulling = false;
132
- boltMat.disableDepthWrite = true;
133
- boltMat.alphaMode = Constants.ALPHA_ADD;
134
- boltMat.setColor3('tint', colorVec(params.boltColor));
135
- boltMat.setFloat('coreStr', params.boltCore);
136
- boltMat.setFloat('corePow', params.boltCorePow);
137
- boltMat.setFloat('bright', params.boltBright);
138
-
139
- // 对象池:单面片,长轴沿局部 +Z,均为 root 子节点(在 root 局部空间飞行)
140
- const pool: Bolt[] = [];
141
- for (let i = 0; i < POOL_SIZE; i++) {
142
- const mesh = CreatePlane(`vfx_pulse_bolt${i}`, { width: BOLT_W, height: BOLT_LEN }, scene);
143
- mesh.rotation.x = Math.PI / 2;
144
- mesh.material = boltMat;
145
- mesh.isPickable = false;
146
- mesh.parent = root;
147
- mesh.setEnabled(false);
148
- excludeFromGlow(scene, mesh);
149
- pool.push({ mesh, left: 0 });
150
- }
151
-
152
- // 发射点白球(不排除辉光,吃 bloom)
153
- const muzzle = CreateSphere('vfx_pulse_muzzle', { diameter: 0.85, segments: 16 }, scene);
154
- const muzzleMat = new StandardMaterial('vfx_pulse_muzzleMat', scene);
155
- muzzleMat.emissiveColor = new Color3(1, 1, 1);
156
- muzzleMat.diffuseColor = Color3.Black();
157
- muzzleMat.disableLighting = true;
158
- muzzleMat.alpha = 0.95;
159
- muzzle.material = muzzleMat;
160
- muzzle.isPickable = false;
161
- muzzle.parent = root;
162
- muzzle.position.set(0, 0, 0);
163
- let muzzleFlash = 0;
164
-
165
- function fireVolley(dist: number): void {
166
- const n = Math.max(1, Math.round(params.boltsPerVolley));
167
- let fired = 0;
168
- for (const b of pool) {
169
- if (fired >= n) break;
170
- if (b.left > 0) continue;
171
- const off = (fired - (n - 1) / 2) * params.boltSpacing;
172
- b.mesh.position.set(off, 0, 0); // 沿局部 X 横向排开,从根部出发
173
- b.mesh.scaling.z = params.boltLen / BOLT_LEN;
174
- b.left = dist / params.boltSpeed;
175
- b.mesh.setEnabled(true);
176
- fired += 1;
177
- }
178
- muzzleFlash = 1;
179
- }
180
-
181
- let volleyTimer = 0;
182
- const observer = scene.onBeforeRenderObservable.add(() => {
183
- const dt = Math.min(scene.getEngine().getDeltaTime() / 1000, 0.05);
184
- const dist = effectiveDist(); // 每帧:有 aim 则朝目标 + 取到目标距离
185
- volleyTimer += dt;
186
- if (volleyTimer >= params.volleyInterval) { volleyTimer = 0; fireVolley(dist); }
187
-
188
- muzzleFlash = Math.max(0, muzzleFlash - dt * 6);
189
- const s = 1 + muzzleFlash * 0.6;
190
- muzzle.scaling.set(s, s, s);
191
-
192
- const step = params.boltSpeed * dt;
193
- for (const b of pool) {
194
- if (b.left <= 0) continue;
195
- b.left -= dt;
196
- b.mesh.position.z += step; // 沿局部 +Z 匀速飞行
197
- if (b.left <= 0) b.mesh.setEnabled(false);
198
- }
199
- });
200
-
201
- let disposed = false;
202
- let ltTimer = 0;
203
- const dispose = (): void => {
204
- if (disposed) return; disposed = true;
205
- if (ltTimer) window.clearTimeout(ltTimer);
206
- scene.onBeforeRenderObservable.remove(observer);
207
- for (const b of pool) b.mesh.dispose();
208
- muzzle.dispose(); muzzleMat.dispose();
209
- boltMat.dispose(); boltTex.dispose();
210
- root.dispose();
211
- };
212
- ltTimer = scheduleLifetime(params, dispose);
213
- return { root, dispose };
214
- },
215
- };
@@ -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
- }