@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,346 +0,0 @@
1
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
2
- import type { Scene } from '@babylonjs/core/scene';
3
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
4
- import { VFX_DEFAULT_GENERATION_PARAMS, VFX_GENERATION_PARAM_KEYS } from './generation';
5
- import type { VfxRegistry } from './registry';
6
- import type {
7
- VfxColorValue,
8
- VfxDebugAnchor,
9
- VfxEffectHandle,
10
- VfxEffectInputs,
11
- VfxEffectPackage,
12
- VfxInputKey,
13
- VfxParamValue,
14
- VfxParamValues,
15
- VfxResolvedInputs,
16
- VfxSpawnParamKeys,
17
- VfxSpawnTransform,
18
- } from './types';
19
-
20
- export interface EffectPackageServiceOptions {
21
- /** 注入的注册表(由项目侧 glob + buildRegistry 构建);库不直接引用项目注册表 */
22
- registry: VfxRegistry;
23
- getDebugAnchorPosition?: (anchor: VfxDebugAnchor) => Vector3 | null;
24
- }
25
-
26
- const DEFAULT_SPAWN_PARAM_KEYS: Required<Pick<VfxSpawnParamKeys, 'offsetX' | 'offsetY' | 'offsetZ'>> = {
27
- offsetX: VFX_GENERATION_PARAM_KEYS.offsetX,
28
- offsetY: VFX_GENERATION_PARAM_KEYS.offsetY,
29
- offsetZ: VFX_GENERATION_PARAM_KEYS.offsetZ,
30
- };
31
-
32
- export class EffectPackageService {
33
- private readonly scene: Scene;
34
- private readonly registry: VfxRegistry;
35
- private readonly getDebugAnchorPosition?: (anchor: VfxDebugAnchor) => Vector3 | null;
36
- private readonly activeHandles = new Set<VfxEffectHandle>();
37
- private readonly savedParams: Record<string, Partial<VfxParamValues>>;
38
-
39
- constructor(scene: Scene, options: EffectPackageServiceOptions) {
40
- this.scene = scene;
41
- this.registry = options.registry;
42
- this.getDebugAnchorPosition = options.getDebugAnchorPosition;
43
- this.savedParams = cloneOverrideMap(options.registry.savedParams);
44
- }
45
-
46
- getEffectPackages(): VfxEffectPackage[] {
47
- return this.registry.getEffectPackages();
48
- }
49
-
50
- getEffectPackage(effectId: string): VfxEffectPackage | undefined {
51
- return this.registry.getEffectPackage(effectId);
52
- }
53
-
54
- getMergedParams(effectId: string, explicitParams?: Partial<VfxParamValues>): VfxParamValues | null {
55
- const effectPackage = this.getEffectPackage(effectId);
56
- if (!effectPackage) return null;
57
- return mergeVfxParams(
58
- VFX_DEFAULT_GENERATION_PARAMS,
59
- effectPackage.defaultParams,
60
- effectPackage.savedParams,
61
- this.savedParams[effectId],
62
- explicitParams,
63
- );
64
- }
65
-
66
- getDebugOverride(effectId: string): Partial<VfxParamValues> {
67
- return cloneParams(this.savedParams[effectId]);
68
- }
69
-
70
- setDebugOverride(effectId: string, params: Partial<VfxParamValues>): void {
71
- this.savedParams[effectId] = cloneParams(params);
72
- const effectPackage = this.getEffectPackage(effectId);
73
- if (effectPackage) effectPackage.savedParams = cloneParams(params);
74
- }
75
-
76
- playEffectPackage(
77
- effectId: string,
78
- explicitParams?: Partial<VfxParamValues>,
79
- spawnTransform?: VfxSpawnTransform | Vector3,
80
- ): VfxEffectHandle | null {
81
- const effectPackage = this.getEffectPackage(effectId);
82
- if (!effectPackage) {
83
- console.warn(`[EffectPackageService] Unknown VFX effect package: ${effectId}`);
84
- return null;
85
- }
86
-
87
- const params = this.getMergedParams(effectId, explicitParams);
88
- if (!params) return null;
89
-
90
- const spawn = this.resolveSpawnTransform(effectPackage, params, spawnTransform);
91
- warnMissingRequiredInputs(effectId, effectPackage.requiredInputs, spawn.inputs);
92
- const runtimeParams = stripSpawnParams(params, effectPackage.spawnParams);
93
- const root = new TransformNode(`vfx_package_${effectId}`, this.scene);
94
- applySpawnToRoot(root, spawn);
95
- // 世界位置(挂到 parent 后即为绝对世界坐标):保证世界空间特效(如拖尾)起点正确。
96
- const worldPosition = root.getAbsolutePosition().clone();
97
- let handle: VfxEffectHandle;
98
- try {
99
- handle = effectPackage.create({
100
- scene: this.scene,
101
- root,
102
- position: worldPosition,
103
- rotationY: spawn.rotationY,
104
- renderingGroupId: spawn.renderingGroupId,
105
- params: runtimeParams,
106
- scale: spawn.scale,
107
- inputs: spawn.inputs,
108
- });
109
- // root 的摆放归 service 独占。许多特效在 create() 里会写 `root.position.copyFrom(position)`
110
- // (用世界坐标),挂到 parent 时会与 local offset 冲突 → 双偏移。这里在 create() 之后
111
- // 重新断言一次 root transform,覆盖回 spawn 的正确值(无 parent 时为同值,无副作用)。
112
- applySpawnToRoot(root, spawn);
113
- } catch (error) {
114
- // 错误兜底:单个坏包不得崩掉整个场景(plan §7)
115
- console.warn(`[EffectPackageService] effect "${effectId}" create() failed; skipped.`, error);
116
- try { root.dispose(); } catch { /* ignore */ }
117
- return null;
118
- }
119
- this.activeHandles.add(handle);
120
- const originalDispose = handle.dispose.bind(handle);
121
- handle.dispose = () => {
122
- this.activeHandles.delete(handle);
123
- originalDispose();
124
- };
125
- // G4/M4:很多特效内部用 scheduleLifetime 自销,直接 dispose root(绕过上面 wrap 的 handle.dispose),
126
- // 会让 activeHandles 残留已死 handle。监听 root 销毁 → 任何路径销毁都清理(自销/手动皆覆盖)。
127
- handle.root.onDisposeObservable.addOnce(() => { this.activeHandles.delete(handle); });
128
- return handle;
129
- }
130
-
131
- dispose(): void {
132
- for (const handle of this.activeHandles) {
133
- handle.dispose();
134
- }
135
- this.activeHandles.clear();
136
- }
137
-
138
- private resolveDebugPosition(effectPackage: VfxEffectPackage): Vector3 {
139
- const anchor = effectPackage.debugAnchor ?? 'playerRight';
140
- const anchorPosition = this.getDebugAnchorPosition?.(anchor);
141
- if (anchorPosition) return anchorPosition.clone();
142
- const cameraTarget = (this.scene.activeCamera as any)?.target as Vector3 | undefined;
143
- if (cameraTarget?.clone) return cameraTarget.clone().addInPlace(new Vector3(1.2, 0, 0));
144
- return new Vector3(0, 0, 0);
145
- }
146
-
147
- private resolveSpawnTransform(
148
- effectPackage: VfxEffectPackage,
149
- params: VfxParamValues,
150
- spawnTransform?: VfxSpawnTransform | Vector3,
151
- ): ResolvedSpawnTransform {
152
- const normalized = normalizeSpawnTransform(spawnTransform);
153
- const parent = normalized.parent ?? null;
154
- // 有 parent(挂载)时默认落在挂点 (0,0,0) local;无 parent 时才回退到 debug 锚点(预览用)。
155
- const basePosition = normalized.position?.clone()
156
- ?? (parent != null ? new Vector3(0, 0, 0) : this.resolveDebugPosition(effectPackage));
157
- const rotation = resolveRotation(normalized.rotation, normalized.rotationY);
158
- const scale = resolveScale(normalized.scale);
159
- // 默认坐标系:给了 parent → local;没给 → 世界(保持现状)。
160
- const offsetIsLocal = normalized.offsetIsLocal ?? parent != null;
161
- const renderingGroupId = readOptionalFiniteNumber(normalized.renderingGroupId);
162
- const offset = readSpawnOffset(params, effectPackage.spawnParams);
163
- let position = basePosition;
164
- if (offset.lengthSquared() > 0.0000001) {
165
- const cos = Math.cos(rotation.y);
166
- const sin = Math.sin(rotation.y);
167
- position = basePosition.addInPlace(new Vector3(
168
- offset.x * cos - offset.z * sin,
169
- offset.y,
170
- offset.x * sin + offset.z * cos,
171
- ));
172
- }
173
- return { position, rotation, rotationY: rotation.y, scale, parent, offsetIsLocal, renderingGroupId, inputs: normalizeInputs(normalized.inputs) };
174
- }
175
- }
176
-
177
- // 把调用方的 inputs(值 or provider)归一化为访问器:create() 里 inputs.aim() 总可调用(动态目标每帧取)。
178
- function normalizeInputs(inputs?: VfxEffectInputs): VfxResolvedInputs | undefined {
179
- if (!inputs) return undefined;
180
- const asFn = <T>(v: T | (() => T) | undefined): (() => T) | undefined =>
181
- v == null ? undefined : (typeof v === 'function' ? v as () => T : () => v);
182
- const out: VfxResolvedInputs = {};
183
- if (inputs.aim != null) out.aim = asFn(inputs.aim);
184
- if (inputs.normal != null) out.normal = asFn(inputs.normal);
185
- if (inputs.endpoints) out.endpoints = inputs.endpoints.map((e) => asFn(e)!).filter(Boolean);
186
- if (inputs.target) out.target = inputs.target;
187
- if (inputs.scalars) {
188
- out.scalars = {};
189
- for (const [k, v] of Object.entries(inputs.scalars)) {
190
- const fn = asFn(v);
191
- if (fn) out.scalars[k] = fn;
192
- }
193
- }
194
- return out;
195
- }
196
-
197
- // G2:特效声明 requiredInputs 但调用方未提供对应 inputs → 警告(漏接静默降级很难查)。optionalInputs 不在此列。
198
- function warnMissingRequiredInputs(effectId: string, required: VfxInputKey[] | undefined, inputs: VfxResolvedInputs | undefined): void {
199
- if (!required || required.length === 0) return;
200
- const missing = required.filter((k) => {
201
- const v = inputs ? (inputs as Record<string, unknown>)[k] : undefined;
202
- return v == null || (k === 'endpoints' && Array.isArray(v) && v.length < 2);
203
- });
204
- if (missing.length > 0) {
205
- console.warn(`[EffectPackageService] effect "${effectId}" 声明 requiredInputs [${missing.join(', ')}] 但未提供 → 可能静默降级。`);
206
- }
207
- }
208
-
209
- interface ResolvedSpawnTransform {
210
- /** 按 offsetIsLocal 解释的位置:local(相对 parent)或世界坐标。 */
211
- position: Vector3;
212
- /** 完整欧拉角旋转(弧度)。 */
213
- rotation: Vector3;
214
- /** 有效 Y 轴旋转(弧度),透传给 create() 上下文(向后兼容)。 */
215
- rotationY: number;
216
- /** 三轴缩放。 */
217
- scale: Vector3;
218
- parent: TransformNode | null;
219
- offsetIsLocal: boolean;
220
- renderingGroupId?: number;
221
- inputs?: VfxResolvedInputs;
222
- }
223
-
224
- // 把解析后的 spawn transform 落到 root 节点上。
225
- // - 没给 parent:position/rotation/scale 即世界坐标(无 parent 时 local == world),与现状一致。
226
- // - 给了 parent + offsetIsLocal(默认):写到 root 的 local transform(随 parent 跟随)。
227
- // - 给了 parent + offsetIsLocal=false:position 按绝对世界坐标落位;rotation/scale 仍按 local 应用。
228
- function applySpawnToRoot(root: TransformNode, spawn: ResolvedSpawnTransform): void {
229
- if (spawn.parent) root.parent = spawn.parent;
230
- root.rotation.copyFrom(spawn.rotation);
231
- root.scaling.copyFrom(spawn.scale);
232
- if (spawn.parent && !spawn.offsetIsLocal) {
233
- // 需要先有 parent 的世界矩阵,才能把世界坐标反解成 local。
234
- root.computeWorldMatrix(true);
235
- root.setAbsolutePosition(spawn.position);
236
- } else {
237
- root.position.copyFrom(spawn.position);
238
- }
239
- root.computeWorldMatrix(true);
240
- }
241
-
242
- function resolveRotation(rotation: VfxSpawnTransform['rotation'], rotationY: unknown): Vector3 {
243
- if (rotation) {
244
- return new Vector3(
245
- readFiniteNumber(rotation.x, 0),
246
- readFiniteNumber(rotation.y, 0),
247
- readFiniteNumber(rotation.z, 0),
248
- );
249
- }
250
- return new Vector3(0, readFiniteNumber(rotationY, 0), 0);
251
- }
252
-
253
- function resolveScale(scale: VfxSpawnTransform['scale']): Vector3 {
254
- if (scale == null) return new Vector3(1, 1, 1);
255
- if (typeof scale === 'number') {
256
- const s = readFiniteNumber(scale, 1);
257
- return new Vector3(s, s, s);
258
- }
259
- return new Vector3(
260
- readFiniteNumber(scale.x, 1),
261
- readFiniteNumber(scale.y, 1),
262
- readFiniteNumber(scale.z, 1),
263
- );
264
- }
265
-
266
- function normalizeSpawnTransform(spawnTransform?: VfxSpawnTransform | Vector3): VfxSpawnTransform {
267
- if (!spawnTransform) return {};
268
- if (spawnTransform instanceof Vector3) return { position: spawnTransform };
269
- return spawnTransform;
270
- }
271
-
272
- function readSpawnOffset(params: VfxParamValues, spawnParams?: VfxSpawnParamKeys): Vector3 {
273
- const keys = resolveSpawnParamKeys(spawnParams);
274
- return new Vector3(
275
- readParamNumber(params[keys.offsetX ?? ''], 0),
276
- readParamNumber(params[keys.offsetY ?? ''], 0),
277
- readParamNumber(params[keys.offsetZ ?? ''], 0),
278
- );
279
- }
280
-
281
- function stripSpawnParams(params: VfxParamValues, spawnParams?: VfxSpawnParamKeys): VfxParamValues {
282
- const result = cloneParams(params) as VfxParamValues;
283
- const keys = resolveSpawnParamKeys(spawnParams);
284
- for (const key of [keys.offsetX, keys.offsetY, keys.offsetZ]) {
285
- if (key) delete result[key];
286
- }
287
- return result;
288
- }
289
-
290
- function resolveSpawnParamKeys(spawnParams?: VfxSpawnParamKeys): VfxSpawnParamKeys {
291
- return spawnParams ?? DEFAULT_SPAWN_PARAM_KEYS;
292
- }
293
-
294
- function readParamNumber(value: VfxParamValue | undefined, fallback: number): number {
295
- return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
296
- }
297
-
298
- function readFiniteNumber(value: unknown, fallback: number): number {
299
- return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
300
- }
301
-
302
- function readOptionalFiniteNumber(value: unknown): number | undefined {
303
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
304
- }
305
-
306
- function mergeVfxParams(
307
- ...sources: Array<Partial<VfxParamValues> | undefined>
308
- ): VfxParamValues {
309
- const result: Partial<VfxParamValues> = {};
310
- for (const source of sources) {
311
- Object.assign(result, cloneParams(source));
312
- }
313
- return result as VfxParamValues;
314
- }
315
-
316
- function cloneParams(value: Partial<VfxParamValues> | undefined): Partial<VfxParamValues> {
317
- if (!value) return {};
318
- const result: Partial<VfxParamValues> = {};
319
- for (const [key, item] of Object.entries(value)) {
320
- if (item === undefined) continue;
321
- result[key] = cloneParamValue(item);
322
- }
323
- return result;
324
- }
325
-
326
- function cloneOverrideMap(value: Record<string, Partial<VfxParamValues>>): Record<string, Partial<VfxParamValues>> {
327
- const result: Record<string, Partial<VfxParamValues>> = {};
328
- for (const [effectId, params] of Object.entries(value)) {
329
- result[effectId] = cloneParams(params);
330
- }
331
- return result;
332
- }
333
-
334
- function cloneParamValue(value: VfxParamValue): VfxParamValue {
335
- if (isColorValue(value)) return { ...value };
336
- return value;
337
- }
338
-
339
- function isColorValue(value: unknown): value is VfxColorValue {
340
- return !!value
341
- && typeof value === 'object'
342
- && !Array.isArray(value)
343
- && typeof (value as VfxColorValue).r === 'number'
344
- && typeof (value as VfxColorValue).g === 'number'
345
- && typeof (value as VfxColorValue).b === 'number';
346
- }
@@ -1,96 +0,0 @@
1
- /**
2
- * SceneVfxService —— 特效播放门面(库纯净版)
3
- *
4
- * 只委托 EffectPackageService:播放 / 预热 / 销毁。注册表经 options.registry 注入。
5
- * 注:旧版的 `initFromConfig`/`createParticleSystem`(读 scene.json + UIImages 的常驻场景粒子路径)
6
- * 属项目耦合,已从库剥离(契约 §17)。场景常驻粒子应建模为普通特效包,由项目在 init 时触发。
7
- */
8
- import { Scene } from '@babylonjs/core/scene';
9
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
10
-
11
- import type { VfxRegistry } from './registry';
12
- import type {
13
- VfxDebugAnchor,
14
- VfxEffectHandle,
15
- VfxEffectPackage,
16
- VfxParamValues,
17
- VfxSpawnTransform,
18
- } from './types';
19
- import { EffectPackageService } from './EffectPackageService';
20
-
21
- export interface SceneVfxWarmupEntry {
22
- effectId: string;
23
- params?: Partial<VfxParamValues>;
24
- spawnTransform?: VfxSpawnTransform | Vector3;
25
- }
26
-
27
- export interface SceneVfxServiceOptions {
28
- /** 注入的注册表(项目侧 glob + buildRegistry 构建) */
29
- registry: VfxRegistry;
30
- getDebugAnchorPosition?: (anchor: VfxDebugAnchor) => Vector3 | null;
31
- }
32
-
33
- export class SceneVfxService {
34
- private effectPackageService: EffectPackageService;
35
-
36
- constructor(scene: Scene, options: SceneVfxServiceOptions) {
37
- this.effectPackageService = new EffectPackageService(scene, {
38
- registry: options.registry,
39
- getDebugAnchorPosition: options.getDebugAnchorPosition,
40
- });
41
- }
42
-
43
- getEffectPackageService(): EffectPackageService {
44
- return this.effectPackageService;
45
- }
46
-
47
- getEffectPackages(): VfxEffectPackage[] {
48
- return this.effectPackageService.getEffectPackages();
49
- }
50
-
51
- getMergedParams(effectId: string, explicitParams?: Partial<VfxParamValues>): VfxParamValues | null {
52
- return this.effectPackageService.getMergedParams(effectId, explicitParams);
53
- }
54
-
55
- playEffectPackage(
56
- effectId: string,
57
- params: Partial<VfxParamValues> = {},
58
- spawnTransform?: VfxSpawnTransform | Vector3,
59
- ): VfxEffectHandle | null {
60
- return this.effectPackageService.playEffectPackage(effectId, params, spawnTransform);
61
- }
62
-
63
- async warmupEffectPackages(entries: SceneVfxWarmupEntry[]): Promise<void> {
64
- await Promise.all(entries.map((entry) => this.warmupEffectPackage(entry)));
65
- }
66
-
67
- private async warmupEffectPackage(entry: SceneVfxWarmupEntry): Promise<void> {
68
- let handle: VfxEffectHandle | null = null;
69
- try {
70
- handle = this.playEffectPackage(entry.effectId, entry.params ?? {}, entry.spawnTransform);
71
- if (!handle) return;
72
- await handle.ready;
73
- await this.waitForAnimationFrames(2);
74
- } catch (error) {
75
- console.warn(`[SceneVfxService] Failed to warm up effect package "${entry.effectId}".`, error);
76
- } finally {
77
- handle?.dispose();
78
- }
79
- }
80
-
81
- private async waitForAnimationFrames(frameCount: number): Promise<void> {
82
- for (let i = 0; i < frameCount; i += 1) {
83
- await new Promise<void>((resolve) => {
84
- if (typeof requestAnimationFrame === 'function') {
85
- requestAnimationFrame(() => resolve());
86
- return;
87
- }
88
- window.setTimeout(resolve, 0);
89
- });
90
- }
91
- }
92
-
93
- dispose(): void {
94
- this.effectPackageService.dispose();
95
- }
96
- }