@fps-games/vfx 0.3.2 → 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 (231) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +5 -4
  3. package/tooling/vfx-cli.mjs +8 -1
  4. package/docs/AUTHORING_EFFECTS.md +0 -297
  5. package/docs/INTEGRATION.md +0 -194
  6. package/docs/VFX_LIBRARY_ARCHITECTURE_chatgpt.md +0 -1580
  7. package/docs/VFX_LIBRARY_EXECUTION_PLAN.md +0 -229
  8. package/docs/VFX_LIBRARY_PLAN.md +0 -546
  9. package/docs/hit-flash-red-method.md +0 -155
  10. package/docs/treasure_attention_fx_babylonjs_guide.md +0 -901
  11. package/docs/vfx-ab/RESULTS.md +0 -35
  12. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/after.png +0 -0
  13. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/before.png +0 -0
  14. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/after.png +0 -0
  15. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/before.png +0 -0
  16. package/docs/vfx-integration_chatgpt.md +0 -462
  17. package/docs/vfx-placement-design.md +0 -164
  18. package/docs/weapon-trail-visualization.html +0 -241
  19. package/packages/EffectPackageService.ts +0 -346
  20. package/packages/SceneVfxService.ts +0 -96
  21. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/runtime/simpleMeshEffect.v1.js +0 -333
  22. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.frag.glsl +0 -25
  23. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.vert.glsl +0 -11
  24. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/textures/effect_jianbian.png +0 -0
  25. package/packages/build.mesh.yellow_build_upgrade_indicator/effect.json +0 -927
  26. package/packages/build.mesh.yellow_build_upgrade_indicator/media/contact-sheet.png +0 -0
  27. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.gif +0 -0
  28. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.mp4 +0 -0
  29. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.webm +0 -0
  30. package/packages/build.mesh.yellow_build_upgrade_indicator/media/thumbnail.png +0 -0
  31. package/packages/build.mesh.yellow_build_upgrade_indicator/metadata/description.json +0 -197
  32. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-effect.original.json +0 -895
  33. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-normalized-vfx-to-babylon-report.json +0 -241
  34. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-overrides.json +0 -542
  35. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-tuning-report.json +0 -69
  36. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/asset-map.json +0 -125
  37. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/lineage.json +0 -44
  38. package/packages/build.particle.golden_upgrade_starburst/assets/runtime/particleEmitter.v1.js +0 -210
  39. package/packages/build.particle.golden_upgrade_starburst/assets/textures/effect_guangdian.png +0 -0
  40. package/packages/build.particle.golden_upgrade_starburst/effect.json +0 -880
  41. package/packages/build.particle.golden_upgrade_starburst/media/contact-sheet.png +0 -0
  42. package/packages/build.particle.golden_upgrade_starburst/media/preview.gif +0 -0
  43. package/packages/build.particle.golden_upgrade_starburst/media/preview.mp4 +0 -0
  44. package/packages/build.particle.golden_upgrade_starburst/media/preview.webm +0 -0
  45. package/packages/build.particle.golden_upgrade_starburst/media/thumbnail.png +0 -0
  46. package/packages/build.particle.golden_upgrade_starburst/metadata/description.json +0 -161
  47. package/packages/build.particle.golden_upgrade_starburst/provenance/03-effect.original.json +0 -762
  48. package/packages/build.particle.golden_upgrade_starburst/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  49. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-overrides.json +0 -436
  50. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-tuning-report.json +0 -40
  51. package/packages/build.particle.golden_upgrade_starburst/provenance/asset-map.json +0 -115
  52. package/packages/build.particle.golden_upgrade_starburst/provenance/lineage.json +0 -40
  53. package/packages/build.particle.orange_upgrade_transform_sparks/assets/runtime/particleEmitter.v1.js +0 -210
  54. package/packages/build.particle.orange_upgrade_transform_sparks/assets/textures/b5b27ab1-e740-4398-b407-848fc2b2c897.png +0 -0
  55. package/packages/build.particle.orange_upgrade_transform_sparks/effect.json +0 -887
  56. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.gif +0 -0
  57. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.mp4 +0 -0
  58. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.webm +0 -0
  59. package/packages/build.particle.orange_upgrade_transform_sparks/media/thumbnail.png +0 -0
  60. package/packages/build.particle.orange_upgrade_transform_sparks/metadata/description.json +0 -179
  61. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-effect.original.json +0 -859
  62. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  63. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-overrides.json +0 -478
  64. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-tuning-report.json +0 -40
  65. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/asset-map.json +0 -114
  66. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/lineage.json +0 -41
  67. package/packages/build.particle.wood_chip_dust_burst/assets/runtime/particleEmitter.v1.js +0 -210
  68. package/packages/build.particle.wood_chip_dust_burst/assets/textures/effect_yanwu4.png +0 -0
  69. package/packages/build.particle.wood_chip_dust_burst/assets/textures/suipian3.png +0 -0
  70. package/packages/build.particle.wood_chip_dust_burst/effect.json +0 -1400
  71. package/packages/build.particle.wood_chip_dust_burst/media/contact-sheet.png +0 -0
  72. package/packages/build.particle.wood_chip_dust_burst/media/preview.gif +0 -0
  73. package/packages/build.particle.wood_chip_dust_burst/media/preview.mp4 +0 -0
  74. package/packages/build.particle.wood_chip_dust_burst/media/preview.webm +0 -0
  75. package/packages/build.particle.wood_chip_dust_burst/media/thumbnail.png +0 -0
  76. package/packages/build.particle.wood_chip_dust_burst/metadata/description.json +0 -173
  77. package/packages/build.particle.wood_chip_dust_burst/provenance/03-effect.original.json +0 -1277
  78. package/packages/build.particle.wood_chip_dust_burst/provenance/03-normalized-vfx-to-babylon-report.json +0 -329
  79. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-overrides.json +0 -749
  80. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-tuning-report.json +0 -60
  81. package/packages/build.particle.wood_chip_dust_burst/provenance/asset-map.json +0 -146
  82. package/packages/build.particle.wood_chip_dust_burst/provenance/lineage.json +0 -69
  83. package/packages/effects/building-upgrade/effect.json +0 -6
  84. package/packages/effects/building-upgrade/index.ts +0 -387
  85. package/packages/effects/character-entrance/effect.json +0 -6
  86. package/packages/effects/character-entrance/index.ts +0 -308
  87. package/packages/effects/coin-burst/index.ts +0 -48
  88. package/packages/effects/comet-trail-flame/assets/textures/trail_5x4.webp +0 -0
  89. package/packages/effects/comet-trail-flame/index.ts +0 -107
  90. package/packages/effects/conveyor-upgrade/effect.json +0 -6
  91. package/packages/effects/conveyor-upgrade/index.ts +0 -401
  92. package/packages/effects/debris-smoke/assets/models/rock_debris.glb +0 -0
  93. package/packages/effects/debris-smoke/assets/textures/smoke22.webp +0 -0
  94. package/packages/effects/debris-smoke/index.ts +0 -102
  95. package/packages/effects/door_open_gold_flakes/assets/runtime/goldFlakes.v1.js +0 -397
  96. package/packages/effects/door_open_gold_flakes/door_open_gold_flakes.json +0 -498
  97. package/packages/effects/door_open_gold_flakes/index.ts +0 -11
  98. package/packages/effects/electric-arc/assets/textures/spark_4x4.webp +0 -0
  99. package/packages/effects/electric-arc/index.ts +0 -63
  100. package/packages/effects/energy-shield/effect.json +0 -7
  101. package/packages/effects/energy-shield/index.ts +0 -432
  102. package/packages/effects/explosion/assets/textures/baozha29.webp +0 -0
  103. package/packages/effects/explosion/assets/textures/glow01.webp +0 -0
  104. package/packages/effects/explosion/assets/textures/lizi01.webp +0 -0
  105. package/packages/effects/explosion/assets/textures/xulie_lizibaozha02_5x5.webp +0 -0
  106. package/packages/effects/explosion/index.ts +0 -115
  107. package/packages/effects/ground-tile-upgrade/effect.json +0 -6
  108. package/packages/effects/ground-tile-upgrade/index.ts +0 -410
  109. package/packages/effects/hit-flash/index.ts +0 -77
  110. package/packages/effects/laser-beam/assets/textures/laser_glow.webp +0 -0
  111. package/packages/effects/laser-beam/assets/textures/laser_noise.webp +0 -0
  112. package/packages/effects/laser-beam/index.ts +0 -6
  113. package/packages/effects/laser-beam-factory.ts +0 -146
  114. package/packages/effects/lumber-cart-suction/effect.json +0 -6
  115. package/packages/effects/lumber-cart-suction/index.ts +0 -399
  116. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb +0 -0
  117. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/.active +0 -1
  118. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/1781589063890_upload.glb +0 -0
  119. package/packages/effects/lumber-debris-scatter/assets/runtime/solidParticleScatter.v1.js +0 -236
  120. package/packages/effects/lumber-debris-scatter/effect.json +0 -228
  121. package/packages/effects/lumber-debris-scatter/index.ts +0 -780
  122. package/packages/effects/lumber_vehicle_gear_spin/assets/runtime/gearSpin.v1.js +0 -202
  123. package/packages/effects/lumber_vehicle_gear_spin/index.ts +0 -11
  124. package/packages/effects/lumber_vehicle_gear_spin/lumber_vehicle_gear_spin.json +0 -338
  125. package/packages/effects/lumber_vehicle_upgrade_aura/assets/runtime/upgradeAura.v1.js +0 -353
  126. package/packages/effects/lumber_vehicle_upgrade_aura/index.ts +0 -13
  127. package/packages/effects/lumber_vehicle_upgrade_aura/lumber_vehicle_upgrade_aura.json +0 -365
  128. package/packages/effects/lumber_vehicle_wood_chips/assets/runtime/solidParticleScatter.v1.js +0 -310
  129. package/packages/effects/lumber_vehicle_wood_chips/index.ts +0 -11
  130. package/packages/effects/lumber_vehicle_wood_chips/lumber_vehicle_wood_chips.json +0 -335
  131. package/packages/effects/monster-hit/index.ts +0 -129
  132. package/packages/effects/player-upgrade/effect.json +0 -6
  133. package/packages/effects/player-upgrade/index.ts +0 -640
  134. package/packages/effects/pulse-laser-volley/assets/textures/bolt_guangshu.webp +0 -0
  135. package/packages/effects/pulse-laser-volley/index.ts +0 -215
  136. package/packages/effects/resource-collect-sparkles/effect.json +0 -6
  137. package/packages/effects/resource-collect-sparkles/index.ts +0 -254
  138. package/packages/effects/run-footstep-dust/assets/runtime/runFootstepDust.v1.js +0 -215
  139. package/packages/effects/run-footstep-dust/effect.json +0 -183
  140. package/packages/effects/run-footstep-dust/index.ts +0 -377
  141. package/packages/effects/space-shared.ts +0 -120
  142. package/packages/effects/thruster-flame/index.ts +0 -306
  143. package/packages/effects/treasure-attention/effect.json +0 -7
  144. package/packages/effects/treasure-attention/index.ts +0 -630
  145. package/packages/effects/tree-chop-dust/assets/textures/tree_chop_dust_atlas.png +0 -0
  146. package/packages/effects/tree-chop-dust/effect.json +0 -8
  147. package/packages/effects/tree-chop-dust/index.ts +0 -202
  148. package/packages/effects/tree-leaf-chop-burst/assets/runtime/particleEmitter.v1.js +0 -303
  149. package/packages/effects/tree-leaf-chop-burst/assets/textures/tree_leaf_chop_burst_leaf_atlas.png +0 -0
  150. package/packages/effects/tree-leaf-chop-burst/effect.json +0 -498
  151. package/packages/effects/tree-leaf-chop-burst/index.ts +0 -294
  152. package/packages/effects/tree_chop_dust_burst/assets/runtime/particleEmitter.v1.js +0 -318
  153. package/packages/effects/tree_chop_dust_burst/assets/textures/tree_chop_dust_burst_dust.png +0 -0
  154. package/packages/effects/tree_chop_dust_burst/index.ts +0 -13
  155. package/packages/effects/tree_chop_dust_burst/tree_chop_dust_burst.json +0 -412
  156. package/packages/effects/ufo-thread-flame/assets/textures/flame_flow.webp +0 -0
  157. package/packages/effects/ufo-thread-flame/assets/textures/flame_spike.webp +0 -0
  158. package/packages/effects/ufo-thread-flame/index.ts +0 -153
  159. package/packages/effects/vehicle-exhaust-smoke/assets/textures/vehicle_exhaust_smoke_atlas.png +0 -0
  160. package/packages/effects/vehicle-exhaust-smoke/effect.json +0 -8
  161. package/packages/effects/vehicle-exhaust-smoke/index.ts +0 -298
  162. package/packages/effects/vehicle-upgrade/effect.json +0 -6
  163. package/packages/effects/vehicle-upgrade/index.ts +0 -404
  164. package/packages/effects/weapon-trail/assets/textures/trail_wedge.png +0 -0
  165. package/packages/effects/weapon-trail/index.ts +0 -317
  166. package/packages/effects/wood-crusher-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  167. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  168. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  169. package/packages/effects/wood-crusher-sawdust/index.ts +0 -43
  170. package/packages/effects/wood-crusher-sawdust/wood_crusher_sawdust.json +0 -592
  171. package/packages/effects/wood-cutter-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  172. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  173. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  174. package/packages/effects/wood-cutter-sawdust/index.ts +0 -43
  175. package/packages/effects/wood-cutter-sawdust/wood_cutter_sawdust.json +0 -592
  176. package/packages/effects/wood-sawdust-effect-factory.ts +0 -194
  177. package/packages/effects/wood_crusher_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  178. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  179. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  180. package/packages/effects/wood_crusher_sawdust/index.ts +0 -29
  181. package/packages/effects/wood_crusher_sawdust/wood_crusher_sawdust.json +0 -592
  182. package/packages/effects/wood_cutter_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  183. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  184. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  185. package/packages/effects/wood_cutter_sawdust/index.ts +0 -29
  186. package/packages/effects/wood_cutter_sawdust/wood_cutter_sawdust.json +0 -592
  187. package/packages/effects/wood_peel_material_test/assets/runtime/woodPeelMaterialTest.v1.js +0 -196
  188. package/packages/effects/wood_peel_material_test/index.ts +0 -11
  189. package/packages/effects/wood_peel_material_test/wood_peel_material_test.json +0 -136
  190. package/packages/env.particle.forest_edge_fog/assets/runtime/particleEmitter.v1.js +0 -210
  191. package/packages/env.particle.forest_edge_fog/assets/textures/f12a23c4-b924-4322-a260-3d982428f1e8.png +0 -0
  192. package/packages/env.particle.forest_edge_fog/assets/textures/yuan.png +0 -0
  193. package/packages/env.particle.forest_edge_fog/effect.json +0 -854
  194. package/packages/env.particle.forest_edge_fog/media/preview.gif +0 -0
  195. package/packages/env.particle.forest_edge_fog/media/preview.mp4 +0 -0
  196. package/packages/env.particle.forest_edge_fog/media/preview.webm +0 -0
  197. package/packages/env.particle.forest_edge_fog/media/thumbnail.png +0 -0
  198. package/packages/env.particle.forest_edge_fog/metadata/description.json +0 -148
  199. package/packages/env.particle.forest_edge_fog/provenance/03-effect.original.json +0 -827
  200. package/packages/env.particle.forest_edge_fog/provenance/03-normalized-vfx-to-babylon-report.json +0 -196
  201. package/packages/env.particle.forest_edge_fog/provenance/04-preview-overrides.json +0 -432
  202. package/packages/env.particle.forest_edge_fog/provenance/04-preview-tuning-report.json +0 -28
  203. package/packages/env.particle.forest_edge_fog/provenance/asset-map.json +0 -124
  204. package/packages/env.particle.forest_edge_fog/provenance/lineage.json +0 -41
  205. package/packages/generation.ts +0 -14
  206. package/packages/index.ts +0 -14
  207. package/packages/legacy-particle-runtime.ts +0 -113
  208. package/packages/manifestRuntimePackage.ts +0 -519
  209. package/packages/registry.ts +0 -85
  210. package/packages/rendering.ts +0 -13
  211. package/packages/resource.particle.viscous_oil_suction/assets/runtime/top_disk_suction/particleEmitter.v1.js +0 -424
  212. package/packages/resource.particle.viscous_oil_suction/assets/textures/Glow ad.png +0 -0
  213. package/packages/resource.particle.viscous_oil_suction/effect.json +0 -1254
  214. package/packages/resource.particle.viscous_oil_suction/media/preview.gif +0 -0
  215. package/packages/resource.particle.viscous_oil_suction/media/preview.mp4 +0 -0
  216. package/packages/resource.particle.viscous_oil_suction/media/preview.webm +0 -0
  217. package/packages/resource.particle.viscous_oil_suction/media/thumbnail.png +0 -0
  218. package/packages/resource.particle.viscous_oil_suction/metadata/description.json +0 -122
  219. package/packages/resource.particle.viscous_oil_suction/provenance/03-effect.original.json +0 -1051
  220. package/packages/resource.particle.viscous_oil_suction/provenance/03-normalized-vfx-to-babylon-report.json +0 -200
  221. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-overrides.json +0 -581
  222. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-tuning-report.json +0 -28
  223. package/packages/resource.particle.viscous_oil_suction/provenance/asset-map.json +0 -111
  224. package/packages/resource.particle.viscous_oil_suction/provenance/lineage.json +0 -45
  225. package/packages/types.ts +0 -195
  226. package/tooling/check-dist-runtime.mjs +0 -166
  227. package/tooling/generate-index.mjs +0 -55
  228. package/tooling/test-guardrails.mjs +0 -157
  229. package/tooling/validate-boundary.mjs +0 -92
  230. package/tooling/validate-effects.mjs +0 -304
  231. 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
- }