@babylonjs/lite 1.9.0 → 1.10.0

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 (128) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/index.js +596 -526
  3. package/dist/index.js.map +1 -1
  4. package/index.d.ts +107 -4
  5. package/lib/engine/engine.js +1 -1
  6. package/lib/engine/engine.js.map +1 -1
  7. package/lib/frame-graph/render-task.js +6 -4
  8. package/lib/frame-graph/render-task.js.map +1 -1
  9. package/lib/index.js +4 -1
  10. package/lib/index.js.map +1 -1
  11. package/lib/loader-gltf/gltf-ext-quantization.js +4 -1
  12. package/lib/loader-gltf/gltf-ext-quantization.js.map +1 -1
  13. package/lib/loader-gltf/gltf-feature-registry.js +11 -1
  14. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  15. package/lib/loader-gltf/gltf-feature-variants.js +11 -1
  16. package/lib/loader-gltf/gltf-feature-variants.js.map +1 -1
  17. package/lib/loader-gltf/gltf-json-asset.js +8 -1
  18. package/lib/loader-gltf/gltf-json-asset.js.map +1 -1
  19. package/lib/loader-gltf/gltf-material.js +4 -29
  20. package/lib/loader-gltf/gltf-material.js.map +1 -1
  21. package/lib/loader-gltf/gltf-parser.js +1 -8
  22. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  23. package/lib/loader-gltf/gltf-pbr-builder.js +1 -15
  24. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  25. package/lib/loader-gltf/gltf-variants.js +5 -10
  26. package/lib/loader-gltf/gltf-variants.js.map +1 -1
  27. package/lib/loader-gltf/load-gltf.js +19 -29
  28. package/lib/loader-gltf/load-gltf.js.map +1 -1
  29. package/lib/material/pbr/background-dds-skybox.js +35 -93
  30. package/lib/material/pbr/background-dds-skybox.js.map +1 -1
  31. package/lib/material/pbr/background-ground.js +13 -18
  32. package/lib/material/pbr/background-ground.js.map +1 -1
  33. package/lib/material/pbr/background-hdr-skybox.js +35 -93
  34. package/lib/material/pbr/background-hdr-skybox.js.map +1 -1
  35. package/lib/material/pbr/background-solid-skybox.js +35 -93
  36. package/lib/material/pbr/background-solid-skybox.js.map +1 -1
  37. package/lib/material/pbr/fragments/clearcoat-fragment.js +5 -1
  38. package/lib/material/pbr/fragments/clearcoat-fragment.js.map +1 -1
  39. package/lib/material/pbr/fragments/iridescence-fragment.js +4 -1
  40. package/lib/material/pbr/fragments/iridescence-fragment.js.map +1 -1
  41. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +7 -2
  42. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  43. package/lib/material/pbr/fragments/sheen-fragment.js +2 -1
  44. package/lib/material/pbr/fragments/sheen-fragment.js.map +1 -1
  45. package/lib/material/pbr/fragments/subsurface-fragment.js +2 -1
  46. package/lib/material/pbr/fragments/subsurface-fragment.js.map +1 -1
  47. package/lib/material/pbr/fragments/unlit-fragment.js +1 -2
  48. package/lib/material/pbr/fragments/unlit-fragment.js.map +1 -1
  49. package/lib/material/pbr/pbr-compose.js +2 -0
  50. package/lib/material/pbr/pbr-compose.js.map +1 -1
  51. package/lib/material/pbr/pbr-flag-bits.js +1 -16
  52. package/lib/material/pbr/pbr-flag-bits.js.map +1 -1
  53. package/lib/material/pbr/pbr-flags.js +1 -1
  54. package/lib/material/pbr/pbr-fog-wgsl.js +1 -1
  55. package/lib/material/pbr/pbr-fog-wgsl.js.map +1 -1
  56. package/lib/material/pbr/pbr-geometry-output-shader.js +2 -1
  57. package/lib/material/pbr/pbr-geometry-output-shader.js.map +1 -1
  58. package/lib/material/pbr/pbr-geometry-view.js +2 -1
  59. package/lib/material/pbr/pbr-geometry-view.js.map +1 -1
  60. package/lib/material/pbr/pbr-renderable.js +4 -0
  61. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  62. package/lib/material/pbr/pbr-template-gamma.js +7 -0
  63. package/lib/material/pbr/pbr-template-gamma.js.map +1 -0
  64. package/lib/material/pbr/pbr-template.js +2 -2
  65. package/lib/material/pbr/pbr-template.js.map +1 -1
  66. package/lib/material/standard/skybox-cubemap.js +1 -1
  67. package/lib/material/standard/skybox-cubemap.js.map +1 -1
  68. package/lib/material/standard/standard-template.js +1 -1
  69. package/lib/material/standard/standard-template.js.map +1 -1
  70. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js +3 -5
  71. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js.map +1 -1
  72. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js +3 -5
  73. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js.map +1 -1
  74. package/lib/particle/node/blocks/particle-condition-block.js +55 -0
  75. package/lib/particle/node/blocks/particle-condition-block.js.map +1 -0
  76. package/lib/particle/node/blocks/particle-float-to-int-block.js +28 -0
  77. package/lib/particle/node/blocks/particle-float-to-int-block.js.map +1 -0
  78. package/lib/particle/node/blocks/particle-gradient-block.js +32 -0
  79. package/lib/particle/node/blocks/particle-gradient-block.js.map +1 -0
  80. package/lib/particle/node/blocks/particle-gradient-value-block.js +11 -0
  81. package/lib/particle/node/blocks/particle-gradient-value-block.js.map +1 -0
  82. package/lib/particle/node/blocks/particle-lerp-block.js +2 -24
  83. package/lib/particle/node/blocks/particle-lerp-block.js.map +1 -1
  84. package/lib/particle/node/blocks/particle-lerp.js +27 -0
  85. package/lib/particle/node/blocks/particle-lerp.js.map +1 -0
  86. package/lib/particle/node/blocks/particle-vector-length-block.js +30 -0
  87. package/lib/particle/node/blocks/particle-vector-length-block.js.map +1 -0
  88. package/lib/particle/node/blocks/system-block.js +13 -3
  89. package/lib/particle/node/blocks/system-block.js.map +1 -1
  90. package/lib/particle/node/blocks/texture-source-block.js +2 -1
  91. package/lib/particle/node/blocks/texture-source-block.js.map +1 -1
  92. package/lib/particle/node/blocks/update-size-block.js +19 -0
  93. package/lib/particle/node/blocks/update-size-block.js.map +1 -0
  94. package/lib/particle/node/npe-build-state.js +10 -0
  95. package/lib/particle/node/npe-build-state.js.map +1 -1
  96. package/lib/particle/node/npe-registry.js +12 -0
  97. package/lib/particle/node/npe-registry.js.map +1 -1
  98. package/lib/particle/particle-system.js +7 -2
  99. package/lib/particle/particle-system.js.map +1 -1
  100. package/lib/picking/billboard-pick-pipeline.js +269 -0
  101. package/lib/picking/billboard-pick-pipeline.js.map +1 -0
  102. package/lib/picking/gpu-picker.js +37 -37
  103. package/lib/picking/gpu-picker.js.map +1 -1
  104. package/lib/picking/gs-picking-pipeline.js +26 -1
  105. package/lib/picking/gs-picking-pipeline.js.map +1 -1
  106. package/lib/picking/pick-contributor.js +13 -0
  107. package/lib/picking/pick-contributor.js.map +1 -0
  108. package/lib/picking/picking-info.js.map +1 -1
  109. package/lib/scene/scene-core.js +2 -2
  110. package/lib/scene/scene-core.js.map +1 -1
  111. package/lib/shader/wgsl-fog.js +18 -0
  112. package/lib/shader/wgsl-fog.js.map +1 -0
  113. package/lib/shader/wgsl-helpers.js +1 -16
  114. package/lib/shader/wgsl-helpers.js.map +1 -1
  115. package/lib/sprite/billboard-scene.js +2 -0
  116. package/lib/sprite/billboard-scene.js.map +1 -1
  117. package/lib/sprite/picking/pick-billboard.js +16 -0
  118. package/lib/sprite/picking/pick-billboard.js.map +1 -0
  119. package/lib/sprite/picking/pick-sprite-2d.js +41 -0
  120. package/lib/sprite/picking/pick-sprite-2d.js.map +1 -0
  121. package/lib/sprite/shared/sprite-atlas.js +4 -1
  122. package/lib/sprite/shared/sprite-atlas.js.map +1 -1
  123. package/lib/sprite/sprite-2d.js.map +1 -1
  124. package/lib/sprite/sprite-pipeline.js +18 -1
  125. package/lib/sprite/sprite-pipeline.js.map +1 -1
  126. package/lib/sprite/sprite-renderer.js +12 -6
  127. package/lib/sprite/sprite-renderer.js.map +1 -1
  128. package/package.json +3 -3
@@ -0,0 +1,32 @@
1
+ import { lerpValue } from './particle-lerp.js';
2
+
3
+ const particleGradientBlock = {
4
+ build(block, ctx) {
5
+ const gradientGetter = ctx.input(block, "gradient", () => 1);
6
+ const entries = block.inputs.filter((input) => input.name.startsWith("value") && input.targetBlockId != null).map((input) => ctx.input(block, input.name)(ctx.state)).sort((a, b) => a.reference - b.reference);
7
+ const getter = (state) => {
8
+ const gradient = gradientGetter(state);
9
+ if (entries.length === 1) {
10
+ return entries[0].value(state);
11
+ }
12
+ let next = null;
13
+ for (let i = entries.length - 1; i >= 0; i--) {
14
+ const entry = entries[i];
15
+ if (entry.reference <= gradient) {
16
+ const currentValue = entry.value(state);
17
+ if (next) {
18
+ const scale = Math.max(0, Math.min(1, (gradient - entry.reference) / (next.reference - entry.reference)));
19
+ return lerpValue(currentValue, next.value(state), scale);
20
+ }
21
+ return currentValue;
22
+ }
23
+ next = entry;
24
+ }
25
+ return 0;
26
+ };
27
+ ctx.setOutput(block.id, "output", getter);
28
+ }
29
+ };
30
+
31
+ export { particleGradientBlock };
32
+ //# sourceMappingURL=particle-gradient-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-gradient-block.js","sources":["../../../../../src/particle/node/blocks/particle-gradient-block.ts"],"sourcesContent":["import { lerpValue } from \"./particle-lerp.js\";\nimport type { ParticleGradientEntry } from \"./particle-gradient-value-block.js\";\nimport type { ParticleBlockEvaluator, NpeGetter } from \"../npe-types.js\";\n\n/**\n * `ParticleGradientBlock` — interpolates between gradient stops (each a `ParticleGradientValueBlock` wired\n * to a `valueN` input) by the scalar `gradient` input (typically the age/lifetime ratio). Stops are sorted\n * by reference once at build; on evaluation the value is lerped between the stop at or below `gradient` and\n * the next one above it (component-wise for vectors/colours). A `gradient` below the lowest stop yields 0.\n * Mirrors BJS `ParticleGradientBlock`.\n */\nexport const particleGradientBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const gradientGetter = ctx.input(block, \"gradient\", () => 1);\n\n // The stops are static blocks, so resolve them (reference + value getter) once and sort by reference.\n const entries = block.inputs\n .filter((input) => input.name.startsWith(\"value\") && input.targetBlockId != null)\n .map((input) => ctx.input(block, input.name)(ctx.state) as unknown as ParticleGradientEntry)\n .sort((a, b) => a.reference - b.reference);\n\n const getter: NpeGetter = (state) => {\n const gradient = gradientGetter(state) as number;\n if (entries.length === 1) {\n return entries[0]!.value(state);\n }\n\n // Walk from the top: interpolate between the stop at/below `gradient` and the next one above it.\n let next: ParticleGradientEntry | null = null;\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i]!;\n if (entry.reference <= gradient) {\n const currentValue = entry.value(state);\n if (next) {\n const scale = Math.max(0, Math.min(1, (gradient - entry.reference) / (next.reference - entry.reference)));\n return lerpValue(currentValue, next.value(state), scale);\n }\n return currentValue;\n }\n next = entry;\n }\n return 0;\n };\n ctx.setOutput(block.id, \"output\", getter);\n },\n};\n"],"names":[],"mappings":";;AAWO,MAAM,qBAAA,GAAgD;AAAA,EACzD,KAAA,CAAM,OAAO,GAAA,EAAK;AACd,IAAA,MAAM,iBAAiB,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,UAAA,EAAY,MAAM,CAAC,CAAA;AAG3D,IAAA,MAAM,OAAA,GAAU,KAAA,CAAM,MAAA,CACjB,MAAA,CAAO,CAAC,KAAA,KAAU,KAAA,CAAM,IAAA,CAAK,UAAA,CAAW,OAAO,CAAA,IAAK,KAAA,CAAM,aAAA,IAAiB,IAAI,EAC/E,GAAA,CAAI,CAAC,KAAA,KAAU,GAAA,CAAI,MAAM,KAAA,EAAO,KAAA,CAAM,IAAI,CAAA,CAAE,IAAI,KAAK,CAAqC,CAAA,CAC1F,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,GAAY,EAAE,SAAS,CAAA;AAE7C,IAAA,MAAM,MAAA,GAAoB,CAAC,KAAA,KAAU;AACjC,MAAA,MAAM,QAAA,GAAW,eAAe,KAAK,CAAA;AACrC,MAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACtB,QAAA,OAAO,OAAA,CAAQ,CAAC,CAAA,CAAG,KAAA,CAAM,KAAK,CAAA;AAAA,MAClC;AAGA,MAAA,IAAI,IAAA,GAAqC,IAAA;AACzC,MAAA,KAAA,IAAS,IAAI,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG,CAAA,IAAK,GAAG,CAAA,EAAA,EAAK;AAC1C,QAAA,MAAM,KAAA,GAAQ,QAAQ,CAAC,CAAA;AACvB,QAAA,IAAI,KAAA,CAAM,aAAa,QAAA,EAAU;AAC7B,UAAA,MAAM,YAAA,GAAe,KAAA,CAAM,KAAA,CAAM,KAAK,CAAA;AACtC,UAAA,IAAI,IAAA,EAAM;AACN,YAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAA,CAAI,QAAA,GAAW,KAAA,CAAM,SAAA,KAAc,IAAA,CAAK,SAAA,GAAY,KAAA,CAAM,UAAU,CAAC,CAAA;AACxG,YAAA,OAAO,UAAU,YAAA,EAAc,IAAA,CAAK,KAAA,CAAM,KAAK,GAAG,KAAK,CAAA;AAAA,UAC3D;AACA,UAAA,OAAO,YAAA;AAAA,QACX;AACA,QAAA,IAAA,GAAO,KAAA;AAAA,MACX;AACA,MAAA,OAAO,CAAA;AAAA,IACX,CAAA;AACA,IAAA,GAAA,CAAI,SAAA,CAAU,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,MAAM,CAAA;AAAA,EAC5C;AACJ;;;;"}
@@ -0,0 +1,11 @@
1
+ const particleGradientValueBlock = {
2
+ build(block, ctx) {
3
+ const reference = typeof block.serialized.reference === "number" ? block.serialized.reference : 0;
4
+ const value = ctx.input(block, "value", () => 0);
5
+ const entry = { reference, value };
6
+ ctx.setOutput(block.id, "output", () => entry);
7
+ }
8
+ };
9
+
10
+ export { particleGradientValueBlock };
11
+ //# sourceMappingURL=particle-gradient-value-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-gradient-value-block.js","sources":["../../../../../src/particle/node/blocks/particle-gradient-value-block.ts"],"sourcesContent":["import type { ParticleBlockEvaluator, ParticleValue, NpeGetter } from \"../npe-types.js\";\n\n/**\n * @internal A single gradient stop, produced by a `ParticleGradientValueBlock` and consumed by its parent\n * `ParticleGradientBlock`. It flows along the graph as the block's output value (mirroring the BJS\n * `*Gradient` connection types). `value` is evaluated lazily, per particle/frame.\n */\nexport interface ParticleGradientEntry {\n readonly reference: number;\n readonly value: NpeGetter;\n}\n\n/**\n * `ParticleGradientValueBlock` — one stop of a gradient: a `reference` key (0..1) and a `value` input. Its\n * output carries the stop itself (a {@link ParticleGradientEntry}); the parent `ParticleGradientBlock`\n * reads the reference and evaluates the value. Mirrors BJS `ParticleGradientValueBlock`.\n */\nexport const particleGradientValueBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const reference = typeof block.serialized.reference === \"number\" ? block.serialized.reference : 0;\n const value = ctx.input(block, \"value\", () => 0);\n const entry: ParticleGradientEntry = { reference, value };\n // The entry is an internal wire value understood only by ParticleGradientBlock.\n ctx.setOutput(block.id, \"output\", () => entry as unknown as ParticleValue);\n },\n};\n"],"names":[],"mappings":"AAiBO,MAAM,0BAAA,GAAqD;AAAA,EAC9D,KAAA,CAAM,OAAO,GAAA,EAAK;AACd,IAAA,MAAM,SAAA,GAAY,OAAO,KAAA,CAAM,UAAA,CAAW,cAAc,QAAA,GAAW,KAAA,CAAM,WAAW,SAAA,GAAY,CAAA;AAChG,IAAA,MAAM,QAAQ,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,OAAA,EAAS,MAAM,CAAC,CAAA;AAC/C,IAAA,MAAM,KAAA,GAA+B,EAAE,SAAA,EAAW,KAAA,EAAM;AAExD,IAAA,GAAA,CAAI,SAAA,CAAU,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,MAAM,KAAiC,CAAA;AAAA,EAC7E;AACJ;;;;"}
@@ -1,27 +1,5 @@
1
- function lerp(g, a, b) {
2
- return (1 - g) * a + g * b;
3
- }
4
- function lerpValue(left, right, gradient) {
5
- if (typeof left === "number") {
6
- return lerp(gradient, left, typeof right === "number" ? right : 0);
7
- }
8
- if (left && typeof left === "object") {
9
- if ("r" in left) {
10
- const a2 = left;
11
- const b2 = right && typeof right === "object" && "r" in right ? right : { r: 0, g: 0, b: 0, a: 0 };
12
- return { r: lerp(gradient, a2.r, b2.r), g: lerp(gradient, a2.g, b2.g), b: lerp(gradient, a2.b, b2.b), a: lerp(gradient, a2.a, b2.a) };
13
- }
14
- if ("z" in left) {
15
- const a2 = left;
16
- const b2 = right && typeof right === "object" && "z" in right ? right : { x: 0, y: 0, z: 0 };
17
- return { x: lerp(gradient, a2.x, b2.x), y: lerp(gradient, a2.y, b2.y), z: lerp(gradient, a2.z, b2.z) };
18
- }
19
- const a = left;
20
- const b = right && typeof right === "object" ? right : { x: 0, y: 0 };
21
- return { x: lerp(gradient, a.x, b.x), y: lerp(gradient, a.y, b.y) };
22
- }
23
- return 0;
24
- }
1
+ import { lerpValue } from './particle-lerp.js';
2
+
25
3
  const particleLerpBlock = {
26
4
  build(block, ctx) {
27
5
  const leftGetter = ctx.input(block, "left");
@@ -1 +1 @@
1
- {"version":3,"file":"particle-lerp-block.js","sources":["../../../../../src/particle/node/blocks/particle-lerp-block.ts"],"sourcesContent":["import type { Vec2, Vec3, Color4 } from \"../../../math/types.js\";\nimport type { ParticleBlockEvaluator, ParticleValue, NpeGetter } from \"../npe-types.js\";\n\n/** Scalar lerp: `(1 - g) * a + g * b`. */\nfunction lerp(g: number, a: number, b: number): number {\n return (1 - g) * a + g * b;\n}\n\nfunction lerpValue(left: ParticleValue, right: ParticleValue, gradient: number): ParticleValue {\n if (typeof left === \"number\") {\n return lerp(gradient, left, typeof right === \"number\" ? right : 0);\n }\n if (left && typeof left === \"object\") {\n if (\"r\" in left) {\n const a = left as Color4;\n const b = right && typeof right === \"object\" && \"r\" in right ? (right as Color4) : { r: 0, g: 0, b: 0, a: 0 };\n return { r: lerp(gradient, a.r, b.r), g: lerp(gradient, a.g, b.g), b: lerp(gradient, a.b, b.b), a: lerp(gradient, a.a, b.a) };\n }\n if (\"z\" in left) {\n const a = left as Vec3;\n const b = right && typeof right === \"object\" && \"z\" in right ? (right as Vec3) : { x: 0, y: 0, z: 0 };\n return { x: lerp(gradient, a.x, b.x), y: lerp(gradient, a.y, b.y), z: lerp(gradient, a.z, b.z) };\n }\n const a = left as Vec2;\n const b = right && typeof right === \"object\" ? (right as Vec2) : { x: 0, y: 0 };\n return { x: lerp(gradient, a.x, b.x), y: lerp(gradient, a.y, b.y) };\n }\n return 0;\n}\n\n/**\n * `ParticleLerpBlock` — linearly interpolates between `left` and `right` by a scalar `gradient`,\n * component-wise for vectors/colours. Mirrors BJS `ParticleLerpBlock`.\n */\nexport const particleLerpBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const leftGetter = ctx.input(block, \"left\");\n const rightGetter = ctx.input(block, \"right\");\n const gradientGetter = ctx.input(block, \"gradient\", () => 0);\n\n const getter: NpeGetter = (state) => {\n const gradient = gradientGetter(state);\n return lerpValue(leftGetter(state), rightGetter(state), typeof gradient === \"number\" ? gradient : 0);\n };\n ctx.setOutput(block.id, \"output\", getter);\n },\n};\n"],"names":["a","b"],"mappings":"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"}
1
+ {"version":3,"file":"particle-lerp-block.js","sources":["../../../../../src/particle/node/blocks/particle-lerp-block.ts"],"sourcesContent":["import { lerpValue } from \"./particle-lerp.js\";\nimport type { ParticleBlockEvaluator, NpeGetter } from \"../npe-types.js\";\n\n/**\n * `ParticleLerpBlock` — linearly interpolates between `left` and `right` by a scalar `gradient`,\n * component-wise for vectors/colours. Mirrors BJS `ParticleLerpBlock`.\n */\nexport const particleLerpBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const leftGetter = ctx.input(block, \"left\");\n const rightGetter = ctx.input(block, \"right\");\n const gradientGetter = ctx.input(block, \"gradient\", () => 0);\n\n const getter: NpeGetter = (state) => {\n const gradient = gradientGetter(state);\n return lerpValue(leftGetter(state), rightGetter(state), typeof gradient === \"number\" ? gradient : 0);\n };\n ctx.setOutput(block.id, \"output\", getter);\n },\n};\n"],"names":[],"mappings":";;AAOO,MAAM,iBAAA,GAA4C;AAAA,EACrD,KAAA,CAAM,OAAO,GAAA,EAAK;AACd,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,MAAM,CAAA;AAC1C,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,OAAO,CAAA;AAC5C,IAAA,MAAM,iBAAiB,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,UAAA,EAAY,MAAM,CAAC,CAAA;AAE3D,IAAA,MAAM,MAAA,GAAoB,CAAC,KAAA,KAAU;AACjC,MAAA,MAAM,QAAA,GAAW,eAAe,KAAK,CAAA;AACrC,MAAA,OAAO,SAAA,CAAU,UAAA,CAAW,KAAK,CAAA,EAAG,WAAA,CAAY,KAAK,CAAA,EAAG,OAAO,QAAA,KAAa,QAAA,GAAW,QAAA,GAAW,CAAC,CAAA;AAAA,IACvG,CAAA;AACA,IAAA,GAAA,CAAI,SAAA,CAAU,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,MAAM,CAAA;AAAA,EAC5C;AACJ;;;;"}
@@ -0,0 +1,27 @@
1
+ function lerp(g, a, b) {
2
+ return (1 - g) * a + g * b;
3
+ }
4
+ function lerpValue(left, right, gradient) {
5
+ if (typeof left === "number") {
6
+ return lerp(gradient, left, typeof right === "number" ? right : 0);
7
+ }
8
+ if (left && typeof left === "object") {
9
+ if ("r" in left) {
10
+ const a2 = left;
11
+ const b2 = right && typeof right === "object" && "r" in right ? right : { r: 0, g: 0, b: 0, a: 0 };
12
+ return { r: lerp(gradient, a2.r, b2.r), g: lerp(gradient, a2.g, b2.g), b: lerp(gradient, a2.b, b2.b), a: lerp(gradient, a2.a, b2.a) };
13
+ }
14
+ if ("z" in left) {
15
+ const a2 = left;
16
+ const b2 = right && typeof right === "object" && "z" in right ? right : { x: 0, y: 0, z: 0 };
17
+ return { x: lerp(gradient, a2.x, b2.x), y: lerp(gradient, a2.y, b2.y), z: lerp(gradient, a2.z, b2.z) };
18
+ }
19
+ const a = left;
20
+ const b = right && typeof right === "object" ? right : { x: 0, y: 0 };
21
+ return { x: lerp(gradient, a.x, b.x), y: lerp(gradient, a.y, b.y) };
22
+ }
23
+ return 0;
24
+ }
25
+
26
+ export { lerp, lerpValue };
27
+ //# sourceMappingURL=particle-lerp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-lerp.js","sources":["../../../../../src/particle/node/blocks/particle-lerp.ts"],"sourcesContent":["import type { Vec2, Vec3, Color4 } from \"../../../math/types.js\";\nimport type { ParticleValue } from \"../npe-types.js\";\n\n/** Scalar lerp: `(1 - g) * a + g * b`. */\nexport function lerp(g: number, a: number, b: number): number {\n return (1 - g) * a + g * b;\n}\n\n/**\n * Component-wise linear interpolation of a particle value (number, Vec2, Vec3, or Color4) by a scalar\n * gradient, dispatched on the runtime shape of `left`. Shared by `ParticleLerpBlock` and\n * `ParticleGradientBlock`.\n */\nexport function lerpValue(left: ParticleValue, right: ParticleValue, gradient: number): ParticleValue {\n if (typeof left === \"number\") {\n return lerp(gradient, left, typeof right === \"number\" ? right : 0);\n }\n if (left && typeof left === \"object\") {\n if (\"r\" in left) {\n const a = left as Color4;\n const b = right && typeof right === \"object\" && \"r\" in right ? (right as Color4) : { r: 0, g: 0, b: 0, a: 0 };\n return { r: lerp(gradient, a.r, b.r), g: lerp(gradient, a.g, b.g), b: lerp(gradient, a.b, b.b), a: lerp(gradient, a.a, b.a) };\n }\n if (\"z\" in left) {\n const a = left as Vec3;\n const b = right && typeof right === \"object\" && \"z\" in right ? (right as Vec3) : { x: 0, y: 0, z: 0 };\n return { x: lerp(gradient, a.x, b.x), y: lerp(gradient, a.y, b.y), z: lerp(gradient, a.z, b.z) };\n }\n const a = left as Vec2;\n const b = right && typeof right === \"object\" ? (right as Vec2) : { x: 0, y: 0 };\n return { x: lerp(gradient, a.x, b.x), y: lerp(gradient, a.y, b.y) };\n }\n return 0;\n}\n"],"names":["a","b"],"mappings":"AAIO,SAAS,IAAA,CAAK,CAAA,EAAW,CAAA,EAAW,CAAA,EAAmB;AAC1D,EAAA,OAAA,CAAQ,CAAA,GAAI,CAAA,IAAK,CAAA,GAAI,CAAA,GAAI,CAAA;AAC7B;AAOO,SAAS,SAAA,CAAU,IAAA,EAAqB,KAAA,EAAsB,QAAA,EAAiC;AAClG,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC1B,IAAA,OAAO,KAAK,QAAA,EAAU,IAAA,EAAM,OAAO,KAAA,KAAU,QAAA,GAAW,QAAQ,CAAC,CAAA;AAAA,EACrE;AACA,EAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AAClC,IAAA,IAAI,OAAO,IAAA,EAAM;AACb,MAAA,MAAMA,EAAAA,GAAI,IAAA;AACV,MAAA,MAAMC,KAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,OAAO,KAAA,GAAS,KAAA,GAAmB,EAAE,CAAA,EAAG,GAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAE;AAC5G,MAAA,OAAO,EAAE,CAAA,EAAG,IAAA,CAAK,QAAA,EAAUD,GAAE,CAAA,EAAGC,EAAAA,CAAE,CAAC,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,QAAA,EAAUD,EAAAA,CAAE,GAAGC,EAAAA,CAAE,CAAC,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,QAAA,EAAUD,EAAAA,CAAE,CAAA,EAAGC,GAAE,CAAC,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,QAAA,EAAUD,EAAAA,CAAE,CAAA,EAAGC,EAAAA,CAAE,CAAC,CAAA,EAAE;AAAA,IAChI;AACA,IAAA,IAAI,OAAO,IAAA,EAAM;AACb,MAAA,MAAMD,EAAAA,GAAI,IAAA;AACV,MAAA,MAAMC,EAAAA,GAAI,KAAA,IAAS,OAAO,KAAA,KAAU,YAAY,GAAA,IAAO,KAAA,GAAS,KAAA,GAAiB,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAE;AACpG,MAAA,OAAO,EAAE,CAAA,EAAG,IAAA,CAAK,QAAA,EAAUD,EAAAA,CAAE,GAAGC,EAAAA,CAAE,CAAC,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,QAAA,EAAUD,GAAE,CAAA,EAAGC,EAAAA,CAAE,CAAC,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,UAAUD,EAAAA,CAAE,CAAA,EAAGC,EAAAA,CAAE,CAAC,CAAA,EAAE;AAAA,IACnG;AACA,IAAA,MAAM,CAAA,GAAI,IAAA;AACV,IAAA,MAAM,CAAA,GAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,GAAY,QAAiB,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAE;AAC9E,IAAA,OAAO,EAAE,CAAA,EAAG,IAAA,CAAK,QAAA,EAAU,CAAA,CAAE,GAAG,CAAA,CAAE,CAAC,CAAA,EAAG,CAAA,EAAG,KAAK,QAAA,EAAU,CAAA,CAAE,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA,EAAE;AAAA,EACtE;AACA,EAAA,OAAO,CAAA;AACX;;;;"}
@@ -0,0 +1,30 @@
1
+ function vectorLength(v) {
2
+ if (typeof v === "number") {
3
+ return Math.abs(v);
4
+ }
5
+ if (v && typeof v === "object") {
6
+ if ("z" in v) {
7
+ const vec = v;
8
+ return Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
9
+ }
10
+ if ("r" in v) {
11
+ const c = v;
12
+ return Math.sqrt(c.r * c.r + c.g * c.g + c.b * c.b + c.a * c.a);
13
+ }
14
+ if ("x" in v) {
15
+ const vec = v;
16
+ return Math.sqrt(vec.x * vec.x + vec.y * vec.y);
17
+ }
18
+ }
19
+ return 0;
20
+ }
21
+ const particleVectorLengthBlock = {
22
+ build(block, ctx) {
23
+ const inputGetter = ctx.input(block, "input");
24
+ const getter = (state) => vectorLength(inputGetter(state));
25
+ ctx.setOutput(block.id, "output", getter);
26
+ }
27
+ };
28
+
29
+ export { particleVectorLengthBlock };
30
+ //# sourceMappingURL=particle-vector-length-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-vector-length-block.js","sources":["../../../../../src/particle/node/blocks/particle-vector-length-block.ts"],"sourcesContent":["import type { Vec2, Vec3, Color4 } from \"../../../math/types.js\";\nimport type { ParticleBlockEvaluator, ParticleValue, NpeGetter } from \"../npe-types.js\";\n\n/** Euclidean length of a Vec2/Vec3 input (or the absolute value of a scalar). Mirrors BJS `Vector.length()`. */\nfunction vectorLength(v: ParticleValue): number {\n if (typeof v === \"number\") {\n return Math.abs(v);\n }\n if (v && typeof v === \"object\") {\n if (\"z\" in v) {\n const vec = v as Vec3;\n return Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);\n }\n if (\"r\" in v) {\n const c = v as Color4;\n return Math.sqrt(c.r * c.r + c.g * c.g + c.b * c.b + c.a * c.a);\n }\n if (\"x\" in v) {\n const vec = v as Vec2;\n return Math.sqrt(vec.x * vec.x + vec.y * vec.y);\n }\n }\n return 0;\n}\n\n/**\n * `ParticleVectorLengthBlock` — outputs the Euclidean length (magnitude) of a vector input. Mirrors BJS\n * `ParticleVectorLengthBlock` (`input.length()`); used, e.g., to measure a particle's speed from its\n * velocity vector for the velocity-limit gradient.\n */\nexport const particleVectorLengthBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const inputGetter = ctx.input(block, \"input\");\n const getter: NpeGetter = (state) => vectorLength(inputGetter(state));\n ctx.setOutput(block.id, \"output\", getter);\n },\n};\n"],"names":[],"mappings":"AAIA,SAAS,aAAa,CAAA,EAA0B;AAC5C,EAAA,IAAI,OAAO,MAAM,QAAA,EAAU;AACvB,IAAA,OAAO,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,EACrB;AACA,EAAA,IAAI,CAAA,IAAK,OAAO,CAAA,KAAM,QAAA,EAAU;AAC5B,IAAA,IAAI,OAAO,CAAA,EAAG;AACV,MAAA,MAAM,GAAA,GAAM,CAAA;AACZ,MAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,CAAA,GAAI,GAAA,CAAI,CAAA,GAAI,GAAA,CAAI,CAAA,GAAI,GAAA,CAAI,CAAA,GAAI,GAAA,CAAI,CAAA,GAAI,IAAI,CAAC,CAAA;AAAA,IAClE;AACA,IAAA,IAAI,OAAO,CAAA,EAAG;AACV,MAAA,MAAM,CAAA,GAAI,CAAA;AACV,MAAA,OAAO,KAAK,IAAA,CAAK,CAAA,CAAE,CAAA,GAAI,CAAA,CAAE,IAAI,CAAA,CAAE,CAAA,GAAI,CAAA,CAAE,CAAA,GAAI,EAAE,CAAA,GAAI,CAAA,CAAE,IAAI,CAAA,CAAE,CAAA,GAAI,EAAE,CAAC,CAAA;AAAA,IAClE;AACA,IAAA,IAAI,OAAO,CAAA,EAAG;AACV,MAAA,MAAM,GAAA,GAAM,CAAA;AACZ,MAAA,OAAO,IAAA,CAAK,KAAK,GAAA,CAAI,CAAA,GAAI,IAAI,CAAA,GAAI,GAAA,CAAI,CAAA,GAAI,GAAA,CAAI,CAAC,CAAA;AAAA,IAClD;AAAA,EACJ;AACA,EAAA,OAAO,CAAA;AACX;AAOO,MAAM,yBAAA,GAAoD;AAAA,EAC7D,KAAA,CAAM,OAAO,GAAA,EAAK;AACd,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,OAAO,CAAA;AAC5C,IAAA,MAAM,SAAoB,CAAC,KAAA,KAAU,YAAA,CAAa,WAAA,CAAY,KAAK,CAAC,CAAA;AACpE,IAAA,GAAA,CAAI,SAAA,CAAU,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,MAAM,CAAA;AAAA,EAC5C;AACJ;;;;"}
@@ -25,9 +25,19 @@ const systemBlock = {
25
25
  if (typeof serialized.capacity === "number") {
26
26
  system.capacity = serialized.capacity;
27
27
  }
28
- const emitRate = ctx.input(block, "emitRate", () => 10)(state);
29
- if (typeof emitRate === "number") {
30
- system.emitRate = emitRate;
28
+ if (ctx.isConnected(block, "emitRate")) {
29
+ const emitRateGetter = ctx.input(block, "emitRate", () => 10);
30
+ system._emitRateGetter = (sys) => {
31
+ state.system = sys;
32
+ const rate = emitRateGetter(state);
33
+ return typeof rate === "number" ? rate : sys.emitRate;
34
+ };
35
+ } else {
36
+ system._emitRateGetter = null;
37
+ const emitRate = ctx.input(block, "emitRate", () => 10)(state);
38
+ if (typeof emitRate === "number") {
39
+ system.emitRate = emitRate;
40
+ }
31
41
  }
32
42
  const targetStop = ctx.input(block, "targetStopDuration", () => 0)(state);
33
43
  if (typeof targetStop === "number") {
@@ -1 +1 @@
1
- {"version":3,"file":"system-block.js","sources":["../../../../../src/particle/node/blocks/system-block.ts"],"sourcesContent":["import type { Texture2D } from \"../../../texture/texture-2d.js\";\nimport type { ParticleSystem } from \"../../particle-system.js\";\nimport type { ParticleBlockEvaluator } from \"../npe-types.js\";\n\n/**\n * `SystemBlock` — the graph root. Applies system-level configuration (capacity, update speed, blend and\n * billboard modes, emit rate, target stop duration, texture) onto the {@link ParticleSystem} built by the\n * upstream `particle` chain. Mirrors the configuration done in BJS `SystemBlock.createSystem`.\n */\nexport const systemBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const state = ctx.state;\n const system = ctx.input(block, \"particle\")(state) as ParticleSystem | null;\n if (!system) {\n return;\n }\n\n system.name = block.name;\n\n const serialized = block.serialized;\n if (typeof serialized.updateSpeed === \"number\") {\n system.updateSpeed = serialized.updateSpeed;\n }\n if (typeof serialized.blendMode === \"number\") {\n system.blendMode = serialized.blendMode;\n }\n if (typeof serialized.billBoardMode === \"number\") {\n system.billboardMode = serialized.billBoardMode;\n }\n if (typeof serialized.isBillboardBased === \"boolean\") {\n system.isBillboardBased = serialized.isBillboardBased;\n }\n if (typeof serialized.isLocal === \"boolean\") {\n system.isLocal = serialized.isLocal;\n }\n if (typeof serialized.capacity === \"number\") {\n system.capacity = serialized.capacity;\n }\n\n const emitRate = ctx.input(block, \"emitRate\", () => 10)(state);\n if (typeof emitRate === \"number\") {\n system.emitRate = emitRate;\n }\n\n const targetStop = ctx.input(block, \"targetStopDuration\", () => 0)(state);\n if (typeof targetStop === \"number\") {\n system.targetStopDuration = targetStop;\n }\n\n // The texture loads asynchronously; bind it once the build's asset promises settle.\n const textureGetter = ctx.input(block, \"texture\");\n system._resolveTexture = () => {\n system.texture = (textureGetter(state) as Texture2D | null) ?? null;\n };\n },\n};\n"],"names":[],"mappings":"AASO,MAAM,WAAA,GAAsC;AAAA,EAC/C,KAAA,CAAM,OAAO,GAAA,EAAK;AACd,IAAA,MAAM,QAAQ,GAAA,CAAI,KAAA;AAClB,IAAA,MAAM,SAAS,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,UAAU,EAAE,KAAK,CAAA;AACjD,IAAA,IAAI,CAAC,MAAA,EAAQ;AACT,MAAA;AAAA,IACJ;AAEA,IAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAEpB,IAAA,MAAM,aAAa,KAAA,CAAM,UAAA;AACzB,IAAA,IAAI,OAAO,UAAA,CAAW,WAAA,KAAgB,QAAA,EAAU;AAC5C,MAAA,MAAA,CAAO,cAAc,UAAA,CAAW,WAAA;AAAA,IACpC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,SAAA,KAAc,QAAA,EAAU;AAC1C,MAAA,MAAA,CAAO,YAAY,UAAA,CAAW,SAAA;AAAA,IAClC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,aAAA,KAAkB,QAAA,EAAU;AAC9C,MAAA,MAAA,CAAO,gBAAgB,UAAA,CAAW,aAAA;AAAA,IACtC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,gBAAA,KAAqB,SAAA,EAAW;AAClD,MAAA,MAAA,CAAO,mBAAmB,UAAA,CAAW,gBAAA;AAAA,IACzC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,OAAA,KAAY,SAAA,EAAW;AACzC,MAAA,MAAA,CAAO,UAAU,UAAA,CAAW,OAAA;AAAA,IAChC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,QAAA,KAAa,QAAA,EAAU;AACzC,MAAA,MAAA,CAAO,WAAW,UAAA,CAAW,QAAA;AAAA,IACjC;AAEA,IAAA,MAAM,QAAA,GAAW,IAAI,KAAA,CAAM,KAAA,EAAO,YAAY,MAAM,EAAE,EAAE,KAAK,CAAA;AAC7D,IAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAC9B,MAAA,MAAA,CAAO,QAAA,GAAW,QAAA;AAAA,IACtB;AAEA,IAAA,MAAM,UAAA,GAAa,IAAI,KAAA,CAAM,KAAA,EAAO,sBAAsB,MAAM,CAAC,EAAE,KAAK,CAAA;AACxE,IAAA,IAAI,OAAO,eAAe,QAAA,EAAU;AAChC,MAAA,MAAA,CAAO,kBAAA,GAAqB,UAAA;AAAA,IAChC;AAGA,IAAA,MAAM,aAAA,GAAgB,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,SAAS,CAAA;AAChD,IAAA,MAAA,CAAO,kBAAkB,MAAM;AAC3B,MAAA,MAAA,CAAO,OAAA,GAAW,aAAA,CAAc,KAAK,CAAA,IAA0B,IAAA;AAAA,IACnE,CAAA;AAAA,EACJ;AACJ;;;;"}
1
+ {"version":3,"file":"system-block.js","sources":["../../../../../src/particle/node/blocks/system-block.ts"],"sourcesContent":["import type { Texture2D } from \"../../../texture/texture-2d.js\";\nimport type { ParticleSystem } from \"../../particle-system.js\";\nimport type { ParticleBlockEvaluator } from \"../npe-types.js\";\n\n/**\n * `SystemBlock` — the graph root. Applies system-level configuration (capacity, update speed, blend and\n * billboard modes, emit rate, target stop duration, texture) onto the {@link ParticleSystem} built by the\n * upstream `particle` chain. Mirrors the configuration done in BJS `SystemBlock.createSystem`.\n */\nexport const systemBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const state = ctx.state;\n const system = ctx.input(block, \"particle\")(state) as ParticleSystem | null;\n if (!system) {\n return;\n }\n\n system.name = block.name;\n\n const serialized = block.serialized;\n if (typeof serialized.updateSpeed === \"number\") {\n system.updateSpeed = serialized.updateSpeed;\n }\n if (typeof serialized.blendMode === \"number\") {\n system.blendMode = serialized.blendMode;\n }\n if (typeof serialized.billBoardMode === \"number\") {\n system.billboardMode = serialized.billBoardMode;\n }\n if (typeof serialized.isBillboardBased === \"boolean\") {\n system.isBillboardBased = serialized.isBillboardBased;\n }\n if (typeof serialized.isLocal === \"boolean\") {\n system.isLocal = serialized.isLocal;\n }\n if (typeof serialized.capacity === \"number\") {\n system.capacity = serialized.capacity;\n }\n\n // A dynamic emit rate (e.g. an emit-rate gradient over the system's target duration) is connected to\n // a source and must be re-evaluated every step from the current `_actualFrame`; store a getter the\n // animate loop calls. A constant emit rate is read once here and frozen.\n if (ctx.isConnected(block, \"emitRate\")) {\n const emitRateGetter = ctx.input(block, \"emitRate\", () => 10);\n system._emitRateGetter = (sys) => {\n state.system = sys;\n const rate = emitRateGetter(state);\n return typeof rate === \"number\" ? rate : sys.emitRate;\n };\n } else {\n // Clear any previously-installed getter so a constant rate is never shadowed by a stale\n // dynamic getter (defensive if this block ever re-runs against an already-configured system).\n system._emitRateGetter = null;\n const emitRate = ctx.input(block, \"emitRate\", () => 10)(state);\n if (typeof emitRate === \"number\") {\n system.emitRate = emitRate;\n }\n }\n\n const targetStop = ctx.input(block, \"targetStopDuration\", () => 0)(state);\n if (typeof targetStop === \"number\") {\n system.targetStopDuration = targetStop;\n }\n\n // The texture loads asynchronously; bind it once the build's asset promises settle.\n const textureGetter = ctx.input(block, \"texture\");\n system._resolveTexture = () => {\n system.texture = (textureGetter(state) as Texture2D | null) ?? null;\n };\n },\n};\n"],"names":[],"mappings":"AASO,MAAM,WAAA,GAAsC;AAAA,EAC/C,KAAA,CAAM,OAAO,GAAA,EAAK;AACd,IAAA,MAAM,QAAQ,GAAA,CAAI,KAAA;AAClB,IAAA,MAAM,SAAS,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,UAAU,EAAE,KAAK,CAAA;AACjD,IAAA,IAAI,CAAC,MAAA,EAAQ;AACT,MAAA;AAAA,IACJ;AAEA,IAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAEpB,IAAA,MAAM,aAAa,KAAA,CAAM,UAAA;AACzB,IAAA,IAAI,OAAO,UAAA,CAAW,WAAA,KAAgB,QAAA,EAAU;AAC5C,MAAA,MAAA,CAAO,cAAc,UAAA,CAAW,WAAA;AAAA,IACpC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,SAAA,KAAc,QAAA,EAAU;AAC1C,MAAA,MAAA,CAAO,YAAY,UAAA,CAAW,SAAA;AAAA,IAClC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,aAAA,KAAkB,QAAA,EAAU;AAC9C,MAAA,MAAA,CAAO,gBAAgB,UAAA,CAAW,aAAA;AAAA,IACtC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,gBAAA,KAAqB,SAAA,EAAW;AAClD,MAAA,MAAA,CAAO,mBAAmB,UAAA,CAAW,gBAAA;AAAA,IACzC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,OAAA,KAAY,SAAA,EAAW;AACzC,MAAA,MAAA,CAAO,UAAU,UAAA,CAAW,OAAA;AAAA,IAChC;AACA,IAAA,IAAI,OAAO,UAAA,CAAW,QAAA,KAAa,QAAA,EAAU;AACzC,MAAA,MAAA,CAAO,WAAW,UAAA,CAAW,QAAA;AAAA,IACjC;AAKA,IAAA,IAAI,GAAA,CAAI,WAAA,CAAY,KAAA,EAAO,UAAU,CAAA,EAAG;AACpC,MAAA,MAAM,iBAAiB,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,UAAA,EAAY,MAAM,EAAE,CAAA;AAC5D,MAAA,MAAA,CAAO,eAAA,GAAkB,CAAC,GAAA,KAAQ;AAC9B,QAAA,KAAA,CAAM,MAAA,GAAS,GAAA;AACf,QAAA,MAAM,IAAA,GAAO,eAAe,KAAK,CAAA;AACjC,QAAA,OAAO,OAAO,IAAA,KAAS,QAAA,GAAW,IAAA,GAAO,GAAA,CAAI,QAAA;AAAA,MACjD,CAAA;AAAA,IACJ,CAAA,MAAO;AAGH,MAAA,MAAA,CAAO,eAAA,GAAkB,IAAA;AACzB,MAAA,MAAM,QAAA,GAAW,IAAI,KAAA,CAAM,KAAA,EAAO,YAAY,MAAM,EAAE,EAAE,KAAK,CAAA;AAC7D,MAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAC9B,QAAA,MAAA,CAAO,QAAA,GAAW,QAAA;AAAA,MACtB;AAAA,IACJ;AAEA,IAAA,MAAM,UAAA,GAAa,IAAI,KAAA,CAAM,KAAA,EAAO,sBAAsB,MAAM,CAAC,EAAE,KAAK,CAAA;AACxE,IAAA,IAAI,OAAO,eAAe,QAAA,EAAU;AAChC,MAAA,MAAA,CAAO,kBAAA,GAAqB,UAAA;AAAA,IAChC;AAGA,IAAA,MAAM,aAAA,GAAgB,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,SAAS,CAAA;AAChD,IAAA,MAAA,CAAO,kBAAkB,MAAM;AAC3B,MAAA,MAAA,CAAO,OAAA,GAAW,aAAA,CAAc,KAAK,CAAA,IAA0B,IAAA;AAAA,IACnE,CAAA;AAAA,EACJ;AACJ;;;;"}
@@ -6,12 +6,13 @@ const textureSourceBlock = {
6
6
  const base = ctx.state.textureBaseUrl;
7
7
  const isAbsolute = /^(https?:)?\/\//.test(rawUrl) || rawUrl.startsWith("/");
8
8
  const url = rawUrl && base && !isAbsolute ? new URL(rawUrl, base).href : rawUrl;
9
+ const blockInvertY = block.serialized.invertY !== false;
9
10
  const holder = { texture: null };
10
11
  if (url) {
11
12
  ctx.addBuildPromise(
12
13
  (async () => {
13
14
  try {
14
- holder.texture = await loadTexture2D(ctx.engine, url);
15
+ holder.texture = await loadTexture2D(ctx.engine, url, { invertY: !blockInvertY });
15
16
  } catch {
16
17
  holder.texture = null;
17
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"texture-source-block.js","sources":["../../../../../src/particle/node/blocks/texture-source-block.ts"],"sourcesContent":["import { loadTexture2D } from \"../../../texture/texture-2d.js\";\nimport type { Texture2D } from \"../../../texture/texture-2d.js\";\nimport type { ParticleBlockEvaluator } from \"../npe-types.js\";\n\n/**\n * `ParticleTextureSourceBlock` — loads the particle texture from its `url` and exposes it on the `texture`\n * output. The load is registered as a build promise so the set is only considered ready once it settles.\n */\nexport const textureSourceBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const rawUrl = typeof block.serialized.url === \"string\" ? block.serialized.url : \"\";\n const base = ctx.state.textureBaseUrl;\n // Resolve relative URLs (e.g. \"textures/flare.png\") against the configured base, mirroring how\n // Babylon.js resolves a particle texture path against the scene's texture base.\n const isAbsolute = /^(https?:)?\\/\\//.test(rawUrl) || rawUrl.startsWith(\"/\");\n const url = rawUrl && base && !isAbsolute ? new URL(rawUrl, base).href : rawUrl;\n const holder: { texture: Texture2D | null } = { texture: null };\n\n if (url) {\n ctx.addBuildPromise(\n (async () => {\n try {\n holder.texture = await loadTexture2D(ctx.engine, url);\n } catch {\n // A failed texture load must not break the simulation; the particle simply\n // renders untextured (and headless/CPU-only builds have no device at all).\n holder.texture = null;\n }\n })()\n );\n }\n\n ctx.setOutput(block.id, \"texture\", () => holder.texture);\n },\n};\n"],"names":[],"mappings":";;AAQO,MAAM,kBAAA,GAA6C;AAAA,EACtD,KAAA,CAAM,OAAO,GAAA,EAAK;AACd,IAAA,MAAM,MAAA,GAAS,OAAO,KAAA,CAAM,UAAA,CAAW,QAAQ,QAAA,GAAW,KAAA,CAAM,WAAW,GAAA,GAAM,EAAA;AACjF,IAAA,MAAM,IAAA,GAAO,IAAI,KAAA,CAAM,cAAA;AAGvB,IAAA,MAAM,aAAa,iBAAA,CAAkB,IAAA,CAAK,MAAM,CAAA,IAAK,MAAA,CAAO,WAAW,GAAG,CAAA;AAC1E,IAAA,MAAM,GAAA,GAAM,MAAA,IAAU,IAAA,IAAQ,CAAC,UAAA,GAAa,IAAI,GAAA,CAAI,MAAA,EAAQ,IAAI,CAAA,CAAE,IAAA,GAAO,MAAA;AACzE,IAAA,MAAM,MAAA,GAAwC,EAAE,OAAA,EAAS,IAAA,EAAK;AAE9D,IAAA,IAAI,GAAA,EAAK;AACL,MAAA,GAAA,CAAI,eAAA;AAAA,QAAA,CACC,YAAY;AACT,UAAA,IAAI;AACA,YAAA,MAAA,CAAO,OAAA,GAAU,MAAM,aAAA,CAAc,GAAA,CAAI,QAAQ,GAAG,CAAA;AAAA,UACxD,CAAA,CAAA,MAAQ;AAGJ,YAAA,MAAA,CAAO,OAAA,GAAU,IAAA;AAAA,UACrB;AAAA,QACJ,CAAA;AAAG,OACP;AAAA,IACJ;AAEA,IAAA,GAAA,CAAI,UAAU,KAAA,CAAM,EAAA,EAAI,SAAA,EAAW,MAAM,OAAO,OAAO,CAAA;AAAA,EAC3D;AACJ;;;;"}
1
+ {"version":3,"file":"texture-source-block.js","sources":["../../../../../src/particle/node/blocks/texture-source-block.ts"],"sourcesContent":["import { loadTexture2D } from \"../../../texture/texture-2d.js\";\nimport type { Texture2D } from \"../../../texture/texture-2d.js\";\nimport type { ParticleBlockEvaluator } from \"../npe-types.js\";\n\n/**\n * `ParticleTextureSourceBlock` — loads the particle texture from its `url` and exposes it on the `texture`\n * output. The load is registered as a build promise so the set is only considered ready once it settles.\n */\nexport const textureSourceBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const rawUrl = typeof block.serialized.url === \"string\" ? block.serialized.url : \"\";\n const base = ctx.state.textureBaseUrl;\n // Resolve relative URLs (e.g. \"textures/flare.png\") against the configured base, mirroring how\n // Babylon.js resolves a particle texture path against the scene's texture base.\n const isAbsolute = /^(https?:)?\\/\\//.test(rawUrl) || rawUrl.startsWith(\"/\");\n const url = rawUrl && base && !isAbsolute ? new URL(rawUrl, base).href : rawUrl;\n // Babylon.js's ParticleTextureSourceBlock.invertY defaults to true. Our billboard renderer samples\n // the V axis opposite to Babylon.js's particle shader (createGridSpriteAtlas maps texture row 0 →\n // uvMin.y, i.e. top-down), so we upload with the *opposite* flip to land on the same pixels. Skipping\n // this made the (nearly symmetric) flare's slightly off-centre hotspot render mirrored vertically —\n // a size-proportional vertical offset that dominated the particle parity error.\n const blockInvertY = block.serialized.invertY !== false;\n const holder: { texture: Texture2D | null } = { texture: null };\n\n if (url) {\n ctx.addBuildPromise(\n (async () => {\n try {\n holder.texture = await loadTexture2D(ctx.engine, url, { invertY: !blockInvertY });\n } catch {\n // A failed texture load must not break the simulation; the particle simply\n // renders untextured (and headless/CPU-only builds have no device at all).\n holder.texture = null;\n }\n })()\n );\n }\n\n ctx.setOutput(block.id, \"texture\", () => holder.texture);\n },\n};\n"],"names":[],"mappings":";;AAQO,MAAM,kBAAA,GAA6C;AAAA,EACtD,KAAA,CAAM,OAAO,GAAA,EAAK;AACd,IAAA,MAAM,MAAA,GAAS,OAAO,KAAA,CAAM,UAAA,CAAW,QAAQ,QAAA,GAAW,KAAA,CAAM,WAAW,GAAA,GAAM,EAAA;AACjF,IAAA,MAAM,IAAA,GAAO,IAAI,KAAA,CAAM,cAAA;AAGvB,IAAA,MAAM,aAAa,iBAAA,CAAkB,IAAA,CAAK,MAAM,CAAA,IAAK,MAAA,CAAO,WAAW,GAAG,CAAA;AAC1E,IAAA,MAAM,GAAA,GAAM,MAAA,IAAU,IAAA,IAAQ,CAAC,UAAA,GAAa,IAAI,GAAA,CAAI,MAAA,EAAQ,IAAI,CAAA,CAAE,IAAA,GAAO,MAAA;AAMzE,IAAA,MAAM,YAAA,GAAe,KAAA,CAAM,UAAA,CAAW,OAAA,KAAY,KAAA;AAClD,IAAA,MAAM,MAAA,GAAwC,EAAE,OAAA,EAAS,IAAA,EAAK;AAE9D,IAAA,IAAI,GAAA,EAAK;AACL,MAAA,GAAA,CAAI,eAAA;AAAA,QAAA,CACC,YAAY;AACT,UAAA,IAAI;AACA,YAAA,MAAA,CAAO,OAAA,GAAU,MAAM,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,KAAK,EAAE,OAAA,EAAS,CAAC,YAAA,EAAc,CAAA;AAAA,UACpF,CAAA,CAAA,MAAQ;AAGJ,YAAA,MAAA,CAAO,OAAA,GAAU,IAAA;AAAA,UACrB;AAAA,QACJ,CAAA;AAAG,OACP;AAAA,IACJ;AAEA,IAAA,GAAA,CAAI,UAAU,KAAA,CAAM,EAAA,EAAI,SAAA,EAAW,MAAM,OAAO,OAAO,CAAA;AAAA,EAC3D;AACJ;;;;"}
@@ -0,0 +1,19 @@
1
+ const updateSizeBlock = {
2
+ build(block, ctx) {
3
+ const state = ctx.state;
4
+ const system = ctx.input(block, "particle")(state);
5
+ ctx.setOutput(block.id, "output", () => system);
6
+ if (!ctx.isConnected(block, "size")) {
7
+ return;
8
+ }
9
+ const sizeGetter = ctx.input(block, "size");
10
+ system._updateQueue.push((particle, sys) => {
11
+ state.particle = particle;
12
+ state.system = sys;
13
+ particle.size = sizeGetter(state);
14
+ });
15
+ }
16
+ };
17
+
18
+ export { updateSizeBlock };
19
+ //# sourceMappingURL=update-size-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-size-block.js","sources":["../../../../../src/particle/node/blocks/update-size-block.ts"],"sourcesContent":["import type { ParticleSystem } from \"../../particle-system.js\";\nimport type { ParticleBlockEvaluator } from \"../npe-types.js\";\n\n/**\n * `UpdateSizeBlock` — each step, sets the particle size to the value of its `size` input (typically a size\n * gradient evaluated at the age/lifetime ratio, so the particle grows or shrinks over its life). Mirrors\n * BJS `UpdateSizeBlock`.\n */\nexport const updateSizeBlock: ParticleBlockEvaluator = {\n build(block, ctx) {\n const state = ctx.state;\n const system = ctx.input(block, \"particle\")(state) as ParticleSystem;\n ctx.setOutput(block.id, \"output\", () => system);\n\n if (!ctx.isConnected(block, \"size\")) {\n return;\n }\n const sizeGetter = ctx.input(block, \"size\");\n\n system._updateQueue.push((particle, sys) => {\n state.particle = particle;\n state.system = sys;\n particle.size = sizeGetter(state) as number;\n });\n },\n};\n"],"names":[],"mappings":"AAQO,MAAM,eAAA,GAA0C;AAAA,EACnD,KAAA,CAAM,OAAO,GAAA,EAAK;AACd,IAAA,MAAM,QAAQ,GAAA,CAAI,KAAA;AAClB,IAAA,MAAM,SAAS,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,UAAU,EAAE,KAAK,CAAA;AACjD,IAAA,GAAA,CAAI,SAAA,CAAU,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,MAAM,MAAM,CAAA;AAE9C,IAAA,IAAI,CAAC,GAAA,CAAI,WAAA,CAAY,KAAA,EAAO,MAAM,CAAA,EAAG;AACjC,MAAA;AAAA,IACJ;AACA,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,MAAM,CAAA;AAE1C,IAAA,MAAA,CAAO,YAAA,CAAa,IAAA,CAAK,CAAC,QAAA,EAAU,GAAA,KAAQ;AACxC,MAAA,KAAA,CAAM,QAAA,GAAW,QAAA;AACjB,MAAA,KAAA,CAAM,MAAA,GAAS,GAAA;AACf,MAAA,QAAA,CAAS,IAAA,GAAO,WAAW,KAAK,CAAA;AAAA,IACpC,CAAC,CAAA;AAAA,EACL;AACJ;;;;"}
@@ -1,5 +1,6 @@
1
1
  import { scaleVec3ToRef } from '../../math/vec3-ref.js';
2
2
  import { scaleColor4ToRef } from '../../math/color4-ref.js';
3
+ import { transformCoordinatesToRef } from '../../math/mat4-transform.js';
3
4
 
4
5
  const CTX_POSITION = 1;
5
6
  const CTX_DIRECTION = 2;
@@ -15,6 +16,7 @@ const CTX_COLOR_DEAD = 20;
15
16
  const CTX_INITIAL_DIRECTION = 21;
16
17
  const CTX_COLOR_STEP = 22;
17
18
  const CTX_SCALED_COLOR_STEP = 23;
19
+ const CTX_LOCAL_POSITION_UPDATED = 24;
18
20
  const CTX_SIZE = 25;
19
21
  const CTX_DIRECTION_SCALE = 32;
20
22
  const SYS_TIME = 1;
@@ -57,6 +59,13 @@ function getContextualValue(state, source) {
57
59
  case CTX_SCALED_COLOR_STEP:
58
60
  scaleColor4ToRef(particle.colorStep, system._scaledUpdateSpeed, system._scaledColorStep);
59
61
  return system._scaledColorStep;
62
+ case CTX_LOCAL_POSITION_UPDATED:
63
+ scaleVec3ToRef(particle.direction, particle._directionScale, particle._scaledDirection);
64
+ particle._localPosition.x += particle._scaledDirection.x;
65
+ particle._localPosition.y += particle._scaledDirection.y;
66
+ particle._localPosition.z += particle._scaledDirection.z;
67
+ transformCoordinatesToRef(particle._localPosition.x, particle._localPosition.y, particle._localPosition.z, state.emitterWorldMatrix, particle.position);
68
+ return particle.position;
60
69
  case CTX_SIZE:
61
70
  return particle.size;
62
71
  case CTX_DIRECTION_SCALE:
@@ -94,6 +103,7 @@ function isContextualSourceSupported(source) {
94
103
  case CTX_INITIAL_DIRECTION:
95
104
  case CTX_COLOR_STEP:
96
105
  case CTX_SCALED_COLOR_STEP:
106
+ case CTX_LOCAL_POSITION_UPDATED:
97
107
  case CTX_SIZE:
98
108
  case CTX_DIRECTION_SCALE:
99
109
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"npe-build-state.js","sources":["../../../../src/particle/node/npe-build-state.ts"],"sourcesContent":["import { scaleVec3ToRef } from \"../../math/vec3-ref.js\";\nimport { scaleColor4ToRef } from \"../../math/color4-ref.js\";\nimport type { NpeBuildState, ParticleValue } from \"./npe-types.js\";\n\n// Contextual source ids (Babylon.js `NodeParticleContextualSources`; values are hex).\nconst CTX_POSITION = 0x0001;\nconst CTX_DIRECTION = 0x0002;\nconst CTX_AGE = 0x0003;\nconst CTX_LIFETIME = 0x0004;\nconst CTX_COLOR = 0x0005;\nconst CTX_SCALED_DIRECTION = 0x0006;\nconst CTX_SCALE = 0x0007;\nconst CTX_AGE_GRADIENT = 0x0008;\nconst CTX_ANGLE = 0x0009;\nconst CTX_INITIAL_COLOR = 0x0013;\nconst CTX_COLOR_DEAD = 0x0014;\nconst CTX_INITIAL_DIRECTION = 0x0015;\nconst CTX_COLOR_STEP = 0x0016;\nconst CTX_SCALED_COLOR_STEP = 0x0017;\nconst CTX_SIZE = 0x0019;\nconst CTX_DIRECTION_SCALE = 0x0020;\n\n// System source ids (Babylon.js `NodeParticleSystemSources`).\nconst SYS_TIME = 1;\nconst SYS_DELTA = 2;\nconst SYS_EMITTER = 3;\n\n/**\n * Resolve a contextual source against the current particle/system (mirrors BJS\n * `NodeParticleBuildState.getContextualValue`). Vector/colour results that derive from per-step scratch\n * (`ScaledDirection`, `ScaledColorStep`) are written into the particle/system scratch buffers in place.\n */\nexport function getContextualValue(state: NpeBuildState, source: number): ParticleValue {\n const particle = state.particle;\n const system = state.system;\n if (!particle || !system) {\n return null;\n }\n\n switch (source) {\n case CTX_POSITION:\n return particle.position;\n case CTX_DIRECTION:\n return particle.direction;\n case CTX_AGE:\n return particle.age;\n case CTX_LIFETIME:\n return particle.lifeTime;\n case CTX_COLOR:\n return particle.color;\n case CTX_SCALED_DIRECTION:\n scaleVec3ToRef(particle.direction, particle._directionScale, particle._scaledDirection);\n return particle._scaledDirection;\n case CTX_SCALE:\n return particle.scale;\n case CTX_AGE_GRADIENT:\n return particle.age / particle.lifeTime;\n case CTX_ANGLE:\n return particle.angle;\n case CTX_INITIAL_COLOR:\n return particle.initialColor;\n case CTX_COLOR_DEAD:\n return particle.colorDead;\n case CTX_INITIAL_DIRECTION:\n return particle._initialDirection;\n case CTX_COLOR_STEP:\n return particle.colorStep;\n case CTX_SCALED_COLOR_STEP:\n scaleColor4ToRef(particle.colorStep, system._scaledUpdateSpeed, system._scaledColorStep);\n return system._scaledColorStep;\n case CTX_SIZE:\n return particle.size;\n case CTX_DIRECTION_SCALE:\n return particle._directionScale;\n default:\n return null;\n }\n}\n\n/** Resolve a system source (mirrors BJS `NodeParticleBuildState.getSystemValue`). */\nexport function getSystemValue(state: NpeBuildState, source: number): ParticleValue {\n const system = state.system;\n switch (source) {\n case SYS_TIME:\n return system ? system._actualFrame : 0;\n case SYS_DELTA:\n return system ? system._scaledUpdateSpeed : 0;\n case SYS_EMITTER:\n return state.emitter;\n default:\n return null;\n }\n}\n\n/** Whether a contextual source id is handled by {@link getContextualValue}. Keep in sync with its switch. */\nexport function isContextualSourceSupported(source: number): boolean {\n switch (source) {\n case CTX_POSITION:\n case CTX_DIRECTION:\n case CTX_AGE:\n case CTX_LIFETIME:\n case CTX_COLOR:\n case CTX_SCALED_DIRECTION:\n case CTX_SCALE:\n case CTX_AGE_GRADIENT:\n case CTX_ANGLE:\n case CTX_INITIAL_COLOR:\n case CTX_COLOR_DEAD:\n case CTX_INITIAL_DIRECTION:\n case CTX_COLOR_STEP:\n case CTX_SCALED_COLOR_STEP:\n case CTX_SIZE:\n case CTX_DIRECTION_SCALE:\n return true;\n default:\n return false;\n }\n}\n\n/** Whether a system source id is handled by {@link getSystemValue}. Keep in sync with its switch. */\nexport function isSystemSourceSupported(source: number): boolean {\n switch (source) {\n case SYS_TIME:\n case SYS_DELTA:\n case SYS_EMITTER:\n return true;\n default:\n return false;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"npe-build-state.js","sources":["../../../../src/particle/node/npe-build-state.ts"],"sourcesContent":["import { scaleVec3ToRef } from \"../../math/vec3-ref.js\";\nimport { scaleColor4ToRef } from \"../../math/color4-ref.js\";\nimport { transformCoordinatesToRef } from \"../../math/mat4-transform.js\";\nimport type { NpeBuildState, ParticleValue } from \"./npe-types.js\";\n\n// Contextual source ids (Babylon.js `NodeParticleContextualSources`; values are hex).\nconst CTX_POSITION = 0x0001;\nconst CTX_DIRECTION = 0x0002;\nconst CTX_AGE = 0x0003;\nconst CTX_LIFETIME = 0x0004;\nconst CTX_COLOR = 0x0005;\nconst CTX_SCALED_DIRECTION = 0x0006;\nconst CTX_SCALE = 0x0007;\nconst CTX_AGE_GRADIENT = 0x0008;\nconst CTX_ANGLE = 0x0009;\nconst CTX_INITIAL_COLOR = 0x0013;\nconst CTX_COLOR_DEAD = 0x0014;\nconst CTX_INITIAL_DIRECTION = 0x0015;\nconst CTX_COLOR_STEP = 0x0016;\nconst CTX_SCALED_COLOR_STEP = 0x0017;\nconst CTX_LOCAL_POSITION_UPDATED = 0x0018;\nconst CTX_SIZE = 0x0019;\nconst CTX_DIRECTION_SCALE = 0x0020;\n\n// System source ids (Babylon.js `NodeParticleSystemSources`).\nconst SYS_TIME = 1;\nconst SYS_DELTA = 2;\nconst SYS_EMITTER = 3;\n\n/**\n * Resolve a contextual source against the current particle/system (mirrors BJS\n * `NodeParticleBuildState.getContextualValue`). Vector/colour results that derive from per-step scratch\n * (`ScaledDirection`, `ScaledColorStep`) are written into the particle/system scratch buffers in place.\n */\nexport function getContextualValue(state: NpeBuildState, source: number): ParticleValue {\n const particle = state.particle;\n const system = state.system;\n if (!particle || !system) {\n return null;\n }\n\n switch (source) {\n case CTX_POSITION:\n return particle.position;\n case CTX_DIRECTION:\n return particle.direction;\n case CTX_AGE:\n return particle.age;\n case CTX_LIFETIME:\n return particle.lifeTime;\n case CTX_COLOR:\n return particle.color;\n case CTX_SCALED_DIRECTION:\n scaleVec3ToRef(particle.direction, particle._directionScale, particle._scaledDirection);\n return particle._scaledDirection;\n case CTX_SCALE:\n return particle.scale;\n case CTX_AGE_GRADIENT:\n return particle.age / particle.lifeTime;\n case CTX_ANGLE:\n return particle.angle;\n case CTX_INITIAL_COLOR:\n return particle.initialColor;\n case CTX_COLOR_DEAD:\n return particle.colorDead;\n case CTX_INITIAL_DIRECTION:\n return particle._initialDirection;\n case CTX_COLOR_STEP:\n return particle.colorStep;\n case CTX_SCALED_COLOR_STEP:\n scaleColor4ToRef(particle.colorStep, system._scaledUpdateSpeed, system._scaledColorStep);\n return system._scaledColorStep;\n case CTX_LOCAL_POSITION_UPDATED:\n // isLocal position integration: advance the local position by the scaled direction, then bake the\n // emitter world matrix to get the world position. Mirrors BJS `LocalPositionUpdated`.\n scaleVec3ToRef(particle.direction, particle._directionScale, particle._scaledDirection);\n particle._localPosition.x += particle._scaledDirection.x;\n particle._localPosition.y += particle._scaledDirection.y;\n particle._localPosition.z += particle._scaledDirection.z;\n transformCoordinatesToRef(particle._localPosition.x, particle._localPosition.y, particle._localPosition.z, state.emitterWorldMatrix, particle.position);\n return particle.position;\n case CTX_SIZE:\n return particle.size;\n case CTX_DIRECTION_SCALE:\n return particle._directionScale;\n default:\n return null;\n }\n}\n\n/** Resolve a system source (mirrors BJS `NodeParticleBuildState.getSystemValue`). */\nexport function getSystemValue(state: NpeBuildState, source: number): ParticleValue {\n const system = state.system;\n switch (source) {\n case SYS_TIME:\n return system ? system._actualFrame : 0;\n case SYS_DELTA:\n return system ? system._scaledUpdateSpeed : 0;\n case SYS_EMITTER:\n return state.emitter;\n default:\n return null;\n }\n}\n\n/** Whether a contextual source id is handled by {@link getContextualValue}. Keep in sync with its switch. */\nexport function isContextualSourceSupported(source: number): boolean {\n switch (source) {\n case CTX_POSITION:\n case CTX_DIRECTION:\n case CTX_AGE:\n case CTX_LIFETIME:\n case CTX_COLOR:\n case CTX_SCALED_DIRECTION:\n case CTX_SCALE:\n case CTX_AGE_GRADIENT:\n case CTX_ANGLE:\n case CTX_INITIAL_COLOR:\n case CTX_COLOR_DEAD:\n case CTX_INITIAL_DIRECTION:\n case CTX_COLOR_STEP:\n case CTX_SCALED_COLOR_STEP:\n case CTX_LOCAL_POSITION_UPDATED:\n case CTX_SIZE:\n case CTX_DIRECTION_SCALE:\n return true;\n default:\n return false;\n }\n}\n\n/** Whether a system source id is handled by {@link getSystemValue}. Keep in sync with its switch. */\nexport function isSystemSourceSupported(source: number): boolean {\n switch (source) {\n case SYS_TIME:\n case SYS_DELTA:\n case SYS_EMITTER:\n return true;\n default:\n return false;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -36,6 +36,18 @@ async function loadParticleBlockEvaluator(className) {
36
36
  return (await import('./blocks/update-angle-block.js')).updateAngleBlock;
37
37
  case "UpdateDirectionBlock":
38
38
  return (await import('./blocks/update-direction-block.js')).updateDirectionBlock;
39
+ case "UpdateSizeBlock":
40
+ return (await import('./blocks/update-size-block.js')).updateSizeBlock;
41
+ case "ParticleGradientBlock":
42
+ return (await import('./blocks/particle-gradient-block.js')).particleGradientBlock;
43
+ case "ParticleGradientValueBlock":
44
+ return (await import('./blocks/particle-gradient-value-block.js')).particleGradientValueBlock;
45
+ case "ParticleVectorLengthBlock":
46
+ return (await import('./blocks/particle-vector-length-block.js')).particleVectorLengthBlock;
47
+ case "ParticleConditionBlock":
48
+ return (await import('./blocks/particle-condition-block.js')).particleConditionBlock;
49
+ case "ParticleFloatToIntBlock":
50
+ return (await import('./blocks/particle-float-to-int-block.js')).particleFloatToIntBlock;
39
51
  default:
40
52
  throw new Error(`NodeParticle: unsupported block class "${className}"`);
41
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"npe-registry.js","sources":["../../../../src/particle/node/npe-registry.ts"],"sourcesContent":["import type { ParticleBlockEvaluator } from \"./npe-types.js\";\n\n/**\n * Lazily load the evaluator for a node-particle block class.\n *\n * Each arm is a dynamic `import(...)` so a scene's bundle includes only the block classes its graph\n * actually references — unused blocks tree-shake to zero bytes (the same discipline as the node-material\n * registry). Add new block classes here as scenes need them.\n */\nexport async function loadParticleBlockEvaluator(className: string): Promise<ParticleBlockEvaluator> {\n switch (className) {\n case \"SystemBlock\":\n return (await import(\"./blocks/system-block.js\")).systemBlock;\n case \"CreateParticleBlock\":\n return (await import(\"./blocks/create-particle-block.js\")).createParticleBlock;\n case \"BoxShapeBlock\":\n return (await import(\"./blocks/box-shape-block.js\")).boxShapeBlock;\n case \"SphereShapeBlock\":\n return (await import(\"./blocks/sphere-shape-block.js\")).sphereShapeBlock;\n case \"PointShapeBlock\":\n return (await import(\"./blocks/point-shape-block.js\")).pointShapeBlock;\n case \"ConeShapeBlock\":\n return (await import(\"./blocks/cone-shape-block.js\")).coneShapeBlock;\n case \"CylinderShapeBlock\":\n return (await import(\"./blocks/cylinder-shape-block.js\")).cylinderShapeBlock;\n case \"MeshShapeBlock\":\n return (await import(\"./blocks/mesh-shape-block.js\")).meshShapeBlock;\n case \"ParticleInputBlock\":\n return (await import(\"./blocks/particle-input-block.js\")).particleInputBlock;\n case \"ParticleTextureSourceBlock\":\n return (await import(\"./blocks/texture-source-block.js\")).textureSourceBlock;\n case \"ParticleRandomBlock\":\n return (await import(\"./blocks/particle-random-block.js\")).particleRandomBlock;\n case \"ParticleLerpBlock\":\n return (await import(\"./blocks/particle-lerp-block.js\")).particleLerpBlock;\n case \"ParticleConverterBlock\":\n return (await import(\"./blocks/particle-converter-block.js\")).particleConverterBlock;\n case \"ParticleMathBlock\":\n return (await import(\"./blocks/particle-math-block.js\")).particleMathBlock;\n case \"UpdatePositionBlock\":\n return (await import(\"./blocks/update-position-block.js\")).updatePositionBlock;\n case \"UpdateColorBlock\":\n return (await import(\"./blocks/update-color-block.js\")).updateColorBlock;\n case \"UpdateAngleBlock\":\n return (await import(\"./blocks/update-angle-block.js\")).updateAngleBlock;\n case \"UpdateDirectionBlock\":\n return (await import(\"./blocks/update-direction-block.js\")).updateDirectionBlock;\n default:\n throw new Error(`NodeParticle: unsupported block class \"${className}\"`);\n }\n}\n"],"names":[],"mappings":"AASA,eAAsB,2BAA2B,SAAA,EAAoD;AACjG,EAAA,QAAQ,SAAA;AAAW,IACf,KAAK,aAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,0BAA0B,CAAA,EAAG,WAAA;AAAA,IACtD,KAAK,qBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,mCAAmC,CAAA,EAAG,mBAAA;AAAA,IAC/D,KAAK,eAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,6BAA6B,CAAA,EAAG,aAAA;AAAA,IACzD,KAAK,kBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,gCAAgC,CAAA,EAAG,gBAAA;AAAA,IAC5D,KAAK,iBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,+BAA+B,CAAA,EAAG,eAAA;AAAA,IAC3D,KAAK,gBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,8BAA8B,CAAA,EAAG,cAAA;AAAA,IAC1D,KAAK,oBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,kCAAkC,CAAA,EAAG,kBAAA;AAAA,IAC9D,KAAK,gBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,8BAA8B,CAAA,EAAG,cAAA;AAAA,IAC1D,KAAK,oBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,kCAAkC,CAAA,EAAG,kBAAA;AAAA,IAC9D,KAAK,4BAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,kCAAkC,CAAA,EAAG,kBAAA;AAAA,IAC9D,KAAK,qBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,mCAAmC,CAAA,EAAG,mBAAA;AAAA,IAC/D,KAAK,mBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,iCAAiC,CAAA,EAAG,iBAAA;AAAA,IAC7D,KAAK,wBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,sCAAsC,CAAA,EAAG,sBAAA;AAAA,IAClE,KAAK,mBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,iCAAiC,CAAA,EAAG,iBAAA;AAAA,IAC7D,KAAK,qBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,mCAAmC,CAAA,EAAG,mBAAA;AAAA,IAC/D,KAAK,kBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,gCAAgC,CAAA,EAAG,gBAAA;AAAA,IAC5D,KAAK,kBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,gCAAgC,CAAA,EAAG,gBAAA;AAAA,IAC5D,KAAK,sBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,oCAAoC,CAAA,EAAG,oBAAA;AAAA,IAChE;AACI,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,uCAAA,EAA0C,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA;AAElF;;;;"}
1
+ {"version":3,"file":"npe-registry.js","sources":["../../../../src/particle/node/npe-registry.ts"],"sourcesContent":["import type { ParticleBlockEvaluator } from \"./npe-types.js\";\n\n/**\n * Lazily load the evaluator for a node-particle block class.\n *\n * Each arm is a dynamic `import(...)` so a scene's bundle includes only the block classes its graph\n * actually references — unused blocks tree-shake to zero bytes (the same discipline as the node-material\n * registry). Add new block classes here as scenes need them.\n */\nexport async function loadParticleBlockEvaluator(className: string): Promise<ParticleBlockEvaluator> {\n switch (className) {\n case \"SystemBlock\":\n return (await import(\"./blocks/system-block.js\")).systemBlock;\n case \"CreateParticleBlock\":\n return (await import(\"./blocks/create-particle-block.js\")).createParticleBlock;\n case \"BoxShapeBlock\":\n return (await import(\"./blocks/box-shape-block.js\")).boxShapeBlock;\n case \"SphereShapeBlock\":\n return (await import(\"./blocks/sphere-shape-block.js\")).sphereShapeBlock;\n case \"PointShapeBlock\":\n return (await import(\"./blocks/point-shape-block.js\")).pointShapeBlock;\n case \"ConeShapeBlock\":\n return (await import(\"./blocks/cone-shape-block.js\")).coneShapeBlock;\n case \"CylinderShapeBlock\":\n return (await import(\"./blocks/cylinder-shape-block.js\")).cylinderShapeBlock;\n case \"MeshShapeBlock\":\n return (await import(\"./blocks/mesh-shape-block.js\")).meshShapeBlock;\n case \"ParticleInputBlock\":\n return (await import(\"./blocks/particle-input-block.js\")).particleInputBlock;\n case \"ParticleTextureSourceBlock\":\n return (await import(\"./blocks/texture-source-block.js\")).textureSourceBlock;\n case \"ParticleRandomBlock\":\n return (await import(\"./blocks/particle-random-block.js\")).particleRandomBlock;\n case \"ParticleLerpBlock\":\n return (await import(\"./blocks/particle-lerp-block.js\")).particleLerpBlock;\n case \"ParticleConverterBlock\":\n return (await import(\"./blocks/particle-converter-block.js\")).particleConverterBlock;\n case \"ParticleMathBlock\":\n return (await import(\"./blocks/particle-math-block.js\")).particleMathBlock;\n case \"UpdatePositionBlock\":\n return (await import(\"./blocks/update-position-block.js\")).updatePositionBlock;\n case \"UpdateColorBlock\":\n return (await import(\"./blocks/update-color-block.js\")).updateColorBlock;\n case \"UpdateAngleBlock\":\n return (await import(\"./blocks/update-angle-block.js\")).updateAngleBlock;\n case \"UpdateDirectionBlock\":\n return (await import(\"./blocks/update-direction-block.js\")).updateDirectionBlock;\n case \"UpdateSizeBlock\":\n return (await import(\"./blocks/update-size-block.js\")).updateSizeBlock;\n case \"ParticleGradientBlock\":\n return (await import(\"./blocks/particle-gradient-block.js\")).particleGradientBlock;\n case \"ParticleGradientValueBlock\":\n return (await import(\"./blocks/particle-gradient-value-block.js\")).particleGradientValueBlock;\n case \"ParticleVectorLengthBlock\":\n return (await import(\"./blocks/particle-vector-length-block.js\")).particleVectorLengthBlock;\n case \"ParticleConditionBlock\":\n return (await import(\"./blocks/particle-condition-block.js\")).particleConditionBlock;\n case \"ParticleFloatToIntBlock\":\n return (await import(\"./blocks/particle-float-to-int-block.js\")).particleFloatToIntBlock;\n default:\n throw new Error(`NodeParticle: unsupported block class \"${className}\"`);\n }\n}\n"],"names":[],"mappings":"AASA,eAAsB,2BAA2B,SAAA,EAAoD;AACjG,EAAA,QAAQ,SAAA;AAAW,IACf,KAAK,aAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,0BAA0B,CAAA,EAAG,WAAA;AAAA,IACtD,KAAK,qBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,mCAAmC,CAAA,EAAG,mBAAA;AAAA,IAC/D,KAAK,eAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,6BAA6B,CAAA,EAAG,aAAA;AAAA,IACzD,KAAK,kBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,gCAAgC,CAAA,EAAG,gBAAA;AAAA,IAC5D,KAAK,iBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,+BAA+B,CAAA,EAAG,eAAA;AAAA,IAC3D,KAAK,gBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,8BAA8B,CAAA,EAAG,cAAA;AAAA,IAC1D,KAAK,oBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,kCAAkC,CAAA,EAAG,kBAAA;AAAA,IAC9D,KAAK,gBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,8BAA8B,CAAA,EAAG,cAAA;AAAA,IAC1D,KAAK,oBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,kCAAkC,CAAA,EAAG,kBAAA;AAAA,IAC9D,KAAK,4BAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,kCAAkC,CAAA,EAAG,kBAAA;AAAA,IAC9D,KAAK,qBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,mCAAmC,CAAA,EAAG,mBAAA;AAAA,IAC/D,KAAK,mBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,iCAAiC,CAAA,EAAG,iBAAA;AAAA,IAC7D,KAAK,wBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,sCAAsC,CAAA,EAAG,sBAAA;AAAA,IAClE,KAAK,mBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,iCAAiC,CAAA,EAAG,iBAAA;AAAA,IAC7D,KAAK,qBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,mCAAmC,CAAA,EAAG,mBAAA;AAAA,IAC/D,KAAK,kBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,gCAAgC,CAAA,EAAG,gBAAA;AAAA,IAC5D,KAAK,kBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,gCAAgC,CAAA,EAAG,gBAAA;AAAA,IAC5D,KAAK,sBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,oCAAoC,CAAA,EAAG,oBAAA;AAAA,IAChE,KAAK,iBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,+BAA+B,CAAA,EAAG,eAAA;AAAA,IAC3D,KAAK,uBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,qCAAqC,CAAA,EAAG,qBAAA;AAAA,IACjE,KAAK,4BAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,2CAA2C,CAAA,EAAG,0BAAA;AAAA,IACvE,KAAK,2BAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,0CAA0C,CAAA,EAAG,yBAAA;AAAA,IACtE,KAAK,wBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,sCAAsC,CAAA,EAAG,sBAAA;AAAA,IAClE,KAAK,yBAAA;AACD,MAAA,OAAA,CAAQ,MAAM,OAAO,yCAAyC,CAAA,EAAG,uBAAA;AAAA,IACrE;AACI,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,uCAAA,EAA0C,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA;AAElF;;;;"}
@@ -20,6 +20,7 @@ function createParticleSystem(name, capacity) {
20
20
  _stopped: false,
21
21
  _actualFrame: 0,
22
22
  _newPartsExcess: 0,
23
+ _emitRateGetter: null,
23
24
  _scaledUpdateSpeed: 0,
24
25
  _emitPower: 1,
25
26
  _nextParticleId: 0,
@@ -48,8 +49,9 @@ function animateParticleSystem(system, scaledRatio) {
48
49
  return;
49
50
  }
50
51
  system._scaledUpdateSpeed = system.updateSpeed * scaledRatio;
51
- let newParticles = system.emitRate * system._scaledUpdateSpeed >> 0;
52
- system._newPartsExcess += system.emitRate * system._scaledUpdateSpeed - newParticles;
52
+ const emitRate = system._emitRateGetter ? system._emitRateGetter(system) : system.emitRate;
53
+ let newParticles = emitRate * system._scaledUpdateSpeed >> 0;
54
+ system._newPartsExcess += emitRate * system._scaledUpdateSpeed - newParticles;
53
55
  if (system._newPartsExcess > 1) {
54
56
  const extra = system._newPartsExcess >> 0;
55
57
  newParticles += extra;
@@ -112,6 +114,9 @@ function runCreationSlots(system, particle) {
112
114
  }
113
115
  if (system._createPosition) {
114
116
  system._createPosition(particle, system);
117
+ particle._localPosition.x = particle.position.x;
118
+ particle._localPosition.y = particle.position.y;
119
+ particle._localPosition.z = particle.position.z;
115
120
  }
116
121
  if (system._createDirection) {
117
122
  system._createDirection(particle, system);
@@ -1 +1 @@
1
- {"version":3,"file":"particle-system.js","sources":["../../../src/particle/particle-system.ts"],"sourcesContent":["import type { Vec3, Color4 } from \"../math/types.js\";\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport type { Particle } from \"./particle.js\";\nimport { createParticle, resetParticle } from \"./particle.js\";\n\n/**\n * A unit of per-particle work run during creation or update. The node-graph build populates a\n * system's create/update queues with these closures (one per graph block that touches a particle).\n */\nexport type ParticleProcess = (particle: Particle, system: ParticleSystem) => void;\n\n/**\n * A CPU-simulated particle system — pure state.\n *\n * The node-graph build configures the public properties and fills {@link ParticleSystem._createQueue}\n * and {@link ParticleSystem._updateQueue}. {@link animateParticleSystem} advances the simulation by a\n * single step. Rendering is handled separately by binding live particles to a billboard system.\n */\nexport interface ParticleSystem {\n /** System name (from the graph's SystemBlock). */\n name: string;\n /** Maximum number of simultaneously alive particles. */\n capacity: number;\n /** Particles emitted per simulated time unit. */\n emitRate: number;\n /** Simulation advance per render frame, before the per-step ratio. */\n updateSpeed: number;\n /** When non-zero, the system stops once `_actualFrame` reaches this value. */\n targetStopDuration: number;\n /** Blend mode index (Babylon.js `BLENDMODE_*`); mapped to a billboard blend descriptor at render time. */\n blendMode: number;\n /** Billboard mode index (Babylon.js `PARTICLES_BILLBOARDMODE_*`). */\n billboardMode: number;\n /** Whether particles render as camera-facing billboards. */\n isBillboardBased: boolean;\n /** Whether particle coordinates are emitter-local rather than world. */\n isLocal: boolean;\n /** Emitter world position (pure-translation emitter). */\n emitter: Vec3;\n /** Particle texture used by the billboard renderer (resolved after async asset loads complete). */\n texture: Texture2D | null;\n\n /** @internal Deferred texture binder, run by the build once async loads settle. */\n _resolveTexture: (() => void) | null;\n\n /** @internal Live particles (compacted; recycling swaps with the last). */\n _particles: Particle[];\n /** @internal Recycled-particle pool for zero-allocation steady state. */\n _stock: Particle[];\n /** @internal Whether the system is started. */\n _started: boolean;\n /** @internal Whether the system has been stopped (drains remaining particles, emits no more). */\n _stopped: boolean;\n /** @internal Accumulated simulated time, in update-speed units. */\n _actualFrame: number;\n /** @internal Fractional emission carry-over between steps. */\n _newPartsExcess: number;\n /** @internal Update speed scaled by the current step ratio. */\n _scaledUpdateSpeed: number;\n /** @internal Emit power of the most recently created particle (set by the creation queue). */\n _emitPower: number;\n /** @internal Monotonic particle-id source. */\n _nextParticleId: number;\n /** @internal Scratch: `colorStep * _scaledUpdateSpeed` (contextual ScaledColorStep). */\n _scaledColorStep: Color4;\n /**\n * @internal Creation slots, run in this fixed order on every spawn — matching the\n * `ThinParticleSystem` creation-queue wiring. The fixed order (not graph build order) is what\n * makes the per-particle `Math.random()` sequence match Babylon.js. Each slot is filled by the\n * block that owns it (CreateParticleBlock fills lifetime/size/angle/colour; the shape block fills\n * position/direction); unfilled slots are skipped.\n */\n _createLifeTime: ParticleProcess | null;\n /** @internal */\n _createPosition: ParticleProcess | null;\n /** @internal */\n _createDirection: ParticleProcess | null;\n /** @internal */\n _createEmitPower: ParticleProcess | null;\n /** @internal */\n _createSize: ParticleProcess | null;\n /** @internal */\n _createAngle: ParticleProcess | null;\n /** @internal */\n _createColor: ParticleProcess | null;\n /** @internal */\n _createColorDead: ParticleProcess | null;\n /** @internal Per-particle update steps, in graph-connection order. */\n _updateQueue: ParticleProcess[];\n}\n\n/** Create an empty particle system with Babylon.js default properties. The graph build overrides these. */\nexport function createParticleSystem(name: string, capacity: number): ParticleSystem {\n return {\n name,\n capacity,\n emitRate: 10,\n updateSpeed: 0.016666666666666666,\n targetStopDuration: 0,\n blendMode: 0,\n billboardMode: 7,\n isBillboardBased: true,\n isLocal: false,\n emitter: { x: 0, y: 0, z: 0 },\n texture: null,\n _resolveTexture: null,\n _particles: [],\n _stock: [],\n _started: false,\n _stopped: false,\n _actualFrame: 0,\n _newPartsExcess: 0,\n _scaledUpdateSpeed: 0,\n _emitPower: 1,\n _nextParticleId: 0,\n _scaledColorStep: { r: 0, g: 0, b: 0, a: 0 },\n _createLifeTime: null,\n _createPosition: null,\n _createDirection: null,\n _createEmitPower: null,\n _createSize: null,\n _createAngle: null,\n _createColor: null,\n _createColorDead: null,\n _updateQueue: [],\n };\n}\n\n/** Start emission. Resets the simulated-time accumulator. */\nexport function startParticleSystem(system: ParticleSystem): void {\n system._started = true;\n system._stopped = false;\n system._actualFrame = 0;\n}\n\n/** Stop emission. Existing particles continue until they expire. */\nexport function stopParticleSystem(system: ParticleSystem): void {\n system._stopped = true;\n}\n\n/**\n * Advance the simulation by one step.\n *\n * `scaledRatio` is the per-step multiplier on {@link ParticleSystem.updateSpeed}: the scene animation\n * ratio for a live frame, or the pre-warm step offset during pre-warm. Mirrors the emission-count,\n * update, recycle, and creation logic of Babylon.js `ThinParticleSystem.animate`.\n */\nexport function animateParticleSystem(system: ParticleSystem, scaledRatio: number): void {\n if (!system._started) {\n return;\n }\n\n system._scaledUpdateSpeed = system.updateSpeed * scaledRatio;\n\n // Emission count: integer part this step, fractional part carried over.\n let newParticles = (system.emitRate * system._scaledUpdateSpeed) >> 0;\n system._newPartsExcess += system.emitRate * system._scaledUpdateSpeed - newParticles;\n if (system._newPartsExcess > 1.0) {\n const extra = system._newPartsExcess >> 0;\n newParticles += extra;\n system._newPartsExcess -= extra;\n }\n\n if (system._stopped) {\n newParticles = 0;\n } else {\n system._actualFrame += system._scaledUpdateSpeed;\n if (system.targetStopDuration && system._actualFrame >= system.targetStopDuration) {\n stopParticleSystem(system);\n }\n }\n\n updateExistingParticles(system);\n createNewParticles(system, newParticles);\n}\n\nfunction updateExistingParticles(system: ParticleSystem): void {\n const particles = system._particles;\n const updateQueue = system._updateQueue;\n\n for (let i = 0; i < particles.length; i++) {\n const particle = particles[i]!;\n\n let stepSpeed = system._scaledUpdateSpeed;\n const previousAge = particle.age;\n particle.age += stepSpeed;\n\n // Clamp the final partial step so a particle dies exactly at its lifetime.\n if (particle.age > particle.lifeTime) {\n const diff = particle.age - previousAge;\n const oldDiff = particle.lifeTime - previousAge;\n stepSpeed = (oldDiff * stepSpeed) / diff;\n particle.age = particle.lifeTime;\n }\n\n particle._directionScale = stepSpeed;\n\n for (let q = 0; q < updateQueue.length; q++) {\n updateQueue[q]!(particle, system);\n }\n\n if (particle.age >= particle.lifeTime) {\n recycleParticle(system, i);\n i--;\n }\n }\n}\n\nfunction createNewParticles(system: ParticleSystem, count: number): void {\n const particles = system._particles;\n\n for (let n = 0; n < count; n++) {\n if (particles.length >= system.capacity) {\n break;\n }\n\n const id = system._nextParticleId++;\n const pooled = system._stock.pop();\n const particle = pooled ?? createParticle(id);\n if (pooled) {\n resetParticle(particle, id);\n }\n\n particles.push(particle);\n runCreationSlots(system, particle);\n }\n}\n\n/** Run the fixed-order creation slots for a freshly spawned particle (skips unfilled slots). */\nfunction runCreationSlots(system: ParticleSystem, particle: Particle): void {\n if (system._createLifeTime) {\n system._createLifeTime(particle, system);\n }\n if (system._createPosition) {\n system._createPosition(particle, system);\n }\n if (system._createDirection) {\n system._createDirection(particle, system);\n }\n if (system._createEmitPower) {\n system._createEmitPower(particle, system);\n }\n if (system._createSize) {\n system._createSize(particle, system);\n }\n if (system._createAngle) {\n system._createAngle(particle, system);\n }\n if (system._createColor) {\n system._createColor(particle, system);\n }\n if (system._createColorDead) {\n system._createColorDead(particle, system);\n }\n}\n\n/** Recycle a dead particle by swapping it with the last live particle and returning it to the pool. */\nfunction recycleParticle(system: ParticleSystem, index: number): void {\n const particles = system._particles;\n const dead = particles[index]!;\n const lastIndex = particles.length - 1;\n if (index !== lastIndex) {\n particles[index] = particles[lastIndex]!;\n }\n particles.pop();\n // Prune this particle's per-life random-lock cache so `OncePerParticle` draws don't accumulate.\n dead._onceRandomValues?.clear();\n system._stock.push(dead);\n}\n"],"names":[],"mappings":";;AA4FO,SAAS,oBAAA,CAAqB,MAAc,QAAA,EAAkC;AACjF,EAAA,OAAO;AAAA,IACH,IAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA,EAAU,EAAA;AAAA,IACV,WAAA,EAAa,oBAAA;AAAA,IACb,kBAAA,EAAoB,CAAA;AAAA,IACpB,SAAA,EAAW,CAAA;AAAA,IACX,aAAA,EAAe,CAAA;AAAA,IACf,gBAAA,EAAkB,IAAA;AAAA,IAClB,OAAA,EAAS,KAAA;AAAA,IACT,SAAS,EAAE,CAAA,EAAG,GAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAE;AAAA,IAC5B,OAAA,EAAS,IAAA;AAAA,IACT,eAAA,EAAiB,IAAA;AAAA,IACjB,YAAY,EAAC;AAAA,IACb,QAAQ,EAAC;AAAA,IACT,QAAA,EAAU,KAAA;AAAA,IACV,QAAA,EAAU,KAAA;AAAA,IACV,YAAA,EAAc,CAAA;AAAA,IACd,eAAA,EAAiB,CAAA;AAAA,IACjB,kBAAA,EAAoB,CAAA;AAAA,IACpB,UAAA,EAAY,CAAA;AAAA,IACZ,eAAA,EAAiB,CAAA;AAAA,IACjB,gBAAA,EAAkB,EAAE,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAE;AAAA,IAC3C,eAAA,EAAiB,IAAA;AAAA,IACjB,eAAA,EAAiB,IAAA;AAAA,IACjB,gBAAA,EAAkB,IAAA;AAAA,IAClB,gBAAA,EAAkB,IAAA;AAAA,IAClB,WAAA,EAAa,IAAA;AAAA,IACb,YAAA,EAAc,IAAA;AAAA,IACd,YAAA,EAAc,IAAA;AAAA,IACd,gBAAA,EAAkB,IAAA;AAAA,IAClB,cAAc;AAAC,GACnB;AACJ;AAGO,SAAS,oBAAoB,MAAA,EAA8B;AAC9D,EAAA,MAAA,CAAO,QAAA,GAAW,IAAA;AAClB,EAAA,MAAA,CAAO,QAAA,GAAW,KAAA;AAClB,EAAA,MAAA,CAAO,YAAA,GAAe,CAAA;AAC1B;AAGO,SAAS,mBAAmB,MAAA,EAA8B;AAC7D,EAAA,MAAA,CAAO,QAAA,GAAW,IAAA;AACtB;AASO,SAAS,qBAAA,CAAsB,QAAwB,WAAA,EAA2B;AACrF,EAAA,IAAI,CAAC,OAAO,QAAA,EAAU;AAClB,IAAA;AAAA,EACJ;AAEA,EAAA,MAAA,CAAO,kBAAA,GAAqB,OAAO,WAAA,GAAc,WAAA;AAGjD,EAAA,IAAI,YAAA,GAAgB,MAAA,CAAO,QAAA,GAAW,MAAA,CAAO,kBAAA,IAAuB,CAAA;AACpE,EAAA,MAAA,CAAO,eAAA,IAAmB,MAAA,CAAO,QAAA,GAAW,MAAA,CAAO,kBAAA,GAAqB,YAAA;AACxE,EAAA,IAAI,MAAA,CAAO,kBAAkB,CAAA,EAAK;AAC9B,IAAA,MAAM,KAAA,GAAQ,OAAO,eAAA,IAAmB,CAAA;AACxC,IAAA,YAAA,IAAgB,KAAA;AAChB,IAAA,MAAA,CAAO,eAAA,IAAmB,KAAA;AAAA,EAC9B;AAEA,EAAA,IAAI,OAAO,QAAA,EAAU;AACjB,IAAA,YAAA,GAAe,CAAA;AAAA,EACnB,CAAA,MAAO;AACH,IAAA,MAAA,CAAO,gBAAgB,MAAA,CAAO,kBAAA;AAC9B,IAAA,IAAI,MAAA,CAAO,kBAAA,IAAsB,MAAA,CAAO,YAAA,IAAgB,OAAO,kBAAA,EAAoB;AAC/E,MAAA,kBAAA,CAAmB,MAAM,CAAA;AAAA,IAC7B;AAAA,EACJ;AAEA,EAAA,uBAAA,CAAwB,MAAM,CAAA;AAC9B,EAAA,kBAAA,CAAmB,QAAQ,YAAY,CAAA;AAC3C;AAEA,SAAS,wBAAwB,MAAA,EAA8B;AAC3D,EAAA,MAAM,YAAY,MAAA,CAAO,UAAA;AACzB,EAAA,MAAM,cAAc,MAAA,CAAO,YAAA;AAE3B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,SAAA,CAAU,QAAQ,CAAA,EAAA,EAAK;AACvC,IAAA,MAAM,QAAA,GAAW,UAAU,CAAC,CAAA;AAE5B,IAAA,IAAI,YAAY,MAAA,CAAO,kBAAA;AACvB,IAAA,MAAM,cAAc,QAAA,CAAS,GAAA;AAC7B,IAAA,QAAA,CAAS,GAAA,IAAO,SAAA;AAGhB,IAAA,IAAI,QAAA,CAAS,GAAA,GAAM,QAAA,CAAS,QAAA,EAAU;AAClC,MAAA,MAAM,IAAA,GAAO,SAAS,GAAA,GAAM,WAAA;AAC5B,MAAA,MAAM,OAAA,GAAU,SAAS,QAAA,GAAW,WAAA;AACpC,MAAA,SAAA,GAAa,UAAU,SAAA,GAAa,IAAA;AACpC,MAAA,QAAA,CAAS,MAAM,QAAA,CAAS,QAAA;AAAA,IAC5B;AAEA,IAAA,QAAA,CAAS,eAAA,GAAkB,SAAA;AAE3B,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,WAAA,CAAY,QAAQ,CAAA,EAAA,EAAK;AACzC,MAAA,WAAA,CAAY,CAAC,CAAA,CAAG,QAAA,EAAU,MAAM,CAAA;AAAA,IACpC;AAEA,IAAA,IAAI,QAAA,CAAS,GAAA,IAAO,QAAA,CAAS,QAAA,EAAU;AACnC,MAAA,eAAA,CAAgB,QAAQ,CAAC,CAAA;AACzB,MAAA,CAAA,EAAA;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,SAAS,kBAAA,CAAmB,QAAwB,KAAA,EAAqB;AACrE,EAAA,MAAM,YAAY,MAAA,CAAO,UAAA;AAEzB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC5B,IAAA,IAAI,SAAA,CAAU,MAAA,IAAU,MAAA,CAAO,QAAA,EAAU;AACrC,MAAA;AAAA,IACJ;AAEA,IAAA,MAAM,KAAK,MAAA,CAAO,eAAA,EAAA;AAClB,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,GAAA,EAAI;AACjC,IAAA,MAAM,QAAA,GAAW,MAAA,IAAU,cAAA,CAAe,EAAE,CAAA;AAC5C,IAAA,IAAI,MAAA,EAAQ;AACR,MAAA,aAAA,CAAc,UAAU,EAAE,CAAA;AAAA,IAC9B;AAEA,IAAA,SAAA,CAAU,KAAK,QAAQ,CAAA;AACvB,IAAA,gBAAA,CAAiB,QAAQ,QAAQ,CAAA;AAAA,EACrC;AACJ;AAGA,SAAS,gBAAA,CAAiB,QAAwB,QAAA,EAA0B;AACxE,EAAA,IAAI,OAAO,eAAA,EAAiB;AACxB,IAAA,MAAA,CAAO,eAAA,CAAgB,UAAU,MAAM,CAAA;AAAA,EAC3C;AACA,EAAA,IAAI,OAAO,eAAA,EAAiB;AACxB,IAAA,MAAA,CAAO,eAAA,CAAgB,UAAU,MAAM,CAAA;AAAA,EAC3C;AACA,EAAA,IAAI,OAAO,gBAAA,EAAkB;AACzB,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,MAAM,CAAA;AAAA,EAC5C;AACA,EAAA,IAAI,OAAO,gBAAA,EAAkB;AACzB,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,MAAM,CAAA;AAAA,EAC5C;AACA,EAAA,IAAI,OAAO,WAAA,EAAa;AACpB,IAAA,MAAA,CAAO,WAAA,CAAY,UAAU,MAAM,CAAA;AAAA,EACvC;AACA,EAAA,IAAI,OAAO,YAAA,EAAc;AACrB,IAAA,MAAA,CAAO,YAAA,CAAa,UAAU,MAAM,CAAA;AAAA,EACxC;AACA,EAAA,IAAI,OAAO,YAAA,EAAc;AACrB,IAAA,MAAA,CAAO,YAAA,CAAa,UAAU,MAAM,CAAA;AAAA,EACxC;AACA,EAAA,IAAI,OAAO,gBAAA,EAAkB;AACzB,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,MAAM,CAAA;AAAA,EAC5C;AACJ;AAGA,SAAS,eAAA,CAAgB,QAAwB,KAAA,EAAqB;AAClE,EAAA,MAAM,YAAY,MAAA,CAAO,UAAA;AACzB,EAAA,MAAM,IAAA,GAAO,UAAU,KAAK,CAAA;AAC5B,EAAA,MAAM,SAAA,GAAY,UAAU,MAAA,GAAS,CAAA;AACrC,EAAA,IAAI,UAAU,SAAA,EAAW;AACrB,IAAA,SAAA,CAAU,KAAK,CAAA,GAAI,SAAA,CAAU,SAAS,CAAA;AAAA,EAC1C;AACA,EAAA,SAAA,CAAU,GAAA,EAAI;AAEd,EAAA,IAAA,CAAK,mBAAmB,KAAA,EAAM;AAC9B,EAAA,MAAA,CAAO,MAAA,CAAO,KAAK,IAAI,CAAA;AAC3B;;;;"}
1
+ {"version":3,"file":"particle-system.js","sources":["../../../src/particle/particle-system.ts"],"sourcesContent":["import type { Vec3, Color4 } from \"../math/types.js\";\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport type { Particle } from \"./particle.js\";\nimport { createParticle, resetParticle } from \"./particle.js\";\n\n/**\n * A unit of per-particle work run during creation or update. The node-graph build populates a\n * system's create/update queues with these closures (one per graph block that touches a particle).\n */\nexport type ParticleProcess = (particle: Particle, system: ParticleSystem) => void;\n\n/**\n * A CPU-simulated particle system — pure state.\n *\n * The node-graph build configures the public properties and fills {@link ParticleSystem._createQueue}\n * and {@link ParticleSystem._updateQueue}. {@link animateParticleSystem} advances the simulation by a\n * single step. Rendering is handled separately by binding live particles to a billboard system.\n */\nexport interface ParticleSystem {\n /** System name (from the graph's SystemBlock). */\n name: string;\n /** Maximum number of simultaneously alive particles. */\n capacity: number;\n /** Particles emitted per simulated time unit. */\n emitRate: number;\n /** Simulation advance per render frame, before the per-step ratio. */\n updateSpeed: number;\n /** When non-zero, the system stops once `_actualFrame` reaches this value. */\n targetStopDuration: number;\n /** Blend mode index (Babylon.js `BLENDMODE_*`); mapped to a billboard blend descriptor at render time. */\n blendMode: number;\n /** Billboard mode index (Babylon.js `PARTICLES_BILLBOARDMODE_*`). */\n billboardMode: number;\n /** Whether particles render as camera-facing billboards. */\n isBillboardBased: boolean;\n /** Whether particle coordinates are emitter-local rather than world. */\n isLocal: boolean;\n /** Emitter world position (pure-translation emitter). */\n emitter: Vec3;\n /** Particle texture used by the billboard renderer (resolved after async asset loads complete). */\n texture: Texture2D | null;\n\n /** @internal Deferred texture binder, run by the build once async loads settle. */\n _resolveTexture: (() => void) | null;\n\n /** @internal Live particles (compacted; recycling swaps with the last). */\n _particles: Particle[];\n /** @internal Recycled-particle pool for zero-allocation steady state. */\n _stock: Particle[];\n /** @internal Whether the system is started. */\n _started: boolean;\n /** @internal Whether the system has been stopped (drains remaining particles, emits no more). */\n _stopped: boolean;\n /** @internal Accumulated simulated time, in update-speed units. */\n _actualFrame: number;\n /** @internal Fractional emission carry-over between steps. */\n _newPartsExcess: number;\n /**\n * @internal Per-frame emit-rate getter, set by the SystemBlock when the emit rate is a dynamic source\n * (e.g. an emit-rate gradient over the system's target duration). When null, the constant\n * {@link ParticleSystem.emitRate} is used instead.\n */\n _emitRateGetter: ((system: ParticleSystem) => number) | null;\n /** @internal Update speed scaled by the current step ratio. */\n _scaledUpdateSpeed: number;\n /** @internal Emit power of the most recently created particle (set by the creation queue). */\n _emitPower: number;\n /** @internal Monotonic particle-id source. */\n _nextParticleId: number;\n /** @internal Scratch: `colorStep * _scaledUpdateSpeed` (contextual ScaledColorStep). */\n _scaledColorStep: Color4;\n /**\n * @internal Creation slots, run in this fixed order on every spawn — matching the\n * `ThinParticleSystem` creation-queue wiring. The fixed order (not graph build order) is what\n * makes the per-particle `Math.random()` sequence match Babylon.js. Each slot is filled by the\n * block that owns it (CreateParticleBlock fills lifetime/size/angle/colour; the shape block fills\n * position/direction); unfilled slots are skipped.\n */\n _createLifeTime: ParticleProcess | null;\n /** @internal */\n _createPosition: ParticleProcess | null;\n /** @internal */\n _createDirection: ParticleProcess | null;\n /** @internal */\n _createEmitPower: ParticleProcess | null;\n /** @internal */\n _createSize: ParticleProcess | null;\n /** @internal */\n _createAngle: ParticleProcess | null;\n /** @internal */\n _createColor: ParticleProcess | null;\n /** @internal */\n _createColorDead: ParticleProcess | null;\n /** @internal Per-particle update steps, in graph-connection order. */\n _updateQueue: ParticleProcess[];\n}\n\n/** Create an empty particle system with Babylon.js default properties. The graph build overrides these. */\nexport function createParticleSystem(name: string, capacity: number): ParticleSystem {\n return {\n name,\n capacity,\n emitRate: 10,\n updateSpeed: 0.016666666666666666,\n targetStopDuration: 0,\n blendMode: 0,\n billboardMode: 7,\n isBillboardBased: true,\n isLocal: false,\n emitter: { x: 0, y: 0, z: 0 },\n texture: null,\n _resolveTexture: null,\n _particles: [],\n _stock: [],\n _started: false,\n _stopped: false,\n _actualFrame: 0,\n _newPartsExcess: 0,\n _emitRateGetter: null,\n _scaledUpdateSpeed: 0,\n _emitPower: 1,\n _nextParticleId: 0,\n _scaledColorStep: { r: 0, g: 0, b: 0, a: 0 },\n _createLifeTime: null,\n _createPosition: null,\n _createDirection: null,\n _createEmitPower: null,\n _createSize: null,\n _createAngle: null,\n _createColor: null,\n _createColorDead: null,\n _updateQueue: [],\n };\n}\n\n/** Start emission. Resets the simulated-time accumulator. */\nexport function startParticleSystem(system: ParticleSystem): void {\n system._started = true;\n system._stopped = false;\n system._actualFrame = 0;\n}\n\n/** Stop emission. Existing particles continue until they expire. */\nexport function stopParticleSystem(system: ParticleSystem): void {\n system._stopped = true;\n}\n\n/**\n * Advance the simulation by one step.\n *\n * `scaledRatio` is the per-step multiplier on {@link ParticleSystem.updateSpeed}: the scene animation\n * ratio for a live frame, or the pre-warm step offset during pre-warm. Mirrors the emission-count,\n * update, recycle, and creation logic of Babylon.js `ThinParticleSystem.animate`.\n */\nexport function animateParticleSystem(system: ParticleSystem, scaledRatio: number): void {\n if (!system._started) {\n return;\n }\n\n system._scaledUpdateSpeed = system.updateSpeed * scaledRatio;\n\n // Emission count: integer part this step, fractional part carried over. A dynamic emit rate (e.g. an\n // emit-rate gradient) is re-evaluated each step from the current `_actualFrame`; otherwise the constant.\n const emitRate = system._emitRateGetter ? system._emitRateGetter(system) : system.emitRate;\n let newParticles = (emitRate * system._scaledUpdateSpeed) >> 0;\n system._newPartsExcess += emitRate * system._scaledUpdateSpeed - newParticles;\n if (system._newPartsExcess > 1.0) {\n const extra = system._newPartsExcess >> 0;\n newParticles += extra;\n system._newPartsExcess -= extra;\n }\n\n if (system._stopped) {\n newParticles = 0;\n } else {\n system._actualFrame += system._scaledUpdateSpeed;\n if (system.targetStopDuration && system._actualFrame >= system.targetStopDuration) {\n stopParticleSystem(system);\n }\n }\n\n updateExistingParticles(system);\n createNewParticles(system, newParticles);\n}\n\nfunction updateExistingParticles(system: ParticleSystem): void {\n const particles = system._particles;\n const updateQueue = system._updateQueue;\n\n for (let i = 0; i < particles.length; i++) {\n const particle = particles[i]!;\n\n let stepSpeed = system._scaledUpdateSpeed;\n const previousAge = particle.age;\n particle.age += stepSpeed;\n\n // Clamp the final partial step so a particle dies exactly at its lifetime.\n if (particle.age > particle.lifeTime) {\n const diff = particle.age - previousAge;\n const oldDiff = particle.lifeTime - previousAge;\n stepSpeed = (oldDiff * stepSpeed) / diff;\n particle.age = particle.lifeTime;\n }\n\n particle._directionScale = stepSpeed;\n\n for (let q = 0; q < updateQueue.length; q++) {\n updateQueue[q]!(particle, system);\n }\n\n if (particle.age >= particle.lifeTime) {\n recycleParticle(system, i);\n i--;\n }\n }\n}\n\nfunction createNewParticles(system: ParticleSystem, count: number): void {\n const particles = system._particles;\n\n for (let n = 0; n < count; n++) {\n if (particles.length >= system.capacity) {\n break;\n }\n\n const id = system._nextParticleId++;\n const pooled = system._stock.pop();\n const particle = pooled ?? createParticle(id);\n if (pooled) {\n resetParticle(particle, id);\n }\n\n particles.push(particle);\n runCreationSlots(system, particle);\n }\n}\n\n/** Run the fixed-order creation slots for a freshly spawned particle (skips unfilled slots). */\nfunction runCreationSlots(system: ParticleSystem, particle: Particle): void {\n if (system._createLifeTime) {\n system._createLifeTime(particle, system);\n }\n if (system._createPosition) {\n system._createPosition(particle, system);\n // Mirror BJS `_CreateLocalPositionData`: seed the local-space position from the emitted position so an\n // isLocal system's `LocalPositionUpdated` source integrates from the correct origin.\n particle._localPosition.x = particle.position.x;\n particle._localPosition.y = particle.position.y;\n particle._localPosition.z = particle.position.z;\n }\n if (system._createDirection) {\n system._createDirection(particle, system);\n }\n if (system._createEmitPower) {\n system._createEmitPower(particle, system);\n }\n if (system._createSize) {\n system._createSize(particle, system);\n }\n if (system._createAngle) {\n system._createAngle(particle, system);\n }\n if (system._createColor) {\n system._createColor(particle, system);\n }\n if (system._createColorDead) {\n system._createColorDead(particle, system);\n }\n}\n\n/** Recycle a dead particle by swapping it with the last live particle and returning it to the pool. */\nfunction recycleParticle(system: ParticleSystem, index: number): void {\n const particles = system._particles;\n const dead = particles[index]!;\n const lastIndex = particles.length - 1;\n if (index !== lastIndex) {\n particles[index] = particles[lastIndex]!;\n }\n particles.pop();\n // Prune this particle's per-life random-lock cache so `OncePerParticle` draws don't accumulate.\n dead._onceRandomValues?.clear();\n system._stock.push(dead);\n}\n"],"names":[],"mappings":"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"}