@woosh/meep-engine 3.11.1 → 3.11.2

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 (73) hide show
  1. package/package.json +1 -1
  2. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  3. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +294 -290
  4. package/src/engine/graphics3/ParticipatingMedia.d.ts +111 -0
  5. package/src/engine/graphics3/ParticipatingMedia.d.ts.map +1 -0
  6. package/src/engine/graphics3/ParticipatingMedia.js +203 -0
  7. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts +34 -0
  8. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts.map +1 -0
  9. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.js +73 -0
  10. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts +87 -0
  11. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts.map +1 -0
  12. package/src/engine/graphics3/ParticipatingMediaSystem3.js +184 -0
  13. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  14. package/src/shade/playground/volumetrics_froxel/README.md +104 -1
  15. package/src/shade/playground/volumetrics_froxel/index.html +20 -1
  16. package/src/shade/playground/volumetrics_froxel/main.d.ts.map +1 -1
  17. package/src/shade/playground/volumetrics_froxel/main.js +298 -0
  18. package/src/shade/playground/volumetrics_froxel/measure.d.ts +119 -0
  19. package/src/shade/playground/volumetrics_froxel/measure.d.ts.map +1 -0
  20. package/src/shade/playground/volumetrics_froxel/measure.js +559 -0
  21. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  22. package/src/shade/renderer/Renderer.js +22 -18
  23. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  24. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts +14 -13
  25. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts.map +1 -1
  26. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.js +133 -129
  27. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  28. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +480 -315
  29. package/src/shade/renderer/volumetrics/NOTES.md +649 -8
  30. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +5 -0
  31. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
  32. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +21 -2
  33. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts +26 -0
  34. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts.map +1 -1
  35. package/src/shade/renderer/volumetrics/SceneVolumetrics.js +64 -21
  36. package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.d.ts.map +1 -1
  37. package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.js +84 -28
  38. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts +98 -0
  39. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts.map +1 -0
  40. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.js +111 -0
  41. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts +84 -0
  42. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts.map +1 -0
  43. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.js +252 -0
  44. package/src/shade/renderer/volumetrics/chunk_integrate_optical_depth.js +75 -75
  45. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts +43 -0
  46. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts.map +1 -0
  47. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.js +145 -0
  48. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts +20 -0
  49. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts.map +1 -0
  50. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.js +48 -0
  51. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts +38 -0
  52. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts.map +1 -0
  53. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.js +107 -0
  54. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +4 -1
  55. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  56. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +577 -471
  57. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
  58. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +64 -13
  59. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts +8 -0
  60. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts.map +1 -0
  61. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js +174 -0
  62. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts +25 -0
  63. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts.map +1 -1
  64. package/src/shade/wgsl/emulator/CPUBitmapData.js +33 -0
  65. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2221 -2215
  66. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  67. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +1297 -1117
  68. package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts +69 -0
  69. package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts.map +1 -0
  70. package/src/shade/wgsl/emulator/cpu_texture_gather.js +148 -0
  71. package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts +16 -0
  72. package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts.map +1 -1
  73. package/src/shade/wgsl/emulator/cpu_texture_sample.js +176 -172
@@ -0,0 +1,111 @@
1
+ /**
2
+ * A box of fog, smoke, dust or cloud that an entity carries around.
3
+ *
4
+ * **What is in here is the medium; the {@link Transform} beside it is the box.** Shade's
5
+ * {@link ParticipatingMediaVolume} is a unit cube centred on the origin, posed by a transform — so
6
+ * position, size and orientation are already an ECS concept and there is nothing to restate. Which
7
+ * is also why this component does nothing on its own: {@link ParticipatingMediaSystem3} links it
8
+ * against a `Transform` and only that pair becomes a volume.
9
+ *
10
+ * **Three numbers, in two different currencies.** {@link particle_spec} is what a single particle
11
+ * does to light — a Mie solution, physical and reusable — and {@link density} is how many of them
12
+ * there are per cubic metre. The pair is what makes smoke and fog different things rather than the
13
+ * same fog at two strengths. Authoring in raw density means knowing the cross-sections, so
14
+ * {@link target_extinction} exists to author the other way round: say how much light the medium
15
+ * should swallow and let the particle decide the count.
16
+ *
17
+ * @see ParticipatingMediaSystem3 what turns this into something the renderer draws
18
+ * @see ParticipatingMediaVolume the renderer's own object
19
+ */
20
+ export class ParticipatingMedia {
21
+ /**
22
+ * @param {*} json
23
+ * @returns {ParticipatingMedia}
24
+ */
25
+ static fromJSON(json: any): ParticipatingMedia;
26
+ /**
27
+ * Particles per cubic metre.
28
+ *
29
+ * Physically a count, so the numbers are large — the default is dense fog — and the useful range
30
+ * depends entirely on {@link particle_spec}: a cloud droplet blocks far more light than a haze
31
+ * particle, so the same count of them is a very different medium.
32
+ *
33
+ * @type {number}
34
+ */
35
+ density: number;
36
+ /**
37
+ * What one particle of this medium does to light.
38
+ *
39
+ * Defaults to fog. The generated `MIE_PARTICLES_STANDARD_PRECOMPUTED` library is where the
40
+ * physically meaningful values live — `VolumetricsParticleSpec.fromMeep` converts an entry of it
41
+ * into one of these.
42
+ *
43
+ * @type {VolumetricsParticleSpec}
44
+ */
45
+ particle_spec: VolumetricsParticleSpec;
46
+ /**
47
+ * How far in from the box's faces the medium fades out, in metres.
48
+ *
49
+ * **World-space, and deliberately not scaled by the transform**, so a 0.5 here is half a metre
50
+ * of softness whether the volume is a metre across or a hundred. Zero gives the box hard edges,
51
+ * which is visible as a straight line across the screen wherever the box crosses the view.
52
+ *
53
+ * @type {number}
54
+ */
55
+ fade_distance: number;
56
+ /**
57
+ * @param {number} value
58
+ */
59
+ set target_extinction(arg: number);
60
+ /**
61
+ * The medium's extinction per metre, which is {@link density} expressed in what it does rather
62
+ * than in how many particles it took.
63
+ *
64
+ * The point of it is that it is comparable across particles: a smoke volume and a fog volume set
65
+ * to the same target extinction swallow the same amount of light, while the same *density* in
66
+ * both would differ by orders of magnitude.
67
+ *
68
+ * **Derived, not stored** — writing it sets {@link density}, and changing `particle_spec`
69
+ * afterwards therefore changes what reading it back gives. The density is what survives a save.
70
+ *
71
+ * @returns {number}
72
+ */
73
+ get target_extinction(): number;
74
+ toJSON(): {
75
+ density: number;
76
+ fade_distance: number;
77
+ particle_spec: {
78
+ scattering: number[];
79
+ extinction: number[];
80
+ radius: number;
81
+ g: number;
82
+ };
83
+ };
84
+ /**
85
+ * @param {number} [density]
86
+ * @param {number} [fade_distance]
87
+ * @param {object} [particle_spec] omitted resets the medium to fog rather than leaving whatever
88
+ * the component already held — a load has to determine the whole component, or an instance being
89
+ * reused would carry the previous level's particle into the next one
90
+ */
91
+ fromJSON({ density, fade_distance, particle_spec }?: number): void;
92
+ /**
93
+ * **The cross-sections and the radius are left out of this on purpose.** They run to 1e-16 and
94
+ * 1e-6, and {@link computeHashFloat} collapses anything that small to zero, so mixing them in
95
+ * would cost a multiply and buy no dispersion at all. A hash is allowed to collide;
96
+ * {@link equals} is the exact one.
97
+ *
98
+ * @returns {number}
99
+ */
100
+ hash(): number;
101
+ /**
102
+ * @param {ParticipatingMedia} other
103
+ * @returns {boolean}
104
+ */
105
+ equals(other: ParticipatingMedia): boolean;
106
+ }
107
+ export namespace ParticipatingMedia {
108
+ let typeName: string;
109
+ }
110
+ import { VolumetricsParticleSpec } from "../../shade/renderer/volumetrics/ParticipatingMediaVolume.js";
111
+ //# sourceMappingURL=ParticipatingMedia.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParticipatingMedia.d.ts","sourceRoot":"","sources":["../../../../src/engine/graphics3/ParticipatingMedia.js"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;;;GAkBG;AACH;IAyEI;;;OAGG;IACH,4BAFa,kBAAkB,CAQ9B;IAjFD;;;;;;;;OAQG;IACH,SAFU,MAAM,CAEU;IAE1B;;;;;;;;OAQG;IACH,eAFU,uBAAuB,CAE8C;IAE/E;;;;;;;;OAQG;IACH,eAFU,MAAM,CAEsB;IAmBtC;;OAEG;IACH,mCAgBC;IApCD;;;;;;;;;;;;OAYG;IACH,gCAEC;IAmCD;;;;;;;;;MAaC;IAED;;;;;;OAMG;IACH,qDANW,MAAM,QA2BhB;IAED;;;;;;;OAOG;IACH,QAFa,MAAM,CAUlB;IAED;;;OAGG;IACH,cAHW,kBAAkB,GAChB,OAAO,CAMnB;CACJ;;kBAIS,MAAM;;wCAjMT,8DAA8D"}
@@ -0,0 +1,203 @@
1
+ import { assert } from "../../core/assert.js";
2
+ import { combine_hash } from "../../core/collection/array/combine_hash.js";
3
+ import { rgb_to_luminance } from "../../core/color/rgb_to_luminance.js";
4
+ import { computeHashFloat } from "../../core/primitives/numbers/computeHashFloat.js";
5
+ import {
6
+ VOLUMETRIC_PARTICLE_SPEC_FOG,
7
+ VolumetricsParticleSpec
8
+ } from "../../shade/renderer/volumetrics/ParticipatingMediaVolume.js";
9
+
10
+ /**
11
+ * Particles per cubic metre of dense fog, which is what a medium is until it is told otherwise.
12
+ * @type {number}
13
+ */
14
+ const DEFAULT_DENSITY = 3e7;
15
+
16
+ /**
17
+ * @type {number}
18
+ */
19
+ const DEFAULT_FADE_DISTANCE = 0.1;
20
+
21
+ /**
22
+ * A box of fog, smoke, dust or cloud that an entity carries around.
23
+ *
24
+ * **What is in here is the medium; the {@link Transform} beside it is the box.** Shade's
25
+ * {@link ParticipatingMediaVolume} is a unit cube centred on the origin, posed by a transform — so
26
+ * position, size and orientation are already an ECS concept and there is nothing to restate. Which
27
+ * is also why this component does nothing on its own: {@link ParticipatingMediaSystem3} links it
28
+ * against a `Transform` and only that pair becomes a volume.
29
+ *
30
+ * **Three numbers, in two different currencies.** {@link particle_spec} is what a single particle
31
+ * does to light — a Mie solution, physical and reusable — and {@link density} is how many of them
32
+ * there are per cubic metre. The pair is what makes smoke and fog different things rather than the
33
+ * same fog at two strengths. Authoring in raw density means knowing the cross-sections, so
34
+ * {@link target_extinction} exists to author the other way round: say how much light the medium
35
+ * should swallow and let the particle decide the count.
36
+ *
37
+ * @see ParticipatingMediaSystem3 what turns this into something the renderer draws
38
+ * @see ParticipatingMediaVolume the renderer's own object
39
+ */
40
+ export class ParticipatingMedia {
41
+
42
+ /**
43
+ * Particles per cubic metre.
44
+ *
45
+ * Physically a count, so the numbers are large — the default is dense fog — and the useful range
46
+ * depends entirely on {@link particle_spec}: a cloud droplet blocks far more light than a haze
47
+ * particle, so the same count of them is a very different medium.
48
+ *
49
+ * @type {number}
50
+ */
51
+ density = DEFAULT_DENSITY;
52
+
53
+ /**
54
+ * What one particle of this medium does to light.
55
+ *
56
+ * Defaults to fog. The generated `MIE_PARTICLES_STANDARD_PRECOMPUTED` library is where the
57
+ * physically meaningful values live — `VolumetricsParticleSpec.fromMeep` converts an entry of it
58
+ * into one of these.
59
+ *
60
+ * @type {VolumetricsParticleSpec}
61
+ */
62
+ particle_spec = VolumetricsParticleSpec.fromJSON(VOLUMETRIC_PARTICLE_SPEC_FOG);
63
+
64
+ /**
65
+ * How far in from the box's faces the medium fades out, in metres.
66
+ *
67
+ * **World-space, and deliberately not scaled by the transform**, so a 0.5 here is half a metre
68
+ * of softness whether the volume is a metre across or a hundred. Zero gives the box hard edges,
69
+ * which is visible as a straight line across the screen wherever the box crosses the view.
70
+ *
71
+ * @type {number}
72
+ */
73
+ fade_distance = DEFAULT_FADE_DISTANCE;
74
+
75
+ /**
76
+ * The medium's extinction per metre, which is {@link density} expressed in what it does rather
77
+ * than in how many particles it took.
78
+ *
79
+ * The point of it is that it is comparable across particles: a smoke volume and a fog volume set
80
+ * to the same target extinction swallow the same amount of light, while the same *density* in
81
+ * both would differ by orders of magnitude.
82
+ *
83
+ * **Derived, not stored** — writing it sets {@link density}, and changing `particle_spec`
84
+ * afterwards therefore changes what reading it back gives. The density is what survives a save.
85
+ *
86
+ * @returns {number}
87
+ */
88
+ get target_extinction() {
89
+ return this.density * rgb_to_luminance(...this.particle_spec.extinction);
90
+ }
91
+
92
+ /**
93
+ * @param {number} value
94
+ */
95
+ set target_extinction(value) {
96
+ assert.isNumber(value, 'target_extinction');
97
+ assert.isFinite(value, 'target_extinction');
98
+
99
+ const density = value / rgb_to_luminance(...this.particle_spec.extinction);
100
+
101
+ /*
102
+ Catches the one way this goes wrong: a particle spec that extinguishes nothing. A default-
103
+ constructed `VolumetricsParticleSpec` has zero cross-sections, and asking such a medium to
104
+ reach any extinction at all needs infinitely many of its particles. Rejecting it here names
105
+ the mistake; letting it through puts an `Infinity` in the GPU table, where it becomes a
106
+ screenful of NaN several passes later.
107
+ */
108
+ assert.isFinite(density, 'the density this target_extinction implies (the particle spec extinguishes nothing)');
109
+
110
+ this.density = density;
111
+ }
112
+
113
+ /**
114
+ * @param {*} json
115
+ * @returns {ParticipatingMedia}
116
+ */
117
+ static fromJSON(json) {
118
+ const r = new ParticipatingMedia();
119
+
120
+ r.fromJSON(json);
121
+
122
+ return r;
123
+ }
124
+
125
+ toJSON() {
126
+ const spec = this.particle_spec;
127
+
128
+ return {
129
+ density: this.density,
130
+ fade_distance: this.fade_distance,
131
+ particle_spec: {
132
+ scattering: spec.scattering.slice(),
133
+ extinction: spec.extinction.slice(),
134
+ radius: spec.radius,
135
+ g: spec.g
136
+ }
137
+ };
138
+ }
139
+
140
+ /**
141
+ * @param {number} [density]
142
+ * @param {number} [fade_distance]
143
+ * @param {object} [particle_spec] omitted resets the medium to fog rather than leaving whatever
144
+ * the component already held — a load has to determine the whole component, or an instance being
145
+ * reused would carry the previous level's particle into the next one
146
+ */
147
+ fromJSON({
148
+ density = DEFAULT_DENSITY,
149
+ fade_distance = DEFAULT_FADE_DISTANCE,
150
+ particle_spec
151
+ }) {
152
+ assert.isNumber(density, 'density');
153
+ assert.isFinite(density, 'density');
154
+ assert.greaterThanOrEqual(density, 0, 'density must not be negative');
155
+
156
+ assert.isNumber(fade_distance, 'fade_distance');
157
+ assert.isFinite(fade_distance, 'fade_distance');
158
+ assert.greaterThanOrEqual(fade_distance, 0, 'fade_distance must not be negative');
159
+
160
+ this.density = density;
161
+ this.fade_distance = fade_distance;
162
+
163
+ if (particle_spec === undefined) {
164
+ this.particle_spec.copy(VOLUMETRIC_PARTICLE_SPEC_FOG);
165
+ } else {
166
+ this.particle_spec.fromJSON(particle_spec);
167
+ }
168
+ }
169
+
170
+ /**
171
+ * **The cross-sections and the radius are left out of this on purpose.** They run to 1e-16 and
172
+ * 1e-6, and {@link computeHashFloat} collapses anything that small to zero, so mixing them in
173
+ * would cost a multiply and buy no dispersion at all. A hash is allowed to collide;
174
+ * {@link equals} is the exact one.
175
+ *
176
+ * @returns {number}
177
+ */
178
+ hash() {
179
+ const spec = this.particle_spec;
180
+
181
+ return combine_hash(
182
+ computeHashFloat(this.density),
183
+ computeHashFloat(this.fade_distance),
184
+ computeHashFloat(spec.g)
185
+ );
186
+ }
187
+
188
+ /**
189
+ * @param {ParticipatingMedia} other
190
+ * @returns {boolean}
191
+ */
192
+ equals(other) {
193
+ return this.density === other.density
194
+ && this.fade_distance === other.fade_distance
195
+ && this.particle_spec.equals(other.particle_spec);
196
+ }
197
+ }
198
+
199
+ /**
200
+ * @readonly
201
+ * @type {string}
202
+ */
203
+ ParticipatingMedia.typeName = "ParticipatingMedia";
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Ten numbers, all of them `float64`.
3
+ *
4
+ * **The width is the whole design decision here, and it is not the usual one.** Component adapters
5
+ * around this one write `float32`, which is what the GPU reads anyway — but a participating medium
6
+ * is authored at the two far ends of the exponent range at once: cross-sections run to 1e-16 and the
7
+ * particle counts that pay for them run to 1e16. `float32` holds both magnitudes comfortably and
8
+ * keeps about seven digits of each, so a round trip through it moves every one of these values a
9
+ * little. Nothing about the rendered picture would change. What would change is that
10
+ * `ParticipatingMedia.equals` stops answering `true` across a save, and a level that reloads into
11
+ * components which merely *look* like the ones that were saved is a level whose blueprints stop
12
+ * matching. Ten doubles is eighty bytes; that is the price of the round trip being exact.
13
+ *
14
+ * **`target_extinction` is not in the format** because it is not in the component — it is
15
+ * {@link ParticipatingMedia.density} read through the particle spec, and both of its inputs are
16
+ * here.
17
+ */
18
+ export class ParticipatingMediaSerializationAdapter extends BinaryClassSerializationAdapter<any> {
19
+ constructor();
20
+ klass: typeof ParticipatingMedia;
21
+ /**
22
+ * @param {BinaryBuffer} buffer
23
+ * @param {ParticipatingMedia} value
24
+ */
25
+ serialize(buffer: BinaryBuffer, value: ParticipatingMedia): void;
26
+ /**
27
+ * @param {BinaryBuffer} buffer
28
+ * @param {ParticipatingMedia} value
29
+ */
30
+ deserialize(buffer: BinaryBuffer, value: ParticipatingMedia): void;
31
+ }
32
+ import { BinaryClassSerializationAdapter } from "../ecs/storage/binary/BinaryClassSerializationAdapter.js";
33
+ import { ParticipatingMedia } from "./ParticipatingMedia.js";
34
+ //# sourceMappingURL=ParticipatingMediaSerializationAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParticipatingMediaSerializationAdapter.d.ts","sourceRoot":"","sources":["../../../../src/engine/graphics3/ParticipatingMediaSerializationAdapter.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;GAgBG;AACH;;IAEI,iCAA2B;IAG3B;;;OAGG;IACH,uCAFW,kBAAkB,QAkB5B;IAED;;;OAGG;IACH,yCAFW,kBAAkB,QAsB5B;CACJ;gDAxE+C,0DAA0D;mCACvE,yBAAyB"}
@@ -0,0 +1,73 @@
1
+ import { BinaryClassSerializationAdapter } from "../ecs/storage/binary/BinaryClassSerializationAdapter.js";
2
+ import { ParticipatingMedia } from "./ParticipatingMedia.js";
3
+
4
+ /**
5
+ * Ten numbers, all of them `float64`.
6
+ *
7
+ * **The width is the whole design decision here, and it is not the usual one.** Component adapters
8
+ * around this one write `float32`, which is what the GPU reads anyway — but a participating medium
9
+ * is authored at the two far ends of the exponent range at once: cross-sections run to 1e-16 and the
10
+ * particle counts that pay for them run to 1e16. `float32` holds both magnitudes comfortably and
11
+ * keeps about seven digits of each, so a round trip through it moves every one of these values a
12
+ * little. Nothing about the rendered picture would change. What would change is that
13
+ * `ParticipatingMedia.equals` stops answering `true` across a save, and a level that reloads into
14
+ * components which merely *look* like the ones that were saved is a level whose blueprints stop
15
+ * matching. Ten doubles is eighty bytes; that is the price of the round trip being exact.
16
+ *
17
+ * **`target_extinction` is not in the format** because it is not in the component — it is
18
+ * {@link ParticipatingMedia.density} read through the particle spec, and both of its inputs are
19
+ * here.
20
+ */
21
+ export class ParticipatingMediaSerializationAdapter extends BinaryClassSerializationAdapter {
22
+
23
+ klass = ParticipatingMedia;
24
+ version = 0;
25
+
26
+ /**
27
+ * @param {BinaryBuffer} buffer
28
+ * @param {ParticipatingMedia} value
29
+ */
30
+ serialize(buffer, value) {
31
+ buffer.writeFloat64(value.density);
32
+ buffer.writeFloat64(value.fade_distance);
33
+
34
+ const spec = value.particle_spec;
35
+
36
+ for (let i = 0; i < 3; i++) {
37
+ buffer.writeFloat64(spec.scattering[i]);
38
+ }
39
+
40
+ for (let i = 0; i < 3; i++) {
41
+ buffer.writeFloat64(spec.extinction[i]);
42
+ }
43
+
44
+ buffer.writeFloat64(spec.radius);
45
+ buffer.writeFloat64(spec.g);
46
+ }
47
+
48
+ /**
49
+ * @param {BinaryBuffer} buffer
50
+ * @param {ParticipatingMedia} value
51
+ */
52
+ deserialize(buffer, value) {
53
+ value.density = buffer.readFloat64();
54
+ value.fade_distance = buffer.readFloat64();
55
+
56
+ const spec = value.particle_spec;
57
+
58
+ /*
59
+ In place, which is safe because a spec owns its arrays: every route into one —
60
+ `fromJSON`, `copy`, `clone` — slices what it was given rather than keeping the reference.
61
+ */
62
+ for (let i = 0; i < 3; i++) {
63
+ spec.scattering[i] = buffer.readFloat64();
64
+ }
65
+
66
+ for (let i = 0; i < 3; i++) {
67
+ spec.extinction[i] = buffer.readFloat64();
68
+ }
69
+
70
+ spec.radius = buffer.readFloat64();
71
+ spec.g = buffer.readFloat64();
72
+ }
73
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Copy what the component and its transform say onto the volume Shade will render.
3
+ *
4
+ * The particle spec is **copied, not referenced**: two entities are routinely built from the same
5
+ * blueprint and would otherwise share one spec, so tuning one volume's particle would silently
6
+ * retune the other's.
7
+ *
8
+ * @param {ParticipatingMediaVolume} volume Shade's
9
+ * @param {ParticipatingMedia} component meep's
10
+ * @param {Transform} transform where the box is
11
+ */
12
+ export function participating_media_volume_write(volume: ParticipatingMediaVolume, component: ParticipatingMedia, transform: Transform): void;
13
+ /**
14
+ * Whether the volume already says what the component and transform say.
15
+ *
16
+ * True on all but a handful of frames, which is the point of asking: a `false` here costs the whole
17
+ * scene's volumetrics table a trip to the GPU.
18
+ *
19
+ * **The transform is compared by its matrix rather than by `Transform.equals`**, because the matrix
20
+ * is the entire transform the GPU is given — the volume's position, rotation and scale are never
21
+ * read on their own. That makes this exactly the question worth asking, "would the upload differ",
22
+ * and it catches a transform posed by writing its matrix, which comparing position, rotation and
23
+ * scale would not.
24
+ *
25
+ * @param {ParticipatingMediaVolume} volume
26
+ * @param {ParticipatingMedia} component
27
+ * @param {Transform} transform
28
+ * @returns {boolean}
29
+ */
30
+ export function participating_media_volume_is_current(volume: ParticipatingMediaVolume, component: ParticipatingMedia, transform: Transform): boolean;
31
+ /**
32
+ * Mirrors fog, smoke and cloud entities into Shade's volumetrics.
33
+ *
34
+ * The same shape as {@link LightSystem3}: the ECS says what exists and where, and this keeps the
35
+ * renderer's own objects in step. A {@link ParticipatingMediaVolume} is a unit cube posed by a
36
+ * transform, so an entity's `Transform` is the volume — position, size and orientation with no
37
+ * translation step in between.
38
+ *
39
+ * **The transform is taken as world-space, with no hierarchy applied**, which is what the volume
40
+ * model can express: a volume is an oriented box in the scene and Shade has no parent chain to walk
41
+ * for it. Parenting a fog volume to a moving entity therefore leaves the fog where the child's own
42
+ * transform puts it. The lights are placed the same way, for the same reason.
43
+ *
44
+ * **Changes are polled once a frame rather than subscribed to.** A `Transform` announces itself and
45
+ * nothing else here does — a density tuned by an editor slider, a particle spec swapped wholesale
46
+ * for a different Mie solution — so subscribing would cover one field in three and the other two
47
+ * would still have to be polled. Two paths that have to agree, where one does the whole job. It
48
+ * costs about two dozen float comparisons per volume per frame and a scene holds volumes in the
49
+ * tens, so the comparison is cheaper than the signal plumbing it replaces. The price is latency: a
50
+ * change is visible within a frame rather than instantly.
51
+ *
52
+ * **Nothing is uploaded unless something actually changed.** The GPU table is repacked whole
53
+ * whenever {@link SceneVolumetrics.version} moves, so an unconditional copy every frame would
54
+ * re-upload every volume in the scene forever. The comparison is what decides, and on almost every
55
+ * frame it decides nothing has to happen.
56
+ *
57
+ * @see ParticipatingMedia the component
58
+ * @see SceneVolumetrics what the volumes are handed to
59
+ */
60
+ export class ParticipatingMediaSystem3 extends System<any> {
61
+ /**
62
+ * @param {GraphicsEngine3} graphics
63
+ * @param {Scene} scene
64
+ */
65
+ constructor(graphics: GraphicsEngine3, scene: Scene);
66
+ dependencies: (typeof Transform | typeof ParticipatingMedia)[];
67
+ startup(entityManager: any): Promise<void>;
68
+ shutdown(entityManager: any): Promise<void>;
69
+ /**
70
+ * @param {ParticipatingMedia} component
71
+ * @param {Transform} transform
72
+ * @param {number} entity
73
+ */
74
+ link(component: ParticipatingMedia, transform: Transform, entity: number): void;
75
+ /**
76
+ * @param {ParticipatingMedia} component
77
+ * @param {Transform} transform
78
+ * @param {number} entity
79
+ */
80
+ unlink(component: ParticipatingMedia, transform: Transform, entity: number): void;
81
+ #private;
82
+ }
83
+ import { ParticipatingMediaVolume } from "../../shade/renderer/volumetrics/ParticipatingMediaVolume.js";
84
+ import { ParticipatingMedia } from "./ParticipatingMedia.js";
85
+ import { Transform } from "../ecs/transform/Transform.js";
86
+ import { System } from "../ecs/System.js";
87
+ //# sourceMappingURL=ParticipatingMediaSystem3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParticipatingMediaSystem3.d.ts","sourceRoot":"","sources":["../../../../src/engine/graphics3/ParticipatingMediaSystem3.js"],"names":[],"mappings":"AA6IA;;;;;;;;;;GAUG;AACH,yDAJW,wBAAwB,aACxB,kBAAkB,aAClB,SAAS,QASnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,8DALW,wBAAwB,aACxB,kBAAkB,aAClB,SAAS,GACP,OAAO,CAOnB;AAhLD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH;IAmBI;;;OAGG;IACH,qDASC;IA9BD,+DAA+C;IAgC/C,2CAIC;IAED,4CAIC;IAED;;;;OAIG;IACH,gBAJW,kBAAkB,aAClB,SAAS,UACT,MAAM,QAUhB;IAED;;;;OAIG;IACH,kBAJW,kBAAkB,aAClB,SAAS,UACT,MAAM,QAIhB;;CAmCJ;yCAzIwC,8DAA8D;mCAGpE,yBAAyB;0BADlC,+BAA+B;uBADlC,kBAAkB"}