@codexo/exojs-particles 0.15.2 → 0.16.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 (211) hide show
  1. package/README.md +21 -43
  2. package/dist/esm/ParticleStorage.d.ts +188 -0
  3. package/dist/esm/ParticleStorage.d.ts.map +1 -0
  4. package/dist/esm/ParticleStorage.js +208 -0
  5. package/dist/esm/ParticleStorage.js.map +1 -0
  6. package/dist/esm/ParticleSystem.d.ts +189 -60
  7. package/dist/esm/ParticleSystem.d.ts.map +1 -0
  8. package/dist/esm/ParticleSystem.js +660 -560
  9. package/dist/esm/ParticleSystem.js.map +1 -1
  10. package/dist/esm/distributions/BoxArea.d.ts +1 -0
  11. package/dist/esm/distributions/BoxArea.d.ts.map +1 -0
  12. package/dist/esm/distributions/BoxArea.js +38 -44
  13. package/dist/esm/distributions/BoxArea.js.map +1 -1
  14. package/dist/esm/distributions/CircleArea.d.ts +1 -0
  15. package/dist/esm/distributions/CircleArea.d.ts.map +1 -0
  16. package/dist/esm/distributions/CircleArea.js +30 -28
  17. package/dist/esm/distributions/CircleArea.js.map +1 -1
  18. package/dist/esm/distributions/ColorGradient.d.ts +3 -2
  19. package/dist/esm/distributions/ColorGradient.d.ts.map +1 -0
  20. package/dist/esm/distributions/ColorGradient.js +70 -75
  21. package/dist/esm/distributions/ColorGradient.js.map +1 -1
  22. package/dist/esm/distributions/ConeDirection.d.ts +2 -1
  23. package/dist/esm/distributions/ConeDirection.d.ts.map +1 -0
  24. package/dist/esm/distributions/ConeDirection.js +41 -39
  25. package/dist/esm/distributions/ConeDirection.js.map +1 -1
  26. package/dist/esm/distributions/Constant.d.ts +1 -0
  27. package/dist/esm/distributions/Constant.d.ts.map +1 -0
  28. package/dist/esm/distributions/Constant.js +32 -32
  29. package/dist/esm/distributions/Constant.js.map +1 -1
  30. package/dist/esm/distributions/Curve.d.ts +2 -1
  31. package/dist/esm/distributions/Curve.d.ts.map +1 -0
  32. package/dist/esm/distributions/Curve.js +46 -55
  33. package/dist/esm/distributions/Curve.js.map +1 -1
  34. package/dist/esm/distributions/Distribution.d.ts +4 -3
  35. package/dist/esm/distributions/Distribution.d.ts.map +1 -0
  36. package/dist/esm/distributions/LineSegment.d.ts +1 -0
  37. package/dist/esm/distributions/LineSegment.d.ts.map +1 -0
  38. package/dist/esm/distributions/LineSegment.js +25 -23
  39. package/dist/esm/distributions/LineSegment.js.map +1 -1
  40. package/dist/esm/distributions/Range.d.ts +1 -0
  41. package/dist/esm/distributions/Range.d.ts.map +1 -0
  42. package/dist/esm/distributions/Range.js +18 -16
  43. package/dist/esm/distributions/Range.js.map +1 -1
  44. package/dist/esm/distributions/VectorRange.d.ts +1 -0
  45. package/dist/esm/distributions/VectorRange.d.ts.map +1 -0
  46. package/dist/esm/distributions/VectorRange.js +29 -27
  47. package/dist/esm/distributions/VectorRange.js.map +1 -1
  48. package/dist/esm/distributions/index.d.ts +1 -0
  49. package/dist/esm/distributions/index.d.ts.map +1 -0
  50. package/dist/esm/gpu/ParticleGpuState.d.ts +143 -18
  51. package/dist/esm/gpu/ParticleGpuState.d.ts.map +1 -0
  52. package/dist/esm/gpu/ParticleGpuState.js +655 -497
  53. package/dist/esm/gpu/ParticleGpuState.js.map +1 -1
  54. package/dist/esm/gpu/shaders/particle-simulate.wgsl.js +6 -0
  55. package/dist/esm/gpu/shaders/particle-simulate.wgsl.js.map +1 -0
  56. package/dist/esm/index.d.ts +1 -0
  57. package/dist/esm/index.d.ts.map +1 -0
  58. package/dist/esm/index.js +39 -32
  59. package/dist/esm/modules/AlphaFadeOverLifetime.d.ts +4 -3
  60. package/dist/esm/modules/AlphaFadeOverLifetime.d.ts.map +1 -0
  61. package/dist/esm/modules/AlphaFadeOverLifetime.js +68 -55
  62. package/dist/esm/modules/AlphaFadeOverLifetime.js.map +1 -1
  63. package/dist/esm/modules/ApplyForce.d.ts +4 -3
  64. package/dist/esm/modules/ApplyForce.d.ts.map +1 -0
  65. package/dist/esm/modules/ApplyForce.js +49 -43
  66. package/dist/esm/modules/ApplyForce.js.map +1 -1
  67. package/dist/esm/modules/AttractToPoint.d.ts +4 -3
  68. package/dist/esm/modules/AttractToPoint.d.ts.map +1 -0
  69. package/dist/esm/modules/AttractToPoint.js +73 -62
  70. package/dist/esm/modules/AttractToPoint.js.map +1 -1
  71. package/dist/esm/modules/BurstSpawn.d.ts +6 -18
  72. package/dist/esm/modules/BurstSpawn.d.ts.map +1 -0
  73. package/dist/esm/modules/BurstSpawn.js +56 -91
  74. package/dist/esm/modules/BurstSpawn.js.map +1 -1
  75. package/dist/esm/modules/ColorOverLifetime.d.ts +4 -3
  76. package/dist/esm/modules/ColorOverLifetime.d.ts.map +1 -0
  77. package/dist/esm/modules/ColorOverLifetime.js +65 -53
  78. package/dist/esm/modules/ColorOverLifetime.js.map +1 -1
  79. package/dist/esm/modules/ColorOverSpeed.d.ts +5 -4
  80. package/dist/esm/modules/ColorOverSpeed.d.ts.map +1 -0
  81. package/dist/esm/modules/ColorOverSpeed.js +90 -75
  82. package/dist/esm/modules/ColorOverSpeed.js.map +1 -1
  83. package/dist/esm/modules/DeathModule.d.ts +26 -10
  84. package/dist/esm/modules/DeathModule.d.ts.map +1 -0
  85. package/dist/esm/modules/DeathModule.js +38 -22
  86. package/dist/esm/modules/DeathModule.js.map +1 -1
  87. package/dist/esm/modules/Drag.d.ts +3 -2
  88. package/dist/esm/modules/Drag.d.ts.map +1 -0
  89. package/dist/esm/modules/Drag.js +43 -37
  90. package/dist/esm/modules/Drag.js.map +1 -1
  91. package/dist/esm/modules/OrbitalForce.d.ts +4 -3
  92. package/dist/esm/modules/OrbitalForce.d.ts.map +1 -0
  93. package/dist/esm/modules/OrbitalForce.js +70 -58
  94. package/dist/esm/modules/OrbitalForce.js.map +1 -1
  95. package/dist/esm/modules/RateSpawn.d.ts +7 -22
  96. package/dist/esm/modules/RateSpawn.d.ts.map +1 -0
  97. package/dist/esm/modules/RateSpawn.js +39 -71
  98. package/dist/esm/modules/RateSpawn.js.map +1 -1
  99. package/dist/esm/modules/RepelFromPoint.d.ts +4 -3
  100. package/dist/esm/modules/RepelFromPoint.d.ts.map +1 -0
  101. package/dist/esm/modules/RepelFromPoint.js +72 -62
  102. package/dist/esm/modules/RepelFromPoint.js.map +1 -1
  103. package/dist/esm/modules/RotateOverLifetime.d.ts +3 -2
  104. package/dist/esm/modules/RotateOverLifetime.d.ts.map +1 -0
  105. package/dist/esm/modules/RotateOverLifetime.js +40 -36
  106. package/dist/esm/modules/RotateOverLifetime.js.map +1 -1
  107. package/dist/esm/modules/ScaleOverLifetime.d.ts +5 -4
  108. package/dist/esm/modules/ScaleOverLifetime.d.ts.map +1 -0
  109. package/dist/esm/modules/ScaleOverLifetime.js +60 -50
  110. package/dist/esm/modules/ScaleOverLifetime.js.map +1 -1
  111. package/dist/esm/modules/SpawnModule.d.ts +15 -12
  112. package/dist/esm/modules/SpawnModule.d.ts.map +1 -0
  113. package/dist/esm/modules/SpawnModule.js +32 -28
  114. package/dist/esm/modules/SpawnModule.js.map +1 -1
  115. package/dist/esm/modules/SpawnOnDeath.d.ts +13 -9
  116. package/dist/esm/modules/SpawnOnDeath.d.ts.map +1 -0
  117. package/dist/esm/modules/SpawnOnDeath.js +41 -43
  118. package/dist/esm/modules/SpawnOnDeath.js.map +1 -1
  119. package/dist/esm/modules/Turbulence.d.ts +4 -3
  120. package/dist/esm/modules/Turbulence.d.ts.map +1 -0
  121. package/dist/esm/modules/Turbulence.js +101 -89
  122. package/dist/esm/modules/Turbulence.js.map +1 -1
  123. package/dist/esm/modules/UpdateModule.d.ts +21 -13
  124. package/dist/esm/modules/UpdateModule.d.ts.map +1 -0
  125. package/dist/esm/modules/UpdateModule.js +65 -63
  126. package/dist/esm/modules/UpdateModule.js.map +1 -1
  127. package/dist/esm/modules/VelocityOverLifetime.d.ts +7 -6
  128. package/dist/esm/modules/VelocityOverLifetime.d.ts.map +1 -0
  129. package/dist/esm/modules/VelocityOverLifetime.js +75 -73
  130. package/dist/esm/modules/VelocityOverLifetime.js.map +1 -1
  131. package/dist/esm/modules/WgslContribution.d.ts +16 -15
  132. package/dist/esm/modules/WgslContribution.d.ts.map +1 -0
  133. package/dist/esm/modules/WgslContribution.js +37 -29
  134. package/dist/esm/modules/WgslContribution.js.map +1 -1
  135. package/dist/esm/modules/index.d.ts +3 -1
  136. package/dist/esm/modules/index.d.ts.map +1 -0
  137. package/dist/esm/modules/spawnFields.d.ts +32 -0
  138. package/dist/esm/modules/spawnFields.d.ts.map +1 -0
  139. package/dist/esm/modules/spawnFields.js +31 -0
  140. package/dist/esm/modules/spawnFields.js.map +1 -0
  141. package/dist/esm/particlesBuildInfo.d.ts +1 -0
  142. package/dist/esm/particlesBuildInfo.d.ts.map +1 -0
  143. package/dist/esm/particlesBuildInfo.js +6 -4
  144. package/dist/esm/particlesBuildInfo.js.map +1 -1
  145. package/dist/esm/particlesExtension.d.ts +13 -5
  146. package/dist/esm/particlesExtension.d.ts.map +1 -0
  147. package/dist/esm/particlesExtension.js +38 -42
  148. package/dist/esm/particlesExtension.js.map +1 -1
  149. package/dist/esm/public.d.ts +11 -0
  150. package/dist/esm/public.d.ts.map +1 -0
  151. package/dist/esm/renderModes/MeshParticles.d.ts +128 -0
  152. package/dist/esm/renderModes/MeshParticles.d.ts.map +1 -0
  153. package/dist/esm/renderModes/MeshParticles.js +263 -0
  154. package/dist/esm/renderModes/MeshParticles.js.map +1 -0
  155. package/dist/esm/renderModes/ParticleBufferLayout.d.ts +59 -0
  156. package/dist/esm/renderModes/ParticleBufferLayout.d.ts.map +1 -0
  157. package/dist/esm/renderModes/ParticleBufferLayout.js +99 -0
  158. package/dist/esm/renderModes/ParticleBufferLayout.js.map +1 -0
  159. package/dist/esm/renderModes/ParticleInstanceWriter.d.ts +58 -0
  160. package/dist/esm/renderModes/ParticleInstanceWriter.d.ts.map +1 -0
  161. package/dist/esm/renderModes/ParticleInstanceWriter.js +173 -0
  162. package/dist/esm/renderModes/ParticleInstanceWriter.js.map +1 -0
  163. package/dist/esm/renderModes/ParticleMaterial.d.ts +18 -0
  164. package/dist/esm/renderModes/ParticleMaterial.d.ts.map +1 -0
  165. package/dist/esm/renderModes/ParticleMaterial.js +24 -0
  166. package/dist/esm/renderModes/ParticleMaterial.js.map +1 -0
  167. package/dist/esm/renderModes/ParticleRenderMode.d.ts +114 -0
  168. package/dist/esm/renderModes/ParticleRenderMode.d.ts.map +1 -0
  169. package/dist/esm/renderModes/ParticleRenderMode.js +99 -0
  170. package/dist/esm/renderModes/ParticleRenderMode.js.map +1 -0
  171. package/dist/esm/renderModes/QuadParticles.d.ts +55 -0
  172. package/dist/esm/renderModes/QuadParticles.d.ts.map +1 -0
  173. package/dist/esm/renderModes/QuadParticles.js +95 -0
  174. package/dist/esm/renderModes/QuadParticles.js.map +1 -0
  175. package/dist/esm/renderModes/RibbonParticles.d.ts +96 -0
  176. package/dist/esm/renderModes/RibbonParticles.d.ts.map +1 -0
  177. package/dist/esm/renderModes/RibbonParticles.js +202 -0
  178. package/dist/esm/renderModes/RibbonParticles.js.map +1 -0
  179. package/dist/esm/renderModes/shaders/mesh-particles.wgsl.js +6 -0
  180. package/dist/esm/renderModes/shaders/mesh-particles.wgsl.js.map +1 -0
  181. package/dist/esm/renderModes/shaders/mesh.vert.js +6 -0
  182. package/dist/esm/renderModes/shaders/mesh.vert.js.map +1 -0
  183. package/dist/esm/renderModes/shaders/quad-particles.wgsl.js +6 -0
  184. package/dist/esm/renderModes/shaders/quad-particles.wgsl.js.map +1 -0
  185. package/dist/esm/renderModes/shaders/ribbon-particles.wgsl.js +6 -0
  186. package/dist/esm/renderModes/shaders/ribbon-particles.wgsl.js.map +1 -0
  187. package/dist/esm/renderModes/shaders/ribbon.frag.js +6 -0
  188. package/dist/esm/renderModes/shaders/ribbon.frag.js.map +1 -0
  189. package/dist/esm/renderModes/shaders/ribbon.vert.js +6 -0
  190. package/dist/esm/renderModes/shaders/ribbon.vert.js.map +1 -0
  191. package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts +48 -20
  192. package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts.map +1 -0
  193. package/dist/esm/renderers/WebGl2ParticleRenderer.js +317 -321
  194. package/dist/esm/renderers/WebGl2ParticleRenderer.js.map +1 -1
  195. package/dist/esm/renderers/WebGpuParticleRenderer.d.ts +99 -18
  196. package/dist/esm/renderers/WebGpuParticleRenderer.d.ts.map +1 -0
  197. package/dist/esm/renderers/WebGpuParticleRenderer.js +542 -551
  198. package/dist/esm/renderers/WebGpuParticleRenderer.js.map +1 -1
  199. package/dist/esm/renderers/shaders/particle.frag.js +6 -0
  200. package/dist/esm/renderers/shaders/particle.frag.js.map +1 -0
  201. package/dist/esm/renderers/shaders/particle.vert.js +6 -0
  202. package/dist/esm/renderers/shaders/particle.vert.js.map +1 -0
  203. package/package.json +7 -14
  204. package/dist/esm/index.js.map +0 -1
  205. package/dist/esm/register.d.ts +0 -1
  206. package/dist/esm/register.js +0 -43
  207. package/dist/esm/register.js.map +0 -1
  208. package/dist/esm/renderers/glsl/particle.frag.js +0 -4
  209. package/dist/esm/renderers/glsl/particle.frag.js.map +0 -1
  210. package/dist/esm/renderers/glsl/particle.vert.js +0 -4
  211. package/dist/esm/renderers/glsl/particle.vert.js.map +0 -1
@@ -1,49 +1,45 @@
1
- import { RenderBackendType } from '@codexo/exojs/renderer-sdk';
2
- import { WebGl2ParticleRenderer } from './renderers/WebGl2ParticleRenderer.js';
3
- import { WebGpuParticleRenderer } from './renderers/WebGpuParticleRenderer.js';
4
- import { ParticleSystem } from './ParticleSystem.js';
1
+ import { WebGl2ParticleRenderer } from "./renderers/WebGl2ParticleRenderer.js";
2
+ import { WebGpuParticleRenderer } from "./renderers/WebGpuParticleRenderer.js";
3
+ import { ParticleSystem } from "./ParticleSystem.js";
4
+ import { RenderBackendType } from "@codexo/exojs/renderer-sdk";
5
5
 
6
- function buildParticlesRendererBinding(batchSize) {
7
- return {
8
- targets: [ParticleSystem],
9
- create(backend) {
10
- if (backend.backendType === RenderBackendType.WebGl2) {
11
- return new WebGl2ParticleRenderer(batchSize);
12
- }
13
- if (backend.backendType === RenderBackendType.WebGpu) {
14
- return new WebGpuParticleRenderer();
15
- }
16
- throw new Error(`Unsupported render backend: ${String(backend.backendType)}`);
17
- },
18
- };
19
- }
6
+ //#region src/particlesExtension.ts
7
+ const buildParticlesRendererBinding = (batchSize) => ({
8
+ targets: [ParticleSystem],
9
+ create(backend) {
10
+ if (backend.backendType === RenderBackendType.WebGl2) return new WebGl2ParticleRenderer(batchSize);
11
+ if (backend.backendType === RenderBackendType.WebGpu) return new WebGpuParticleRenderer();
12
+ throw new Error(`Unsupported render backend: ${String(backend.backendType)}`);
13
+ }
14
+ });
20
15
  /**
21
- * Default immutable Particles extension descriptor.
22
- * Use with `ApplicationOptions.extensions` or call
23
- * `import '@codexo/exojs-particles/register'` for global auto-registration.
24
- */
16
+ * Default immutable Particles extension descriptor.
17
+ * Pass it to the application that should have it via
18
+ * `ApplicationOptions.extensions`.
19
+ */
25
20
  const particlesExtension = Object.freeze({
26
- id: '@codexo/exojs-particles',
27
- renderers: [buildParticlesRendererBinding(8192)],
21
+ id: "@codexo/exojs-particles",
22
+ renderers: [buildParticlesRendererBinding(8192)]
28
23
  });
29
24
  /**
30
- * Create a Particles extension with custom configuration.
31
- * Returns an application-local descriptor safe to pass to one
32
- * `Application` only. For shared use, prefer {@link particlesExtension}.
33
- *
34
- * @example
35
- * ```ts
36
- * const ext = createParticlesExtension({ batchSize: 4096 });
37
- * const app = new Application({ extensions: [ext] });
38
- * ```
39
- */
40
- function createParticlesExtension(options = {}) {
41
- const batchSize = options.batchSize ?? 8192;
42
- return {
43
- id: '@codexo/exojs-particles',
44
- renderers: [buildParticlesRendererBinding(batchSize)],
45
- };
46
- }
25
+ * Create a Particles extension with custom configuration.
26
+ * Returns an application-local descriptor - safe to pass to one
27
+ * `Application` only. For shared use, prefer {@link particlesExtension}.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * const ext = createParticlesExtension({ batchSize: 4096 });
32
+ * const app = new Application({ extensions: [ext] });
33
+ * ```
34
+ */
35
+ const createParticlesExtension = (options = {}) => {
36
+ const batchSize = options.batchSize ?? 8192;
37
+ return {
38
+ id: "@codexo/exojs-particles",
39
+ renderers: [buildParticlesRendererBinding(batchSize)]
40
+ };
41
+ };
47
42
 
43
+ //#endregion
48
44
  export { createParticlesExtension, particlesExtension };
49
- //# sourceMappingURL=particlesExtension.js.map
45
+ //# sourceMappingURL=particlesExtension.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"particlesExtension.js","sources":["../../../src/particlesExtension.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;AAeA,SAAS,6BAA6B,CAAC,SAAiB,EAAA;IACtD,OAAO;QACL,OAAO,EAAE,CAAC,cAAc,CAAC;AACzB,QAAA,MAAM,CAAC,OAAsB,EAAA;YAC3B,IAAI,OAAO,CAAC,WAAW,KAAK,iBAAiB,CAAC,MAAM,EAAE;AACpD,gBAAA,OAAO,IAAI,sBAAsB,CAAC,SAAS,CAAC;YAC9C;YAEA,IAAI,OAAO,CAAC,WAAW,KAAK,iBAAiB,CAAC,MAAM,EAAE;gBACpD,OAAO,IAAI,sBAAsB,EAAE;YACrC;AAEA,YAAA,MAAM,IAAI,KAAK,CAAC,CAAA,4BAAA,EAA+B,MAAM,CAAC,OAAO,CAAC,WAA2B,CAAC,CAAA,CAAE,CAAC;QAC/F,CAAC;KACF;AACH;AAEA;;;;AAIG;AACI,MAAM,kBAAkB,GAAc,MAAM,CAAC,MAAM,CAAC;AACzD,IAAA,EAAE,EAAE,yBAAyB;AAC7B,IAAA,SAAS,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,CAAC;AACjD,CAAA;AAED;;;;;;;;;;AAUG;AACG,SAAU,wBAAwB,CAAC,OAAA,GAAqC,EAAE,EAAA;AAC9E,IAAA,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI;IAE3C,OAAO;AACL,QAAA,EAAE,EAAE,yBAAyB;AAC7B,QAAA,SAAS,EAAE,CAAC,6BAA6B,CAAC,SAAS,CAAC,CAAC;KACtD;AACH;;;;"}
1
+ {"version":3,"file":"particlesExtension.js","names":[],"sources":["../../src/particlesExtension.ts"],"sourcesContent":["import type { Extension, RendererBinding } from '@codexo/exojs/extensions';\nimport type { RenderBackend } from '@codexo/exojs/renderer-sdk';\nimport { RenderBackendType } from '@codexo/exojs/renderer-sdk';\n\nimport { WebGl2ParticleRenderer } from '#renderers/WebGl2ParticleRenderer';\nimport { WebGpuParticleRenderer } from '#renderers/WebGpuParticleRenderer';\n\nimport { ParticleSystem } from './ParticleSystem';\n\n/** Options for {@link createParticlesExtension}. */\nexport interface ParticlesExtensionOptions {\n /**\n * Particles the WebGL2 renderer's instance buffer is pre-sized for.\n * Defaults to 8192.\n *\n * This is a starting allocation, not a limit: a system with more live\n * particles grows the buffer instead of dropping the surplus, so raise it\n * only to avoid the initial growth steps for a known-large system.\n */\n readonly batchSize?: number;\n}\n\nconst buildParticlesRendererBinding = (batchSize: number): RendererBinding => ({\n targets: [ParticleSystem],\n create(backend: RenderBackend) {\n if (backend.backendType === RenderBackendType.WebGl2) {\n return new WebGl2ParticleRenderer(batchSize);\n }\n\n if (backend.backendType === RenderBackendType.WebGpu) {\n return new WebGpuParticleRenderer();\n }\n\n throw new Error(`Unsupported render backend: ${String(backend.backendType satisfies never)}`);\n },\n});\n\n/**\n * Default immutable Particles extension descriptor.\n * Pass it to the application that should have it via\n * `ApplicationOptions.extensions`.\n */\nexport const particlesExtension: Extension = Object.freeze({\n id: '@codexo/exojs-particles',\n renderers: [buildParticlesRendererBinding(8192)],\n});\n\n/**\n * Create a Particles extension with custom configuration.\n * Returns an application-local descriptor - safe to pass to one\n * `Application` only. For shared use, prefer {@link particlesExtension}.\n *\n * @example\n * ```ts\n * const ext = createParticlesExtension({ batchSize: 4096 });\n * const app = new Application({ extensions: [ext] });\n * ```\n */\nexport const createParticlesExtension = (options: ParticlesExtensionOptions = {}): Extension => {\n const batchSize = options.batchSize ?? 8192;\n\n return {\n id: '@codexo/exojs-particles',\n renderers: [buildParticlesRendererBinding(batchSize)],\n };\n};\n"],"mappings":";;;;;;AAsBA,MAAM,iCAAiC,eAAwC;CAC7E,SAAS,CAAC,cAAc;CACxB,OAAO,SAAwB;EAC7B,IAAI,QAAQ,gBAAgB,kBAAkB,QAC5C,OAAO,IAAI,uBAAuB,SAAS;EAG7C,IAAI,QAAQ,gBAAgB,kBAAkB,QAC5C,OAAO,IAAI,uBAAuB;EAGpC,MAAM,IAAI,MAAM,+BAA+B,OAAO,QAAQ,WAA2B,GAAG;CAC9F;AACF;;;;;;AAOA,MAAa,qBAAgC,OAAO,OAAO;CACzD,IAAI;CACJ,WAAW,CAAC,8BAA8B,IAAI,CAAC;AACjD,CAAC;;;;;;;;;;;;AAaD,MAAa,4BAA4B,UAAqC,CAAC,MAAiB;CAC9F,MAAM,YAAY,QAAQ,aAAa;CAEvC,OAAO;EACL,IAAI;EACJ,WAAW,CAAC,8BAA8B,SAAS,CAAC;CACtD;AACF"}
@@ -4,6 +4,17 @@ export type { ParticlesBuildInfo } from './particlesBuildInfo';
4
4
  export { particlesBuildInfo } from './particlesBuildInfo';
5
5
  export type { ParticlesExtensionOptions } from './particlesExtension';
6
6
  export { createParticlesExtension, particlesExtension } from './particlesExtension';
7
+ export type { ParticleBatch, ParticleDeathContext, ParticleEmitter, ParticleRotationChannel, ParticleTimingChannel, ParticleVectorChannel, ParticleVectorWriter, ParticleWriter, } from './ParticleStorage';
7
8
  export type { ParticleSystemOptions } from './ParticleSystem';
8
9
  export { ParticleSystem } from './ParticleSystem';
10
+ export type { MeshParticlesOptions } from './renderModes/MeshParticles';
11
+ export { MeshParticles } from './renderModes/MeshParticles';
12
+ export type { ParticleBufferLayoutOptions } from './renderModes/ParticleBufferLayout';
13
+ export { ParticleBufferLayout } from './renderModes/ParticleBufferLayout';
14
+ export { ParticleMaterial } from './renderModes/ParticleMaterial';
15
+ export { ParticleRenderMode } from './renderModes/ParticleRenderMode';
16
+ export { QuadParticles } from './renderModes/QuadParticles';
17
+ export type { RibbonParticlesOptions } from './renderModes/RibbonParticles';
18
+ export { RibbonParticles } from './renderModes/RibbonParticles';
9
19
  export type { Extension, RendererBinding } from '@codexo/exojs/extensions';
20
+ //# sourceMappingURL=public.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/public.ts"],"names":[],"mappings":"AAGA,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACpF,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,YAAY,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,YAAY,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,128 @@
1
+ import type { Material } from '@codexo/exojs';
2
+ import { Geometry } from '@codexo/exojs';
3
+ import type { ParticleBatch } from '#ParticleStorage';
4
+ import type { ParticleSystem } from '#ParticleSystem';
5
+ import { ParticleBufferLayout } from './ParticleBufferLayout';
6
+ import { ParticleRenderMode } from './ParticleRenderMode';
7
+ /** Construction options for {@link MeshParticles}. */
8
+ export interface MeshParticlesOptions {
9
+ /**
10
+ * The shape one particle draws, in system-local units before the
11
+ * per-particle scale and rotation are applied.
12
+ *
13
+ * Its position attribute (`a_position`/`position`) supplies the vertex
14
+ * positions and its texcoord attribute (`a_texcoord`/`texcoord`/`a_uv`/`uv`)
15
+ * the UVs. See {@link MeshParticles} for what a geometry without the latter
16
+ * renders as, and for how to make a mutation take effect.
17
+ */
18
+ readonly geometry: Geometry;
19
+ }
20
+ /**
21
+ * WGSL counterpart of `shaders/mesh.vert` plus the quad's fragment stage. Vertex
22
+ * and fragment entry points share one source per WGSL convention. The
23
+ * per-instance attributes bind at `@location(0..5)`, matching the declaration
24
+ * order and byte offsets of {@link instanceAttributes}, and the mesh's own
25
+ * vertices follow at `@location(6..7)` from the second vertex buffer.
26
+ *
27
+ * The uniform struct is the one the WebGPU particle renderer writes for every
28
+ * mode, so `localBounds` and `uvBounds` are declared but unused here: a mesh
29
+ * carries its own local footprint rather than taking it from the texture frame.
30
+ */
31
+ export declare const meshParticleWgsl: string;
32
+ /**
33
+ * One caller-supplied mesh per particle, drawn as a single instanced draw.
34
+ *
35
+ * The cheap counterpart to `RibbonParticles`: the relationship stays one
36
+ * particle, one instance, and the per-instance data is byte-identical to
37
+ * `QuadParticles`'s - {@link instanceAttributes}, filled by the same
38
+ * writer. Only the shape changes. That is what makes this mode **GPU-eligible**
39
+ * without touching the compute pipeline: the pipeline emits precisely that
40
+ * layout, so a system on the GPU path binds its instance buffer directly and
41
+ * never calls {@link build}.
42
+ *
43
+ * **The mesh rides in its own vertex buffer**, alongside the per-instance one.
44
+ * The shader is fixed and shared, so any number of `MeshParticles` compile one
45
+ * program per backend regardless of how many distinct shapes they draw, and a
46
+ * mesh is limited only by what a vertex buffer holds.
47
+ *
48
+ * **Mutating the mesh works.** Its vertices are read into a normalised
49
+ * `(x, y, u, v)` table - the shader binds fixed names, a caller's geometry may
50
+ * use any - and re-read whenever the geometry's `version` changes. Edit the
51
+ * geometry in place, call `invalidate()` on it, and the next draw picks it up.
52
+ * Changing the vertex *count* is equally fine.
53
+ *
54
+ * **Atlas frames still work.** The mesh's own UVs are treated exactly like the
55
+ * quad's 0..1 corners: the shader mixes them across the particle's resolved
56
+ * frame (`uv = mix(uvMin, uvMax, meshUV)`), so `system.frames` plus a
57
+ * per-particle `textureIndex` selects a frame the same way. A geometry with
58
+ * **no texcoord attribute** therefore carries UV `(0, 0)` on every vertex and
59
+ * the mix degenerates to `uvMin` - the whole mesh samples the single texel at
60
+ * the frame's top-left corner, which on the default 1×1 white texture is
61
+ * exactly "untextured, tinted by the particle's `color`".
62
+ *
63
+ * **Sizing is the mesh's own.** Unlike the quad, which derives its footprint
64
+ * from the texture frame, a mesh particle's footprint is whatever its vertex
65
+ * positions say, in system-local units, multiplied by the per-particle
66
+ * `scaleX`/`scaleY`. The texture only supplies colour.
67
+ *
68
+ * @example
69
+ * const shard = new Geometry({
70
+ * attributes: [
71
+ * { name: 'a_position', size: 2, type: 'f32', normalized: false, offset: 0 },
72
+ * { name: 'a_uv', size: 2, type: 'f32', normalized: false, offset: 8 },
73
+ * ],
74
+ * vertexData: new Float32Array([0, -8, 0.5, 0, 6, 6, 1, 1, -6, 6, 0, 1]),
75
+ * stride: 16,
76
+ * });
77
+ *
78
+ * const debris = new ParticleSystem(rockTexture, {
79
+ * capacity: 512,
80
+ * render: new MeshParticles({ geometry: shard }),
81
+ * });
82
+ *
83
+ * debris.addUpdateModule(new RotateOverLifetime(180)); // each shard tumbles
84
+ */
85
+ export declare class MeshParticles extends ParticleRenderMode {
86
+ readonly gpuEligible = true;
87
+ readonly instanced = true;
88
+ /**
89
+ * The geometry this mode was constructed with - the shape one particle
90
+ * draws. Owned by the caller: {@link destroy} leaves it alone, so one mesh
91
+ * can back several modes.
92
+ */
93
+ readonly mesh: Geometry;
94
+ /**
95
+ * The normalised mesh the executors bind as the per-vertex buffer: the
96
+ * caller's {@link mesh} reduced to `(x, y, u, v)` per vertex, keeping its
97
+ * topology and index list. Owned by this mode and disposed with it.
98
+ */
99
+ readonly vertexGeometry: Geometry;
100
+ /** Per-instance layout, byte-identical to the quad's and to the compute output. */
101
+ readonly dataLayout: ParticleBufferLayout;
102
+ private readonly _writer;
103
+ private _meshTable;
104
+ private _meshVersion;
105
+ private _material;
106
+ private _float32;
107
+ private _uint32;
108
+ constructor(options: MeshParticlesOptions);
109
+ /**
110
+ * Built on first read rather than in the constructor: a system may be
111
+ * simulated without ever being drawn, and the shader pair is only needed
112
+ * once a backend actually compiles it.
113
+ */
114
+ get material(): Material;
115
+ build(system: ParticleSystem, particles: ParticleBatch): void;
116
+ destroy(): void;
117
+ protected _onBufferGrown(data: ArrayBuffer): void;
118
+ /**
119
+ * Re-read the caller's mesh when it has been mutated since the last look.
120
+ *
121
+ * Re-runs from {@link build} rather than from a draw so it also happens for a
122
+ * system whose backend has not compiled a pipeline yet. A vertex count change
123
+ * swaps the table, which is why the geometry's own `vertexData` is reassigned
124
+ * rather than written through - the executors notice via its `version`.
125
+ */
126
+ private _syncMesh;
127
+ }
128
+ //# sourceMappingURL=MeshParticles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MeshParticles.d.ts","sourceRoot":"","sources":["../../../src/renderModes/MeshParticles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoC,QAAQ,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAgB,MAAM,eAAe,CAAC;AAEvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,EAAkC,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AA6B1D,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AA2ED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAA+B,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,qBAAa,aAAc,SAAQ,kBAAkB;IACnD,SAAyB,WAAW,QAAQ;IAC5C,SAAgB,SAAS,QAAQ;IAEjC;;;;OAIG;IACH,SAAgB,IAAI,EAAE,QAAQ,CAAC;IAE/B;;;;OAIG;IACH,SAAyB,cAAc,EAAE,QAAQ,CAAC;IAElD,mFAAmF;IACnF,SAAgB,UAAU,uBAIvB;IAEH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IAExD,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAA8B;gBAE1B,OAAO,EAAE,oBAAoB;IAoBhD;;;;OAIG;IACH,IAAW,QAAQ,IAAI,QAAQ,CAS9B;IAEM,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI;IAUpD,OAAO,IAAI,IAAI;cAMZ,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAK1D;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;CAgBlB"}
@@ -0,0 +1,263 @@
1
+ import { ParticleBufferLayout, assertVertexGeometryCompatible } from "./ParticleBufferLayout.js";
2
+ import particle_default from "../renderers/shaders/particle.frag.js";
3
+ import { ParticleInstanceWriter, instanceAttributes } from "./ParticleInstanceWriter.js";
4
+ import { ParticleMaterial } from "./ParticleMaterial.js";
5
+ import { ParticleRenderMode } from "./ParticleRenderMode.js";
6
+ import mesh_default from "./shaders/mesh.vert.js";
7
+ import mesh_particles_default from "./shaders/mesh-particles.wgsl.js";
8
+ import { Geometry, ShaderSource } from "@codexo/exojs";
9
+
10
+ //#region src/renderModes/MeshParticles.ts
11
+ /** Floats one entry of the normalised mesh vertex table occupies: x, y, u, v. */
12
+ const floatsPerMeshVertex = 4;
13
+ const meshVertexStrideBytes = floatsPerMeshVertex * Float32Array.BYTES_PER_ELEMENT;
14
+ /**
15
+ * Layout the normalised mesh table is read back with. Named apart from the
16
+ * per-instance attributes because both sets bind into the same shader.
17
+ */
18
+ const meshVertexAttributes = [{
19
+ name: "a_meshPosition",
20
+ size: 2,
21
+ type: "f32",
22
+ normalized: false,
23
+ offset: 0
24
+ }, {
25
+ name: "a_meshTexcoord",
26
+ size: 2,
27
+ type: "f32",
28
+ normalized: false,
29
+ offset: 8
30
+ }];
31
+ const componentByteSizes = {
32
+ f32: 4,
33
+ u8: 1,
34
+ u16: 2,
35
+ u32: 4,
36
+ i32: 4
37
+ };
38
+ const positionAttributeNames = /* @__PURE__ */ new Set(["a_position", "position"]);
39
+ const texcoordAttributeNames = /* @__PURE__ */ new Set([
40
+ "a_texcoord",
41
+ "texcoord",
42
+ "a_uv",
43
+ "uv"
44
+ ]);
45
+ /** Read one component of `attribute`, applying its normalisation. */
46
+ const readComponent = (view, attribute, byteOffset) => {
47
+ switch (attribute.type) {
48
+ case "f32": return view.getFloat32(byteOffset, true);
49
+ case "u8": {
50
+ const value = view.getUint8(byteOffset);
51
+ return attribute.normalized ? value / 255 : value;
52
+ }
53
+ case "u16": {
54
+ const value = view.getUint16(byteOffset, true);
55
+ return attribute.normalized ? value / 65535 : value;
56
+ }
57
+ case "u32": {
58
+ const value = view.getUint32(byteOffset, true);
59
+ return attribute.normalized ? value / 4294967295 : value;
60
+ }
61
+ case "i32": {
62
+ const value = view.getInt32(byteOffset, true);
63
+ return attribute.normalized ? Math.max(value / 2147483647, -1) : value;
64
+ }
65
+ }
66
+ };
67
+ /**
68
+ * Read a mesh geometry's (x, y, u, v) per vertex into one flat table.
69
+ *
70
+ * The shader this mode ships is fixed, so it binds fixed attribute names and a
71
+ * fixed stride, while a caller's mesh may declare any names in any supported
72
+ * types. Normalising here is what lets one compiled program serve every mesh.
73
+ *
74
+ * `Geometry` guarantees a position attribute exists - it resolves one in its
75
+ * own constructor and throws otherwise - so the same resolution order is
76
+ * mirrored here rather than re-validated. A geometry without a texcoord
77
+ * attribute leaves the UV columns at zero.
78
+ *
79
+ * @param out Table to fill; a new one is allocated when it is the wrong size,
80
+ * so a re-read after an in-place mutation reuses the existing allocation.
81
+ */
82
+ const readMeshTable = (mesh, out = null) => {
83
+ const { attributes, stride, vertexData } = mesh;
84
+ const vertexCount = mesh.vertexCount;
85
+ const table = out !== null && out.length === vertexCount * floatsPerMeshVertex ? out : new Float32Array(vertexCount * floatsPerMeshVertex);
86
+ const view = vertexData instanceof Float32Array ? new DataView(vertexData.buffer, vertexData.byteOffset, vertexData.byteLength) : new DataView(vertexData);
87
+ const position = attributes.find((attribute) => positionAttributeNames.has(attribute.name)) ?? attributes.find((attribute) => attribute.name.toLowerCase().includes("position"));
88
+ const texcoord = attributes.find((attribute) => texcoordAttributeNames.has(attribute.name)) ?? null;
89
+ for (let i = 0; i < vertexCount; i++) {
90
+ const vertexStart = i * stride;
91
+ const target = i * floatsPerMeshVertex;
92
+ table[target + 0] = readComponent(view, position, vertexStart + position.offset);
93
+ table[target + 1] = readComponent(view, position, vertexStart + position.offset + componentByteSizes[position.type]);
94
+ if (texcoord !== null) {
95
+ table[target + 2] = readComponent(view, texcoord, vertexStart + texcoord.offset);
96
+ table[target + 3] = readComponent(view, texcoord, vertexStart + texcoord.offset + componentByteSizes[texcoord.type]);
97
+ } else {
98
+ table[target + 2] = 0;
99
+ table[target + 3] = 0;
100
+ }
101
+ }
102
+ return table;
103
+ };
104
+ /**
105
+ * WGSL counterpart of `shaders/mesh.vert` plus the quad's fragment stage. Vertex
106
+ * and fragment entry points share one source per WGSL convention. The
107
+ * per-instance attributes bind at `@location(0..5)`, matching the declaration
108
+ * order and byte offsets of {@link instanceAttributes}, and the mesh's own
109
+ * vertices follow at `@location(6..7)` from the second vertex buffer.
110
+ *
111
+ * The uniform struct is the one the WebGPU particle renderer writes for every
112
+ * mode, so `localBounds` and `uvBounds` are declared but unused here: a mesh
113
+ * carries its own local footprint rather than taking it from the texture frame.
114
+ */
115
+ const meshParticleWgsl = mesh_particles_default;
116
+ /**
117
+ * One caller-supplied mesh per particle, drawn as a single instanced draw.
118
+ *
119
+ * The cheap counterpart to `RibbonParticles`: the relationship stays one
120
+ * particle, one instance, and the per-instance data is byte-identical to
121
+ * `QuadParticles`'s - {@link instanceAttributes}, filled by the same
122
+ * writer. Only the shape changes. That is what makes this mode **GPU-eligible**
123
+ * without touching the compute pipeline: the pipeline emits precisely that
124
+ * layout, so a system on the GPU path binds its instance buffer directly and
125
+ * never calls {@link build}.
126
+ *
127
+ * **The mesh rides in its own vertex buffer**, alongside the per-instance one.
128
+ * The shader is fixed and shared, so any number of `MeshParticles` compile one
129
+ * program per backend regardless of how many distinct shapes they draw, and a
130
+ * mesh is limited only by what a vertex buffer holds.
131
+ *
132
+ * **Mutating the mesh works.** Its vertices are read into a normalised
133
+ * `(x, y, u, v)` table - the shader binds fixed names, a caller's geometry may
134
+ * use any - and re-read whenever the geometry's `version` changes. Edit the
135
+ * geometry in place, call `invalidate()` on it, and the next draw picks it up.
136
+ * Changing the vertex *count* is equally fine.
137
+ *
138
+ * **Atlas frames still work.** The mesh's own UVs are treated exactly like the
139
+ * quad's 0..1 corners: the shader mixes them across the particle's resolved
140
+ * frame (`uv = mix(uvMin, uvMax, meshUV)`), so `system.frames` plus a
141
+ * per-particle `textureIndex` selects a frame the same way. A geometry with
142
+ * **no texcoord attribute** therefore carries UV `(0, 0)` on every vertex and
143
+ * the mix degenerates to `uvMin` - the whole mesh samples the single texel at
144
+ * the frame's top-left corner, which on the default 1×1 white texture is
145
+ * exactly "untextured, tinted by the particle's `color`".
146
+ *
147
+ * **Sizing is the mesh's own.** Unlike the quad, which derives its footprint
148
+ * from the texture frame, a mesh particle's footprint is whatever its vertex
149
+ * positions say, in system-local units, multiplied by the per-particle
150
+ * `scaleX`/`scaleY`. The texture only supplies colour.
151
+ *
152
+ * @example
153
+ * const shard = new Geometry({
154
+ * attributes: [
155
+ * { name: 'a_position', size: 2, type: 'f32', normalized: false, offset: 0 },
156
+ * { name: 'a_uv', size: 2, type: 'f32', normalized: false, offset: 8 },
157
+ * ],
158
+ * vertexData: new Float32Array([0, -8, 0.5, 0, 6, 6, 1, 1, -6, 6, 0, 1]),
159
+ * stride: 16,
160
+ * });
161
+ *
162
+ * const debris = new ParticleSystem(rockTexture, {
163
+ * capacity: 512,
164
+ * render: new MeshParticles({ geometry: shard }),
165
+ * });
166
+ *
167
+ * debris.addUpdateModule(new RotateOverLifetime(180)); // each shard tumbles
168
+ */
169
+ var MeshParticles = class extends ParticleRenderMode {
170
+ gpuEligible = true;
171
+ instanced = true;
172
+ /**
173
+ * The geometry this mode was constructed with - the shape one particle
174
+ * draws. Owned by the caller: {@link destroy} leaves it alone, so one mesh
175
+ * can back several modes.
176
+ */
177
+ mesh;
178
+ /**
179
+ * The normalised mesh the executors bind as the per-vertex buffer: the
180
+ * caller's {@link mesh} reduced to `(x, y, u, v)` per vertex, keeping its
181
+ * topology and index list. Owned by this mode and disposed with it.
182
+ */
183
+ vertexGeometry;
184
+ /** Per-instance layout, byte-identical to the quad's and to the compute output. */
185
+ dataLayout = new ParticleBufferLayout({
186
+ attributes: instanceAttributes,
187
+ stride: 40,
188
+ usage: "stream"
189
+ });
190
+ _writer = new ParticleInstanceWriter();
191
+ _meshTable;
192
+ _meshVersion;
193
+ _material = null;
194
+ _float32 = new Float32Array(this.data);
195
+ _uint32 = new Uint32Array(this.data);
196
+ constructor(options) {
197
+ super();
198
+ const mesh = options.geometry;
199
+ this.mesh = mesh;
200
+ this._meshTable = readMeshTable(mesh);
201
+ this._meshVersion = mesh.version;
202
+ this.vertexGeometry = new Geometry({
203
+ attributes: meshVertexAttributes,
204
+ vertexData: this._meshTable,
205
+ stride: meshVertexStrideBytes,
206
+ indices: mesh.indices,
207
+ topology: mesh.topology,
208
+ usage: mesh.usage
209
+ });
210
+ assertVertexGeometryCompatible(this.dataLayout, this.vertexGeometry, this.instanced, "MeshParticles");
211
+ }
212
+ /**
213
+ * Built on first read rather than in the constructor: a system may be
214
+ * simulated without ever being drawn, and the shader pair is only needed
215
+ * once a backend actually compiles it.
216
+ */
217
+ get material() {
218
+ this._material ??= new ParticleMaterial({ shader: new ShaderSource({
219
+ glsl: {
220
+ vertex: mesh_default,
221
+ fragment: particle_default
222
+ },
223
+ wgsl: meshParticleWgsl
224
+ }) });
225
+ return this._material;
226
+ }
227
+ build(system, particles) {
228
+ this._syncMesh();
229
+ this._ensureCapacity(particles.count * 40);
230
+ this._setCount(this._writer.write(system, particles, this._float32, this._uint32));
231
+ }
232
+ destroy() {
233
+ this._material?.destroy();
234
+ this._material = null;
235
+ this.vertexGeometry.destroy();
236
+ }
237
+ _onBufferGrown(data) {
238
+ this._float32 = new Float32Array(data);
239
+ this._uint32 = new Uint32Array(data);
240
+ }
241
+ /**
242
+ * Re-read the caller's mesh when it has been mutated since the last look.
243
+ *
244
+ * Re-runs from {@link build} rather than from a draw so it also happens for a
245
+ * system whose backend has not compiled a pipeline yet. A vertex count change
246
+ * swaps the table, which is why the geometry's own `vertexData` is reassigned
247
+ * rather than written through - the executors notice via its `version`.
248
+ */
249
+ _syncMesh() {
250
+ if (this._meshVersion === this.mesh.version) return;
251
+ this._meshVersion = this.mesh.version;
252
+ const table = readMeshTable(this.mesh, this._meshTable);
253
+ if (table !== this._meshTable) {
254
+ this._meshTable = table;
255
+ this.vertexGeometry.vertexData = table;
256
+ }
257
+ this.vertexGeometry.invalidate();
258
+ }
259
+ };
260
+
261
+ //#endregion
262
+ export { MeshParticles, meshParticleWgsl };
263
+ //# sourceMappingURL=MeshParticles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MeshParticles.js","names":["meshParticleWgslModule","vertexSource","fragmentSource"],"sources":["../../../src/renderModes/MeshParticles.ts"],"sourcesContent":["import type { AttributeType, GeometryAttribute, Material } from '@codexo/exojs';\nimport { Geometry, ShaderSource } from '@codexo/exojs';\n\nimport type { ParticleBatch } from '#ParticleStorage';\nimport type { ParticleSystem } from '#ParticleSystem';\n\nimport fragmentSource from '../renderers/shaders/particle.frag';\nimport { assertVertexGeometryCompatible, ParticleBufferLayout } from './ParticleBufferLayout';\nimport { instanceAttributes, instanceStrideBytes, ParticleInstanceWriter } from './ParticleInstanceWriter';\nimport { ParticleMaterial } from './ParticleMaterial';\nimport { ParticleRenderMode } from './ParticleRenderMode';\nimport vertexSource from './shaders/mesh.vert';\nimport meshParticleWgslModule from './shaders/mesh-particles.wgsl';\n\n/** Floats one entry of the normalised mesh vertex table occupies: x, y, u, v. */\nconst floatsPerMeshVertex = 4;\n\nconst meshVertexStrideBytes = floatsPerMeshVertex * Float32Array.BYTES_PER_ELEMENT;\n\n/**\n * Layout the normalised mesh table is read back with. Named apart from the\n * per-instance attributes because both sets bind into the same shader.\n */\nconst meshVertexAttributes: readonly GeometryAttribute[] = [\n { name: 'a_meshPosition', size: 2, type: 'f32', normalized: false, offset: 0 },\n { name: 'a_meshTexcoord', size: 2, type: 'f32', normalized: false, offset: 8 },\n];\n\nconst componentByteSizes: Record<AttributeType, number> = {\n f32: 4,\n u8: 1,\n u16: 2,\n u32: 4,\n i32: 4,\n};\n\nconst positionAttributeNames = new Set<string>(['a_position', 'position']);\nconst texcoordAttributeNames = new Set<string>(['a_texcoord', 'texcoord', 'a_uv', 'uv']);\n\n/** Construction options for {@link MeshParticles}. */\nexport interface MeshParticlesOptions {\n /**\n * The shape one particle draws, in system-local units before the\n * per-particle scale and rotation are applied.\n *\n * Its position attribute (`a_position`/`position`) supplies the vertex\n * positions and its texcoord attribute (`a_texcoord`/`texcoord`/`a_uv`/`uv`)\n * the UVs. See {@link MeshParticles} for what a geometry without the latter\n * renders as, and for how to make a mutation take effect.\n */\n readonly geometry: Geometry;\n}\n\n/** Read one component of `attribute`, applying its normalisation. */\nconst readComponent = (view: DataView, attribute: GeometryAttribute, byteOffset: number): number => {\n switch (attribute.type) {\n case 'f32':\n return view.getFloat32(byteOffset, true);\n case 'u8': {\n const value = view.getUint8(byteOffset);\n\n return attribute.normalized ? value / 255 : value;\n }\n case 'u16': {\n const value = view.getUint16(byteOffset, true);\n\n return attribute.normalized ? value / 65535 : value;\n }\n case 'u32': {\n const value = view.getUint32(byteOffset, true);\n\n return attribute.normalized ? value / 4294967295 : value;\n }\n case 'i32': {\n const value = view.getInt32(byteOffset, true);\n\n return attribute.normalized ? Math.max(value / 2147483647, -1) : value;\n }\n }\n};\n\n/**\n * Read a mesh geometry's (x, y, u, v) per vertex into one flat table.\n *\n * The shader this mode ships is fixed, so it binds fixed attribute names and a\n * fixed stride, while a caller's mesh may declare any names in any supported\n * types. Normalising here is what lets one compiled program serve every mesh.\n *\n * `Geometry` guarantees a position attribute exists - it resolves one in its\n * own constructor and throws otherwise - so the same resolution order is\n * mirrored here rather than re-validated. A geometry without a texcoord\n * attribute leaves the UV columns at zero.\n *\n * @param out Table to fill; a new one is allocated when it is the wrong size,\n * so a re-read after an in-place mutation reuses the existing allocation.\n */\nconst readMeshTable = (mesh: Geometry, out: Float32Array | null = null): Float32Array => {\n const { attributes, stride, vertexData } = mesh;\n const vertexCount = mesh.vertexCount;\n const table = out !== null && out.length === vertexCount * floatsPerMeshVertex ? out : new Float32Array(vertexCount * floatsPerMeshVertex);\n const view = vertexData instanceof Float32Array ? new DataView(vertexData.buffer, vertexData.byteOffset, vertexData.byteLength) : new DataView(vertexData);\n\n const position =\n attributes.find(attribute => positionAttributeNames.has(attribute.name)) ??\n attributes.find(attribute => attribute.name.toLowerCase().includes('position'))!;\n const texcoord = attributes.find(attribute => texcoordAttributeNames.has(attribute.name)) ?? null;\n\n for (let i = 0; i < vertexCount; i++) {\n const vertexStart = i * stride;\n const target = i * floatsPerMeshVertex;\n\n table[target + 0] = readComponent(view, position, vertexStart + position.offset);\n table[target + 1] = readComponent(view, position, vertexStart + position.offset + componentByteSizes[position.type]);\n\n if (texcoord !== null) {\n table[target + 2] = readComponent(view, texcoord, vertexStart + texcoord.offset);\n table[target + 3] = readComponent(view, texcoord, vertexStart + texcoord.offset + componentByteSizes[texcoord.type]);\n } else {\n table[target + 2] = 0;\n table[target + 3] = 0;\n }\n }\n\n return table;\n};\n\n/**\n * WGSL counterpart of `shaders/mesh.vert` plus the quad's fragment stage. Vertex\n * and fragment entry points share one source per WGSL convention. The\n * per-instance attributes bind at `@location(0..5)`, matching the declaration\n * order and byte offsets of {@link instanceAttributes}, and the mesh's own\n * vertices follow at `@location(6..7)` from the second vertex buffer.\n *\n * The uniform struct is the one the WebGPU particle renderer writes for every\n * mode, so `localBounds` and `uvBounds` are declared but unused here: a mesh\n * carries its own local footprint rather than taking it from the texture frame.\n */\nexport const meshParticleWgsl: string = meshParticleWgslModule;\n\n/**\n * One caller-supplied mesh per particle, drawn as a single instanced draw.\n *\n * The cheap counterpart to `RibbonParticles`: the relationship stays one\n * particle, one instance, and the per-instance data is byte-identical to\n * `QuadParticles`'s - {@link instanceAttributes}, filled by the same\n * writer. Only the shape changes. That is what makes this mode **GPU-eligible**\n * without touching the compute pipeline: the pipeline emits precisely that\n * layout, so a system on the GPU path binds its instance buffer directly and\n * never calls {@link build}.\n *\n * **The mesh rides in its own vertex buffer**, alongside the per-instance one.\n * The shader is fixed and shared, so any number of `MeshParticles` compile one\n * program per backend regardless of how many distinct shapes they draw, and a\n * mesh is limited only by what a vertex buffer holds.\n *\n * **Mutating the mesh works.** Its vertices are read into a normalised\n * `(x, y, u, v)` table - the shader binds fixed names, a caller's geometry may\n * use any - and re-read whenever the geometry's `version` changes. Edit the\n * geometry in place, call `invalidate()` on it, and the next draw picks it up.\n * Changing the vertex *count* is equally fine.\n *\n * **Atlas frames still work.** The mesh's own UVs are treated exactly like the\n * quad's 0..1 corners: the shader mixes them across the particle's resolved\n * frame (`uv = mix(uvMin, uvMax, meshUV)`), so `system.frames` plus a\n * per-particle `textureIndex` selects a frame the same way. A geometry with\n * **no texcoord attribute** therefore carries UV `(0, 0)` on every vertex and\n * the mix degenerates to `uvMin` - the whole mesh samples the single texel at\n * the frame's top-left corner, which on the default 1×1 white texture is\n * exactly \"untextured, tinted by the particle's `color`\".\n *\n * **Sizing is the mesh's own.** Unlike the quad, which derives its footprint\n * from the texture frame, a mesh particle's footprint is whatever its vertex\n * positions say, in system-local units, multiplied by the per-particle\n * `scaleX`/`scaleY`. The texture only supplies colour.\n *\n * @example\n * const shard = new Geometry({\n * attributes: [\n * { name: 'a_position', size: 2, type: 'f32', normalized: false, offset: 0 },\n * { name: 'a_uv', size: 2, type: 'f32', normalized: false, offset: 8 },\n * ],\n * vertexData: new Float32Array([0, -8, 0.5, 0, 6, 6, 1, 1, -6, 6, 0, 1]),\n * stride: 16,\n * });\n *\n * const debris = new ParticleSystem(rockTexture, {\n * capacity: 512,\n * render: new MeshParticles({ geometry: shard }),\n * });\n *\n * debris.addUpdateModule(new RotateOverLifetime(180)); // each shard tumbles\n */\nexport class MeshParticles extends ParticleRenderMode {\n public override readonly gpuEligible = true;\n public readonly instanced = true;\n\n /**\n * The geometry this mode was constructed with - the shape one particle\n * draws. Owned by the caller: {@link destroy} leaves it alone, so one mesh\n * can back several modes.\n */\n public readonly mesh: Geometry;\n\n /**\n * The normalised mesh the executors bind as the per-vertex buffer: the\n * caller's {@link mesh} reduced to `(x, y, u, v)` per vertex, keeping its\n * topology and index list. Owned by this mode and disposed with it.\n */\n public override readonly vertexGeometry: Geometry;\n\n /** Per-instance layout, byte-identical to the quad's and to the compute output. */\n public readonly dataLayout = new ParticleBufferLayout({\n attributes: instanceAttributes,\n stride: instanceStrideBytes,\n usage: 'stream',\n });\n\n private readonly _writer = new ParticleInstanceWriter();\n\n private _meshTable: Float32Array;\n private _meshVersion: number;\n private _material: ParticleMaterial | null = null;\n private _float32 = new Float32Array(this.data);\n private _uint32 = new Uint32Array(this.data);\n\n public constructor(options: MeshParticlesOptions) {\n super();\n\n const mesh = options.geometry;\n\n this.mesh = mesh;\n this._meshTable = readMeshTable(mesh);\n this._meshVersion = mesh.version;\n this.vertexGeometry = new Geometry({\n attributes: meshVertexAttributes,\n vertexData: this._meshTable,\n stride: meshVertexStrideBytes,\n indices: mesh.indices,\n topology: mesh.topology,\n usage: mesh.usage,\n });\n\n assertVertexGeometryCompatible(this.dataLayout, this.vertexGeometry, this.instanced, 'MeshParticles');\n }\n\n /**\n * Built on first read rather than in the constructor: a system may be\n * simulated without ever being drawn, and the shader pair is only needed\n * once a backend actually compiles it.\n */\n public get material(): Material {\n this._material ??= new ParticleMaterial({\n shader: new ShaderSource({\n glsl: { vertex: vertexSource, fragment: fragmentSource },\n wgsl: meshParticleWgsl,\n }),\n });\n\n return this._material;\n }\n\n public build(system: ParticleSystem, particles: ParticleBatch): void {\n this._syncMesh();\n\n // Must precede the view reads below: growing swaps in fresh typed-array\n // views over a new backing buffer.\n this._ensureCapacity(particles.count * instanceStrideBytes);\n\n this._setCount(this._writer.write(system, particles, this._float32, this._uint32));\n }\n\n public override destroy(): void {\n this._material?.destroy();\n this._material = null;\n this.vertexGeometry.destroy();\n }\n\n protected override _onBufferGrown(data: ArrayBuffer): void {\n this._float32 = new Float32Array(data);\n this._uint32 = new Uint32Array(data);\n }\n\n /**\n * Re-read the caller's mesh when it has been mutated since the last look.\n *\n * Re-runs from {@link build} rather than from a draw so it also happens for a\n * system whose backend has not compiled a pipeline yet. A vertex count change\n * swaps the table, which is why the geometry's own `vertexData` is reassigned\n * rather than written through - the executors notice via its `version`.\n */\n private _syncMesh(): void {\n if (this._meshVersion === this.mesh.version) {\n return;\n }\n\n this._meshVersion = this.mesh.version;\n\n const table = readMeshTable(this.mesh, this._meshTable);\n\n if (table !== this._meshTable) {\n this._meshTable = table;\n this.vertexGeometry.vertexData = table;\n }\n\n this.vertexGeometry.invalidate();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import type { Geometry, GeometryAttribute, GeometryUsage, Topology } from '@codexo/exojs';
2
+ /** Construction options for {@link ParticleBufferLayout}. */
3
+ export interface ParticleBufferLayoutOptions {
4
+ /** Interleaved attributes, in declaration order. Shader locations follow this order. */
5
+ readonly attributes: readonly GeometryAttribute[];
6
+ /** Bytes one record occupies. Must be a positive integer. */
7
+ readonly stride: number;
8
+ /** Upload hint for the buffer this layout describes. Defaults to `stream`. */
9
+ readonly usage?: GeometryUsage;
10
+ /**
11
+ * Topology of the draw, used only when the mode declares no per-vertex
12
+ * geometry to take it from. Defaults to `triangle-list`.
13
+ */
14
+ readonly topology?: Topology;
15
+ /**
16
+ * Index list for the draw, used only when the mode declares no per-vertex
17
+ * geometry to take it from. Defaults to none.
18
+ */
19
+ readonly indices?: Uint16Array | Uint32Array | null;
20
+ }
21
+ /**
22
+ * Describes the interleaved buffer a render mode's `build()` fills each frame.
23
+ *
24
+ * Deliberately not a `Geometry`. A `Geometry` requires a position attribute,
25
+ * requires that attribute to carry at least two components, and validates any
26
+ * index list against the vertex count implied by its `vertexData` - three rules
27
+ * that are meaningful for geometry and meaningless for a per-instance record.
28
+ * Satisfying them used to cost the instanced modes a zero-filled placeholder
29
+ * buffer that was never uploaded, sized purely to keep the index check quiet.
30
+ *
31
+ * What is checked here is the subset that protects a real GPU binding: a
32
+ * positive stride, at least one attribute, unique names, and ranges that
33
+ * neither overlap each other nor reach past the stride.
34
+ *
35
+ * A layout is a description rather than a resource: it owns no GPU handle and
36
+ * needs no disposal.
37
+ */
38
+ export declare class ParticleBufferLayout {
39
+ readonly attributes: readonly GeometryAttribute[];
40
+ readonly stride: number;
41
+ readonly usage: GeometryUsage;
42
+ readonly topology: Topology;
43
+ readonly indices: Uint16Array | Uint32Array | null;
44
+ constructor(options: ParticleBufferLayoutOptions);
45
+ /** Elements one draw consumes from {@link indices}, or 0 when there are none. */
46
+ get indexCount(): number;
47
+ }
48
+ /**
49
+ * Guard the two seam combinations a mode must not declare.
50
+ *
51
+ * Called by a mode's own constructor, so the error names the mistake where it
52
+ * was made, and repeated by each executor when it first realises a mode, so a
53
+ * mode written outside this package cannot slip past. Both checks are cheap and
54
+ * run once per mode rather than per frame.
55
+ *
56
+ * @param modeName Class name of the mode, used to prefix the error.
57
+ */
58
+ export declare const assertVertexGeometryCompatible: (dataLayout: ParticleBufferLayout, vertexGeometry: Geometry | null, instanced: boolean, modeName: string) => void;
59
+ //# sourceMappingURL=ParticleBufferLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParticleBufferLayout.d.ts","sourceRoot":"","sources":["../../../src/renderModes/ParticleBufferLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,QAAQ,EAAE,iBAAiB,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AA+DzG,6DAA6D;AAC7D,MAAM,WAAW,2BAA2B;IAC1C,wFAAwF;IACxF,QAAQ,CAAC,UAAU,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAElD,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI,CAAC;CACrD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,oBAAoB;IAC/B,SAAgB,UAAU,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACzD,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,KAAK,EAAE,aAAa,CAAC;IACrC,SAAgB,QAAQ,EAAE,QAAQ,CAAC;IACnC,SAAgB,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI,CAAC;gBAEvC,OAAO,EAAE,2BAA2B;IA4BvD,iFAAiF;IACjF,IAAW,UAAU,IAAI,MAAM,CAE9B;CACF;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,8BAA8B,GACzC,YAAY,oBAAoB,EAChC,gBAAgB,QAAQ,GAAG,IAAI,EAC/B,WAAW,OAAO,EAClB,UAAU,MAAM,KACf,IAoBF,CAAC"}