@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,111 +0,0 @@
1
- {
2
- "schemaVersion": "vfx-asset-map/1.0",
3
- "generatedAt": "2026-05-21T13:04:29.644Z",
4
- "sourcePackage": "vfx-intermediate/cocos-babylon-packages/gas_town_oil_suction",
5
- "policy": {
6
- "runtimeAssetsRoot": "assets/",
7
- "sourceAssetsRoot": "source/",
8
- "note": "assets/ contains files loaded by Babylon runtime. source/cocos/assets/ preserves the original Cocos asset tree for traceability and reconversion."
9
- },
10
- "entries": [
11
- {
12
- "id": "glow_ad",
13
- "kind": "texture",
14
- "group": "textures",
15
- "role": "babylon-runtime",
16
- "runtimeUse": true,
17
- "packagePath": "assets/textures/Glow ad.png",
18
- "sourcePackagePath": "assets/textures/Glow ad.png",
19
- "sourceArchivePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/Glow ad.png",
20
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/Glow ad.png",
21
- "sourceId": "dd44b0a9-0998-4c24-ba2e-d6ac9d1bebb7@6c48a",
22
- "usedBy": [],
23
- "originalProjectPath": "assets/ArtResources/Effect/GasTown_OilSuction/Glow ad.png"
24
- },
25
- {
26
- "id": "asset",
27
- "kind": "material",
28
- "group": "materials",
29
- "role": "source-evidence",
30
- "runtimeUse": false,
31
- "packagePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/石油.mtl",
32
- "sourcePackagePath": "assets/materials/石油.mtl",
33
- "sourceArchivePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/石油.mtl",
34
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/石油.mtl",
35
- "sourceId": "4aabf2b0-1d20-4e13-9384-ae47eb8522aa",
36
- "usedBy": [],
37
- "originalProjectPath": "assets/ArtResources/Effect/GasTown_OilSuction/石油.mtl"
38
- },
39
- {
40
- "id": "particle_emitter_oil_suction_v1",
41
- "kind": "script",
42
- "group": "scripts",
43
- "role": "babylon-runtime",
44
- "runtimeUse": true,
45
- "packagePath": "assets/runtime/gas_town_oil_suction/particleEmitter.v1.js",
46
- "sourcePackagePath": "assets/runtime/gas_town_oil_suction/particleEmitter.v1.js",
47
- "sourceArchivePath": null,
48
- "sourcePath": null,
49
- "sourceId": null,
50
- "usedBy": []
51
- },
52
- {
53
- "id": "02-normalized-vfx",
54
- "kind": "raw",
55
- "group": "raw",
56
- "role": "source-evidence",
57
- "runtimeUse": false,
58
- "packagePath": "source/02-normalized-vfx.json",
59
- "sourcePackagePath": "source/02-normalized-vfx.json",
60
- "sourceArchivePath": null,
61
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/02-normalized-vfx.json",
62
- "sourceId": null,
63
- "usedBy": []
64
- },
65
- {
66
- "id": "01-source-dump",
67
- "kind": "raw",
68
- "group": "raw",
69
- "role": "source-evidence",
70
- "runtimeUse": false,
71
- "packagePath": "source/01-source-dump.json",
72
- "sourcePackagePath": "source/01-source-dump.json",
73
- "sourceArchivePath": null,
74
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/01-source-dump.json",
75
- "sourceId": null,
76
- "usedBy": []
77
- },
78
- {
79
- "id": "asset",
80
- "kind": "raw",
81
- "group": "raw",
82
- "role": "source-evidence",
83
- "runtimeUse": false,
84
- "packagePath": "source/raw/吸石油.prefab",
85
- "sourcePackagePath": "source/raw/吸石油.prefab",
86
- "sourceArchivePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab",
87
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab",
88
- "sourceId": "29954e7e-d137-4c8b-a730-4f0320cbece8",
89
- "usedBy": [],
90
- "originalProjectPath": "assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab"
91
- },
92
- {
93
- "id": "asset.meta",
94
- "kind": "raw",
95
- "group": "raw",
96
- "role": "source-evidence",
97
- "runtimeUse": false,
98
- "packagePath": "source/raw/吸石油.prefab.meta",
99
- "sourcePackagePath": "source/raw/吸石油.prefab.meta",
100
- "sourceArchivePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab.meta",
101
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab.meta",
102
- "sourceId": "29954e7e-d137-4c8b-a730-4f0320cbece8",
103
- "usedBy": [],
104
- "originalProjectPath": "assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab.meta"
105
- }
106
- ],
107
- "gcsPrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction",
108
- "sourceArchivePrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source",
109
- "fullProvenancePrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/provenance",
110
- "canonicalSource": "gcs"
111
- }
@@ -1,45 +0,0 @@
1
- {
2
- "schemaVersion": "vfx-library-lineage/1.0",
3
- "savedAt": "2026-05-21T13:04:29.665Z",
4
- "sourcePackage": "vfx-intermediate/cocos-babylon-packages/gas_town_oil_suction",
5
- "sourceEffect": "03-effect.json",
6
- "libraryPackage": "packages/resource.particle.viscous_oil_suction",
7
- "effectId": "resource.particle.viscous_oil_suction",
8
- "media": {
9
- "previewVideo": "media/preview.webm",
10
- "previewWebm": "media/preview.webm",
11
- "previewMp4": "media/preview.mp4",
12
- "previewGif": "media/preview.gif",
13
- "thumbnail": "media/thumbnail.png",
14
- "gcsUri": null
15
- },
16
- "recording": {
17
- "provider": "browser-mediarecorder",
18
- "recordedAt": "2026-05-21T13:04:30.575Z",
19
- "mimeType": "video/webm;codecs=vp9",
20
- "durationSec": 10,
21
- "fps": 30,
22
- "media": {
23
- "previewVideo": "media/preview.webm",
24
- "previewWebm": "media/preview.webm",
25
- "previewMp4": "media/preview.mp4",
26
- "previewGif": "media/preview.gif",
27
- "thumbnail": "media/thumbnail.png",
28
- "gcsUri": null
29
- }
30
- },
31
- "description": {
32
- "provider": "litellm",
33
- "status": "described",
34
- "model": "gemini-3.1-pro-preview",
35
- "generatedAt": "2026-05-21T13:30:34.263Z"
36
- },
37
- "gcsPrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction",
38
- "sourceArchivePrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source",
39
- "fullProvenancePrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/provenance",
40
- "previousIds": [
41
- "world.particle.gas_town_oil_suction",
42
- "world.particle.viscous_oil_suction"
43
- ],
44
- "renamedAt": "2026-05-21T13:39:08.485Z"
45
- }
package/packages/types.ts DELETED
@@ -1,195 +0,0 @@
1
- import type { Scene } from '@babylonjs/core/scene';
2
- import type { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import type { TransformNode } from '@babylonjs/core/Meshes/transformNode';
4
- import type { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
5
-
6
- export type VfxDebugAnchor = 'playerRight' | 'player';
7
-
8
- // ── 运行时外部信息(inputs)──────────────────────────────────────────────
9
- // 与 params(设计期可调、保存)区分:inputs 是运行时由游戏逻辑提供的外部上下文,常动态。
10
- // 值可为静态值或 provider(() => 值);动态目标(移动)用 provider 每帧取。见 docs/vfx-placement-design.md。
11
- export type VfxInput<T> = T | (() => T);
12
- export type VfxInputKey = 'aim' | 'normal' | 'endpoints' | 'target';
13
-
14
- /** 调用方提供的 inputs(放在 VfxSpawnTransform.inputs)。 */
15
- export interface VfxEffectInputs {
16
- aim?: VfxInput<Vector3>; // 瞄准点(激光/定向武器)
17
- normal?: VfxInput<Vector3>; // 表面法线(贴花/冲击)
18
- endpoints?: Array<VfxInput<Vector3>>; // link/链式(≥2 端点)
19
- target?: AbstractMesh; // target-material 类(改目标自身材质)
20
- /** 运行时标量(throttle/intensity 等),值可静态或 provider;特效每帧读取做活改(G5)。 */
21
- scalars?: Record<string, VfxInput<number>>;
22
- }
23
-
24
- /** create() 收到的、已归一化为访问器的 inputs(provider 与静态值都变成可调用的函数)。 */
25
- export interface VfxResolvedInputs {
26
- aim?: () => Vector3;
27
- normal?: () => Vector3;
28
- endpoints?: Array<() => Vector3>;
29
- target?: AbstractMesh;
30
- scalars?: Record<string, () => number>;
31
- }
32
-
33
- export type VfxParamValue = number | string | VfxColorValue;
34
- export type VfxParamValues = Record<string, VfxParamValue>;
35
-
36
- export interface VfxColorValue {
37
- r: number;
38
- g: number;
39
- b: number;
40
- }
41
-
42
- export type VfxParamKind =
43
- | 'number'
44
- | 'color'
45
- | 'enum'
46
- | 'opacity'
47
- | 'lifetime'
48
- | 'scale';
49
-
50
- export interface VfxParamDefinitionBase {
51
- key: string;
52
- label: string;
53
- kind: VfxParamKind;
54
- description?: string;
55
- }
56
-
57
- export interface VfxNumberParamDefinition extends VfxParamDefinitionBase {
58
- kind: 'number' | 'opacity' | 'lifetime' | 'scale';
59
- min?: number;
60
- max?: number;
61
- step?: number;
62
- }
63
-
64
- export interface VfxColorParamDefinition extends VfxParamDefinitionBase {
65
- kind: 'color';
66
- }
67
-
68
- export interface VfxEnumParamOption {
69
- value: string;
70
- label: string;
71
- }
72
-
73
- export interface VfxEnumParamDefinition extends VfxParamDefinitionBase {
74
- kind: 'enum';
75
- options: VfxEnumParamOption[];
76
- }
77
-
78
- export type VfxParamDefinition = VfxNumberParamDefinition | VfxColorParamDefinition | VfxEnumParamDefinition;
79
-
80
- export interface VfxEffectPackageContext<PParams extends VfxParamValues = VfxParamValues> {
81
- scene: Scene;
82
- root: TransformNode;
83
- position: Vector3;
84
- rotationY: number;
85
- renderingGroupId?: number;
86
- params: PParams;
87
- /**
88
- * 解析后的三轴缩放(来自 spawnTransform.scale,默认 (1,1,1))。
89
- * root 的缩放由 service 写好;但**几何不挂 root 的 world/detached 特效**(粒子/抛物池/世界坐标 emitter)
90
- * root.scaling 作用不到,须自行把 ctx.scale 乘进几何尺寸/初速(G3)。
91
- */
92
- scale: Vector3;
93
- /** 运行时外部信息(已归一化为访问器)。link/target/定向武器/活改标量等按 required/optionalInputs 读取。 */
94
- inputs?: VfxResolvedInputs;
95
- }
96
-
97
- export interface VfxEffectHandle {
98
- root: TransformNode;
99
- ready?: Promise<void>;
100
- dispose(): void;
101
- /** 可选:运行时活改 params(无须 dispose+replay,避免重置缓动/可见闪)。声明即纳入契约(G5)。 */
102
- update?(params: Partial<VfxParamValues>): void;
103
- }
104
-
105
- export interface VfxEffectManifest {
106
- id: string;
107
- nameZh: string;
108
- assets?: string[];
109
- source?: string;
110
- }
111
-
112
- export interface VfxSpawnParamKeys {
113
- offsetX?: string;
114
- offsetY?: string;
115
- offsetZ?: string;
116
- }
117
-
118
- /** 欧拉角(弧度)/ 三轴缩放的纯数据三元组。 */
119
- export interface VfxVector3Like {
120
- x: number;
121
- y: number;
122
- z: number;
123
- }
124
-
125
- export interface VfxSpawnTransform {
126
- position?: Vector3;
127
- /**
128
- * 兼容别名:绕 Y 轴旋转(弧度)。
129
- * 若同时给了 `rotation`,以 `rotation` 为准;否则回退到 `rotationY`。
130
- */
131
- rotationY?: number;
132
- /** 完整欧拉角旋转(弧度);给了就覆盖 `rotationY`。 */
133
- rotation?: VfxVector3Like;
134
- /** 等比缩放(number)或三轴缩放;默认 1。 */
135
- scale?: number | VfxVector3Like;
136
- /** 挂载点节点;给了就把特效 root parent 到它(特效随之跟随)。 */
137
- parent?: TransformNode;
138
- /**
139
- * position/rotation/scale 的坐标系解释:
140
- * - 给了 `parent` 时默认 `true`:按相对 parent 的 **local** transform 解释;
141
- * - 没给 `parent` 时默认 `false`:按 **世界坐标** 解释(保持现状行为)。
142
- */
143
- offsetIsLocal?: boolean;
144
- renderingGroupId?: number;
145
- /** 运行时外部信息(瞄准点/法线/端点/目标 mesh);值可为静态或 provider(动态每帧取)。 */
146
- inputs?: VfxEffectInputs;
147
- }
148
-
149
- export interface VfxParamsFile {
150
- schemaVersion: string;
151
- effectId?: string;
152
- updatedAt?: string | null;
153
- params?: Partial<VfxParamValues>;
154
- }
155
-
156
- /**
157
- * 特效的放置本质(内禀标记)。决定系统/binding/面板怎么放它,不靠猜。见 docs/vfx-placement-design.md。
158
- * - 'socket' local:挂对象命名挂点、随它走(parent + follow)。
159
- * - 'world' global:放世界某点、不跟随(fire-and-forget,可按法线定向)。
160
- * - 'screen' global:屏幕/相机层(后处理/相机扰动/DOM 全屏),无 3D transform。
161
- * - 'target' 改目标自身材质/渲染态,没有可 spawn 的 root。
162
- * - 'link' 连 ≥2 个端点(每帧重建几何),单 parent 表达不了。
163
- */
164
- export type VfxPlacement = 'socket' | 'world' | 'screen' | 'target' | 'link';
165
-
166
- export interface VfxEffectPackage<PParams extends VfxParamValues = VfxParamValues> {
167
- id: string;
168
- nameZh: string;
169
- manifest?: VfxEffectManifest;
170
- defaultParams: PParams;
171
- debugAnchor?: VfxDebugAnchor;
172
- spawnParams?: VfxSpawnParamKeys;
173
- savedParams?: Partial<VfxParamValues>;
174
- debugParams: VfxParamDefinition[];
175
- /** 放置本质标记(默认 'world')。 */
176
- placement?: VfxPlacement;
177
- /** 声明**必需**的运行时 inputs(link 必须给 endpoints/aim、target 类必须给 target mesh);service 缺失时 warn。 */
178
- requiredInputs?: VfxInputKey[];
179
- /** 声明**可选**的运行时 inputs(给了就用、不给走默认,如定向武器的 aim:给了朝目标、不给走挂点朝向);不触发缺失 warn(G2)。 */
180
- optionalInputs?: VfxInputKey[];
181
- /**
182
- * 几何空间:'local'=几何挂 root 子节点随它刚性变换;'world'=几何用世界坐标、root 仅作每帧采样源(拖尾)。
183
- * 默认随 placement 推断('socket'→local,其余→world)。
184
- */
185
- geometrySpace?: 'local' | 'world';
186
- /**
187
- * 构建特效。**约定(G6):root 的 transform(position/rotation/scaling/parent)归 service 独占** ——
188
- * service 在 create() 后会二次 `applySpawnToRoot` 覆盖,create 内写 `root.position/rotation/scaling` 会被静默清掉。
189
- * 需要内部局部偏移(如抬升)请用 root 下的**子 TransformNode** 承载,或声明 `spawnParams.offsetY` 走 service 存活通道。
190
- * 几何挂 root 子树即随挂载跟随;world/拖尾类几何走世界坐标、读 ctx.scale 缩放。
191
- */
192
- create(context: VfxEffectPackageContext<PParams>): VfxEffectHandle;
193
- }
194
-
195
- export type VfxDebugOverrides = Record<string, Partial<VfxParamValues>>;
@@ -1,166 +0,0 @@
1
- #!/usr/bin/env node
2
- // 新增特效 guardrail · Layer 2(构建产物禁运行时扫描,见 issue #3,最强一层)。
3
- // `pnpm build` 之后扫 dist/,镜像消费方 Playable bundle 策略(lumber-order
4
- // scripts/check-playable-asset-bundle.mjs 的 forbiddenTokens),把"会坑消费方"的重运行时
5
- // 提前到库侧拦住 —— 即便它是被某个特效"间接"拉进来的(顺着 dist 的 chunk import 图归因到特效)。
6
- //
7
- // 用法:
8
- // node tooling/check-dist-runtime.mjs # 扫 dist/
9
- // node tooling/check-dist-runtime.mjs --dist <dir> # 换扫描目录(回归 fixture 用)
10
- // 退出码:命中 → 1;干净 → 0。
11
- import { readdirSync, statSync, readFileSync, existsSync } from 'node:fs';
12
- import { resolve, dirname, join, relative } from 'node:path';
13
- import { fileURLToPath } from 'node:url';
14
-
15
- const LIB_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
16
- const argv = process.argv.slice(2);
17
- const distArg = argv.indexOf('--dist') >= 0 ? argv[argv.indexOf('--dist') + 1] : undefined;
18
- const DIST = resolve(distArg ?? join(LIB_ROOT, 'dist'));
19
-
20
- // 精确 token —— 类名/扩展名/MIME,绝不用宽松子串(如 `basis` 会误报 RingBasis 变量)。
21
- // 与 lumber-order check-playable-asset-bundle.mjs 的 forbiddenTokens 对齐 + 补 draco/ktx2。
22
- const SUBSTRING_TOKENS = [
23
- 'EXT_meshopt_compression', 'MeshoptDecoder', 'meshopt_decoder',
24
- 'KHR_draco_mesh_compression', 'DracoCompression', 'DracoDecoder', 'draco_decoder', 'draco_wasm_wrapper',
25
- 'KHR_texture_basisu', 'KhronosTextureContainer2', '_KTX2Decoder', 'MSCTranscoder', 'basis_transcoder',
26
- 'application/x-glb-brotli', 'brotli-dec-wasm',
27
- 'application/wasm', 'data:application/wasm',
28
- 'WebAssembly.Module', 'WebAssembly.instantiate',
29
- ];
30
- // `.wasm` 资源 URL:要求路径分隔符(/),只拦真资源路径如 "./decoder.wasm"/"chunks/x.wasm",
31
- // 不误报纯文件名字符串(错误消息 "physics.wasm" / debug 标签 "ringBurst.wasm" 无 /)。
32
- const WASM_URL_RE = /['"][^'"]*\/[^'"/]*\.wasm(\?[^'"]*)?['"]/;
33
-
34
- // 与 Layer 1 一致:扫描前剥注释(避免 dist 里残留的解释性注释/banner 误报)。
35
- function stripComments(src) {
36
- return src.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(?<!:)\/\/[^\n]*/g, '');
37
- }
38
-
39
- function tokensIn(rawContent) {
40
- const content = stripComments(rawContent);
41
- const hits = [];
42
- for (const t of SUBSTRING_TOKENS) if (content.includes(t)) hits.push(t);
43
- const m = content.match(WASM_URL_RE);
44
- if (m) hits.push(m[0]);
45
- return hits;
46
- }
47
-
48
- // dist 模块文件:.js + .mjs/.cjs(防止重运行时藏在非 .js chunk 里绕过)。
49
- const DIST_MODULE_EXT = /\.(js|mjs|cjs)$/;
50
- function jsFilesIn(dir, out = []) {
51
- if (!existsSync(dir)) return out;
52
- for (const e of readdirSync(dir)) {
53
- const p = join(dir, e);
54
- const s = statSync(p);
55
- if (s.isDirectory()) jsFilesIn(p, out);
56
- else if (DIST_MODULE_EXT.test(e)) out.push(p);
57
- }
58
- return out;
59
- }
60
-
61
- // 解析一个 dist 模块的相对 import(构 chunk 依赖图;裸包如 @babylonjs/* 不进图)。
62
- function relImports(file) {
63
- const src = readFileSync(file, 'utf8');
64
- const specs = new Set();
65
- let m;
66
- const re = /\bfrom\s*['"]([^'"]+)['"]/g;
67
- while ((m = re.exec(src))) specs.add(m[1]);
68
- const side = /\bimport\s*['"]([^'"]+)['"]/g;
69
- while ((m = side.exec(src))) specs.add(m[1]);
70
- const dyn = /\bimport\(\s*['"]([^'"]+)['"]\s*\)/g;
71
- while ((m = dyn.exec(src))) specs.add(m[1]);
72
- const resolved = [];
73
- for (const spec of specs) {
74
- if (!spec.startsWith('.')) continue;
75
- const target = resolve(dirname(file), spec);
76
- const cands = [target, target + '.js', target + '.mjs', target + '.cjs',
77
- join(target, 'index.js'), join(target, 'index.mjs'), join(target, 'index.cjs')];
78
- for (const cand of cands) {
79
- if (existsSync(cand) && statSync(cand).isFile()) { resolved.push(cand); break; }
80
- }
81
- }
82
- return resolved;
83
- }
84
-
85
- // dist/<dir>/index.{js,mjs,cjs} 取第一个存在的入口。
86
- function indexEntry(dir) {
87
- for (const ext of ['js', 'mjs', 'cjs']) {
88
- const p = join(dir, `index.${ext}`);
89
- if (existsSync(p)) return p;
90
- }
91
- return null;
92
- }
93
-
94
- function reachableFrom(entry, importGraph) {
95
- const seen = new Set();
96
- const stack = [entry];
97
- while (stack.length) {
98
- const f = stack.pop();
99
- if (seen.has(f)) continue;
100
- seen.add(f);
101
- for (const dep of importGraph.get(f) ?? []) if (!seen.has(dep)) stack.push(dep);
102
- }
103
- return seen;
104
- }
105
-
106
- // ── 主流程 ────────────────────────────────────────────────────────────────────
107
- if (!existsSync(DIST)) {
108
- console.error(`✗ dist 不存在:${DIST}(先 \`pnpm build\`)。`);
109
- process.exit(1);
110
- }
111
-
112
- const allJs = jsFilesIn(DIST);
113
- const importGraph = new Map(allJs.map((f) => [f, relImports(f)]));
114
- const tokenByFile = new Map();
115
- for (const f of allJs) {
116
- const hits = tokensIn(readFileSync(f, 'utf8'));
117
- if (hits.length) tokenByFile.set(f, [...new Set(hits)]);
118
- }
119
-
120
- // 入口 = dist/index.{js,mjs,cjs}(runtime 核心)+ 每个 dist/effects/<id>/index.{js,mjs,cjs}。
121
- const entries = [];
122
- const rootIndex = indexEntry(DIST);
123
- if (rootIndex) entries.push({ label: 'runtime (index)', entry: rootIndex });
124
- const effectsDist = join(DIST, 'effects');
125
- if (existsSync(effectsDist)) {
126
- for (const id of readdirSync(effectsDist)) {
127
- const idx = indexEntry(join(effectsDist, id));
128
- if (idx) entries.push({ label: id, entry: idx });
129
- }
130
- }
131
-
132
- // 归因:每个入口 → 它可达的、命中 token 的文件。
133
- const findings = []; // {label, file, tokens}
134
- const attributedFiles = new Set();
135
- for (const { label, entry } of entries) {
136
- const reach = reachableFrom(entry, importGraph);
137
- for (const f of reach) {
138
- if (tokenByFile.has(f)) {
139
- attributedFiles.add(f);
140
- findings.push({ label, file: f, tokens: tokenByFile.get(f) });
141
- }
142
- }
143
- }
144
- // 没被任何入口可达但仍命中的文件(孤儿,理论上不该有)。
145
- for (const [f, tokens] of tokenByFile) {
146
- if (!attributedFiles.has(f)) findings.push({ label: '(未归因/孤儿 chunk)', file: f, tokens });
147
- }
148
-
149
- if (findings.length === 0) {
150
- console.log(`✅ Layer 2 dist 禁运行时扫描通过:${allJs.length} 个 js,0 命中(无 meshopt/draco/ktx2/brotli/wasm 运行时)。`);
151
- process.exit(0);
152
- }
153
-
154
- console.log(`❌ Layer 2 dist 禁运行时扫描:发现 ${findings.length} 处命中。\n`);
155
- // 按特效聚合展示
156
- const byLabel = {};
157
- for (const f of findings) (byLabel[f.label] ??= []).push(f);
158
- for (const [label, items] of Object.entries(byLabel)) {
159
- console.log(` 特效/入口:${label}`);
160
- for (const it of items) {
161
- console.log(` ✗ ${relative(DIST, it.file)} ← ${it.tokens.join(', ')}`);
162
- }
163
- }
164
- console.log('\n修复:定位拉入该运行时的特效(多半是全量 glTF loader / 含 meshopt|draco|ktx2 的 GLB)。');
165
- console.log(' 改用核心 loader、把 GLB 预解压成 plain、或用 ?url 内联的轻量资源。详见 Layer 1 提示与 #2 v0.1.1。');
166
- process.exit(1);
@@ -1,55 +0,0 @@
1
- #!/usr/bin/env node
2
- // 生成顶层 index.json:扫 packages/effects/<id>/,汇总 id/nameZh/kind/category/tags/media。
3
- // 富检索字段(category/tags/media)优先取 description.json,避免与 manifest 双源。
4
- import { readdirSync, statSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
5
- import { resolve, dirname, join } from 'node:path';
6
- import { fileURLToPath } from 'node:url';
7
-
8
- const LIB_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
9
- const EFFECTS_DIR = join(LIB_ROOT, 'packages', 'effects');
10
-
11
- function readJsonSafe(p) {
12
- try { return JSON.parse(readFileSync(p, 'utf8')); } catch { return null; }
13
- }
14
-
15
- const effects = [];
16
- for (const entry of readdirSync(EFFECTS_DIR)) {
17
- const dir = join(EFFECTS_DIR, entry);
18
- if (!statSync(dir).isDirectory()) continue;
19
- const indexPath = join(dir, 'index.ts');
20
- if (!existsSync(indexPath)) continue;
21
-
22
- const effectJson = readJsonSafe(join(dir, 'effect.json')) ?? {};
23
- const desc = readJsonSafe(join(dir, 'description.json')) ?? {};
24
- const indexSrc = readFileSync(indexPath, 'utf8');
25
- const kind = /createManifestRuntimeEffectPackage|createDataDrivenEffectPackage/.test(indexSrc) ? 'data' : 'code';
26
-
27
- const mediaDir = join(dir, 'media');
28
- const media = {};
29
- if (existsSync(mediaDir)) {
30
- for (const f of readdirSync(mediaDir)) {
31
- if (/thumbnail/.test(f)) media.thumbnail = `packages/effects/${entry}/media/${f}`;
32
- else if (/preview\.(mp4|webm|gif)$/.test(f)) media.preview = `packages/effects/${entry}/media/${f}`;
33
- }
34
- }
35
-
36
- effects.push({
37
- id: effectJson.id || entry,
38
- dir: entry,
39
- nameZh: effectJson.nameZh || desc.searchTitleZh || entry,
40
- kind,
41
- category: desc.semanticTags?.[0] || (effectJson.id?.split('.')[0]) || null,
42
- tags: desc.searchKeywordsEn || desc.visualTags || effectJson.search?.tags || [],
43
- media,
44
- path: `packages/effects/${entry}`,
45
- });
46
- }
47
- effects.sort((a, b) => a.id.localeCompare(b.id));
48
-
49
- const out = {
50
- schemaVersion: 'vfx-index/1.0',
51
- count: effects.length,
52
- effects,
53
- };
54
- writeFileSync(join(LIB_ROOT, 'index.json'), JSON.stringify(out, null, 2) + '\n');
55
- console.log(`✅ index.json 生成:${effects.length} 个特效。`);