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.
- package/CHANGELOG.md +1194 -8
- package/README.md +13 -9
- package/dist/babylon.quarks.cjs +1 -1
- package/dist/babylon.quarks.cjs.map +1 -1
- package/dist/babylon.quarks.esm.js +1 -1
- package/dist/babylon.quarks.esm.js.map +1 -1
- package/dist/babylon.quarks.umd.min.js +1 -1
- package/dist/babylon.quarks.umd.min.js.map +1 -1
- package/dist/types/index.d.cts +10108 -0
- package/dist/types/index.d.ts +5794 -998
- package/package.json +18 -7
- package/src/BatchedRenderer.ts +274 -156
- package/src/EffectPool.ts +94 -12
- package/src/MeshSurfaceEmitter.ts +50 -14
- package/src/ParticleEmitter.ts +75 -17
- package/src/ParticleSystem.ts +981 -2066
- package/src/QuarksLoader.ts +666 -194
- package/src/QuarksPrefab.ts +367 -73
- package/src/QuarksSerializer.ts +512 -0
- package/src/QuarksUtil.ts +77 -7
- package/src/SpriteBatch.ts +127 -252
- package/src/TrailBatch.ts +545 -348
- package/src/VFXBatch.ts +238 -596
- package/src/batchKey.ts +239 -0
- package/src/batchSettings.ts +546 -0
- package/src/effectDocumentJSON.ts +261 -0
- package/src/effectResources.ts +76 -0
- package/src/envAtlas.ts +126 -18
- package/src/geometryUtil.ts +1 -1
- package/src/index.ts +69 -35
- package/src/nodeId.ts +36 -0
- package/src/shaderProgram.ts +419 -0
- package/src/shaderVariants.ts +245 -0
- package/src/shaders/chunks/blend.ts +31 -0
- package/src/shaders/chunks/fog.ts +12 -5
- package/src/shaders/chunks/sheet.ts +111 -0
- package/src/shaders/chunks/soft.ts +85 -0
- package/src/shaders/chunks/surface.ts +12 -2
- package/src/shaders/local_particle_physics_vert.glsl.ts +14 -28
- package/src/shaders/local_particle_physics_vert.wgsl.ts +16 -29
- package/src/shaders/particle_frag.glsl.ts +5 -22
- package/src/shaders/particle_frag.wgsl.ts +5 -23
- package/src/shaders/particle_physics_frag.glsl.ts +16 -26
- package/src/shaders/particle_physics_frag.wgsl.ts +14 -26
- package/src/shaders/particle_vert.glsl.ts +5 -27
- package/src/shaders/particle_vert.wgsl.ts +5 -26
- package/src/shaders/stretched_bb_particle_vert.glsl.ts +7 -33
- package/src/shaders/stretched_bb_particle_vert.wgsl.ts +7 -33
- package/src/shaders/trail_frag.glsl.ts +13 -0
- package/src/shaders/trail_frag.wgsl.ts +13 -0
- package/src/shaders/trail_vert.glsl.ts +45 -36
- package/src/shaders/trail_vert.wgsl.ts +46 -40
- package/src/softDepth.ts +136 -0
- package/src/system/BehaviorPlan.ts +85 -0
- package/src/system/BoundsTracker.ts +181 -0
- package/src/system/EmissionController.ts +358 -0
- package/src/system/EventDispatcher.ts +60 -0
- package/src/system/ParticleSpawner.ts +274 -0
- package/src/system/SimulationClock.ts +130 -0
- package/src/system/geometry.ts +104 -0
- package/src/system/integrate.ts +428 -0
- package/src/system/materialResolve.ts +165 -0
- package/src/system/normalize.ts +100 -0
- package/src/system/serialization.ts +422 -0
- package/src/system/settingsSchema.ts +339 -0
- package/src/system/types.ts +449 -0
- package/src/textureSource.ts +30 -0
- package/src/trailSettings.ts +54 -0
- package/src/BatchedParticleRenderer.ts +0 -3
- package/src/materials/ParticleMaterials.ts +0 -23
- package/src/materials/index.ts +0 -1
- package/src/shaders/chunks/index.ts +0 -1
- package/src/shaders/chunks/register-shader-chunks.ts +0 -32
- package/src/shaders/chunks/soft_fragment.glsl.ts +0 -13
- package/src/shaders/chunks/soft_pars_fragment.glsl.ts +0 -20
- package/src/shaders/chunks/soft_pars_vertex.glsl.ts +0 -6
- package/src/shaders/chunks/soft_vertex.glsl.ts +0 -6
- package/src/shaders/chunks/tile_fragment.glsl.ts +0 -9
- package/src/shaders/chunks/tile_pars_fragment.glsl.ts +0 -9
- package/src/shaders/chunks/tile_pars_vertex.glsl.ts +0 -28
- package/src/shaders/chunks/tile_vertex.glsl.ts +0 -18
- package/src/shaders/index.ts +0 -9
- package/src/shaders/local_particle_vert.glsl.ts +0 -50
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import type {BaseTexture} from '@babylonjs/core/Materials/Textures/baseTexture';
|
|
2
|
+
import type {
|
|
3
|
+
MeshLighting,
|
|
4
|
+
MeshRim,
|
|
5
|
+
ParticleErosionOptions,
|
|
6
|
+
ParticleGradientMap,
|
|
7
|
+
ParticleUVDistortionOptions,
|
|
8
|
+
TextureUV,
|
|
9
|
+
} from './batchSettings';
|
|
10
|
+
import type {QuarksEffectJSON, QuarksNodeJSON, QuarksTextureJSON} from './QuarksSerializer';
|
|
11
|
+
import type {ParticleMaterialSource} from './system/materialResolve';
|
|
12
|
+
|
|
13
|
+
// The parts of an effect document `QuarksLoader` reads: what `serializeEffect` writes, and the
|
|
14
|
+
// three.js object format three.quarks files use. Every field a file may leave out is optional; the
|
|
15
|
+
// loader checks what it reads, so a field of the wrong kind warns or is skipped, never throws.
|
|
16
|
+
|
|
17
|
+
/** Mesh data as typed arrays, the form a loaded geometry takes. */
|
|
18
|
+
export interface GeometryArrays {
|
|
19
|
+
/** `x, y, z` per vertex. */
|
|
20
|
+
positions: Float32Array;
|
|
21
|
+
/** Three per triangle. */
|
|
22
|
+
indices: Uint32Array | Uint16Array;
|
|
23
|
+
/** First UV set, `u, v` per vertex. */
|
|
24
|
+
uvs?: Float32Array;
|
|
25
|
+
/** Second UV set, `u, v` per vertex. */
|
|
26
|
+
uvs2?: Float32Array;
|
|
27
|
+
/** `x, y, z` per vertex. */
|
|
28
|
+
normals?: Float32Array;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A three.js `BufferAttribute`, inline or as a view into an interleaved buffer. */
|
|
32
|
+
export interface BufferAttributeJSON {
|
|
33
|
+
/** The values, when the attribute holds its own. */
|
|
34
|
+
array?: number[];
|
|
35
|
+
/** Components per vertex. */
|
|
36
|
+
itemSize?: number;
|
|
37
|
+
/** The typed array type, e.g. `'Float32Array'` or `'Uint16Array'`. */
|
|
38
|
+
type?: string;
|
|
39
|
+
/** Set when the attribute reads an interleaved buffer. */
|
|
40
|
+
isInterleavedBufferAttribute?: boolean;
|
|
41
|
+
/** The interleaved buffer's uuid. */
|
|
42
|
+
data?: string;
|
|
43
|
+
/** Where the attribute starts in each interleaved vertex. */
|
|
44
|
+
offset?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A three.js interleaved buffer: a stride over one of the geometry's array buffers. */
|
|
48
|
+
export interface InterleavedBufferJSON {
|
|
49
|
+
/** The array buffer's uuid. */
|
|
50
|
+
buffer: string;
|
|
51
|
+
/** Floats per vertex. */
|
|
52
|
+
stride: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The vertex attributes of a three.js `BufferGeometry` that quarks reads. */
|
|
56
|
+
export interface BufferGeometryAttributesJSON {
|
|
57
|
+
/** Positions, `x, y, z` per vertex. */
|
|
58
|
+
position?: BufferAttributeJSON;
|
|
59
|
+
/** The first UV set. */
|
|
60
|
+
uv?: BufferAttributeJSON;
|
|
61
|
+
/** The second UV set, as three.js r151 on names it. */
|
|
62
|
+
uv1?: BufferAttributeJSON;
|
|
63
|
+
/** The second UV set, as three.js before r151 names it. */
|
|
64
|
+
uv2?: BufferAttributeJSON;
|
|
65
|
+
/** Normals, `x, y, z` per vertex. */
|
|
66
|
+
normal?: BufferAttributeJSON;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The index of a three.js `BufferGeometry`. */
|
|
70
|
+
export interface BufferIndexJSON {
|
|
71
|
+
/** `'Uint16Array'` or `'Uint32Array'`; 32-bit when absent. */
|
|
72
|
+
type?: string;
|
|
73
|
+
/** Three per triangle. */
|
|
74
|
+
array: number[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The `data` of a three.js `BufferGeometry`. */
|
|
78
|
+
export interface BufferGeometryDataJSON {
|
|
79
|
+
/** The vertex attributes. */
|
|
80
|
+
attributes?: BufferGeometryAttributesJSON;
|
|
81
|
+
/** The triangle indices. */
|
|
82
|
+
index?: BufferIndexJSON;
|
|
83
|
+
/** Interleaved buffers by uuid. */
|
|
84
|
+
interleavedBuffers?: {[uuid: string]: InterleavedBufferJSON};
|
|
85
|
+
/** The bytes of the interleaved buffers by uuid, as 32-bit words. */
|
|
86
|
+
arrayBuffers?: {[uuid: string]: number[] | ArrayBuffer};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** A three.js `BufferGeometry`. */
|
|
90
|
+
export interface BufferGeometryJSON {
|
|
91
|
+
/** Unique within the file. */
|
|
92
|
+
uuid: string;
|
|
93
|
+
/** Always `'BufferGeometry'`. */
|
|
94
|
+
type: 'BufferGeometry';
|
|
95
|
+
/** The vertex data. */
|
|
96
|
+
data?: BufferGeometryDataJSON;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** A three.js `PlaneGeometry`: a quad in XY. */
|
|
100
|
+
export interface PlaneGeometryJSON {
|
|
101
|
+
/** Unique within the file. */
|
|
102
|
+
uuid: string;
|
|
103
|
+
/** Always `'PlaneGeometry'`. */
|
|
104
|
+
type: 'PlaneGeometry';
|
|
105
|
+
/** Default 1. */
|
|
106
|
+
width?: number;
|
|
107
|
+
/** Default 1. */
|
|
108
|
+
height?: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** A three.js `SphereGeometry`. */
|
|
112
|
+
export interface SphereGeometryJSON {
|
|
113
|
+
/** Unique within the file. */
|
|
114
|
+
uuid: string;
|
|
115
|
+
/** Always `'SphereGeometry'`. */
|
|
116
|
+
type: 'SphereGeometry';
|
|
117
|
+
/** Default 0.5. */
|
|
118
|
+
radius?: number;
|
|
119
|
+
/** Default 16, at least 3. */
|
|
120
|
+
widthSegments?: number;
|
|
121
|
+
/** Default 8, at least 2. */
|
|
122
|
+
heightSegments?: number;
|
|
123
|
+
/** Default 0. */
|
|
124
|
+
phiStart?: number;
|
|
125
|
+
/** Default 2π. */
|
|
126
|
+
phiLength?: number;
|
|
127
|
+
/** Default 0. */
|
|
128
|
+
thetaStart?: number;
|
|
129
|
+
/** Default π. */
|
|
130
|
+
thetaLength?: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Quarks' own mesh data, inline (see `QuarksGeometryJSON`) or in its own file at `url`, which
|
|
135
|
+
* `QuarksLoader.resolveResources` inlines before parsing.
|
|
136
|
+
*/
|
|
137
|
+
export interface QuarksGeometryEntryJSON {
|
|
138
|
+
/** Unique within the file. */
|
|
139
|
+
uuid: string;
|
|
140
|
+
/** Always `'QuarksGeometry'`. */
|
|
141
|
+
type: 'QuarksGeometry';
|
|
142
|
+
/** The file that holds the data, until it is inlined. */
|
|
143
|
+
url?: string;
|
|
144
|
+
/** `x, y, z` per vertex. */
|
|
145
|
+
positions?: number[];
|
|
146
|
+
/** Three per triangle. */
|
|
147
|
+
indices?: number[];
|
|
148
|
+
/** First UV set. */
|
|
149
|
+
uvs?: number[];
|
|
150
|
+
/** Second UV set. */
|
|
151
|
+
uvs2?: number[];
|
|
152
|
+
/** `x, y, z` per vertex. */
|
|
153
|
+
normals?: number[];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** A geometry entry of an effect document. */
|
|
157
|
+
export type EffectGeometryJSON = QuarksGeometryEntryJSON | BufferGeometryJSON | PlaneGeometryJSON | SphereGeometryJSON;
|
|
158
|
+
|
|
159
|
+
/** An image entry of an effect document. */
|
|
160
|
+
export interface EffectImageJSON {
|
|
161
|
+
/** Unique within the file. */
|
|
162
|
+
uuid: string;
|
|
163
|
+
/** Relative to the file's base url, absolute, or a `data:` url; absent for an image with no file. */
|
|
164
|
+
url?: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* A texture slot of a {@link QuarksMaterialJSON}: a texture uuid in a file, the texture itself once
|
|
169
|
+
* a loader resolved it, or null for a texture that could not be made.
|
|
170
|
+
*/
|
|
171
|
+
export type MaterialTextureRef = string | BaseTexture | null;
|
|
172
|
+
|
|
173
|
+
/** The gradient mapping of a {@link QuarksMaterialJSON}; see `ParticleSystem.gradientMap`. */
|
|
174
|
+
export interface GradientMapJSON extends Omit<Partial<ParticleGradientMap>, 'ramp'> {
|
|
175
|
+
/** The ramp texture. */
|
|
176
|
+
ramp?: MaterialTextureRef;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** The erosion of a {@link QuarksMaterialJSON}; see `ParticleSystem.erosion`. */
|
|
180
|
+
export type ErosionJSON = Omit<ParticleErosionOptions, 'mask'> & {
|
|
181
|
+
/** The mask texture. */
|
|
182
|
+
mask?: MaterialTextureRef;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/** The UV distortion of a {@link QuarksMaterialJSON}; see `ParticleSystem.uvDistortion`. */
|
|
186
|
+
export type UVDistortionJSON = Omit<ParticleUVDistortionOptions, 'map'> & {
|
|
187
|
+
/** The distortion texture. */
|
|
188
|
+
map?: MaterialTextureRef;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* A material entry of an effect document: what a particle system's material writes (`type`
|
|
193
|
+
* `'QuarksMaterial'`), or a three.js material, whose `map` and `blending` the loader reads too.
|
|
194
|
+
*/
|
|
195
|
+
export interface QuarksMaterialJSON {
|
|
196
|
+
/** Unique within the file. */
|
|
197
|
+
uuid: string;
|
|
198
|
+
/** `'QuarksMaterial'`, or the three.js material type. */
|
|
199
|
+
type?: string;
|
|
200
|
+
/** Whether the particles blend. Default true. */
|
|
201
|
+
transparent?: boolean;
|
|
202
|
+
/** A Babylon alpha mode constant. */
|
|
203
|
+
alphaMode?: number;
|
|
204
|
+
/** A three.js blending code (1 normal, 2 additive, 3 subtractive, 4 multiply), read without `alphaMode`. */
|
|
205
|
+
blending?: number;
|
|
206
|
+
/** A three.js side code; 2 draws both faces. */
|
|
207
|
+
side?: number;
|
|
208
|
+
/** Default true. */
|
|
209
|
+
depthTest?: boolean;
|
|
210
|
+
/** Default false. */
|
|
211
|
+
depthWrite?: boolean;
|
|
212
|
+
/** Default 0. */
|
|
213
|
+
alphaTest?: number;
|
|
214
|
+
/** The particle texture. */
|
|
215
|
+
texture?: MaterialTextureRef;
|
|
216
|
+
/** A three.js material's texture, read when there is no `texture`. */
|
|
217
|
+
map?: string;
|
|
218
|
+
/** A 3×2 environment atlas for reflections. */
|
|
219
|
+
reflectionAtlas?: MaterialTextureRef;
|
|
220
|
+
/** How strong the reflection is. */
|
|
221
|
+
reflectionLevel?: number;
|
|
222
|
+
/** Mesh particle lighting; `'fixed'` when absent. */
|
|
223
|
+
lighting?: MeshLighting;
|
|
224
|
+
/** Mesh particle rim. */
|
|
225
|
+
rim?: Partial<MeshRim>;
|
|
226
|
+
/** The particle texture's UV transform. */
|
|
227
|
+
mapUV?: Partial<TextureUV>;
|
|
228
|
+
/** Gradient mapping. */
|
|
229
|
+
gradientMap?: GradientMapJSON;
|
|
230
|
+
/** Erosion. */
|
|
231
|
+
erosion?: ErosionJSON;
|
|
232
|
+
/** UV distortion. */
|
|
233
|
+
uvDistortion?: UVDistortionJSON;
|
|
234
|
+
/** While writing: the material the system read its settings from. Never written to a file. */
|
|
235
|
+
sourceMaterial?: ParticleMaterialSource;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* A geometry as an effect's resources hold it: an entry of the document, or, once loaded, its
|
|
240
|
+
* typed arrays.
|
|
241
|
+
*/
|
|
242
|
+
export type GeometryEntry = EffectGeometryJSON | GeometryArrays;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* An effect document as `QuarksLoader` reads it: what `serializeEffect` writes
|
|
246
|
+
* (`QuarksEffectJSON`), or a three.quarks / three.js object file. Only `object` is required.
|
|
247
|
+
*/
|
|
248
|
+
export interface EffectDocumentJSON {
|
|
249
|
+
/** What wrote the file. */
|
|
250
|
+
metadata?: Partial<QuarksEffectJSON['metadata']>;
|
|
251
|
+
/** Meshes the nodes and particle systems use. */
|
|
252
|
+
geometries?: EffectGeometryJSON[];
|
|
253
|
+
/** Materials, referred to by uuid. */
|
|
254
|
+
materials?: QuarksMaterialJSON[];
|
|
255
|
+
/** Textures, referred to by uuid. */
|
|
256
|
+
textures?: QuarksTextureJSON[];
|
|
257
|
+
/** Image files the textures load. */
|
|
258
|
+
images?: EffectImageJSON[];
|
|
259
|
+
/** The effect's root node. */
|
|
260
|
+
object: QuarksNodeJSON;
|
|
261
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type {Material} from '@babylonjs/core/Materials/material';
|
|
2
|
+
import type {BaseTexture} from '@babylonjs/core/Materials/Textures/baseTexture';
|
|
3
|
+
import type {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
4
|
+
import type {Node} from '@babylonjs/core/node';
|
|
5
|
+
|
|
6
|
+
/** What an effect root holds a count on: the textures it draws with and the materials made for the effect. */
|
|
7
|
+
interface EffectResources {
|
|
8
|
+
textures: Set<BaseTexture>;
|
|
9
|
+
materials: Set<Material>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const owned = new WeakMap<Node, EffectResources>();
|
|
13
|
+
/** How many effect roots hold each texture and material. */
|
|
14
|
+
const holders = new WeakMap<BaseTexture | Material, number>();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Records that `root` uses `textures` and the `materials` made for the effect. Each is freed once
|
|
18
|
+
* the last root holding it is disposed, however that happens: {@link disposeEffect},
|
|
19
|
+
* `root.dispose()`, a parent's dispose or the scene's.
|
|
20
|
+
*/
|
|
21
|
+
export function retainEffectResources(
|
|
22
|
+
root: TransformNode,
|
|
23
|
+
textures: Iterable<BaseTexture>,
|
|
24
|
+
materials: Iterable<Material>
|
|
25
|
+
): void {
|
|
26
|
+
if (owned.has(root)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const resources: EffectResources = {textures: new Set(textures), materials: new Set(materials)};
|
|
30
|
+
for (const resource of [...resources.textures, ...resources.materials]) {
|
|
31
|
+
holders.set(resource, (holders.get(resource) ?? 0) + 1);
|
|
32
|
+
}
|
|
33
|
+
owned.set(root, resources);
|
|
34
|
+
root.onDisposeObservable.addOnce(() => releaseEffectResources(root));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Gives `copy` a hold on the textures and materials the effect `source` belongs to holds, as a
|
|
39
|
+
* copy of the effect, or of a node in it, draws with them too.
|
|
40
|
+
*/
|
|
41
|
+
export function shareEffectResources(source: TransformNode, copy: TransformNode): void {
|
|
42
|
+
for (let node: Node | null = source; node !== null; node = node.parent) {
|
|
43
|
+
const resources = owned.get(node);
|
|
44
|
+
if (resources !== undefined) {
|
|
45
|
+
retainEffectResources(copy, resources.textures, resources.materials);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function releaseEffectResources(root: Node): void {
|
|
52
|
+
const resources = owned.get(root);
|
|
53
|
+
if (resources === undefined) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
owned.delete(root);
|
|
57
|
+
for (const resource of [...resources.materials, ...resources.textures]) {
|
|
58
|
+
const count = (holders.get(resource) ?? 1) - 1;
|
|
59
|
+
if (count > 0) {
|
|
60
|
+
holders.set(resource, count);
|
|
61
|
+
} else {
|
|
62
|
+
holders.delete(resource);
|
|
63
|
+
resource.dispose();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Disposes an effect root and its nodes and systems, and each material and texture it holds once no
|
|
70
|
+
* other effect root holds it. A root nothing was recorded for is only disposed.
|
|
71
|
+
*/
|
|
72
|
+
export function disposeEffect(root: TransformNode): void {
|
|
73
|
+
root.dispose();
|
|
74
|
+
// A root disposed earlier, before its hold was recorded, still lets go.
|
|
75
|
+
releaseEffectResources(root);
|
|
76
|
+
}
|
package/src/envAtlas.ts
CHANGED
|
@@ -2,8 +2,20 @@ import {BaseTexture} from '@babylonjs/core/Materials/Textures/baseTexture';
|
|
|
2
2
|
import {Texture} from '@babylonjs/core/Materials/Textures/texture';
|
|
3
3
|
import {Scene} from '@babylonjs/core/scene';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Atlases by cube, then by the scene they were built for: a texture belongs to one scene and one
|
|
7
|
+
* engine, so a second scene (an editor preview, a second canvas) gets an atlas of its own.
|
|
8
|
+
*/
|
|
9
|
+
const atlasesByCube = new WeakMap<BaseTexture, Map<Scene, Texture>>();
|
|
10
|
+
const pendingByCube = new WeakMap<BaseTexture, Map<Scene, Promise<Texture | null>>>();
|
|
11
|
+
/** Atlases this module built, which it disposes; an atlas given to {@link cacheEnvAtlas} stays the caller's. */
|
|
12
|
+
const builtAtlases = new WeakSet<Texture>();
|
|
13
|
+
/** The faces each cube's atlas failed to build from: not tried, or warned about, again. */
|
|
14
|
+
const failedFaces = new WeakMap<BaseTexture, string>();
|
|
15
|
+
/** Atlases whose dispose this module already listens to. */
|
|
16
|
+
const watchedAtlases = new WeakSet<Texture>();
|
|
17
|
+
/** Cubes whose dispose this module already listens to. */
|
|
18
|
+
const watchedCubes = new WeakSet<BaseTexture>();
|
|
7
19
|
|
|
8
20
|
/** Reads the six face URLs from a Babylon CubeTexture (px py pz nx ny nz). */
|
|
9
21
|
export function cubeFaceUrls(reflection: BaseTexture | null | undefined): string[] | null {
|
|
@@ -15,14 +27,86 @@ export function cubeFaceUrls(reflection: BaseTexture | null | undefined): string
|
|
|
15
27
|
return files?.length === 6 ? files.slice() : null;
|
|
16
28
|
}
|
|
17
29
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
return
|
|
30
|
+
/** The scene a texture belongs to, or null for a disposed texture or one with no scene. */
|
|
31
|
+
function sceneOf(texture: BaseTexture): Scene | null {
|
|
32
|
+
return (texture as {getScene?: () => Scene | null}).getScene?.() ?? null;
|
|
21
33
|
}
|
|
22
34
|
|
|
23
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Returns the atlas already built or cached for this cube in `scene`, if any.
|
|
37
|
+
*
|
|
38
|
+
* @param cube - The cube texture the atlas was made from.
|
|
39
|
+
* @param scene - The scene the atlas must belong to. Defaults to the cube's own scene.
|
|
40
|
+
*/
|
|
41
|
+
export function getCachedEnvAtlas(cube: BaseTexture, scene: Scene | null = sceneOf(cube)): Texture | null {
|
|
42
|
+
return (scene && atlasesByCube.get(cube)?.get(scene)) || null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Stores a prebuilt atlas for a cube in the atlas's scene, so systems that use the cube in that
|
|
47
|
+
* scene take it instead of building one. The atlas stays the caller's: quarks never disposes it,
|
|
48
|
+
* and drops it from the cache when it, or the cube, is disposed.
|
|
49
|
+
*
|
|
50
|
+
* @param cube - The cube texture the atlas stands in for.
|
|
51
|
+
* @param atlas - A 3×2 atlas in the layout of {@link createEnvAtlasFromFaceUrls}.
|
|
52
|
+
*/
|
|
24
53
|
export function cacheEnvAtlas(cube: BaseTexture, atlas: Texture): void {
|
|
25
|
-
|
|
54
|
+
const scene = sceneOf(atlas) ?? sceneOf(cube);
|
|
55
|
+
if (scene) {
|
|
56
|
+
store(cube, scene, atlas);
|
|
57
|
+
} else {
|
|
58
|
+
console.warn('[babylon.quarks] cacheEnvAtlas: neither the atlas nor the cube has a scene; not cached');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Drops the atlases made for `cube` from the cache and disposes the ones quarks built. Runs by
|
|
64
|
+
* itself when the cube is disposed; call it when an environment is swapped out but the cube is
|
|
65
|
+
* kept. Systems that use the cube build a new atlas the next time their material is resolved.
|
|
66
|
+
*
|
|
67
|
+
* @param cube - The cube texture whose atlases to drop.
|
|
68
|
+
* @param scene - Only the atlas for this scene. Defaults to every scene.
|
|
69
|
+
*/
|
|
70
|
+
export function disposeEnvAtlas(cube: BaseTexture, scene?: Scene): void {
|
|
71
|
+
failedFaces.delete(cube);
|
|
72
|
+
const atlases = atlasesByCube.get(cube);
|
|
73
|
+
if (!atlases) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const [atlasScene, atlas] of [...atlases]) {
|
|
77
|
+
if (scene && atlasScene !== scene) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
atlases.delete(atlasScene);
|
|
81
|
+
if (builtAtlases.has(atlas)) {
|
|
82
|
+
atlas.dispose();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function store(cube: BaseTexture, scene: Scene, atlas: Texture): void {
|
|
88
|
+
let atlases = atlasesByCube.get(cube);
|
|
89
|
+
if (!atlases) {
|
|
90
|
+
atlases = new Map();
|
|
91
|
+
atlasesByCube.set(cube, atlases);
|
|
92
|
+
}
|
|
93
|
+
atlases.set(scene, atlas);
|
|
94
|
+
// A scene disposes its textures, so this also lets go of a disposed scene.
|
|
95
|
+
if (!watchedAtlases.has(atlas) && atlas.onDisposeObservable) {
|
|
96
|
+
watchedAtlases.add(atlas);
|
|
97
|
+
atlas.onDisposeObservable.addOnce(() => {
|
|
98
|
+
if (atlasesByCube.get(cube)?.get(scene) === atlas) {
|
|
99
|
+
atlasesByCube.get(cube)!.delete(scene);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (!watchedCubes.has(cube) && cube.onDisposeObservable) {
|
|
104
|
+
watchedCubes.add(cube);
|
|
105
|
+
cube.onDisposeObservable.addOnce(() => {
|
|
106
|
+
watchedCubes.delete(cube);
|
|
107
|
+
disposeEnvAtlas(cube);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
26
110
|
}
|
|
27
111
|
|
|
28
112
|
/**
|
|
@@ -63,23 +147,32 @@ export const envAtlasBuilder = {
|
|
|
63
147
|
};
|
|
64
148
|
|
|
65
149
|
/**
|
|
66
|
-
* Ensures a 3×2 env atlas exists for a CubeTexture
|
|
67
|
-
* synchronously when available; otherwise starts (or joins) an async build and
|
|
68
|
-
*
|
|
150
|
+
* Ensures a 3×2 env atlas exists for a CubeTexture in `scene`. Returns a cached atlas
|
|
151
|
+
* synchronously when available; otherwise starts (or joins) an async build and invokes onReady
|
|
152
|
+
* when the atlas is usable. A cube disposed before its build finishes gets no atlas. A build that
|
|
153
|
+
* fails warns once and is not tried again until the cube's faces change or
|
|
154
|
+
* {@link disposeEnvAtlas} is called for it; {@link cacheEnvAtlas} still supplies an atlas.
|
|
69
155
|
*/
|
|
70
156
|
export function ensureEnvAtlasFromCube(
|
|
71
157
|
cube: BaseTexture,
|
|
72
158
|
scene: Scene,
|
|
73
159
|
onReady: (atlas: Texture) => void
|
|
74
160
|
): Texture | null {
|
|
75
|
-
const cached =
|
|
161
|
+
const cached = getCachedEnvAtlas(cube, scene);
|
|
76
162
|
if (cached) {
|
|
77
163
|
onReady(cached);
|
|
78
164
|
return cached;
|
|
79
165
|
}
|
|
80
166
|
|
|
167
|
+
let pendingByScene = pendingByCube.get(cube);
|
|
168
|
+
if (!pendingByScene) {
|
|
169
|
+
pendingByScene = new Map();
|
|
170
|
+
pendingByCube.set(cube, pendingByScene);
|
|
171
|
+
}
|
|
172
|
+
const pendings = pendingByScene;
|
|
173
|
+
|
|
81
174
|
const kick = () => {
|
|
82
|
-
const existingPending =
|
|
175
|
+
const existingPending = pendings.get(scene);
|
|
83
176
|
if (existingPending) {
|
|
84
177
|
existingPending.then((atlas) => {
|
|
85
178
|
if (atlas) {
|
|
@@ -90,24 +183,39 @@ export function ensureEnvAtlasFromCube(
|
|
|
90
183
|
}
|
|
91
184
|
|
|
92
185
|
const urls = cubeFaceUrls(cube);
|
|
93
|
-
if (!urls) {
|
|
186
|
+
if (!urls || failedFaces.get(cube) === urls.join('\n')) {
|
|
94
187
|
return;
|
|
95
188
|
}
|
|
96
189
|
|
|
190
|
+
let cubeDisposed = false;
|
|
191
|
+
// Removed once the build settles, so it does not keep the scene and onReady alive.
|
|
192
|
+
const cubeObserver = cube.onDisposeObservable?.addOnce(() => (cubeDisposed = true));
|
|
193
|
+
const settle = () => {
|
|
194
|
+
pendings.delete(scene);
|
|
195
|
+
if (cubeObserver) {
|
|
196
|
+
cube.onDisposeObservable.remove(cubeObserver);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
97
199
|
const pending = envAtlasBuilder
|
|
98
200
|
.createFromFaceUrls(urls, scene)
|
|
99
201
|
.then((atlas) => {
|
|
100
|
-
|
|
101
|
-
|
|
202
|
+
settle();
|
|
203
|
+
if (cubeDisposed || scene.isDisposed) {
|
|
204
|
+
atlas.dispose();
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
builtAtlases.add(atlas);
|
|
208
|
+
store(cube, scene, atlas);
|
|
102
209
|
onReady(atlas);
|
|
103
210
|
return atlas;
|
|
104
211
|
})
|
|
105
212
|
.catch((err) => {
|
|
106
|
-
|
|
213
|
+
settle();
|
|
214
|
+
failedFaces.set(cube, urls.join('\n'));
|
|
107
215
|
console.warn('[babylon.quarks] Failed to build env atlas from cubemap faces', err);
|
|
108
216
|
return null;
|
|
109
217
|
});
|
|
110
|
-
|
|
218
|
+
pendings.set(scene, pending);
|
|
111
219
|
};
|
|
112
220
|
|
|
113
221
|
if (cube.isReady()) {
|
|
@@ -121,7 +229,7 @@ export function ensureEnvAtlasFromCube(
|
|
|
121
229
|
}
|
|
122
230
|
}
|
|
123
231
|
|
|
124
|
-
return
|
|
232
|
+
return getCachedEnvAtlas(cube, scene);
|
|
125
233
|
}
|
|
126
234
|
|
|
127
235
|
/** Loads one image URL as an HTMLImageElement. */
|
package/src/geometryUtil.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** `indices` when there are some, else `0..N-1`: each three vertices of `positions` one triangle. */
|
|
2
2
|
export function ensureTriangleIndices(
|
|
3
3
|
positions: Float32Array,
|
|
4
4
|
indices?: Uint32Array | Uint16Array
|