babylon.quarks 0.26.0 → 0.28.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 (83) hide show
  1. package/CHANGELOG.md +1194 -8
  2. package/README.md +13 -9
  3. package/dist/babylon.quarks.cjs +1 -1
  4. package/dist/babylon.quarks.cjs.map +1 -1
  5. package/dist/babylon.quarks.esm.js +1 -1
  6. package/dist/babylon.quarks.esm.js.map +1 -1
  7. package/dist/babylon.quarks.umd.min.js +1 -1
  8. package/dist/babylon.quarks.umd.min.js.map +1 -1
  9. package/dist/types/index.d.cts +10108 -0
  10. package/dist/types/index.d.ts +5794 -998
  11. package/package.json +18 -7
  12. package/src/BatchedRenderer.ts +274 -156
  13. package/src/EffectPool.ts +94 -12
  14. package/src/MeshSurfaceEmitter.ts +50 -14
  15. package/src/ParticleEmitter.ts +75 -17
  16. package/src/ParticleSystem.ts +981 -2066
  17. package/src/QuarksLoader.ts +666 -194
  18. package/src/QuarksPrefab.ts +367 -73
  19. package/src/QuarksSerializer.ts +512 -0
  20. package/src/QuarksUtil.ts +77 -7
  21. package/src/SpriteBatch.ts +127 -252
  22. package/src/TrailBatch.ts +545 -348
  23. package/src/VFXBatch.ts +238 -596
  24. package/src/batchKey.ts +239 -0
  25. package/src/batchSettings.ts +546 -0
  26. package/src/effectDocumentJSON.ts +261 -0
  27. package/src/effectResources.ts +76 -0
  28. package/src/envAtlas.ts +126 -18
  29. package/src/geometryUtil.ts +1 -1
  30. package/src/index.ts +69 -35
  31. package/src/nodeId.ts +36 -0
  32. package/src/shaderProgram.ts +419 -0
  33. package/src/shaderVariants.ts +245 -0
  34. package/src/shaders/chunks/blend.ts +31 -0
  35. package/src/shaders/chunks/fog.ts +12 -5
  36. package/src/shaders/chunks/sheet.ts +111 -0
  37. package/src/shaders/chunks/soft.ts +85 -0
  38. package/src/shaders/chunks/surface.ts +12 -2
  39. package/src/shaders/local_particle_physics_vert.glsl.ts +14 -28
  40. package/src/shaders/local_particle_physics_vert.wgsl.ts +16 -29
  41. package/src/shaders/particle_frag.glsl.ts +5 -22
  42. package/src/shaders/particle_frag.wgsl.ts +5 -23
  43. package/src/shaders/particle_physics_frag.glsl.ts +16 -26
  44. package/src/shaders/particle_physics_frag.wgsl.ts +14 -26
  45. package/src/shaders/particle_vert.glsl.ts +5 -27
  46. package/src/shaders/particle_vert.wgsl.ts +5 -26
  47. package/src/shaders/stretched_bb_particle_vert.glsl.ts +7 -33
  48. package/src/shaders/stretched_bb_particle_vert.wgsl.ts +7 -33
  49. package/src/shaders/trail_frag.glsl.ts +13 -0
  50. package/src/shaders/trail_frag.wgsl.ts +13 -0
  51. package/src/shaders/trail_vert.glsl.ts +45 -36
  52. package/src/shaders/trail_vert.wgsl.ts +46 -40
  53. package/src/softDepth.ts +136 -0
  54. package/src/system/BehaviorPlan.ts +85 -0
  55. package/src/system/BoundsTracker.ts +181 -0
  56. package/src/system/EmissionController.ts +358 -0
  57. package/src/system/EventDispatcher.ts +60 -0
  58. package/src/system/ParticleSpawner.ts +274 -0
  59. package/src/system/SimulationClock.ts +130 -0
  60. package/src/system/geometry.ts +104 -0
  61. package/src/system/integrate.ts +428 -0
  62. package/src/system/materialResolve.ts +165 -0
  63. package/src/system/normalize.ts +100 -0
  64. package/src/system/serialization.ts +422 -0
  65. package/src/system/settingsSchema.ts +339 -0
  66. package/src/system/types.ts +449 -0
  67. package/src/textureSource.ts +30 -0
  68. package/src/trailSettings.ts +54 -0
  69. package/src/BatchedParticleRenderer.ts +0 -3
  70. package/src/materials/ParticleMaterials.ts +0 -23
  71. package/src/materials/index.ts +0 -1
  72. package/src/shaders/chunks/index.ts +0 -1
  73. package/src/shaders/chunks/register-shader-chunks.ts +0 -32
  74. package/src/shaders/chunks/soft_fragment.glsl.ts +0 -13
  75. package/src/shaders/chunks/soft_pars_fragment.glsl.ts +0 -20
  76. package/src/shaders/chunks/soft_pars_vertex.glsl.ts +0 -6
  77. package/src/shaders/chunks/soft_vertex.glsl.ts +0 -6
  78. package/src/shaders/chunks/tile_fragment.glsl.ts +0 -9
  79. package/src/shaders/chunks/tile_pars_fragment.glsl.ts +0 -9
  80. package/src/shaders/chunks/tile_pars_vertex.glsl.ts +0 -28
  81. package/src/shaders/chunks/tile_vertex.glsl.ts +0 -18
  82. package/src/shaders/index.ts +0 -9
  83. package/src/shaders/local_particle_vert.glsl.ts +0 -50
package/src/index.ts CHANGED
@@ -1,23 +1,44 @@
1
1
  import {loadPlugin} from 'quarks.core';
2
2
  import {MeshSurfaceEmitterPlugin} from './MeshSurfaceEmitter';
3
- import {registerShaderChunks} from './shaders';
4
3
 
5
4
  export * from 'quarks.core';
6
- export {BatchedParticleRenderer} from './BatchedParticleRenderer';
7
5
  export {BatchedRenderer} from './BatchedRenderer';
8
6
  export type {AdaptivePerformanceOptions, AdaptivePerformanceState, VFXBatchSettings} from './BatchedRenderer';
7
+ export type {
8
+ BufferAttributeJSON,
9
+ BufferGeometryAttributesJSON,
10
+ BufferGeometryDataJSON,
11
+ BufferGeometryJSON,
12
+ BufferIndexJSON,
13
+ EffectDocumentJSON,
14
+ EffectGeometryJSON,
15
+ EffectImageJSON,
16
+ ErosionJSON,
17
+ GeometryArrays,
18
+ GeometryEntry,
19
+ GradientMapJSON,
20
+ InterleavedBufferJSON,
21
+ MaterialTextureRef,
22
+ PlaneGeometryJSON,
23
+ QuarksGeometryEntryJSON,
24
+ QuarksMaterialJSON,
25
+ SphereGeometryJSON,
26
+ UVDistortionJSON,
27
+ } from './effectDocumentJSON';
9
28
  export {EffectPool} from './EffectPool';
10
- export type {EffectPoolOptions} from './EffectPool';
29
+ export type {EffectPlacement, EffectPoolOptions} from './EffectPool';
11
30
  export {
12
31
  cacheEnvAtlas,
13
32
  createEnvAtlasFromFaceUrls,
14
33
  cubeFaceUrls,
34
+ disposeEnvAtlas,
15
35
  ensureEnvAtlasFromCube,
16
36
  getCachedEnvAtlas,
17
37
  } from './envAtlas';
18
- export * from './materials/';
19
38
  export {MeshSurfaceEmitter, MeshSurfaceEmitterPlugin} from './MeshSurfaceEmitter';
39
+ export {quarksNodeId} from './nodeId';
20
40
  export {ParticleEmitter} from './ParticleEmitter';
41
+ export type {ParticleEmitterJSON} from './ParticleEmitter';
21
42
  export {ParticleSystem} from './ParticleSystem';
22
43
  export type {
23
44
  BabylonMetaData,
@@ -29,22 +50,59 @@ export type {
29
50
  ParticleSystemParameters,
30
51
  ScalingMode,
31
52
  } from './ParticleSystem';
32
- export {QuarksLoader} from './QuarksLoader';
53
+ export {QuarksFormatError, QuarksLoader} from './QuarksLoader';
33
54
  export type {
34
55
  QuarksBatchLoadOptions,
35
56
  QuarksJSONLoadEntry,
36
57
  QuarksLoaderOptions,
58
+ QuarksLoadOptions,
59
+ QuarksResolveOptions,
37
60
  QuarksResourceKind,
38
61
  QuarksResourceResolver,
39
62
  } from './QuarksLoader';
40
63
  export {QuarksPrefab} from './QuarksPrefab';
41
- export type {AnimationData, QuarksTimelineClip} from './QuarksPrefab';
64
+ export type {AnimationData, AnimationJSON, QuarksPrefabJSON, QuarksTimelineClip} from './QuarksPrefab';
65
+ export {QUARKS_FORMAT_VERSION, serializeEffect} from './QuarksSerializer';
66
+ export type {
67
+ QuarksEffectJSON,
68
+ QuarksGeometryJSON,
69
+ QuarksImageJSON,
70
+ QuarksNodeJSON,
71
+ QuarksTextureJSON,
72
+ SerializeEffectOptions,
73
+ } from './QuarksSerializer';
42
74
  export {QuarksUtil} from './QuarksUtil';
43
- export * from './shaders/';
44
- export {SpriteBatch} from './SpriteBatch';
45
- export {TrailBatch} from './TrailBatch';
75
+ export {blendCategory, shaderProgram} from './shaderProgram';
76
+ export type {
77
+ BlendCategory,
78
+ BlendOutput,
79
+ FogTarget,
80
+ ShaderContext,
81
+ ShaderDepth,
82
+ ShaderProgram,
83
+ ShaderSettings,
84
+ } from './shaderProgram';
85
+ export type {ShaderSources} from './shaders/shaderLanguageSupport';
86
+ export {shaderVariants} from './shaderVariants';
87
+ export type {ShaderVariant} from './shaderVariants';
88
+ export {decodeSoftDepth, softDepthParams} from './softDepth';
89
+ export type {DepthTextureOptions, SoftDepthEncoding} from './softDepth';
90
+ export type {ParticleMaterialSource} from './system/materialResolve';
91
+ export type {RendererEmitterSettingsJSON, TrailSettingsJSON} from './system/types';
92
+ export {textureSourceUrl} from './textureSource';
93
+ export {
94
+ MAX_TRAIL_TESSELLATION,
95
+ trailCurveTension,
96
+ trailLifetime,
97
+ trailMinVertexDistance,
98
+ trailTessellation,
99
+ trailTextureMode,
100
+ trailTextureScale,
101
+ type TrailOptions,
102
+ } from './trailSettings';
46
103
  export {
47
104
  defaultMeshLighting,
105
+ EROSION_DEFAULT_SOFTNESS,
48
106
  erosionEqual,
49
107
  GRADIENT_MAP_WEIGHTS,
50
108
  gradientMapEqual,
@@ -60,6 +118,7 @@ export {
60
118
  RenderMode,
61
119
  textureUVEqual,
62
120
  textureUVToJSON,
121
+ UV_DISTORTION_DEFAULT_STRENGTH,
63
122
  UV_DISTORTION_DEFAULT_UV,
64
123
  uvDistortionEqual,
65
124
  VFXBatch,
@@ -74,36 +133,11 @@ export type {
74
133
  ParticleGradientMap,
75
134
  ParticleUVDistortion,
76
135
  ParticleUVDistortionOptions,
136
+ RenderFace,
77
137
  SortMode,
78
138
  StoredBatchSettings,
79
139
  TextureUV,
80
140
  UVChannel,
81
141
  } from './VFXBatch';
82
142
 
83
- // Re-export quarks.core types (interfaces - type-only)
84
- export type {
85
- Behavior,
86
- BillBoardSettings,
87
- ColorGenerator,
88
- EmissionState,
89
- EmitterShape,
90
- FunctionColorGenerator,
91
- FunctionValueGenerator,
92
- IEmitter,
93
- IParticle,
94
- IParticleSystem,
95
- MeshSettings,
96
- Particle,
97
- ParticleSystemEvent,
98
- ParticleSystemEventType,
99
- RendererEmitterSettings,
100
- RotationGenerator,
101
- SerializationOptions,
102
- StretchedBillBoardSettings,
103
- TrailSettings,
104
- ValueGenerator,
105
- Vector3Generator,
106
- } from 'quarks.core';
107
-
108
- registerShaderChunks();
109
143
  loadPlugin(MeshSurfaceEmitterPlugin);
package/src/nodeId.ts ADDED
@@ -0,0 +1,36 @@
1
+ import {RandomGUID} from '@babylonjs/core/Misc/guid';
2
+
3
+ /** The id each node is known by in effect files, set when it is loaded or first asked for. */
4
+ const nodeIds = new WeakMap<object, string>();
5
+
6
+ /**
7
+ * The id an effect file knows a node by: the uuid it was loaded under, or, for a node made in code,
8
+ * a GUID given to it the first time it is asked for and kept from then on. Every link written into
9
+ * a file (a sub emitter's target, a mesh surface shape's mesh, a prefab animation's target) and
10
+ * every node's own `uuid` use this, so an effect loaded, saved and loaded again keeps its links,
11
+ * and a node made in code never takes the id of one loaded from a file.
12
+ */
13
+ export function quarksNodeId(node: object): string {
14
+ let id = nodeIds.get(node);
15
+ if (id === undefined) {
16
+ id = RandomGUID();
17
+ nodeIds.set(node, id);
18
+ }
19
+ return id;
20
+ }
21
+
22
+ /**
23
+ * The uuid `node` was loaded under or was given, or undefined for a node no file or save has named.
24
+ * @internal
25
+ */
26
+ export function fileNodeId(node: object): string | undefined {
27
+ return nodeIds.get(node);
28
+ }
29
+
30
+ /**
31
+ * Names `node` with `id`, the uuid a file gave it.
32
+ * @internal
33
+ */
34
+ export function setQuarksNodeId(node: object, id: string): void {
35
+ nodeIds.set(node, id);
36
+ }
@@ -0,0 +1,419 @@
1
+ import {Constants} from '@babylonjs/core/Engines/constants';
2
+ import {hasSizeLimits, takesPivotAndSizeLimits, tilesSheet} from './batchKey';
3
+ import {readsUV1, RenderMode, StoredBatchSettings} from './batchSettings';
4
+ import local_particle_physics_vert from './shaders/local_particle_physics_vert.glsl';
5
+ import local_particle_physics_vert_wgsl from './shaders/local_particle_physics_vert.wgsl';
6
+ import particle_frag from './shaders/particle_frag.glsl';
7
+ import particle_frag_wgsl from './shaders/particle_frag.wgsl';
8
+ import particle_physics_frag from './shaders/particle_physics_frag.glsl';
9
+ import particle_physics_frag_wgsl from './shaders/particle_physics_frag.wgsl';
10
+ import particle_vert from './shaders/particle_vert.glsl';
11
+ import particle_vert_wgsl from './shaders/particle_vert.wgsl';
12
+ import type {ShaderSources} from './shaders/shaderLanguageSupport';
13
+ import stretched_bb_particle_vert from './shaders/stretched_bb_particle_vert.glsl';
14
+ import stretched_bb_particle_vert_wgsl from './shaders/stretched_bb_particle_vert.wgsl';
15
+ import trail_frag from './shaders/trail_frag.glsl';
16
+ import trail_frag_wgsl from './shaders/trail_frag.wgsl';
17
+ import trail_vert from './shaders/trail_vert.glsl';
18
+ import trail_vert_wgsl from './shaders/trail_vert.wgsl';
19
+
20
+ /**
21
+ * The part of a batch's settings that decides its shader: the render mode, which textures and
22
+ * effects are compiled in, and the blend state the fog and the fade read. Everything else a
23
+ * batch holds (geometry, draw order, the effects' parameters) is a buffer or a uniform value.
24
+ */
25
+ export type ShaderSettings = Pick<
26
+ StoredBatchSettings,
27
+ | 'renderMode'
28
+ | 'texture'
29
+ | 'reflectionAtlas'
30
+ | 'uTileCount'
31
+ | 'vTileCount'
32
+ | 'blendTiles'
33
+ | 'softParticles'
34
+ | 'materialAlphaTest'
35
+ | 'materialTransparent'
36
+ | 'materialBlendMode'
37
+ | 'applyFog'
38
+ | 'pivot'
39
+ | 'minParticleSize'
40
+ | 'maxParticleSize'
41
+ | 'lighting'
42
+ | 'rim'
43
+ | 'mapUV'
44
+ | 'uvDistortion'
45
+ | 'gradientMap'
46
+ | 'erosion'
47
+ >;
48
+
49
+ /** What the material needs to know about the depth texture soft particles read. */
50
+ export interface ShaderDepth {
51
+ /** Whether the depth is packed into four 8-bit channels, as a `DepthRenderer` of type `UNSIGNED_BYTE` writes it. */
52
+ packed: boolean;
53
+ }
54
+
55
+ /**
56
+ * What a batch knows at compile time that its settings do not say: whether the textures it
57
+ * depends on are there to be sampled.
58
+ */
59
+ export interface ShaderContext {
60
+ /**
61
+ * The depth texture soft particles read, when one is attached to the renderer. Soft particles
62
+ * compile only with a depth texture.
63
+ */
64
+ depth: ShaderDepth | null;
65
+ /** Whether the mesh batch's reflection atlas has finished loading; it is sampled only then. */
66
+ envAtlasReady: boolean;
67
+ /** Whether the mesh batch has a second UV set to read for {@link UVChannel} 1. */
68
+ meshUV2: boolean;
69
+ }
70
+
71
+ /**
72
+ * One compiled shader of the library: the sources in both languages and the defines, attributes,
73
+ * uniforms and samplers the material is built with. Built by {@link shaderProgram} from a
74
+ * batch's settings, so a batch and a shader validator describe the same material.
75
+ */
76
+ export interface ShaderProgram {
77
+ /** The name the sources are registered under in Babylon's shader store, one per render mode. */
78
+ name: string;
79
+ /** The vertex shader in both languages. */
80
+ vertex: ShaderSources;
81
+ /** The fragment shader in both languages. */
82
+ fragment: ShaderSources;
83
+ /** The preprocessor defines, without the `#define`. */
84
+ defines: string[];
85
+ /** The vertex attributes the material binds. */
86
+ attributes: string[];
87
+ /** The uniforms the material declares. */
88
+ uniforms: string[];
89
+ /** The texture samplers the material declares. */
90
+ samplers: string[];
91
+ }
92
+
93
+ /** The vertex attributes every sprite shader reads. */
94
+ const SPRITE_ATTRIBUTES = ['position', 'uv', 'offset', 'color', 'size', 'rotation', 'uvTile'];
95
+ /** The vertex attributes the trail shader reads. */
96
+ const TRAIL_ATTRIBUTES = ['position', 'previous', 'next', 'side', 'width', 'uv', 'color'];
97
+ /** The fog uniforms, declared on every material so a scene can turn its fog on without a recompile. */
98
+ export const FOG_UNIFORMS = ['vFogInfos', 'vFogColor'];
99
+
100
+ /** Whether `renderMode` is drawn by the stretched shader: along a per-instance vector. */
101
+ export function drawsAlongVelocity(renderMode: RenderMode): boolean {
102
+ return renderMode === RenderMode.StretchedBillBoard || renderMode === RenderMode.Beam;
103
+ }
104
+
105
+ /**
106
+ * Whether the material samples a map: the settings' texture, or the white placeholder a mesh
107
+ * batch gets, since a mesh effect with no sampler at all often never becomes drawable on iOS
108
+ * WebKit.
109
+ */
110
+ export function samplesMap(settings: {renderMode: RenderMode; texture: unknown}): boolean {
111
+ return settings.texture !== null || settings.renderMode === RenderMode.Mesh;
112
+ }
113
+
114
+ /**
115
+ * How a particle's colour has to leave the fragment shader for its blend mode, so that its alpha
116
+ * means coverage: alpha 0 leaves the frame as it was, alpha 1 draws the colour in full, and the
117
+ * fades (soft particles, erosion, a rim fade, alpha over life) only lower alpha. Unity's particle
118
+ * shaders do the same for their Alpha, Premultiply, Additive and Multiply modes. Alpha has no
119
+ * such meaning for an opaque material, nor under `ALPHA_INTERPOLATE` (constant blend colour),
120
+ * `ALPHA_REPLACE_COLOR` (replaces the frame) and `ALPHA_DUAL_SRC0_ADD_SRC1xDST` (reads a second
121
+ * shader output the particle shaders do not write); these leave the colour as it is.
122
+ *
123
+ * - `'straight'`: the blend weighs the colour by its alpha itself (Babylon's `ALPHA_COMBINE`,
124
+ * `ALPHA_ADD`, `ALPHA_LAYER_ACCUMULATE`, …) or does not blend; the colour leaves as it is.
125
+ * - `'premultiply'`: the blend takes the colour at full weight, or the frame by one minus the
126
+ * colour (`ALPHA_PREMULTIPLIED`, `ALPHA_ONEONE`, `ALPHA_SCREENMODE`, `ALPHA_SUBTRACT`,
127
+ * `ALPHA_MAXIMIZED`, `ALPHA_MAX`, …), so the shader multiplies it by its alpha: black, which
128
+ * these modes leave the frame alone with, at alpha 0. Textures stay straight alpha, as in
129
+ * Unity's Premultiply mode. The fade is linear in alpha except under `ALPHA_MAXIMIZED`, whose
130
+ * blend weighs the colour by alpha once more, and `ALPHA_MAX`.
131
+ * - `'multiply'`: the blend multiplies the frame by the colour (`ALPHA_MULTIPLY`, `ALPHA_MIN`), so
132
+ * the shader moves it toward white, which leaves the frame alone, as alpha falls. `ALPHA_MIN`
133
+ * clamps a frame brighter than 1 (an HDR target) to 1 where a particle draws, even at alpha 0.
134
+ */
135
+ export type BlendOutput = 'straight' | 'premultiply' | 'multiply';
136
+
137
+ /**
138
+ * What the scene's fog turns a particle's colour into with distance: the fog colour where the
139
+ * particle covers the frame (`'color'`), black where it adds to it (`'black'`, or the fog would
140
+ * add itself to the whole screen), white where it multiplies it (`'white'`). Unity fogs its
141
+ * additive and multiply particles toward black and white for the same reason.
142
+ */
143
+ export type FogTarget = 'color' | 'black' | 'white';
144
+
145
+ /** The {@link BlendOutput} and {@link FogTarget} of a blend mode. */
146
+ export interface BlendCategory {
147
+ /** How the colour leaves the shader. */
148
+ output: BlendOutput;
149
+ /** Where the fog takes the colour. */
150
+ fogTarget: FogTarget;
151
+ }
152
+
153
+ const PREMULTIPLY_MODES = new Set<number>([
154
+ Constants.ALPHA_SUBTRACT,
155
+ Constants.ALPHA_MAXIMIZED,
156
+ Constants.ALPHA_ONEONE,
157
+ Constants.ALPHA_PREMULTIPLIED,
158
+ Constants.ALPHA_PREMULTIPLIED_PORTERDUFF,
159
+ Constants.ALPHA_SCREENMODE,
160
+ Constants.ALPHA_ONEONE_ONEONE,
161
+ Constants.ALPHA_ALPHATOCOLOR,
162
+ Constants.ALPHA_REVERSEONEMINUS,
163
+ Constants.ALPHA_SRC_DSTONEMINUSSRCALPHA,
164
+ Constants.ALPHA_ONEONE_ONEZERO,
165
+ Constants.ALPHA_EXCLUSION,
166
+ Constants.ALPHA_MAX,
167
+ ]);
168
+ const MULTIPLY_MODES = new Set<number>([Constants.ALPHA_MULTIPLY, Constants.ALPHA_MIN]);
169
+ /** The modes whose fog is the fog colour: those that lay the colour over the frame or replace it. */
170
+ const OVER_MODES = new Set<number>([
171
+ Constants.ALPHA_COMBINE,
172
+ Constants.ALPHA_PREMULTIPLIED,
173
+ Constants.ALPHA_PREMULTIPLIED_PORTERDUFF,
174
+ Constants.ALPHA_INTERPOLATE,
175
+ Constants.ALPHA_SRC_DSTONEMINUSSRCALPHA,
176
+ Constants.ALPHA_LAYER_ACCUMULATE,
177
+ Constants.ALPHA_REPLACE_COLOR,
178
+ ]);
179
+
180
+ /**
181
+ * The {@link BlendCategory} of a material: an opaque one (`materialTransparent` false, or
182
+ * `ALPHA_DISABLE`) is straight and fogs toward the fog colour; a transparent one by its Babylon
183
+ * alpha mode. A mode this table does not know is straight and fogs toward the fog colour.
184
+ */
185
+ export function blendCategory(settings: {materialTransparent: boolean; materialBlendMode: number}): BlendCategory {
186
+ const mode = settings.materialBlendMode;
187
+ if (!settings.materialTransparent || mode === Constants.ALPHA_DISABLE) {
188
+ return {output: 'straight', fogTarget: 'color'};
189
+ }
190
+ const output: BlendOutput = PREMULTIPLY_MODES.has(mode)
191
+ ? 'premultiply'
192
+ : MULTIPLY_MODES.has(mode)
193
+ ? 'multiply'
194
+ : 'straight';
195
+ const fogTarget: FogTarget =
196
+ output === 'multiply'
197
+ ? 'white'
198
+ : OVER_MODES.has(mode) || (output === 'straight' && mode !== Constants.ALPHA_ADD)
199
+ ? 'color'
200
+ : 'black';
201
+ return {output, fogTarget};
202
+ }
203
+
204
+ /** The shader program a batch with `settings` compiles, given what it knows about its textures. */
205
+ export function shaderProgram(settings: ShaderSettings, context: ShaderContext): ShaderProgram {
206
+ return settings.renderMode === RenderMode.Trail
207
+ ? trailShaderProgram(settings, context)
208
+ : spriteShaderProgram(settings, context);
209
+ }
210
+
211
+ /** The program of a sprite, stretched or mesh batch. */
212
+ function spriteShaderProgram(settings: ShaderSettings, context: ShaderContext): ShaderProgram {
213
+ const renderMode = settings.renderMode;
214
+ const isMesh = renderMode === RenderMode.Mesh;
215
+ // The sources only depend on the render mode, so the store entry is registered once per
216
+ // mode: a unique name per material would leak store entries and defeat the effect cache.
217
+ const name = `quarksParticle_${renderMode}`;
218
+ let vertex: ShaderSources;
219
+ let fragment: ShaderSources;
220
+ if (isMesh) {
221
+ vertex = {glsl: local_particle_physics_vert, wgsl: local_particle_physics_vert_wgsl};
222
+ fragment = {glsl: particle_physics_frag, wgsl: particle_physics_frag_wgsl};
223
+ } else if (drawsAlongVelocity(renderMode)) {
224
+ vertex = {glsl: stretched_bb_particle_vert, wgsl: stretched_bb_particle_vert_wgsl};
225
+ fragment = {glsl: particle_frag, wgsl: particle_frag_wgsl};
226
+ } else {
227
+ vertex = {glsl: particle_vert, wgsl: particle_vert_wgsl};
228
+ fragment = {glsl: particle_frag, wgsl: particle_frag_wgsl};
229
+ }
230
+
231
+ const defines = blendDefines(settings);
232
+ const attributes = [...SPRITE_ATTRIBUTES];
233
+ const uniforms = ['world', 'view', 'projection', ...FOG_UNIFORMS];
234
+ const samplers: string[] = [];
235
+
236
+ if (samplesMap(settings)) {
237
+ defines.push('USE_MAP');
238
+ samplers.push('map');
239
+ }
240
+ const envAtlas = isMesh && settings.reflectionAtlas !== null && context.envAtlasReady;
241
+ if (envAtlas) {
242
+ defines.push('USE_ENVMAP_ATLAS');
243
+ samplers.push('envAtlas');
244
+ uniforms.push('reflectionLevel');
245
+ }
246
+ if (tilesSheet(settings)) {
247
+ defines.push('UV_TILE');
248
+ uniforms.push('tileCountX', 'tileCountY');
249
+ // The second cell only exists on a sheet; a 1×1 sheet has nothing to blend into.
250
+ if (settings.blendTiles) {
251
+ defines.push('TILE_BLEND');
252
+ }
253
+ }
254
+ softDefines(settings, context, defines, uniforms, samplers);
255
+ if (renderMode === RenderMode.VerticalBillBoard) {
256
+ defines.push('VERTICAL');
257
+ }
258
+ if (renderMode === RenderMode.HorizontalBillBoard) {
259
+ defines.push('HORIZONTAL');
260
+ }
261
+ if (renderMode === RenderMode.Beam) {
262
+ defines.push('BEAM');
263
+ }
264
+ if (takesPivotAndSizeLimits(renderMode)) {
265
+ if (settings.pivot) {
266
+ defines.push('PIVOT');
267
+ uniforms.push('pivot');
268
+ }
269
+ if (hasSizeLimits(settings)) {
270
+ defines.push('SIZE_LIMITS');
271
+ uniforms.push('sizeLimits');
272
+ }
273
+ }
274
+ const lit = isMesh && (settings.lighting ?? 'fixed') !== 'unlit';
275
+ if (isMesh && !lit) {
276
+ defines.push('MESH_UNLIT');
277
+ }
278
+ if (lit) {
279
+ uniforms.push('lightDirection', 'lightColor', 'ambientColor');
280
+ }
281
+ const rim = isMesh ? settings.rim : undefined;
282
+ if (rim) {
283
+ defines.push('MESH_RIM');
284
+ if (rim.mode === 'fade') {
285
+ defines.push('MESH_RIM_FADE');
286
+ } else if (rim.mode === 'mix') {
287
+ defines.push('MESH_RIM_MIX');
288
+ }
289
+ uniforms.push('rimColor', 'rimParams');
290
+ }
291
+ if (envAtlas || rim) {
292
+ uniforms.push('eyePosition');
293
+ }
294
+ surfaceDefines(settings, defines, uniforms, samplers);
295
+ if (isMesh) {
296
+ attributes.push('normal');
297
+ if (readsUV1(settings) && context.meshUV2) {
298
+ defines.push('MESH_UV2');
299
+ attributes.push('uv2');
300
+ }
301
+ }
302
+ if (drawsAlongVelocity(renderMode)) {
303
+ attributes.push('velocity');
304
+ }
305
+ return {name, vertex, fragment, defines, attributes, uniforms, samplers};
306
+ }
307
+
308
+ /** The program of a trail batch. */
309
+ function trailShaderProgram(settings: ShaderSettings, context: ShaderContext): ShaderProgram {
310
+ const defines = blendDefines(settings);
311
+ const attributes = [...TRAIL_ATTRIBUTES];
312
+ const uniforms = ['view', 'projection', ...FOG_UNIFORMS];
313
+ const samplers: string[] = [];
314
+ if (settings.texture !== null) {
315
+ defines.push('USE_MAP');
316
+ samplers.push('map');
317
+ }
318
+ surfaceDefines(settings, defines, uniforms, samplers);
319
+ softDefines(settings, context, defines, uniforms, samplers);
320
+ if (readsUV1(settings)) {
321
+ // A trail's channel 1 arrives as its own vertex buffer.
322
+ attributes.push('uv2');
323
+ }
324
+ return {
325
+ name: 'quarksTrail',
326
+ vertex: {glsl: trail_vert, wgsl: trail_vert_wgsl},
327
+ fragment: {glsl: trail_frag, wgsl: trail_frag_wgsl},
328
+ defines,
329
+ attributes,
330
+ uniforms,
331
+ samplers,
332
+ };
333
+ }
334
+
335
+ /**
336
+ * The blend defines: `BLEND_PREMULTIPLY` or `BLEND_MULTIPLY` for how the colour leaves the shader,
337
+ * and with `FOG` (when the settings apply the scene's fog) `FOG_ADDITIVE` or `FOG_MULTIPLY` for a
338
+ * fog toward black or white. See {@link blendCategory}.
339
+ */
340
+ function blendDefines(settings: ShaderSettings): string[] {
341
+ const {output, fogTarget} = blendCategory(settings);
342
+ const defines: string[] = [];
343
+ if (output === 'premultiply') {
344
+ defines.push('BLEND_PREMULTIPLY');
345
+ } else if (output === 'multiply') {
346
+ defines.push('BLEND_MULTIPLY');
347
+ }
348
+ if (settings.applyFog) {
349
+ defines.push('FOG');
350
+ if (fogTarget === 'black') {
351
+ defines.push('FOG_ADDITIVE');
352
+ } else if (fogTarget === 'white') {
353
+ defines.push('FOG_MULTIPLY');
354
+ }
355
+ }
356
+ return defines;
357
+ }
358
+
359
+ /** Soft particles, when on and a depth texture is there to read, and the alpha test. */
360
+ function softDefines(
361
+ settings: ShaderSettings,
362
+ context: ShaderContext,
363
+ defines: string[],
364
+ uniforms: string[],
365
+ samplers: string[]
366
+ ): void {
367
+ if (settings.softParticles && context.depth !== null) {
368
+ defines.push('SOFT_PARTICLES');
369
+ if (context.depth.packed) {
370
+ defines.push('SOFT_DEPTH_PACKED');
371
+ }
372
+ uniforms.push('softParams', 'projParams');
373
+ samplers.push('depthTexture');
374
+ }
375
+ if (settings.materialAlphaTest > 0) {
376
+ defines.push('USE_ALPHATEST');
377
+ uniforms.push('alphaTest');
378
+ }
379
+ }
380
+
381
+ /** The texture effects of `chunks/surface.ts`: the map's layout, distortion, gradient map and erosion. */
382
+ function surfaceDefines(settings: ShaderSettings, defines: string[], uniforms: string[], samplers: string[]): void {
383
+ const {mapUV, uvDistortion, gradientMap, erosion} = settings;
384
+ if (readsUV1(settings)) {
385
+ defines.push('UV1');
386
+ }
387
+ if (mapUV) {
388
+ defines.push('MAP_TRANSFORM');
389
+ if (mapUV.channel === 1) {
390
+ defines.push('MAP_UV1');
391
+ }
392
+ uniforms.push('mapTransform');
393
+ }
394
+ if (uvDistortion) {
395
+ defines.push('UV_DISTORTION');
396
+ if (uvDistortion.mapUV.channel === 1) {
397
+ defines.push('UV_DISTORTION_UV1');
398
+ }
399
+ samplers.push('distortionMap');
400
+ uniforms.push('distortionTransform', 'distortionStrength');
401
+ }
402
+ if (gradientMap) {
403
+ defines.push('GRADIENT_MAP');
404
+ samplers.push('gradientMap');
405
+ uniforms.push('gradientMapRange', 'gradientMapChannel');
406
+ }
407
+ if (erosion) {
408
+ defines.push('EROSION');
409
+ uniforms.push('erosionParams', 'erosionEdgeColor');
410
+ if (erosion.mask) {
411
+ defines.push('EROSION_MASK');
412
+ if (erosion.maskUV.channel === 1) {
413
+ defines.push('EROSION_MASK_UV1');
414
+ }
415
+ samplers.push('erosionMask');
416
+ uniforms.push('erosionMaskTransform');
417
+ }
418
+ }
419
+ }