@codexo/exojs-particles 0.15.3 → 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.
- package/README.md +21 -43
- package/dist/esm/ParticleStorage.d.ts +188 -0
- package/dist/esm/ParticleStorage.d.ts.map +1 -0
- package/dist/esm/ParticleStorage.js +208 -0
- package/dist/esm/ParticleStorage.js.map +1 -0
- package/dist/esm/ParticleSystem.d.ts +189 -60
- package/dist/esm/ParticleSystem.d.ts.map +1 -0
- package/dist/esm/ParticleSystem.js +660 -560
- package/dist/esm/ParticleSystem.js.map +1 -1
- package/dist/esm/distributions/BoxArea.d.ts +1 -0
- package/dist/esm/distributions/BoxArea.d.ts.map +1 -0
- package/dist/esm/distributions/BoxArea.js +38 -44
- package/dist/esm/distributions/BoxArea.js.map +1 -1
- package/dist/esm/distributions/CircleArea.d.ts +1 -0
- package/dist/esm/distributions/CircleArea.d.ts.map +1 -0
- package/dist/esm/distributions/CircleArea.js +30 -28
- package/dist/esm/distributions/CircleArea.js.map +1 -1
- package/dist/esm/distributions/ColorGradient.d.ts +3 -2
- package/dist/esm/distributions/ColorGradient.d.ts.map +1 -0
- package/dist/esm/distributions/ColorGradient.js +70 -75
- package/dist/esm/distributions/ColorGradient.js.map +1 -1
- package/dist/esm/distributions/ConeDirection.d.ts +2 -1
- package/dist/esm/distributions/ConeDirection.d.ts.map +1 -0
- package/dist/esm/distributions/ConeDirection.js +41 -39
- package/dist/esm/distributions/ConeDirection.js.map +1 -1
- package/dist/esm/distributions/Constant.d.ts +1 -0
- package/dist/esm/distributions/Constant.d.ts.map +1 -0
- package/dist/esm/distributions/Constant.js +32 -32
- package/dist/esm/distributions/Constant.js.map +1 -1
- package/dist/esm/distributions/Curve.d.ts +2 -1
- package/dist/esm/distributions/Curve.d.ts.map +1 -0
- package/dist/esm/distributions/Curve.js +46 -55
- package/dist/esm/distributions/Curve.js.map +1 -1
- package/dist/esm/distributions/Distribution.d.ts +4 -3
- package/dist/esm/distributions/Distribution.d.ts.map +1 -0
- package/dist/esm/distributions/LineSegment.d.ts +1 -0
- package/dist/esm/distributions/LineSegment.d.ts.map +1 -0
- package/dist/esm/distributions/LineSegment.js +25 -23
- package/dist/esm/distributions/LineSegment.js.map +1 -1
- package/dist/esm/distributions/Range.d.ts +1 -0
- package/dist/esm/distributions/Range.d.ts.map +1 -0
- package/dist/esm/distributions/Range.js +18 -16
- package/dist/esm/distributions/Range.js.map +1 -1
- package/dist/esm/distributions/VectorRange.d.ts +1 -0
- package/dist/esm/distributions/VectorRange.d.ts.map +1 -0
- package/dist/esm/distributions/VectorRange.js +29 -27
- package/dist/esm/distributions/VectorRange.js.map +1 -1
- package/dist/esm/distributions/index.d.ts +1 -0
- package/dist/esm/distributions/index.d.ts.map +1 -0
- package/dist/esm/gpu/ParticleGpuState.d.ts +143 -18
- package/dist/esm/gpu/ParticleGpuState.d.ts.map +1 -0
- package/dist/esm/gpu/ParticleGpuState.js +655 -497
- package/dist/esm/gpu/ParticleGpuState.js.map +1 -1
- package/dist/esm/gpu/shaders/particle-simulate.wgsl.js +6 -0
- package/dist/esm/gpu/shaders/particle-simulate.wgsl.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +39 -32
- package/dist/esm/modules/AlphaFadeOverLifetime.d.ts +4 -3
- package/dist/esm/modules/AlphaFadeOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/AlphaFadeOverLifetime.js +68 -55
- package/dist/esm/modules/AlphaFadeOverLifetime.js.map +1 -1
- package/dist/esm/modules/ApplyForce.d.ts +4 -3
- package/dist/esm/modules/ApplyForce.d.ts.map +1 -0
- package/dist/esm/modules/ApplyForce.js +49 -43
- package/dist/esm/modules/ApplyForce.js.map +1 -1
- package/dist/esm/modules/AttractToPoint.d.ts +4 -3
- package/dist/esm/modules/AttractToPoint.d.ts.map +1 -0
- package/dist/esm/modules/AttractToPoint.js +73 -62
- package/dist/esm/modules/AttractToPoint.js.map +1 -1
- package/dist/esm/modules/BurstSpawn.d.ts +6 -18
- package/dist/esm/modules/BurstSpawn.d.ts.map +1 -0
- package/dist/esm/modules/BurstSpawn.js +56 -91
- package/dist/esm/modules/BurstSpawn.js.map +1 -1
- package/dist/esm/modules/ColorOverLifetime.d.ts +4 -3
- package/dist/esm/modules/ColorOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/ColorOverLifetime.js +65 -53
- package/dist/esm/modules/ColorOverLifetime.js.map +1 -1
- package/dist/esm/modules/ColorOverSpeed.d.ts +5 -4
- package/dist/esm/modules/ColorOverSpeed.d.ts.map +1 -0
- package/dist/esm/modules/ColorOverSpeed.js +90 -75
- package/dist/esm/modules/ColorOverSpeed.js.map +1 -1
- package/dist/esm/modules/DeathModule.d.ts +26 -10
- package/dist/esm/modules/DeathModule.d.ts.map +1 -0
- package/dist/esm/modules/DeathModule.js +38 -22
- package/dist/esm/modules/DeathModule.js.map +1 -1
- package/dist/esm/modules/Drag.d.ts +3 -2
- package/dist/esm/modules/Drag.d.ts.map +1 -0
- package/dist/esm/modules/Drag.js +43 -37
- package/dist/esm/modules/Drag.js.map +1 -1
- package/dist/esm/modules/OrbitalForce.d.ts +4 -3
- package/dist/esm/modules/OrbitalForce.d.ts.map +1 -0
- package/dist/esm/modules/OrbitalForce.js +70 -58
- package/dist/esm/modules/OrbitalForce.js.map +1 -1
- package/dist/esm/modules/RateSpawn.d.ts +7 -22
- package/dist/esm/modules/RateSpawn.d.ts.map +1 -0
- package/dist/esm/modules/RateSpawn.js +39 -71
- package/dist/esm/modules/RateSpawn.js.map +1 -1
- package/dist/esm/modules/RepelFromPoint.d.ts +4 -3
- package/dist/esm/modules/RepelFromPoint.d.ts.map +1 -0
- package/dist/esm/modules/RepelFromPoint.js +72 -62
- package/dist/esm/modules/RepelFromPoint.js.map +1 -1
- package/dist/esm/modules/RotateOverLifetime.d.ts +3 -2
- package/dist/esm/modules/RotateOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/RotateOverLifetime.js +40 -36
- package/dist/esm/modules/RotateOverLifetime.js.map +1 -1
- package/dist/esm/modules/ScaleOverLifetime.d.ts +5 -4
- package/dist/esm/modules/ScaleOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/ScaleOverLifetime.js +60 -50
- package/dist/esm/modules/ScaleOverLifetime.js.map +1 -1
- package/dist/esm/modules/SpawnModule.d.ts +15 -12
- package/dist/esm/modules/SpawnModule.d.ts.map +1 -0
- package/dist/esm/modules/SpawnModule.js +32 -28
- package/dist/esm/modules/SpawnModule.js.map +1 -1
- package/dist/esm/modules/SpawnOnDeath.d.ts +13 -9
- package/dist/esm/modules/SpawnOnDeath.d.ts.map +1 -0
- package/dist/esm/modules/SpawnOnDeath.js +41 -43
- package/dist/esm/modules/SpawnOnDeath.js.map +1 -1
- package/dist/esm/modules/Turbulence.d.ts +4 -3
- package/dist/esm/modules/Turbulence.d.ts.map +1 -0
- package/dist/esm/modules/Turbulence.js +101 -89
- package/dist/esm/modules/Turbulence.js.map +1 -1
- package/dist/esm/modules/UpdateModule.d.ts +21 -13
- package/dist/esm/modules/UpdateModule.d.ts.map +1 -0
- package/dist/esm/modules/UpdateModule.js +65 -63
- package/dist/esm/modules/UpdateModule.js.map +1 -1
- package/dist/esm/modules/VelocityOverLifetime.d.ts +7 -6
- package/dist/esm/modules/VelocityOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/VelocityOverLifetime.js +75 -73
- package/dist/esm/modules/VelocityOverLifetime.js.map +1 -1
- package/dist/esm/modules/WgslContribution.d.ts +16 -15
- package/dist/esm/modules/WgslContribution.d.ts.map +1 -0
- package/dist/esm/modules/WgslContribution.js +37 -29
- package/dist/esm/modules/WgslContribution.js.map +1 -1
- package/dist/esm/modules/index.d.ts +3 -1
- package/dist/esm/modules/index.d.ts.map +1 -0
- package/dist/esm/modules/spawnFields.d.ts +32 -0
- package/dist/esm/modules/spawnFields.d.ts.map +1 -0
- package/dist/esm/modules/spawnFields.js +31 -0
- package/dist/esm/modules/spawnFields.js.map +1 -0
- package/dist/esm/particlesBuildInfo.d.ts +1 -0
- package/dist/esm/particlesBuildInfo.d.ts.map +1 -0
- package/dist/esm/particlesBuildInfo.js +6 -4
- package/dist/esm/particlesBuildInfo.js.map +1 -1
- package/dist/esm/particlesExtension.d.ts +13 -5
- package/dist/esm/particlesExtension.d.ts.map +1 -0
- package/dist/esm/particlesExtension.js +38 -42
- package/dist/esm/particlesExtension.js.map +1 -1
- package/dist/esm/public.d.ts +11 -0
- package/dist/esm/public.d.ts.map +1 -0
- package/dist/esm/renderModes/MeshParticles.d.ts +128 -0
- package/dist/esm/renderModes/MeshParticles.d.ts.map +1 -0
- package/dist/esm/renderModes/MeshParticles.js +263 -0
- package/dist/esm/renderModes/MeshParticles.js.map +1 -0
- package/dist/esm/renderModes/ParticleBufferLayout.d.ts +59 -0
- package/dist/esm/renderModes/ParticleBufferLayout.d.ts.map +1 -0
- package/dist/esm/renderModes/ParticleBufferLayout.js +99 -0
- package/dist/esm/renderModes/ParticleBufferLayout.js.map +1 -0
- package/dist/esm/renderModes/ParticleInstanceWriter.d.ts +58 -0
- package/dist/esm/renderModes/ParticleInstanceWriter.d.ts.map +1 -0
- package/dist/esm/renderModes/ParticleInstanceWriter.js +173 -0
- package/dist/esm/renderModes/ParticleInstanceWriter.js.map +1 -0
- package/dist/esm/renderModes/ParticleMaterial.d.ts +18 -0
- package/dist/esm/renderModes/ParticleMaterial.d.ts.map +1 -0
- package/dist/esm/renderModes/ParticleMaterial.js +24 -0
- package/dist/esm/renderModes/ParticleMaterial.js.map +1 -0
- package/dist/esm/renderModes/ParticleRenderMode.d.ts +114 -0
- package/dist/esm/renderModes/ParticleRenderMode.d.ts.map +1 -0
- package/dist/esm/renderModes/ParticleRenderMode.js +99 -0
- package/dist/esm/renderModes/ParticleRenderMode.js.map +1 -0
- package/dist/esm/renderModes/QuadParticles.d.ts +55 -0
- package/dist/esm/renderModes/QuadParticles.d.ts.map +1 -0
- package/dist/esm/renderModes/QuadParticles.js +95 -0
- package/dist/esm/renderModes/QuadParticles.js.map +1 -0
- package/dist/esm/renderModes/RibbonParticles.d.ts +96 -0
- package/dist/esm/renderModes/RibbonParticles.d.ts.map +1 -0
- package/dist/esm/renderModes/RibbonParticles.js +202 -0
- package/dist/esm/renderModes/RibbonParticles.js.map +1 -0
- package/dist/esm/renderModes/shaders/mesh-particles.wgsl.js +6 -0
- package/dist/esm/renderModes/shaders/mesh-particles.wgsl.js.map +1 -0
- package/dist/esm/renderModes/shaders/mesh.vert.js +6 -0
- package/dist/esm/renderModes/shaders/mesh.vert.js.map +1 -0
- package/dist/esm/renderModes/shaders/quad-particles.wgsl.js +6 -0
- package/dist/esm/renderModes/shaders/quad-particles.wgsl.js.map +1 -0
- package/dist/esm/renderModes/shaders/ribbon-particles.wgsl.js +6 -0
- package/dist/esm/renderModes/shaders/ribbon-particles.wgsl.js.map +1 -0
- package/dist/esm/renderModes/shaders/ribbon.frag.js +6 -0
- package/dist/esm/renderModes/shaders/ribbon.frag.js.map +1 -0
- package/dist/esm/renderModes/shaders/ribbon.vert.js +6 -0
- package/dist/esm/renderModes/shaders/ribbon.vert.js.map +1 -0
- package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts +48 -20
- package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts.map +1 -0
- package/dist/esm/renderers/WebGl2ParticleRenderer.js +317 -321
- package/dist/esm/renderers/WebGl2ParticleRenderer.js.map +1 -1
- package/dist/esm/renderers/WebGpuParticleRenderer.d.ts +99 -18
- package/dist/esm/renderers/WebGpuParticleRenderer.d.ts.map +1 -0
- package/dist/esm/renderers/WebGpuParticleRenderer.js +542 -551
- package/dist/esm/renderers/WebGpuParticleRenderer.js.map +1 -1
- package/dist/esm/renderers/shaders/particle.frag.js +6 -0
- package/dist/esm/renderers/shaders/particle.frag.js.map +1 -0
- package/dist/esm/renderers/shaders/particle.vert.js +6 -0
- package/dist/esm/renderers/shaders/particle.vert.js.map +1 -0
- package/package.json +8 -15
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/register.d.ts +0 -1
- package/dist/esm/register.js +0 -43
- package/dist/esm/register.js.map +0 -1
- package/dist/esm/renderers/glsl/particle.frag.js +0 -4
- package/dist/esm/renderers/glsl/particle.frag.js.map +0 -1
- package/dist/esm/renderers/glsl/particle.vert.js +0 -4
- package/dist/esm/renderers/glsl/particle.vert.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParticleRenderMode.js","names":[],"sources":["../../../src/renderModes/ParticleRenderMode.ts"],"sourcesContent":["import type { Geometry, Material } from '@codexo/exojs';\n\nimport type { ParticleBatch } from '#ParticleStorage';\nimport type { ParticleSystem } from '#ParticleSystem';\n\nimport type { ParticleBufferLayout } from './ParticleBufferLayout';\n\n/**\n * Turns a particle system's SoA storage into drawable vertex data.\n *\n * A mode owns the whole \"how\": the vertex layout, the shader pair, the draw\n * model, and the loop that fills the buffer. The backend renderers only upload\n * what it produces and issue the draw it declares, so a new primitive is a new\n * mode rather than a renderer change.\n *\n * Implementations are fixed at system construction (see\n * `ParticleSystemOptions.render`) and default to `QuadParticles`.\n *\n * **Limits of the seam.** The executors are deliberately thin, so a new mode\n * has to stay inside what they can express:\n *\n * - The geometry's `stride` must be a multiple of 4. WebGPU's\n * `queue.writeBuffer` validates its copy size against that alignment and\n * rejects anything else, so a mode with, say, a 38-byte stride draws on\n * WebGL2 and fails validation on WebGPU.\n * - An **indexed, non-instanced** mode always draws the geometry's fixed\n * `indexCount` and ignores {@link count} on both backends. That is right for\n * a fixed-topology mode and wrong for one whose element count varies per\n * frame; such a mode needs the executors taught to derive an index count\n * from {@link count} first. No mode ships in that shape today.\n * - An **instanced** mode that declares no {@link vertexGeometry} has to derive\n * its vertices in the shader from the vertex index, and its\n * {@link dataLayout} must carry the indices that address them. Without either\n * there is nothing to tell the executors how many vertices one instance\n * spans.\n */\nexport abstract class ParticleRenderMode {\n /**\n * Layout of the buffer {@link build} fills each frame: attributes, stride,\n * upload hint, and - when this mode declares no {@link vertexGeometry} - the\n * topology and index list the draw uses.\n *\n * Named after {@link data}, the buffer it describes. It holds per-instance\n * records for an instanced mode and per-vertex records otherwise, so a name\n * fixed to either would be wrong for one of the two draw models.\n */\n public abstract readonly dataLayout: ParticleBufferLayout;\n\n /**\n * Fixed per-vertex geometry this mode instances, or `null` when it derives\n * its vertices in the shader from the vertex index - which is what\n * `QuadParticles` and `RibbonParticles` do.\n *\n * When set, it supplies the topology, index list and index count for the draw\n * in place of {@link dataLayout}'s, and the executors bind its `vertexData`\n * as a second vertex buffer stepping per vertex beside the per-instance one.\n * Only meaningful on an instanced mode: without instancing both buffers would\n * step per vertex and no draw is expressible.\n *\n * Attribute names must not collide with {@link dataLayout}'s, since both sets\n * bind into the same shader. Mutating the geometry is picked up on the next\n * draw through its `version`, so `invalidate()` after an in-place edit is\n * enough to reach the GPU.\n */\n public readonly vertexGeometry: Geometry | null = null;\n\n /** Shader pair plus uniforms/textures for this mode. */\n public abstract readonly material: Material;\n\n /**\n * Draw model. `true` issues an instanced draw, `false` a plain draw over the\n * built vertex buffer. Declared here because a layout carries topology but\n * has no instancing concept.\n */\n public abstract readonly instanced: boolean;\n\n /**\n * Whether this mode can run while the system is in GPU compute mode.\n * Mirrors `UpdateModule.wgsl()`: a mode that cannot forces the whole system\n * onto the CPU path, silently and observably via `ParticleSystem.gpuMode`.\n *\n * A GPU-eligible mode's layout must match what the compute pipeline emits\n * into `gpuState.instanceBuffer` - the shared 40-byte per-instance layout\n * (`instanceAttributes`). `QuadParticles` and `MeshParticles` both declare\n * exactly that layout and differ only in the shape they expand it into,\n * which is why both are eligible without the compute shader knowing either.\n */\n public readonly gpuEligible: boolean = false;\n\n private _data: ArrayBuffer = new ArrayBuffer(0);\n private _count = 0;\n\n /**\n * Element count of the draw call this mode's last {@link build} produced.\n * Draw-model relative: instance count when {@link instanced}, vertex count\n * otherwise. Do not assume one meaning.\n */\n public get count(): number {\n return this._count;\n }\n\n /** The buffer the renderer uploads. Valid until the next {@link build}. */\n public get data(): ArrayBuffer {\n return this._data;\n }\n\n /**\n * Fill the scratch buffer from the system's live particles.\n *\n * `particles` is the simulation's own channel storage, handed in rather than\n * read off the system: a render mode is the one consumer that legitimately\n * works in bulk, and it runs at the point in the frame where those values are\n * the ones being drawn.\n */\n public abstract build(system: ParticleSystem, particles: ParticleBatch): void;\n\n /** Optional cleanup, called from `ParticleSystem.destroy`. */\n public destroy(): void {}\n\n /** Record the element count produced by a {@link build}. */\n protected _setCount(count: number): void {\n this._count = count;\n }\n\n /**\n * Grow the scratch buffer to hold at least `byteLength`. Grow-only: a\n * shrinking particle count reuses the larger buffer rather than\n * reallocating, matching the renderers' existing buffer policy.\n */\n protected _ensureCapacity(byteLength: number): void {\n if (this._data.byteLength >= byteLength) {\n return;\n }\n\n let next = Math.max(this._data.byteLength, 1);\n\n while (next < byteLength) {\n next *= 2;\n }\n\n this._data = new ArrayBuffer(next);\n this._onBufferGrown(this._data);\n }\n\n /** Re-create typed-array views after {@link _ensureCapacity} reallocates. */\n protected _onBufferGrown(_data: ArrayBuffer): void {}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,IAAsB,qBAAtB,MAAyC;;;;;;;;;;;;;;;;;CA4BvC,AAAgB,iBAAkC;;;;;;;;;;;;CAuBlD,AAAgB,cAAuB;CAEvC,AAAQ,wBAAqB,IAAI,YAAY,CAAC;CAC9C,AAAQ,SAAS;;;;;;CAOjB,IAAW,QAAgB;EACzB,OAAO,KAAK;CACd;;CAGA,IAAW,OAAoB;EAC7B,OAAO,KAAK;CACd;;CAaA,AAAO,UAAgB,CAAC;;CAGxB,AAAU,UAAU,OAAqB;EACvC,KAAK,SAAS;CAChB;;;;;;CAOA,AAAU,gBAAgB,YAA0B;EAClD,IAAI,KAAK,MAAM,cAAc,YAC3B;EAGF,IAAI,OAAO,KAAK,IAAI,KAAK,MAAM,YAAY,CAAC;EAE5C,OAAO,OAAO,YACZ,QAAQ;EAGV,KAAK,QAAQ,IAAI,YAAY,IAAI;EACjC,KAAK,eAAe,KAAK,KAAK;CAChC;;CAGA,AAAU,eAAe,OAA0B,CAAC;AACtD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Material } from '@codexo/exojs';
|
|
2
|
+
import type { ParticleBatch } from '#ParticleStorage';
|
|
3
|
+
import type { ParticleSystem } from '#ParticleSystem';
|
|
4
|
+
import { ParticleBufferLayout } from './ParticleBufferLayout';
|
|
5
|
+
import { ParticleRenderMode } from './ParticleRenderMode';
|
|
6
|
+
/**
|
|
7
|
+
* WGSL counterpart of `shaders/particle.vert` + `shaders/particle.frag`. Vertex and
|
|
8
|
+
* fragment entry points share one source per WGSL convention, and the
|
|
9
|
+
* per-instance attributes bind by `@location`, matching the declaration order
|
|
10
|
+
* and byte offsets of {@link QuadParticles.dataLayout}.
|
|
11
|
+
*
|
|
12
|
+
* The quad's corner is derived from `vertex_index` exactly as the GLSL derives
|
|
13
|
+
* it from `gl_VertexID`, so both backends need nothing beyond this mode's own
|
|
14
|
+
* interleaved buffer and index buffer.
|
|
15
|
+
*/
|
|
16
|
+
export declare const quadParticleWgsl: string;
|
|
17
|
+
/**
|
|
18
|
+
* The default render mode: one textured, rotated, tinted quad per particle,
|
|
19
|
+
* drawn as a single instanced triangle-list.
|
|
20
|
+
*
|
|
21
|
+
* {@link build} fills the shared 40-byte per-instance layout described by
|
|
22
|
+
* {@link instanceAttributes}. UVs are baked per-particle so the system can
|
|
23
|
+
* carry an atlas of frames - `system.frames` declares the rectangles and each
|
|
24
|
+
* particle's `textureIndex` selects one, resolved to UVs once per particle per
|
|
25
|
+
* frame; no per-instance shader-side indexing needed.
|
|
26
|
+
*
|
|
27
|
+
* GPU-eligible: the system's compute pipeline emits exactly that layout into
|
|
28
|
+
* its instance buffer, so a system running on the GPU path can bind that buffer
|
|
29
|
+
* directly and skip {@link build} entirely.
|
|
30
|
+
*/
|
|
31
|
+
export declare class QuadParticles extends ParticleRenderMode {
|
|
32
|
+
readonly gpuEligible = true;
|
|
33
|
+
readonly instanced = true;
|
|
34
|
+
/**
|
|
35
|
+
* The quad's four corners are derived in the shader - from `gl_VertexID` on
|
|
36
|
+
* WebGL2 and from `vertex_index` on WebGPU - so this mode declares no
|
|
37
|
+
* per-vertex geometry and its layout carries the indices that address those
|
|
38
|
+
* four corner slots.
|
|
39
|
+
*/
|
|
40
|
+
readonly dataLayout: ParticleBufferLayout;
|
|
41
|
+
private readonly _writer;
|
|
42
|
+
private _material;
|
|
43
|
+
private _float32;
|
|
44
|
+
private _uint32;
|
|
45
|
+
/**
|
|
46
|
+
* Built on first read rather than in the constructor: a system may be
|
|
47
|
+
* simulated without ever being drawn, and the shader pair is only needed
|
|
48
|
+
* once a backend actually compiles it.
|
|
49
|
+
*/
|
|
50
|
+
get material(): Material;
|
|
51
|
+
build(system: ParticleSystem, particles: ParticleBatch): void;
|
|
52
|
+
destroy(): void;
|
|
53
|
+
protected _onBufferGrown(data: ArrayBuffer): void;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=QuadParticles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuadParticles.d.ts","sourceRoot":"","sources":["../../../src/renderModes/QuadParticles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAItD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAK1D;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAA+B,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAc,SAAQ,kBAAkB;IACnD,SAAyB,WAAW,QAAQ;IAC5C,SAAgB,SAAS,QAAQ;IAEjC;;;;;OAKG;IACH,SAAgB,UAAU,uBAMvB;IAEH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IAExD,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAA8B;IAE7C;;;;OAIG;IACH,IAAW,QAAQ,IAAI,QAAQ,CAS9B;IAEM,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI;IAQpD,OAAO,IAAI,IAAI;cAKZ,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;CAI3D"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { ParticleBufferLayout } from "./ParticleBufferLayout.js";
|
|
2
|
+
import particle_default from "../renderers/shaders/particle.frag.js";
|
|
3
|
+
import particle_default$1 from "../renderers/shaders/particle.vert.js";
|
|
4
|
+
import { ParticleInstanceWriter, instanceAttributes } from "./ParticleInstanceWriter.js";
|
|
5
|
+
import { ParticleMaterial } from "./ParticleMaterial.js";
|
|
6
|
+
import { ParticleRenderMode } from "./ParticleRenderMode.js";
|
|
7
|
+
import quad_particles_default from "./shaders/quad-particles.wgsl.js";
|
|
8
|
+
import { ShaderSource } from "@codexo/exojs";
|
|
9
|
+
|
|
10
|
+
//#region src/renderModes/QuadParticles.ts
|
|
11
|
+
const quadIndices = new Uint16Array([
|
|
12
|
+
0,
|
|
13
|
+
1,
|
|
14
|
+
2,
|
|
15
|
+
0,
|
|
16
|
+
2,
|
|
17
|
+
3
|
|
18
|
+
]);
|
|
19
|
+
/**
|
|
20
|
+
* WGSL counterpart of `shaders/particle.vert` + `shaders/particle.frag`. Vertex and
|
|
21
|
+
* fragment entry points share one source per WGSL convention, and the
|
|
22
|
+
* per-instance attributes bind by `@location`, matching the declaration order
|
|
23
|
+
* and byte offsets of {@link QuadParticles.dataLayout}.
|
|
24
|
+
*
|
|
25
|
+
* The quad's corner is derived from `vertex_index` exactly as the GLSL derives
|
|
26
|
+
* it from `gl_VertexID`, so both backends need nothing beyond this mode's own
|
|
27
|
+
* interleaved buffer and index buffer.
|
|
28
|
+
*/
|
|
29
|
+
const quadParticleWgsl = quad_particles_default;
|
|
30
|
+
/**
|
|
31
|
+
* The default render mode: one textured, rotated, tinted quad per particle,
|
|
32
|
+
* drawn as a single instanced triangle-list.
|
|
33
|
+
*
|
|
34
|
+
* {@link build} fills the shared 40-byte per-instance layout described by
|
|
35
|
+
* {@link instanceAttributes}. UVs are baked per-particle so the system can
|
|
36
|
+
* carry an atlas of frames - `system.frames` declares the rectangles and each
|
|
37
|
+
* particle's `textureIndex` selects one, resolved to UVs once per particle per
|
|
38
|
+
* frame; no per-instance shader-side indexing needed.
|
|
39
|
+
*
|
|
40
|
+
* GPU-eligible: the system's compute pipeline emits exactly that layout into
|
|
41
|
+
* its instance buffer, so a system running on the GPU path can bind that buffer
|
|
42
|
+
* directly and skip {@link build} entirely.
|
|
43
|
+
*/
|
|
44
|
+
var QuadParticles = class extends ParticleRenderMode {
|
|
45
|
+
gpuEligible = true;
|
|
46
|
+
instanced = true;
|
|
47
|
+
/**
|
|
48
|
+
* The quad's four corners are derived in the shader - from `gl_VertexID` on
|
|
49
|
+
* WebGL2 and from `vertex_index` on WebGPU - so this mode declares no
|
|
50
|
+
* per-vertex geometry and its layout carries the indices that address those
|
|
51
|
+
* four corner slots.
|
|
52
|
+
*/
|
|
53
|
+
dataLayout = new ParticleBufferLayout({
|
|
54
|
+
attributes: instanceAttributes,
|
|
55
|
+
stride: 40,
|
|
56
|
+
indices: quadIndices,
|
|
57
|
+
topology: "triangle-list",
|
|
58
|
+
usage: "stream"
|
|
59
|
+
});
|
|
60
|
+
_writer = new ParticleInstanceWriter();
|
|
61
|
+
_material = null;
|
|
62
|
+
_float32 = new Float32Array(this.data);
|
|
63
|
+
_uint32 = new Uint32Array(this.data);
|
|
64
|
+
/**
|
|
65
|
+
* Built on first read rather than in the constructor: a system may be
|
|
66
|
+
* simulated without ever being drawn, and the shader pair is only needed
|
|
67
|
+
* once a backend actually compiles it.
|
|
68
|
+
*/
|
|
69
|
+
get material() {
|
|
70
|
+
this._material ??= new ParticleMaterial({ shader: new ShaderSource({
|
|
71
|
+
glsl: {
|
|
72
|
+
vertex: particle_default$1,
|
|
73
|
+
fragment: particle_default
|
|
74
|
+
},
|
|
75
|
+
wgsl: quadParticleWgsl
|
|
76
|
+
}) });
|
|
77
|
+
return this._material;
|
|
78
|
+
}
|
|
79
|
+
build(system, particles) {
|
|
80
|
+
this._ensureCapacity(particles.count * 40);
|
|
81
|
+
this._setCount(this._writer.write(system, particles, this._float32, this._uint32));
|
|
82
|
+
}
|
|
83
|
+
destroy() {
|
|
84
|
+
this._material?.destroy();
|
|
85
|
+
this._material = null;
|
|
86
|
+
}
|
|
87
|
+
_onBufferGrown(data) {
|
|
88
|
+
this._float32 = new Float32Array(data);
|
|
89
|
+
this._uint32 = new Uint32Array(data);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
export { QuadParticles, quadParticleWgsl };
|
|
95
|
+
//# sourceMappingURL=QuadParticles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuadParticles.js","names":["quadParticleWgslModule","vertexSource","fragmentSource"],"sources":["../../../src/renderModes/QuadParticles.ts"],"sourcesContent":["import type { Material } from '@codexo/exojs';\nimport { ShaderSource } from '@codexo/exojs';\n\nimport type { ParticleBatch } from '#ParticleStorage';\nimport type { ParticleSystem } from '#ParticleSystem';\n\nimport fragmentSource from '../renderers/shaders/particle.frag';\nimport vertexSource from '../renderers/shaders/particle.vert';\nimport { ParticleBufferLayout } from './ParticleBufferLayout';\nimport { instanceAttributes, instanceStrideBytes, ParticleInstanceWriter } from './ParticleInstanceWriter';\nimport { ParticleMaterial } from './ParticleMaterial';\nimport { ParticleRenderMode } from './ParticleRenderMode';\nimport quadParticleWgslModule from './shaders/quad-particles.wgsl';\n\nconst quadIndices = new Uint16Array([0, 1, 2, 0, 2, 3]);\n\n/**\n * WGSL counterpart of `shaders/particle.vert` + `shaders/particle.frag`. Vertex and\n * fragment entry points share one source per WGSL convention, and the\n * per-instance attributes bind by `@location`, matching the declaration order\n * and byte offsets of {@link QuadParticles.dataLayout}.\n *\n * The quad's corner is derived from `vertex_index` exactly as the GLSL derives\n * it from `gl_VertexID`, so both backends need nothing beyond this mode's own\n * interleaved buffer and index buffer.\n */\nexport const quadParticleWgsl: string = quadParticleWgslModule;\n\n/**\n * The default render mode: one textured, rotated, tinted quad per particle,\n * drawn as a single instanced triangle-list.\n *\n * {@link build} fills the shared 40-byte per-instance layout described by\n * {@link instanceAttributes}. UVs are baked per-particle so the system can\n * carry an atlas of frames - `system.frames` declares the rectangles and each\n * particle's `textureIndex` selects one, resolved to UVs once per particle per\n * frame; no per-instance shader-side indexing needed.\n *\n * GPU-eligible: the system's compute pipeline emits exactly that layout into\n * its instance buffer, so a system running on the GPU path can bind that buffer\n * directly and skip {@link build} entirely.\n */\nexport class QuadParticles extends ParticleRenderMode {\n public override readonly gpuEligible = true;\n public readonly instanced = true;\n\n /**\n * The quad's four corners are derived in the shader - from `gl_VertexID` on\n * WebGL2 and from `vertex_index` on WebGPU - so this mode declares no\n * per-vertex geometry and its layout carries the indices that address those\n * four corner slots.\n */\n public readonly dataLayout = new ParticleBufferLayout({\n attributes: instanceAttributes,\n stride: instanceStrideBytes,\n indices: quadIndices,\n topology: 'triangle-list',\n usage: 'stream',\n });\n\n private readonly _writer = new ParticleInstanceWriter();\n\n private _material: ParticleMaterial | null = null;\n private _float32 = new Float32Array(this.data);\n private _uint32 = new Uint32Array(this.data);\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: quadParticleWgsl,\n }),\n });\n\n return this._material;\n }\n\n public build(system: ParticleSystem, particles: ParticleBatch): void {\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 }\n\n protected override _onBufferGrown(data: ArrayBuffer): void {\n this._float32 = new Float32Array(data);\n this._uint32 = new Uint32Array(data);\n }\n}\n"],"mappings":";;;;;;;;;;AAcA,MAAM,cAAc,IAAI,YAAY;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;AAAC,CAAC;;;;;;;;;;;AAYtD,MAAa,mBAA2BA;;;;;;;;;;;;;;;AAgBxC,IAAa,gBAAb,cAAmC,mBAAmB;CACpD,AAAyB,cAAc;CACvC,AAAgB,YAAY;;;;;;;CAQ5B,AAAgB,aAAa,IAAI,qBAAqB;EACpD,YAAY;EACZ;EACA,SAAS;EACT,UAAU;EACV,OAAO;CACT,CAAC;CAED,AAAiB,UAAU,IAAI,uBAAuB;CAEtD,AAAQ,YAAqC;CAC7C,AAAQ,WAAW,IAAI,aAAa,KAAK,IAAI;CAC7C,AAAQ,UAAU,IAAI,YAAY,KAAK,IAAI;;;;;;CAO3C,IAAW,WAAqB;EAC9B,KAAK,cAAc,IAAI,iBAAiB,EACtC,QAAQ,IAAI,aAAa;GACvB,MAAM;IAAE,QAAQC;IAAc,UAAUC;GAAe;GACvD,MAAM;EACR,CAAC,EACH,CAAC;EAED,OAAO,KAAK;CACd;CAEA,AAAO,MAAM,QAAwB,WAAgC;EAGnE,KAAK,gBAAgB,UAAU,UAA2B;EAE1D,KAAK,UAAU,KAAK,QAAQ,MAAM,QAAQ,WAAW,KAAK,UAAU,KAAK,OAAO,CAAC;CACnF;CAEA,AAAgB,UAAgB;EAC9B,KAAK,WAAW,QAAQ;EACxB,KAAK,YAAY;CACnB;CAEA,AAAmB,eAAe,MAAyB;EACzD,KAAK,WAAW,IAAI,aAAa,IAAI;EACrC,KAAK,UAAU,IAAI,YAAY,IAAI;CACrC;AACF"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Material } from '@codexo/exojs';
|
|
2
|
+
import type { ParticleBatch } from '#ParticleStorage';
|
|
3
|
+
import type { ParticleSystem } from '#ParticleSystem';
|
|
4
|
+
import { ParticleBufferLayout } from './ParticleBufferLayout';
|
|
5
|
+
import { ParticleRenderMode } from './ParticleRenderMode';
|
|
6
|
+
/** Construction options for {@link RibbonParticles}. */
|
|
7
|
+
export interface RibbonParticlesOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Width of the strip in system-local units, before per-particle scaling.
|
|
10
|
+
* The half-width actually emitted is `width * scaleX[i] / 2`.
|
|
11
|
+
*
|
|
12
|
+
* @default 1
|
|
13
|
+
*/
|
|
14
|
+
readonly width?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* WGSL counterpart of `shaders/ribbon.vert` + `shaders/ribbon.frag`. Vertex and
|
|
18
|
+
* fragment entry points share one source per WGSL convention, and the
|
|
19
|
+
* per-vertex attributes bind by `@location`, matching the declaration order and
|
|
20
|
+
* byte offsets of {@link RibbonParticles.dataLayout}.
|
|
21
|
+
*
|
|
22
|
+
* The uniform struct is the one the WebGPU particle renderer writes for every
|
|
23
|
+
* mode, so `localBounds` and `uvBounds` are declared but unused here: the strip
|
|
24
|
+
* carries its own final positions and UVs, and only the projection, the system
|
|
25
|
+
* transform and the premultiply flag are read.
|
|
26
|
+
*/
|
|
27
|
+
export declare const ribbonParticleWgsl: string;
|
|
28
|
+
/**
|
|
29
|
+
* A connected triangle strip through the system's particles: one ribbon per
|
|
30
|
+
* system, drawn as a single non-instanced draw.
|
|
31
|
+
*
|
|
32
|
+
* Every particle contributes two vertices, offset either side of the path
|
|
33
|
+
* running through its neighbours, so the whole live range becomes one continuous
|
|
34
|
+
* band - the shape a sword arc, a projectile streak or a smoke plume wants,
|
|
35
|
+
* where a quad-per-particle would show as a dotted line.
|
|
36
|
+
*
|
|
37
|
+
* Layout of the per-vertex buffer {@link build} fills (20 bytes, 3 attributes):
|
|
38
|
+
*
|
|
39
|
+
* ```
|
|
40
|
+
* a_position f32x2 (offset 0, 8 bytes) strip vertex (system-local)
|
|
41
|
+
* a_texcoord f32x2 (offset 8, 8 bytes) u along the strip, v across it
|
|
42
|
+
* a_color u8x4 (offset 16, 4 bytes) RGBA tint, normalised
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* **No ribbon-specific styling parameters.** Half-width comes from the
|
|
46
|
+
* particle's `scaleX` and the tint from its `color`, so the existing update
|
|
47
|
+
* modules already produce the expected look: `ScaleOverLifetime` tapers the
|
|
48
|
+
* tail, `ColorOverLifetime` gradients along the streak, and
|
|
49
|
+
* `AlphaFadeOverLifetime` fades it out. {@link RibbonParticlesOptions.width}
|
|
50
|
+
* only sets the base the scale multiplies.
|
|
51
|
+
*
|
|
52
|
+
* **The strip is built on the CPU**, so this mode is not GPU-eligible and a
|
|
53
|
+
* system using it stays on the CPU simulation path (observable through
|
|
54
|
+
* `ParticleSystem.gpuMode`). That is what guarantees the builder reads valid
|
|
55
|
+
* positions in emission order: CPU-mode slots are dense and compaction copies
|
|
56
|
+
* survivors forward stably, which is exactly the ordering a connected strip
|
|
57
|
+
* needs.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* const trail = new ParticleSystem(sparkTexture, {
|
|
61
|
+
* capacity: 64,
|
|
62
|
+
* render: new RibbonParticles({ width: 12 }),
|
|
63
|
+
* });
|
|
64
|
+
*
|
|
65
|
+
* trail.addUpdateModule(new ScaleOverLifetime(1, 0)); // tapers to a point
|
|
66
|
+
*/
|
|
67
|
+
export declare class RibbonParticles extends ParticleRenderMode {
|
|
68
|
+
readonly instanced = false;
|
|
69
|
+
/**
|
|
70
|
+
* Floats spanned by one vertex. Exposed so callers reading {@link data} can
|
|
71
|
+
* step through it without hard-coding the stride.
|
|
72
|
+
*/
|
|
73
|
+
readonly floatsPerVertex: number;
|
|
74
|
+
/**
|
|
75
|
+
* A strip has no fixed vertex count - the draw covers whatever {@link build}
|
|
76
|
+
* emitted this frame. Non-indexed by construction: the strip's own vertex
|
|
77
|
+
* order is its topology, and an index list would pin the draw to a fixed
|
|
78
|
+
* index count.
|
|
79
|
+
*/
|
|
80
|
+
readonly dataLayout: ParticleBufferLayout;
|
|
81
|
+
private readonly _width;
|
|
82
|
+
private _material;
|
|
83
|
+
private _float32;
|
|
84
|
+
private _uint32;
|
|
85
|
+
constructor(options?: RibbonParticlesOptions);
|
|
86
|
+
/**
|
|
87
|
+
* Built on first read rather than in the constructor: a system may be
|
|
88
|
+
* simulated without ever being drawn, and the shader pair is only needed
|
|
89
|
+
* once a backend actually compiles it.
|
|
90
|
+
*/
|
|
91
|
+
get material(): Material;
|
|
92
|
+
build(_system: ParticleSystem, particles: ParticleBatch): void;
|
|
93
|
+
destroy(): void;
|
|
94
|
+
protected _onBufferGrown(data: ArrayBuffer): void;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=RibbonParticles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RibbonParticles.d.ts","sourceRoot":"","sources":["../../../src/renderModes/RibbonParticles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAS1D,wDAAwD;AACxD,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAiC,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBAAa,eAAgB,SAAQ,kBAAkB;IACrD,SAAgB,SAAS,SAAS;IAElC;;;OAGG;IACH,SAAgB,eAAe,SAAkB;IAEjD;;;;;OAKG;IACH,SAAgB,UAAU,uBASvB;IAEH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAA8B;gBAE1B,OAAO,GAAE,sBAA2B;IAMvD;;;;OAIG;IACH,IAAW,QAAQ,IAAI,QAAQ,CAS9B;IAEM,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI;IA0GrD,OAAO,IAAI,IAAI;cAKZ,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;CAI3D"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { ParticleBufferLayout } from "./ParticleBufferLayout.js";
|
|
2
|
+
import { ParticleMaterial } from "./ParticleMaterial.js";
|
|
3
|
+
import { ParticleRenderMode } from "./ParticleRenderMode.js";
|
|
4
|
+
import ribbon_default from "./shaders/ribbon.frag.js";
|
|
5
|
+
import ribbon_default$1 from "./shaders/ribbon.vert.js";
|
|
6
|
+
import ribbon_particles_default from "./shaders/ribbon-particles.wgsl.js";
|
|
7
|
+
import { ShaderSource } from "@codexo/exojs";
|
|
8
|
+
|
|
9
|
+
//#region src/renderModes/RibbonParticles.ts
|
|
10
|
+
const vertexStrideBytes = 20;
|
|
11
|
+
const wordsPerVertex = vertexStrideBytes / Float32Array.BYTES_PER_ELEMENT;
|
|
12
|
+
const defaultWidth = 1;
|
|
13
|
+
/**
|
|
14
|
+
* WGSL counterpart of `shaders/ribbon.vert` + `shaders/ribbon.frag`. Vertex and
|
|
15
|
+
* fragment entry points share one source per WGSL convention, and the
|
|
16
|
+
* per-vertex attributes bind by `@location`, matching the declaration order and
|
|
17
|
+
* byte offsets of {@link RibbonParticles.dataLayout}.
|
|
18
|
+
*
|
|
19
|
+
* The uniform struct is the one the WebGPU particle renderer writes for every
|
|
20
|
+
* mode, so `localBounds` and `uvBounds` are declared but unused here: the strip
|
|
21
|
+
* carries its own final positions and UVs, and only the projection, the system
|
|
22
|
+
* transform and the premultiply flag are read.
|
|
23
|
+
*/
|
|
24
|
+
const ribbonParticleWgsl = ribbon_particles_default;
|
|
25
|
+
/**
|
|
26
|
+
* A connected triangle strip through the system's particles: one ribbon per
|
|
27
|
+
* system, drawn as a single non-instanced draw.
|
|
28
|
+
*
|
|
29
|
+
* Every particle contributes two vertices, offset either side of the path
|
|
30
|
+
* running through its neighbours, so the whole live range becomes one continuous
|
|
31
|
+
* band - the shape a sword arc, a projectile streak or a smoke plume wants,
|
|
32
|
+
* where a quad-per-particle would show as a dotted line.
|
|
33
|
+
*
|
|
34
|
+
* Layout of the per-vertex buffer {@link build} fills (20 bytes, 3 attributes):
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* a_position f32x2 (offset 0, 8 bytes) strip vertex (system-local)
|
|
38
|
+
* a_texcoord f32x2 (offset 8, 8 bytes) u along the strip, v across it
|
|
39
|
+
* a_color u8x4 (offset 16, 4 bytes) RGBA tint, normalised
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* **No ribbon-specific styling parameters.** Half-width comes from the
|
|
43
|
+
* particle's `scaleX` and the tint from its `color`, so the existing update
|
|
44
|
+
* modules already produce the expected look: `ScaleOverLifetime` tapers the
|
|
45
|
+
* tail, `ColorOverLifetime` gradients along the streak, and
|
|
46
|
+
* `AlphaFadeOverLifetime` fades it out. {@link RibbonParticlesOptions.width}
|
|
47
|
+
* only sets the base the scale multiplies.
|
|
48
|
+
*
|
|
49
|
+
* **The strip is built on the CPU**, so this mode is not GPU-eligible and a
|
|
50
|
+
* system using it stays on the CPU simulation path (observable through
|
|
51
|
+
* `ParticleSystem.gpuMode`). That is what guarantees the builder reads valid
|
|
52
|
+
* positions in emission order: CPU-mode slots are dense and compaction copies
|
|
53
|
+
* survivors forward stably, which is exactly the ordering a connected strip
|
|
54
|
+
* needs.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const trail = new ParticleSystem(sparkTexture, {
|
|
58
|
+
* capacity: 64,
|
|
59
|
+
* render: new RibbonParticles({ width: 12 }),
|
|
60
|
+
* });
|
|
61
|
+
*
|
|
62
|
+
* trail.addUpdateModule(new ScaleOverLifetime(1, 0)); // tapers to a point
|
|
63
|
+
*/
|
|
64
|
+
var RibbonParticles = class extends ParticleRenderMode {
|
|
65
|
+
instanced = false;
|
|
66
|
+
/**
|
|
67
|
+
* Floats spanned by one vertex. Exposed so callers reading {@link data} can
|
|
68
|
+
* step through it without hard-coding the stride.
|
|
69
|
+
*/
|
|
70
|
+
floatsPerVertex = wordsPerVertex;
|
|
71
|
+
/**
|
|
72
|
+
* A strip has no fixed vertex count - the draw covers whatever {@link build}
|
|
73
|
+
* emitted this frame. Non-indexed by construction: the strip's own vertex
|
|
74
|
+
* order is its topology, and an index list would pin the draw to a fixed
|
|
75
|
+
* index count.
|
|
76
|
+
*/
|
|
77
|
+
dataLayout = new ParticleBufferLayout({
|
|
78
|
+
attributes: [
|
|
79
|
+
{
|
|
80
|
+
name: "a_position",
|
|
81
|
+
size: 2,
|
|
82
|
+
type: "f32",
|
|
83
|
+
normalized: false,
|
|
84
|
+
offset: 0
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "a_texcoord",
|
|
88
|
+
size: 2,
|
|
89
|
+
type: "f32",
|
|
90
|
+
normalized: false,
|
|
91
|
+
offset: 8
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "a_color",
|
|
95
|
+
size: 4,
|
|
96
|
+
type: "u8",
|
|
97
|
+
normalized: true,
|
|
98
|
+
offset: 16
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
stride: vertexStrideBytes,
|
|
102
|
+
topology: "triangle-strip",
|
|
103
|
+
usage: "stream"
|
|
104
|
+
});
|
|
105
|
+
_width;
|
|
106
|
+
_material = null;
|
|
107
|
+
_float32 = new Float32Array(this.data);
|
|
108
|
+
_uint32 = new Uint32Array(this.data);
|
|
109
|
+
constructor(options = {}) {
|
|
110
|
+
super();
|
|
111
|
+
this._width = options.width ?? defaultWidth;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Built on first read rather than in the constructor: a system may be
|
|
115
|
+
* simulated without ever being drawn, and the shader pair is only needed
|
|
116
|
+
* once a backend actually compiles it.
|
|
117
|
+
*/
|
|
118
|
+
get material() {
|
|
119
|
+
this._material ??= new ParticleMaterial({ shader: new ShaderSource({
|
|
120
|
+
glsl: {
|
|
121
|
+
vertex: ribbon_default$1,
|
|
122
|
+
fragment: ribbon_default
|
|
123
|
+
},
|
|
124
|
+
wgsl: ribbonParticleWgsl
|
|
125
|
+
}) });
|
|
126
|
+
return this._material;
|
|
127
|
+
}
|
|
128
|
+
build(_system, particles) {
|
|
129
|
+
const limit = particles.count;
|
|
130
|
+
if (limit < 2) {
|
|
131
|
+
this._setCount(0);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const { x: posX, y: posY } = particles.position;
|
|
135
|
+
const { x: scaleX } = particles.scale;
|
|
136
|
+
const { color } = particles;
|
|
137
|
+
let totalLength = 0;
|
|
138
|
+
for (let i = 1; i < limit; i++) totalLength += Math.sqrt((posX[i] - posX[i - 1]) ** 2 + (posY[i] - posY[i - 1]) ** 2);
|
|
139
|
+
if (totalLength === 0) {
|
|
140
|
+
this._setCount(0);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this._ensureCapacity(limit * 2 * vertexStrideBytes);
|
|
144
|
+
const f32 = this._float32;
|
|
145
|
+
const u32 = this._uint32;
|
|
146
|
+
const width = this._width;
|
|
147
|
+
let vertexCount = 0;
|
|
148
|
+
let travelled = 0;
|
|
149
|
+
let previousDirectionX = 0;
|
|
150
|
+
let previousDirectionY = 0;
|
|
151
|
+
let hasPreviousDirection = false;
|
|
152
|
+
for (let i = 0; i < limit; i++) {
|
|
153
|
+
if (i > 0) travelled += Math.sqrt((posX[i] - posX[i - 1]) ** 2 + (posY[i] - posY[i - 1]) ** 2);
|
|
154
|
+
const before = i === 0 ? 0 : i - 1;
|
|
155
|
+
const after = i === limit - 1 ? i : i + 1;
|
|
156
|
+
let directionX = posX[after] - posX[before];
|
|
157
|
+
let directionY = posY[after] - posY[before];
|
|
158
|
+
const length = Math.sqrt(directionX ** 2 + directionY ** 2);
|
|
159
|
+
if (length > 0) {
|
|
160
|
+
directionX /= length;
|
|
161
|
+
directionY /= length;
|
|
162
|
+
previousDirectionX = directionX;
|
|
163
|
+
previousDirectionY = directionY;
|
|
164
|
+
hasPreviousDirection = true;
|
|
165
|
+
} else if (hasPreviousDirection) {
|
|
166
|
+
directionX = previousDirectionX;
|
|
167
|
+
directionY = previousDirectionY;
|
|
168
|
+
} else continue;
|
|
169
|
+
const halfWidth = width * scaleX[i] / 2;
|
|
170
|
+
const offsetX = -directionY * halfWidth;
|
|
171
|
+
const offsetY = directionX * halfWidth;
|
|
172
|
+
const u = travelled / totalLength;
|
|
173
|
+
const packedColor = color[i];
|
|
174
|
+
let offset = vertexCount * wordsPerVertex;
|
|
175
|
+
f32[offset + 0] = posX[i] - offsetX;
|
|
176
|
+
f32[offset + 1] = posY[i] - offsetY;
|
|
177
|
+
f32[offset + 2] = u;
|
|
178
|
+
f32[offset + 3] = 0;
|
|
179
|
+
u32[offset + 4] = packedColor;
|
|
180
|
+
offset += wordsPerVertex;
|
|
181
|
+
f32[offset + 0] = posX[i] + offsetX;
|
|
182
|
+
f32[offset + 1] = posY[i] + offsetY;
|
|
183
|
+
f32[offset + 2] = u;
|
|
184
|
+
f32[offset + 3] = 1;
|
|
185
|
+
u32[offset + 4] = packedColor;
|
|
186
|
+
vertexCount += 2;
|
|
187
|
+
}
|
|
188
|
+
this._setCount(vertexCount);
|
|
189
|
+
}
|
|
190
|
+
destroy() {
|
|
191
|
+
this._material?.destroy();
|
|
192
|
+
this._material = null;
|
|
193
|
+
}
|
|
194
|
+
_onBufferGrown(data) {
|
|
195
|
+
this._float32 = new Float32Array(data);
|
|
196
|
+
this._uint32 = new Uint32Array(data);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
//#endregion
|
|
201
|
+
export { RibbonParticles, ribbonParticleWgsl };
|
|
202
|
+
//# sourceMappingURL=RibbonParticles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RibbonParticles.js","names":["ribbonParticleWgslModule","vertexSource","fragmentSource"],"sources":["../../../src/renderModes/RibbonParticles.ts"],"sourcesContent":["import type { Material } from '@codexo/exojs';\nimport { ShaderSource } from '@codexo/exojs';\n\nimport type { ParticleBatch } from '#ParticleStorage';\nimport type { ParticleSystem } from '#ParticleSystem';\n\nimport { ParticleBufferLayout } from './ParticleBufferLayout';\nimport { ParticleMaterial } from './ParticleMaterial';\nimport { ParticleRenderMode } from './ParticleRenderMode';\nimport fragmentSource from './shaders/ribbon.frag';\nimport vertexSource from './shaders/ribbon.vert';\nimport ribbonParticleWgslModule from './shaders/ribbon-particles.wgsl';\n\nconst vertexStrideBytes = 20;\nconst wordsPerVertex = vertexStrideBytes / Float32Array.BYTES_PER_ELEMENT;\nconst defaultWidth = 1;\n\n/** Construction options for {@link RibbonParticles}. */\nexport interface RibbonParticlesOptions {\n /**\n * Width of the strip in system-local units, before per-particle scaling.\n * The half-width actually emitted is `width * scaleX[i] / 2`.\n *\n * @default 1\n */\n readonly width?: number;\n}\n\n/**\n * WGSL counterpart of `shaders/ribbon.vert` + `shaders/ribbon.frag`. Vertex and\n * fragment entry points share one source per WGSL convention, and the\n * per-vertex attributes bind by `@location`, matching the declaration order and\n * byte offsets of {@link RibbonParticles.dataLayout}.\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: the strip\n * carries its own final positions and UVs, and only the projection, the system\n * transform and the premultiply flag are read.\n */\nexport const ribbonParticleWgsl: string = ribbonParticleWgslModule;\n\n/**\n * A connected triangle strip through the system's particles: one ribbon per\n * system, drawn as a single non-instanced draw.\n *\n * Every particle contributes two vertices, offset either side of the path\n * running through its neighbours, so the whole live range becomes one continuous\n * band - the shape a sword arc, a projectile streak or a smoke plume wants,\n * where a quad-per-particle would show as a dotted line.\n *\n * Layout of the per-vertex buffer {@link build} fills (20 bytes, 3 attributes):\n *\n * ```\n * a_position f32x2 (offset 0, 8 bytes) strip vertex (system-local)\n * a_texcoord f32x2 (offset 8, 8 bytes) u along the strip, v across it\n * a_color u8x4 (offset 16, 4 bytes) RGBA tint, normalised\n * ```\n *\n * **No ribbon-specific styling parameters.** Half-width comes from the\n * particle's `scaleX` and the tint from its `color`, so the existing update\n * modules already produce the expected look: `ScaleOverLifetime` tapers the\n * tail, `ColorOverLifetime` gradients along the streak, and\n * `AlphaFadeOverLifetime` fades it out. {@link RibbonParticlesOptions.width}\n * only sets the base the scale multiplies.\n *\n * **The strip is built on the CPU**, so this mode is not GPU-eligible and a\n * system using it stays on the CPU simulation path (observable through\n * `ParticleSystem.gpuMode`). That is what guarantees the builder reads valid\n * positions in emission order: CPU-mode slots are dense and compaction copies\n * survivors forward stably, which is exactly the ordering a connected strip\n * needs.\n *\n * @example\n * const trail = new ParticleSystem(sparkTexture, {\n * capacity: 64,\n * render: new RibbonParticles({ width: 12 }),\n * });\n *\n * trail.addUpdateModule(new ScaleOverLifetime(1, 0)); // tapers to a point\n */\nexport class RibbonParticles extends ParticleRenderMode {\n public readonly instanced = false;\n\n /**\n * Floats spanned by one vertex. Exposed so callers reading {@link data} can\n * step through it without hard-coding the stride.\n */\n public readonly floatsPerVertex = wordsPerVertex;\n\n /**\n * A strip has no fixed vertex count - the draw covers whatever {@link build}\n * emitted this frame. Non-indexed by construction: the strip's own vertex\n * order is its topology, and an index list would pin the draw to a fixed\n * index count.\n */\n public readonly dataLayout = new ParticleBufferLayout({\n attributes: [\n { name: 'a_position', size: 2, type: 'f32', normalized: false, offset: 0 },\n { name: 'a_texcoord', size: 2, type: 'f32', normalized: false, offset: 8 },\n { name: 'a_color', size: 4, type: 'u8', normalized: true, offset: 16 },\n ],\n stride: vertexStrideBytes,\n topology: 'triangle-strip',\n usage: 'stream',\n });\n\n private readonly _width: number;\n\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: RibbonParticlesOptions = {}) {\n super();\n\n this._width = options.width ?? defaultWidth;\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: ribbonParticleWgsl,\n }),\n });\n\n return this._material;\n }\n\n public build(_system: ParticleSystem, particles: ParticleBatch): void {\n const limit = particles.count;\n\n // A strip needs a segment, and a segment needs two particles.\n if (limit < 2) {\n this._setCount(0);\n\n return;\n }\n\n const { x: posX, y: posY } = particles.position;\n const { x: scaleX } = particles.scale;\n const { color } = particles;\n\n // UVs run along the strip by arc length rather than by particle index, so\n // unevenly spaced particles do not stretch the texture unevenly. That needs\n // the total up front, hence the separate pass.\n let totalLength = 0;\n\n for (let i = 1; i < limit; i++) {\n totalLength += Math.sqrt((posX[i]! - posX[i - 1]!) ** 2 + (posY[i]! - posY[i - 1]!) ** 2);\n }\n\n // Every particle sits on the same spot: no path, no direction, nothing to\n // expand a strip around.\n if (totalLength === 0) {\n this._setCount(0);\n\n return;\n }\n\n // Must precede the view reads below: growing swaps in fresh typed-array\n // views over a new backing buffer.\n this._ensureCapacity(limit * 2 * vertexStrideBytes);\n\n const f32 = this._float32;\n const u32 = this._uint32;\n const width = this._width;\n\n let vertexCount = 0;\n let travelled = 0;\n let previousDirectionX = 0;\n let previousDirectionY = 0;\n let hasPreviousDirection = false;\n\n for (let i = 0; i < limit; i++) {\n if (i > 0) {\n travelled += Math.sqrt((posX[i]! - posX[i - 1]!) ** 2 + (posY[i]! - posY[i - 1]!) ** 2);\n }\n\n // Central difference through the neighbours, clamped to a one-sided\n // difference at the two ends of the strip.\n const before = i === 0 ? 0 : i - 1;\n const after = i === limit - 1 ? i : i + 1;\n\n let directionX = posX[after]! - posX[before]!;\n let directionY = posY[after]! - posY[before]!;\n\n const length = Math.sqrt(directionX ** 2 + directionY ** 2);\n\n if (length > 0) {\n directionX /= length;\n directionY /= length;\n previousDirectionX = directionX;\n previousDirectionY = directionY;\n hasPreviousDirection = true;\n } else if (hasPreviousDirection) {\n // Coincident neighbours leave no direction to expand around; carrying\n // the last one keeps the strip continuous instead of collapsing it.\n directionX = previousDirectionX;\n directionY = previousDirectionY;\n } else {\n // Head of the strip with nothing to inherit - this particle cannot be\n // placed, so it contributes no pair.\n continue;\n }\n\n const halfWidth = (width * scaleX[i]!) / 2;\n // The direction rotated 90°, scaled to the half-width in one step.\n const offsetX = -directionY * halfWidth;\n const offsetY = directionX * halfWidth;\n const u = travelled / totalLength;\n const packedColor = color[i]!;\n\n let offset = vertexCount * wordsPerVertex;\n\n f32[offset + 0] = posX[i]! - offsetX;\n f32[offset + 1] = posY[i]! - offsetY;\n f32[offset + 2] = u;\n f32[offset + 3] = 0;\n u32[offset + 4] = packedColor;\n\n offset += wordsPerVertex;\n\n f32[offset + 0] = posX[i]! + offsetX;\n f32[offset + 1] = posY[i]! + offsetY;\n f32[offset + 2] = u;\n f32[offset + 3] = 1;\n u32[offset + 4] = packedColor;\n\n vertexCount += 2;\n }\n\n this._setCount(vertexCount);\n }\n\n public override destroy(): void {\n this._material?.destroy();\n this._material = null;\n }\n\n protected override _onBufferGrown(data: ArrayBuffer): void {\n this._float32 = new Float32Array(data);\n this._uint32 = new Uint32Array(data);\n }\n}\n"],"mappings":";;;;;;;;;AAaA,MAAM,oBAAoB;AAC1B,MAAM,iBAAiB,oBAAoB,aAAa;AACxD,MAAM,eAAe;;;;;;;;;;;;AAwBrB,MAAa,qBAA6BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyC1C,IAAa,kBAAb,cAAqC,mBAAmB;CACtD,AAAgB,YAAY;;;;;CAM5B,AAAgB,kBAAkB;;;;;;;CAQlC,AAAgB,aAAa,IAAI,qBAAqB;EACpD,YAAY;GACV;IAAE,MAAM;IAAc,MAAM;IAAG,MAAM;IAAO,YAAY;IAAO,QAAQ;GAAE;GACzE;IAAE,MAAM;IAAc,MAAM;IAAG,MAAM;IAAO,YAAY;IAAO,QAAQ;GAAE;GACzE;IAAE,MAAM;IAAW,MAAM;IAAG,MAAM;IAAM,YAAY;IAAM,QAAQ;GAAG;EACvE;EACA,QAAQ;EACR,UAAU;EACV,OAAO;CACT,CAAC;CAED,AAAiB;CAEjB,AAAQ,YAAqC;CAC7C,AAAQ,WAAW,IAAI,aAAa,KAAK,IAAI;CAC7C,AAAQ,UAAU,IAAI,YAAY,KAAK,IAAI;CAE3C,AAAO,YAAY,UAAkC,CAAC,GAAG;EACvD,MAAM;EAEN,KAAK,SAAS,QAAQ,SAAS;CACjC;;;;;;CAOA,IAAW,WAAqB;EAC9B,KAAK,cAAc,IAAI,iBAAiB,EACtC,QAAQ,IAAI,aAAa;GACvB,MAAM;IAAE,QAAQC;IAAc,UAAUC;GAAe;GACvD,MAAM;EACR,CAAC,EACH,CAAC;EAED,OAAO,KAAK;CACd;CAEA,AAAO,MAAM,SAAyB,WAAgC;EACpE,MAAM,QAAQ,UAAU;EAGxB,IAAI,QAAQ,GAAG;GACb,KAAK,UAAU,CAAC;GAEhB;EACF;EAEA,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,UAAU;EACvC,MAAM,EAAE,GAAG,WAAW,UAAU;EAChC,MAAM,EAAE,UAAU;EAKlB,IAAI,cAAc;EAElB,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KACzB,eAAe,KAAK,MAAM,KAAK,KAAM,KAAK,IAAI,OAAQ,KAAK,KAAK,KAAM,KAAK,IAAI,OAAQ,CAAC;EAK1F,IAAI,gBAAgB,GAAG;GACrB,KAAK,UAAU,CAAC;GAEhB;EACF;EAIA,KAAK,gBAAgB,QAAQ,IAAI,iBAAiB;EAElD,MAAM,MAAM,KAAK;EACjB,MAAM,MAAM,KAAK;EACjB,MAAM,QAAQ,KAAK;EAEnB,IAAI,cAAc;EAClB,IAAI,YAAY;EAChB,IAAI,qBAAqB;EACzB,IAAI,qBAAqB;EACzB,IAAI,uBAAuB;EAE3B,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;GAC9B,IAAI,IAAI,GACN,aAAa,KAAK,MAAM,KAAK,KAAM,KAAK,IAAI,OAAQ,KAAK,KAAK,KAAM,KAAK,IAAI,OAAQ,CAAC;GAKxF,MAAM,SAAS,MAAM,IAAI,IAAI,IAAI;GACjC,MAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI,IAAI;GAExC,IAAI,aAAa,KAAK,SAAU,KAAK;GACrC,IAAI,aAAa,KAAK,SAAU,KAAK;GAErC,MAAM,SAAS,KAAK,KAAK,cAAc,IAAI,cAAc,CAAC;GAE1D,IAAI,SAAS,GAAG;IACd,cAAc;IACd,cAAc;IACd,qBAAqB;IACrB,qBAAqB;IACrB,uBAAuB;GACzB,OAAO,IAAI,sBAAsB;IAG/B,aAAa;IACb,aAAa;GACf,OAGE;GAGF,MAAM,YAAa,QAAQ,OAAO,KAAO;GAEzC,MAAM,UAAU,CAAC,aAAa;GAC9B,MAAM,UAAU,aAAa;GAC7B,MAAM,IAAI,YAAY;GACtB,MAAM,cAAc,MAAM;GAE1B,IAAI,SAAS,cAAc;GAE3B,IAAI,SAAS,KAAK,KAAK,KAAM;GAC7B,IAAI,SAAS,KAAK,KAAK,KAAM;GAC7B,IAAI,SAAS,KAAK;GAClB,IAAI,SAAS,KAAK;GAClB,IAAI,SAAS,KAAK;GAElB,UAAU;GAEV,IAAI,SAAS,KAAK,KAAK,KAAM;GAC7B,IAAI,SAAS,KAAK,KAAK,KAAM;GAC7B,IAAI,SAAS,KAAK;GAClB,IAAI,SAAS,KAAK;GAClB,IAAI,SAAS,KAAK;GAElB,eAAe;EACjB;EAEA,KAAK,UAAU,WAAW;CAC5B;CAEA,AAAgB,UAAgB;EAC9B,KAAK,WAAW,QAAQ;EACxB,KAAK,YAAY;CACnB;CAEA,AAAmB,eAAe,MAAyB;EACzD,KAAK,WAAW,IAAI,aAAa,IAAI;EACrC,KAAK,UAAU,IAAI,YAAY,IAAI;CACrC;AACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/renderModes/shaders/mesh-particles.wgsl
|
|
2
|
+
var mesh_particles_default = "struct ProjectionUniforms {\n projection: mat4x4<f32>,\n translation: mat4x4<f32>,\n flags: vec4<f32>,\n localBounds: vec4<f32>, // quadMin.xy, quadSize.xy — unused by this mode\n uvBounds: vec4<f32>, // uvMin.xy, uvMax.xy — unused by this mode\n};\n\n@group(0) @binding(0)\nvar<uniform> uniforms: ProjectionUniforms;\n\n@group(1) @binding(0)\nvar particleTexture: texture_2d<f32>;\n\n@group(1) @binding(1)\nvar particleSampler: sampler;\n\nstruct VertexInput {\n // Per-instance (one entry per particle, 40 bytes total).\n @location(0) translation: vec2<f32>,\n @location(1) scale: vec2<f32>,\n @location(2) rotation: f32,\n @location(3) color: vec4<f32>,\n @location(4) uvMin: vec2<f32>, // pre-resolved frame UV (top-left)\n @location(5) uvMax: vec2<f32>, // pre-resolved frame UV (bottom-right)\n // Per-vertex, from the mode's mesh geometry.\n @location(6) meshPosition: vec2<f32>,\n @location(7) meshTexcoord: vec2<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) texcoord: vec2<f32>,\n @location(1) color: vec4<f32>,\n};\n\n@vertex\nfn vertexMain(input: VertexInput) -> VertexOutput {\n let localPosition = input.meshPosition;\n\n let radians = radians(input.rotation);\n let sinValue = sin(radians);\n let cosValue = cos(radians);\n let rotated = vec2<f32>(\n (localPosition.x * (input.scale.x * cosValue)) + (localPosition.y * (input.scale.y * sinValue)) + input.translation.x,\n (localPosition.x * (input.scale.x * -sinValue)) + (localPosition.y * (input.scale.y * cosValue)) + input.translation.y\n );\n\n var output: VertexOutput;\n\n output.position = uniforms.projection * uniforms.translation * vec4<f32>(rotated, 0.0, 1.0);\n // The mesh's own UVs address the particle's frame rather than the whole\n // texture, so a mesh particle still selects an atlas frame.\n output.texcoord = mix(input.uvMin, input.uvMax, input.meshTexcoord);\n output.color = vec4(input.color.rgb * input.color.a, input.color.a);\n\n return output;\n}\n\n@fragment\nfn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {\n let sampled = textureSample(particleTexture, particleSampler, input.texcoord);\n let premultipliedSample = select(sampled, vec4(sampled.rgb * sampled.a, sampled.a), uniforms.flags.x > 0.5);\n\n return premultipliedSample * input.color;\n}\n";
|
|
3
|
+
|
|
4
|
+
//#endregion
|
|
5
|
+
export { mesh_particles_default as default };
|
|
6
|
+
//# sourceMappingURL=mesh-particles.wgsl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesh-particles.wgsl.js","names":[],"sources":["../../../../src/renderModes/shaders/mesh-particles.wgsl"],"sourcesContent":["export default \"struct ProjectionUniforms {\\n projection: mat4x4<f32>,\\n translation: mat4x4<f32>,\\n flags: vec4<f32>,\\n localBounds: vec4<f32>, // quadMin.xy, quadSize.xy — unused by this mode\\n uvBounds: vec4<f32>, // uvMin.xy, uvMax.xy — unused by this mode\\n};\\n\\n@group(0) @binding(0)\\nvar<uniform> uniforms: ProjectionUniforms;\\n\\n@group(1) @binding(0)\\nvar particleTexture: texture_2d<f32>;\\n\\n@group(1) @binding(1)\\nvar particleSampler: sampler;\\n\\nstruct VertexInput {\\n // Per-instance (one entry per particle, 40 bytes total).\\n @location(0) translation: vec2<f32>,\\n @location(1) scale: vec2<f32>,\\n @location(2) rotation: f32,\\n @location(3) color: vec4<f32>,\\n @location(4) uvMin: vec2<f32>, // pre-resolved frame UV (top-left)\\n @location(5) uvMax: vec2<f32>, // pre-resolved frame UV (bottom-right)\\n // Per-vertex, from the mode's mesh geometry.\\n @location(6) meshPosition: vec2<f32>,\\n @location(7) meshTexcoord: vec2<f32>,\\n};\\n\\nstruct VertexOutput {\\n @builtin(position) position: vec4<f32>,\\n @location(0) texcoord: vec2<f32>,\\n @location(1) color: vec4<f32>,\\n};\\n\\n@vertex\\nfn vertexMain(input: VertexInput) -> VertexOutput {\\n let localPosition = input.meshPosition;\\n\\n let radians = radians(input.rotation);\\n let sinValue = sin(radians);\\n let cosValue = cos(radians);\\n let rotated = vec2<f32>(\\n (localPosition.x * (input.scale.x * cosValue)) + (localPosition.y * (input.scale.y * sinValue)) + input.translation.x,\\n (localPosition.x * (input.scale.x * -sinValue)) + (localPosition.y * (input.scale.y * cosValue)) + input.translation.y\\n );\\n\\n var output: VertexOutput;\\n\\n output.position = uniforms.projection * uniforms.translation * vec4<f32>(rotated, 0.0, 1.0);\\n // The mesh's own UVs address the particle's frame rather than the whole\\n // texture, so a mesh particle still selects an atlas frame.\\n output.texcoord = mix(input.uvMin, input.uvMax, input.meshTexcoord);\\n output.color = vec4(input.color.rgb * input.color.a, input.color.a);\\n\\n return output;\\n}\\n\\n@fragment\\nfn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {\\n let sampled = textureSample(particleTexture, particleSampler, input.texcoord);\\n let premultipliedSample = select(sampled, vec4(sampled.rgb * sampled.a, sampled.a), uniforms.flags.x > 0.5);\\n\\n return premultipliedSample * input.color;\\n}\\n\";"],"mappings":";AAAA,6BAAe"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/renderModes/shaders/mesh.vert
|
|
2
|
+
var mesh_default = "#version 300 es\nprecision highp float;\nprecision highp int;\n\n// Per-instance attributes (one entry per particle, 40 bytes total).\nlayout(location = 0) in vec2 a_position; // particle position in system-local space\nlayout(location = 1) in vec2 a_scale; // particle scale\nlayout(location = 2) in float a_rotation; // particle rotation in degrees\nlayout(location = 3) in vec4 a_color; // RGBA tint\nlayout(location = 4) in vec2 a_uvMin; // top-left UV (u, v) — pre-resolved per instance\nlayout(location = 5) in vec2 a_uvMax; // bottom-right UV (u, v) — pre-resolved per instance\n\n// Per-vertex attributes from the mode's mesh geometry, normalised to (x, y)\n// and (u, v) so this shader is the same for every mesh.\nlayout(location = 6) in vec2 a_meshPosition;\nlayout(location = 7) in vec2 a_meshTexcoord;\n\nuniform mat3 u_projection;\nuniform mat3 u_systemTransform;\n\nout vec2 v_texcoord;\nout vec4 v_color;\n\nvoid main(void) {\n // Per-particle scale + rotation, identical to the quad's.\n vec2 rotation = vec2(sin(radians(a_rotation)), cos(radians(a_rotation)));\n vec2 transformed = vec2(\n (a_meshPosition.x * (a_scale.x * rotation.y)) + (a_meshPosition.y * (a_scale.y * rotation.x)),\n (a_meshPosition.x * (a_scale.x * -rotation.x)) + (a_meshPosition.y * (a_scale.y * rotation.y))\n );\n\n vec3 worldPos = vec3(transformed + a_position, 1.0);\n\n gl_Position = vec4((u_projection * u_systemTransform * worldPos).xy, 0.0, 1.0);\n\n // The mesh's own UVs address the particle's frame rather than the whole\n // texture, so a mesh particle still selects an atlas frame.\n v_texcoord = mix(a_uvMin, a_uvMax, a_meshTexcoord);\n\n v_color = vec4(a_color.rgb * a_color.a, a_color.a);\n}\n";
|
|
3
|
+
|
|
4
|
+
//#endregion
|
|
5
|
+
export { mesh_default as default };
|
|
6
|
+
//# sourceMappingURL=mesh.vert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesh.vert.js","names":[],"sources":["../../../../src/renderModes/shaders/mesh.vert"],"sourcesContent":["export default \"#version 300 es\\nprecision highp float;\\nprecision highp int;\\n\\n// Per-instance attributes (one entry per particle, 40 bytes total).\\nlayout(location = 0) in vec2 a_position; // particle position in system-local space\\nlayout(location = 1) in vec2 a_scale; // particle scale\\nlayout(location = 2) in float a_rotation; // particle rotation in degrees\\nlayout(location = 3) in vec4 a_color; // RGBA tint\\nlayout(location = 4) in vec2 a_uvMin; // top-left UV (u, v) — pre-resolved per instance\\nlayout(location = 5) in vec2 a_uvMax; // bottom-right UV (u, v) — pre-resolved per instance\\n\\n// Per-vertex attributes from the mode's mesh geometry, normalised to (x, y)\\n// and (u, v) so this shader is the same for every mesh.\\nlayout(location = 6) in vec2 a_meshPosition;\\nlayout(location = 7) in vec2 a_meshTexcoord;\\n\\nuniform mat3 u_projection;\\nuniform mat3 u_systemTransform;\\n\\nout vec2 v_texcoord;\\nout vec4 v_color;\\n\\nvoid main(void) {\\n // Per-particle scale + rotation, identical to the quad's.\\n vec2 rotation = vec2(sin(radians(a_rotation)), cos(radians(a_rotation)));\\n vec2 transformed = vec2(\\n (a_meshPosition.x * (a_scale.x * rotation.y)) + (a_meshPosition.y * (a_scale.y * rotation.x)),\\n (a_meshPosition.x * (a_scale.x * -rotation.x)) + (a_meshPosition.y * (a_scale.y * rotation.y))\\n );\\n\\n vec3 worldPos = vec3(transformed + a_position, 1.0);\\n\\n gl_Position = vec4((u_projection * u_systemTransform * worldPos).xy, 0.0, 1.0);\\n\\n // The mesh's own UVs address the particle's frame rather than the whole\\n // texture, so a mesh particle still selects an atlas frame.\\n v_texcoord = mix(a_uvMin, a_uvMax, a_meshTexcoord);\\n\\n v_color = vec4(a_color.rgb * a_color.a, a_color.a);\\n}\\n\";"],"mappings":";AAAA,mBAAe"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/renderModes/shaders/quad-particles.wgsl
|
|
2
|
+
var quad_particles_default = "struct ProjectionUniforms {\n projection: mat4x4<f32>,\n translation: mat4x4<f32>,\n flags: vec4<f32>,\n localBounds: vec4<f32>, // quadMin.xy, quadSize.xy\n uvBounds: vec4<f32>, // uvMin.xy, uvMax.xy\n};\n\n@group(0) @binding(0)\nvar<uniform> uniforms: ProjectionUniforms;\n\n@group(1) @binding(0)\nvar particleTexture: texture_2d<f32>;\n\n@group(1) @binding(1)\nvar particleSampler: sampler;\n\n// Per-instance attributes (one entry per particle, 40 bytes total).\nstruct VertexInput {\n @builtin(vertex_index) vertexIndex: u32,\n @location(0) translation: vec2<f32>,\n @location(1) scale: vec2<f32>,\n @location(2) rotation: f32,\n @location(3) color: vec4<f32>,\n @location(4) uvMin: vec2<f32>, // pre-resolved frame UV (top-left)\n @location(5) uvMax: vec2<f32>, // pre-resolved frame UV (bottom-right)\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) texcoord: vec2<f32>,\n @location(1) color: vec4<f32>,\n};\n\n@vertex\nfn vertexMain(input: VertexInput) -> VertexOutput {\n let quadMin = uniforms.localBounds.xy;\n let quadSize = uniforms.localBounds.zw;\n\n // Unit-quad corner from the index buffer's value: 0 -> (0,0), 1 -> (1,0),\n // 2 -> (1,1), 3 -> (0,1) — the same mapping the GLSL derives from gl_VertexID.\n let unitPosition = vec2<f32>(\n f32(((input.vertexIndex + 1u) >> 1u) & 1u),\n f32(input.vertexIndex >> 1u)\n );\n\n let localPosition = quadMin + (unitPosition * quadSize);\n let radians = radians(input.rotation);\n let sinValue = sin(radians);\n let cosValue = cos(radians);\n let rotated = vec2<f32>(\n (localPosition.x * (input.scale.x * cosValue)) + (localPosition.y * (input.scale.y * sinValue)) + input.translation.x,\n (localPosition.x * (input.scale.x * -sinValue)) + (localPosition.y * (input.scale.y * cosValue)) + input.translation.y\n );\n\n var output: VertexOutput;\n\n output.position = uniforms.projection * uniforms.translation * vec4<f32>(rotated, 0.0, 1.0);\n output.texcoord = input.uvMin + ((input.uvMax - input.uvMin) * unitPosition);\n output.color = vec4(input.color.rgb * input.color.a, input.color.a);\n\n return output;\n}\n\n@fragment\nfn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {\n let sample = textureSample(particleTexture, particleSampler, input.texcoord);\n let premultipliedSample = select(sample, vec4(sample.rgb * sample.a, sample.a), uniforms.flags.x > 0.5);\n\n return premultipliedSample * input.color;\n}\n";
|
|
3
|
+
|
|
4
|
+
//#endregion
|
|
5
|
+
export { quad_particles_default as default };
|
|
6
|
+
//# sourceMappingURL=quad-particles.wgsl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quad-particles.wgsl.js","names":[],"sources":["../../../../src/renderModes/shaders/quad-particles.wgsl"],"sourcesContent":["export default \"struct ProjectionUniforms {\\n projection: mat4x4<f32>,\\n translation: mat4x4<f32>,\\n flags: vec4<f32>,\\n localBounds: vec4<f32>, // quadMin.xy, quadSize.xy\\n uvBounds: vec4<f32>, // uvMin.xy, uvMax.xy\\n};\\n\\n@group(0) @binding(0)\\nvar<uniform> uniforms: ProjectionUniforms;\\n\\n@group(1) @binding(0)\\nvar particleTexture: texture_2d<f32>;\\n\\n@group(1) @binding(1)\\nvar particleSampler: sampler;\\n\\n// Per-instance attributes (one entry per particle, 40 bytes total).\\nstruct VertexInput {\\n @builtin(vertex_index) vertexIndex: u32,\\n @location(0) translation: vec2<f32>,\\n @location(1) scale: vec2<f32>,\\n @location(2) rotation: f32,\\n @location(3) color: vec4<f32>,\\n @location(4) uvMin: vec2<f32>, // pre-resolved frame UV (top-left)\\n @location(5) uvMax: vec2<f32>, // pre-resolved frame UV (bottom-right)\\n};\\n\\nstruct VertexOutput {\\n @builtin(position) position: vec4<f32>,\\n @location(0) texcoord: vec2<f32>,\\n @location(1) color: vec4<f32>,\\n};\\n\\n@vertex\\nfn vertexMain(input: VertexInput) -> VertexOutput {\\n let quadMin = uniforms.localBounds.xy;\\n let quadSize = uniforms.localBounds.zw;\\n\\n // Unit-quad corner from the index buffer's value: 0 -> (0,0), 1 -> (1,0),\\n // 2 -> (1,1), 3 -> (0,1) — the same mapping the GLSL derives from gl_VertexID.\\n let unitPosition = vec2<f32>(\\n f32(((input.vertexIndex + 1u) >> 1u) & 1u),\\n f32(input.vertexIndex >> 1u)\\n );\\n\\n let localPosition = quadMin + (unitPosition * quadSize);\\n let radians = radians(input.rotation);\\n let sinValue = sin(radians);\\n let cosValue = cos(radians);\\n let rotated = vec2<f32>(\\n (localPosition.x * (input.scale.x * cosValue)) + (localPosition.y * (input.scale.y * sinValue)) + input.translation.x,\\n (localPosition.x * (input.scale.x * -sinValue)) + (localPosition.y * (input.scale.y * cosValue)) + input.translation.y\\n );\\n\\n var output: VertexOutput;\\n\\n output.position = uniforms.projection * uniforms.translation * vec4<f32>(rotated, 0.0, 1.0);\\n output.texcoord = input.uvMin + ((input.uvMax - input.uvMin) * unitPosition);\\n output.color = vec4(input.color.rgb * input.color.a, input.color.a);\\n\\n return output;\\n}\\n\\n@fragment\\nfn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {\\n let sample = textureSample(particleTexture, particleSampler, input.texcoord);\\n let premultipliedSample = select(sample, vec4(sample.rgb * sample.a, sample.a), uniforms.flags.x > 0.5);\\n\\n return premultipliedSample * input.color;\\n}\\n\";"],"mappings":";AAAA,6BAAe"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/renderModes/shaders/ribbon-particles.wgsl
|
|
2
|
+
var ribbon_particles_default = "struct ProjectionUniforms {\n projection: mat4x4<f32>,\n translation: mat4x4<f32>,\n flags: vec4<f32>,\n localBounds: vec4<f32>, // quadMin.xy, quadSize.xy — unused by this mode\n uvBounds: vec4<f32>, // uvMin.xy, uvMax.xy — unused by this mode\n};\n\n@group(0) @binding(0)\nvar<uniform> uniforms: ProjectionUniforms;\n\n@group(1) @binding(0)\nvar particleTexture: texture_2d<f32>;\n\n@group(1) @binding(1)\nvar particleSampler: sampler;\n\n// Per-vertex attributes (two vertices per particle, 20 bytes each).\nstruct VertexInput {\n @location(0) position: vec2<f32>, // strip vertex in system-local space\n @location(1) texcoord: vec2<f32>, // u along the strip, v across it\n @location(2) color: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) texcoord: vec2<f32>,\n @location(1) color: vec4<f32>,\n};\n\n@vertex\nfn vertexMain(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n\n // The strip is already expanded on the CPU — each vertex carries its final\n // system-local position, so there is no per-particle transform to rebuild here.\n output.position = uniforms.projection * uniforms.translation * vec4<f32>(input.position, 0.0, 1.0);\n output.texcoord = input.texcoord;\n output.color = vec4(input.color.rgb * input.color.a, input.color.a);\n\n return output;\n}\n\n@fragment\nfn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {\n let sample = textureSample(particleTexture, particleSampler, input.texcoord);\n let premultipliedSample = select(sample, vec4(sample.rgb * sample.a, sample.a), uniforms.flags.x > 0.5);\n\n return premultipliedSample * input.color;\n}\n";
|
|
3
|
+
|
|
4
|
+
//#endregion
|
|
5
|
+
export { ribbon_particles_default as default };
|
|
6
|
+
//# sourceMappingURL=ribbon-particles.wgsl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ribbon-particles.wgsl.js","names":[],"sources":["../../../../src/renderModes/shaders/ribbon-particles.wgsl"],"sourcesContent":["export default \"struct ProjectionUniforms {\\n projection: mat4x4<f32>,\\n translation: mat4x4<f32>,\\n flags: vec4<f32>,\\n localBounds: vec4<f32>, // quadMin.xy, quadSize.xy — unused by this mode\\n uvBounds: vec4<f32>, // uvMin.xy, uvMax.xy — unused by this mode\\n};\\n\\n@group(0) @binding(0)\\nvar<uniform> uniforms: ProjectionUniforms;\\n\\n@group(1) @binding(0)\\nvar particleTexture: texture_2d<f32>;\\n\\n@group(1) @binding(1)\\nvar particleSampler: sampler;\\n\\n// Per-vertex attributes (two vertices per particle, 20 bytes each).\\nstruct VertexInput {\\n @location(0) position: vec2<f32>, // strip vertex in system-local space\\n @location(1) texcoord: vec2<f32>, // u along the strip, v across it\\n @location(2) color: vec4<f32>,\\n};\\n\\nstruct VertexOutput {\\n @builtin(position) position: vec4<f32>,\\n @location(0) texcoord: vec2<f32>,\\n @location(1) color: vec4<f32>,\\n};\\n\\n@vertex\\nfn vertexMain(input: VertexInput) -> VertexOutput {\\n var output: VertexOutput;\\n\\n // The strip is already expanded on the CPU — each vertex carries its final\\n // system-local position, so there is no per-particle transform to rebuild here.\\n output.position = uniforms.projection * uniforms.translation * vec4<f32>(input.position, 0.0, 1.0);\\n output.texcoord = input.texcoord;\\n output.color = vec4(input.color.rgb * input.color.a, input.color.a);\\n\\n return output;\\n}\\n\\n@fragment\\nfn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {\\n let sample = textureSample(particleTexture, particleSampler, input.texcoord);\\n let premultipliedSample = select(sample, vec4(sample.rgb * sample.a, sample.a), uniforms.flags.x > 0.5);\\n\\n return premultipliedSample * input.color;\\n}\\n\";"],"mappings":";AAAA,+BAAe"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/renderModes/shaders/ribbon.frag
|
|
2
|
+
var ribbon_default = "#version 300 es\nprecision lowp float;\n\nuniform sampler2D u_texture;\n\n// UVs need full precision on mobile GLES (the lowp default would quantise\n// them); the color varying stays lowp for 8-bit output.\nin highp vec2 v_texcoord;\nin vec4 v_color;\n\nlayout(location = 0) out vec4 fragColor;\n\nvoid main(void) {\n fragColor = texture(u_texture, v_texcoord) * v_color;\n}\n";
|
|
3
|
+
|
|
4
|
+
//#endregion
|
|
5
|
+
export { ribbon_default as default };
|
|
6
|
+
//# sourceMappingURL=ribbon.frag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ribbon.frag.js","names":[],"sources":["../../../../src/renderModes/shaders/ribbon.frag"],"sourcesContent":["export default \"#version 300 es\\nprecision lowp float;\\n\\nuniform sampler2D u_texture;\\n\\n// UVs need full precision on mobile GLES (the lowp default would quantise\\n// them); the color varying stays lowp for 8-bit output.\\nin highp vec2 v_texcoord;\\nin vec4 v_color;\\n\\nlayout(location = 0) out vec4 fragColor;\\n\\nvoid main(void) {\\n fragColor = texture(u_texture, v_texcoord) * v_color;\\n}\\n\";"],"mappings":";AAAA,qBAAe"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/renderModes/shaders/ribbon.vert
|
|
2
|
+
var ribbon_default = "#version 300 es\nprecision highp float;\n\n// Per-vertex attributes (two vertices per particle, 20 bytes each).\nlayout(location = 0) in vec2 a_position; // strip vertex in system-local space\nlayout(location = 1) in vec2 a_texcoord; // u along the strip, v across it\nlayout(location = 2) in vec4 a_color; // RGBA tint of the particle this pair came from\n\nuniform mat3 u_projection;\nuniform mat3 u_systemTransform;\n\nout vec2 v_texcoord;\nout vec4 v_color;\n\nvoid main(void) {\n // The strip is already expanded on the CPU — each vertex carries its final\n // system-local position, so there is no per-particle transform to rebuild here.\n gl_Position = vec4((u_projection * u_systemTransform * vec3(a_position, 1.0)).xy, 0.0, 1.0);\n\n v_texcoord = a_texcoord;\n v_color = vec4(a_color.rgb * a_color.a, a_color.a);\n}\n";
|
|
3
|
+
|
|
4
|
+
//#endregion
|
|
5
|
+
export { ribbon_default as default };
|
|
6
|
+
//# sourceMappingURL=ribbon.vert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ribbon.vert.js","names":[],"sources":["../../../../src/renderModes/shaders/ribbon.vert"],"sourcesContent":["export default \"#version 300 es\\nprecision highp float;\\n\\n// Per-vertex attributes (two vertices per particle, 20 bytes each).\\nlayout(location = 0) in vec2 a_position; // strip vertex in system-local space\\nlayout(location = 1) in vec2 a_texcoord; // u along the strip, v across it\\nlayout(location = 2) in vec4 a_color; // RGBA tint of the particle this pair came from\\n\\nuniform mat3 u_projection;\\nuniform mat3 u_systemTransform;\\n\\nout vec2 v_texcoord;\\nout vec4 v_color;\\n\\nvoid main(void) {\\n // The strip is already expanded on the CPU — each vertex carries its final\\n // system-local position, so there is no per-particle transform to rebuild here.\\n gl_Position = vec4((u_projection * u_systemTransform * vec3(a_position, 1.0)).xy, 0.0, 1.0);\\n\\n v_texcoord = a_texcoord;\\n v_color = vec4(a_color.rgb * a_color.a, a_color.a);\\n}\\n\";"],"mappings":";AAAA,qBAAe"}
|