@woosh/meep-engine 2.75.2 → 2.75.4

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 (37) hide show
  1. package/build/meep.cjs +56 -60
  2. package/build/meep.min.js +1 -1
  3. package/build/meep.module.js +56 -60
  4. package/package.json +1 -1
  5. package/src/core/geom/3d/ray/ray_computeNearestPointToPoint.js +7 -1
  6. package/src/core/geom/3d/topology/simplify/compute_face_normal_change_dot_product.js +19 -39
  7. package/src/core/geom/3d/v3_compute_triangle_normal.js +1 -1
  8. package/src/core/geom/Quaternion.js +0 -59
  9. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +2 -1
  10. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshHighlightSystem.js +7 -1
  11. package/src/engine/graphics/geometry/VertexDataSpec.d.ts +10 -0
  12. package/src/engine/graphics/geometry/VertexDataSpec.js +20 -21
  13. package/src/engine/graphics/render/visibility/hiz/buildCanvasViewFromTexture.js +41 -10
  14. package/src/engine/graphics/texture/formatToChannelCount.js +2 -1
  15. package/src/engine/graphics/texture/sampler/filter/sampler2d_scale_down_generic.js +2 -2
  16. package/src/engine/graphics/texture/sampler/prototypeSamplerFiltering.js +20 -19
  17. package/src/engine/graphics/texture/virtual/v2/NOTES.md +93 -1
  18. package/src/engine/graphics/texture/virtual/v2/VirtualTextureMaterial.js +193 -0
  19. package/src/engine/graphics/texture/virtual/v2/VirtualTextureMemoryMapping.js +203 -0
  20. package/src/engine/graphics/texture/virtual/v2/{PageTexture.js → VirtualTexturePage.js} +151 -22
  21. package/src/engine/graphics/texture/virtual/v2/VirtualTextureTileLoader.js +10 -1
  22. package/src/engine/graphics/texture/virtual/v2/VirtualTextureUsage.js +43 -10
  23. package/src/engine/graphics/texture/virtual/v2/VirtualTextureUsageShader.js +3 -3
  24. package/src/engine/graphics/texture/virtual/v2/VirtualTextureUsageUpdater.js +39 -11
  25. package/src/engine/graphics/texture/virtual/v2/debug/ResidencyDebugView.js +20 -5
  26. package/src/engine/graphics/texture/virtual/v2/prototype.js +148 -62
  27. package/src/engine/graphics/texture/virtual/v2/tile/VirtualTextureTile.js +4 -0
  28. package/src/core/bvh2/sah/surfaceAreaHeuristic.js +0 -15
  29. package/src/core/bvh2/sah/surfaceAreaHeuristicFull.js +0 -14
  30. package/src/core/geom/3d/aabb/computeBoundingBoxFromVertexData.js +0 -12
  31. package/src/core/geom/3d/frustum/array_normalize_plane.js +0 -25
  32. package/src/core/geom/3d/plane/lerp_planes_to_array.js +0 -53
  33. package/src/core/geom/3d/plane/planeRayIntersection.js +0 -14
  34. package/src/core/geom/3d/voxel/DenseBitVolume3D.js +0 -87
  35. package/src/core/geom/3d/voxel/buildVolumeFromProjectedGeometry.js +0 -23
  36. package/src/engine/graphics/texture/virtual/v2/ResidentTileTexture.js +0 -46
  37. /package/src/core/math/{bessel_i0.spec.js → bessel_j0.spec.js} +0 -0
@@ -24,4 +24,96 @@ Based on the `Usage` data, we populate our `Physical` texture of pages, and base
24
24
 
25
25
  Most papers suggest introducing a border into each tile of ~4 pixels.
26
26
  4 pixel border on a 128x128 pixel tile is going to take up 12.1% of space, which is quite a lot.
27
- We can do filtering in software, that is - sample individual pixels and perform interpolation inside the shader.
27
+ We can do filtering in software, that is - sample individual pixels and perform interpolation inside the shader.
28
+
29
+ ### Speeding up WebGL read-back
30
+
31
+ From [Babylon](https://github.com/BabylonJS/Babylon.js/blob/90dbafed8de9de752cdc399604f9c7c51116d630/src/Engines/engine.ts#L1772)
32
+ ```ts
33
+ public _readPixelsAsync(x: number, y: number, w: number, h: number, format: number, type: number, outputBuffer: ArrayBufferView) {
34
+ if (this._webGLVersion < 2) {
35
+ throw new Error("_readPixelsAsync only work on WebGL2+");
36
+ }
37
+
38
+ let gl = <WebGL2RenderingContext>(this._gl as any);
39
+ const buf = gl.createBuffer();
40
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf);
41
+ gl.bufferData(gl.PIXEL_PACK_BUFFER, outputBuffer.byteLength, gl.STREAM_READ);
42
+ gl.readPixels(x, y, w, h, format, type, 0);
43
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
44
+
45
+ const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
46
+ if (!sync) {
47
+ return null;
48
+ }
49
+
50
+ gl.flush();
51
+
52
+ return this._clientWaitAsync(sync, 0, 10).then(() => {
53
+ gl.deleteSync(sync);
54
+
55
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf);
56
+ gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, outputBuffer);
57
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
58
+ gl.deleteBuffer(buf);
59
+
60
+ return outputBuffer;
61
+ });
62
+ }
63
+
64
+ private _clientWaitAsync(sync: WebGLSync, flags = 0, intervalms = 10): Promise<void> {
65
+ const gl = <WebGL2RenderingContext>(this._gl as any);
66
+ return new Promise((resolve, reject) => {
67
+ const check = () => {
68
+ const res = gl.clientWaitSync(sync, flags, 0);
69
+ if (res == gl.WAIT_FAILED) {
70
+ reject();
71
+ return;
72
+ }
73
+ if (res == gl.TIMEOUT_EXPIRED) {
74
+ setTimeout(check, intervalms);
75
+ return;
76
+ }
77
+ resolve();
78
+ };
79
+
80
+ check();
81
+ });
82
+ }
83
+ ```
84
+
85
+ more links:
86
+ https://forum.babylonjs.com/t/speeding-up-readpixels/12739
87
+
88
+
89
+ ## References
90
+
91
+ * https://studiopixl.com/2022-04-27/sparse-virtual-textures
92
+ * Anisotropic filtering in a shader: https://www.shadertoy.com/view/4lXfzn
93
+
94
+ ## WebGL fails
95
+
96
+ WebGL doesn't seem to support mipmaps on integer textures
97
+
98
+ ## Anisotropic filtering:
99
+
100
+ ```glsl
101
+ // R is viewport resolution
102
+ // p is UV
103
+ vec4 textureAniso(sampler2D T, vec2 R, vec2 p) {
104
+ mat2 J = inverse(mat2(dFdx(p),dFdy(p))); // dFdxy: pixel footprint in texture space
105
+ J = transpose(J)*J; // quadratic form
106
+ float d = determinant(J), t = J[0][0]+J[1][1], // find ellipse: eigenvalues, max eigenvector
107
+ D = sqrt(abs(t*t-4.*d)), // abs() fix a bug: in weird view angles 0 can be slightly negative
108
+ V = (t-D)/2., v = (t+D)/2., // eigenvalues. ( ATTENTION: not sorted )
109
+ M = 1./sqrt(V), m = 1./sqrt(v), l =log2(m*R.y); // = 1./radii^2
110
+
111
+ //if (M/m>16.) l = log2(M/16.*R.y); // optional
112
+
113
+ vec2 A = M * normalize(vec2( -J[0][1] , J[0][0]-V )); // max eigenvector = main axis
114
+ vec4 O = vec4(0);
115
+ for (float i = -7.5; i<8.; i++) // sample x16 along main axis at LOD min-radius
116
+ O += textureLod(T, p+(i/16.)*A, l);
117
+ return O/16.;
118
+ }
119
+ ```
@@ -0,0 +1,193 @@
1
+ import { DoubleSide, ShaderMaterial, Vector2 } from "three";
2
+
3
+ export class VirtualTextureMaterial extends ShaderMaterial {
4
+ /**
5
+ *
6
+ * @param {VirtualTexturePage} page
7
+ * @param {VirtualTextureMemoryMapping} mapping
8
+ */
9
+ constructor({
10
+ page,
11
+ mapping
12
+ }) {
13
+ super({
14
+ side: DoubleSide,
15
+ uniforms: {
16
+ u_page: { value: page.texture },
17
+ u_page_resolution: {
18
+ value: new Vector2(
19
+ page.page_texture_resolution_in_tiles[0],
20
+ page.page_texture_resolution_in_tiles[1]
21
+ )
22
+ },
23
+ u_tile_resolution: { value: page.tile_resolution },
24
+ u_tile_padding: { value: page.tile_margin },
25
+ u_mapping: { value: mapping.texture },
26
+ u_texture_resolution: { value: mapping.resolution * page.tile_resolution },
27
+ u_max_mip_level: { value: mapping.max_mip_level },
28
+ u_mapping_texture_width: { value: mapping.texture.image.width }
29
+ },
30
+ vertexShader: `
31
+
32
+ // uniform mat4 modelViewMatrix;
33
+ // uniform mat4 projectionMatrix;
34
+
35
+ // in vec2 uv;
36
+ // in vec3 position;
37
+ out vec2 vUv;
38
+
39
+ void main()
40
+ {
41
+ vUv = uv;
42
+
43
+ vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
44
+ gl_Position = projectionMatrix * mvPosition;
45
+ }
46
+ `,
47
+ fragmentShader: `
48
+
49
+ precision highp usampler2D;
50
+
51
+ uniform sampler2D u_page;
52
+ uniform uvec2 u_page_resolution;
53
+ uniform usampler2D u_mapping;
54
+
55
+ uniform uint u_tile_resolution;
56
+ uniform uint u_tile_padding;
57
+
58
+ uniform uint u_texture_resolution;
59
+ uniform uint u_max_mip_level;
60
+ uniform uint u_mapping_texture_width;
61
+
62
+ in vec2 vUv;
63
+
64
+ float computeTextureLod(const in vec2 uv){
65
+ float scale_x = float(u_texture_resolution) ;
66
+ float scale_y = float(u_texture_resolution) ;
67
+
68
+ vec2 dx = dFdx( uv * scale_x ) ;
69
+ vec2 dy = dFdy( uv * scale_y ) ;
70
+
71
+ float d = max( dot( dx, dx ), dot( dy, dy ) );
72
+
73
+ return log2( d ) * 0.5;
74
+ }
75
+
76
+ uint split_by_2(const in uint a) {
77
+ uint x = a;
78
+ x = (x | x << 16) & 0x0000FFFFu;
79
+ x = (x | x << 8) & 0x00FF00FFu;
80
+ x = (x | x << 4) & 0x0F0F0F0Fu;
81
+ x = (x | x << 2) & 0x33333333u;
82
+ x = (x | x << 1) & 0x55555555u;
83
+ return x;
84
+ }
85
+
86
+ uint compose_tile_address(uint mip, uint x, uint y){
87
+ uint mip_resolution = 1u << mip;
88
+
89
+ uint mip_mask = mip_resolution - 1u;
90
+ uint index_offset = split_by_2(mip_mask);
91
+
92
+ return index_offset + x + y * mip_resolution;
93
+ }
94
+
95
+ uvec2 PageTilePosition(
96
+ const in uint tile_lod,
97
+ const in uvec2 tile_coordinate,
98
+ out float f_mip_size
99
+ ){
100
+
101
+ uint tile_address = compose_tile_address(tile_lod, tile_coordinate.x , tile_coordinate.y);
102
+
103
+ uint tile_mapping_x = tile_address % u_mapping_texture_width;
104
+ uint tile_mapping_y = tile_address / u_mapping_texture_width;
105
+
106
+ uint encoded_mapping = texelFetch(u_mapping, ivec2(tile_mapping_x, tile_mapping_y), 0 ).r;
107
+
108
+ // decode mapping
109
+ uint slot_index = encoded_mapping & 0x3FFFFFu;
110
+ uint mip_level = ( encoded_mapping >> 22) & 0x3FFu;
111
+
112
+ uint mip_size = 1u << mip_level;
113
+
114
+ f_mip_size = float(mip_size);
115
+
116
+ return uvec2(
117
+ slot_index % u_page_resolution.x,
118
+ slot_index / u_page_resolution.x
119
+ );
120
+ }
121
+
122
+ vec2 PageTileSoft(in vec2 uv , in uvec2 virtual_tile_position, uint tile_lod){
123
+
124
+ float f_mip_size;
125
+
126
+ uvec2 tp00 = PageTilePosition( tile_lod, virtual_tile_position, f_mip_size) ;
127
+
128
+ vec2 tile_uv = fract(uv * f_mip_size);
129
+
130
+ return vec2(tp00) + tile_uv;
131
+ }
132
+
133
+ vec4 VirtualFetchLod(const in vec2 uv,const in uint texture_lod_0){
134
+ uint ui_texture_lod_0 = u_max_mip_level - texture_lod_0;
135
+ uint tile_mip_size = 1u << ui_texture_lod_0;
136
+
137
+ vec2 uvt = vec2(uv * float(tile_mip_size));
138
+
139
+ vec2 stuv = PageTileSoft(uv, uvec2(floor(uvt.x), floor(uvt.y)), ui_texture_lod_0);
140
+
141
+ vec2 tile_uv = fract(stuv);
142
+ vec2 tile_offset = stuv - tile_uv;
143
+
144
+ float f_tile_resolution = float(u_tile_resolution);
145
+ float slot_size = float(u_tile_resolution + u_tile_padding*2u);
146
+
147
+ ivec2 texture_size = textureSize(u_page,0);
148
+
149
+ vec2 texel = tile_offset * slot_size + float(u_tile_padding) + tile_uv*(f_tile_resolution) ;
150
+ // vec2 texel = tile_offset * slot_size + float(u_tile_padding) + tile_uv*(f_tile_resolution) - 0.5;
151
+
152
+ vec2 page_uv = texel / vec2(texture_size);
153
+
154
+ return textureLod(u_page, page_uv, 0.0);
155
+ }
156
+
157
+ vec4 VirtualFetch(const in vec2 uv){
158
+
159
+ float texture_lod = computeTextureLod(uv);
160
+
161
+ float lod_fract = fract(texture_lod);
162
+
163
+ // return vec4(lod_fract, 1.0, 1.0, 1.0);
164
+
165
+ float texture_lod_0 = float(texture_lod);
166
+ float texture_lod_1 = ceil(texture_lod);
167
+
168
+
169
+ vec4 lod_0 = VirtualFetchLod(uv, uint(texture_lod_0));
170
+ vec4 lod_1 = VirtualFetchLod(uv, uint(texture_lod_1));
171
+
172
+ vec4 mip_blend = mix(
173
+ lod_0, lod_1, lod_fract
174
+ );
175
+
176
+ return mip_blend;
177
+
178
+ // return vec4(lod_fract, mip_blend.y, mip_blend.z, 1.0);
179
+
180
+ // return texelFetch( u_page, tuv, 0 );
181
+
182
+ }
183
+
184
+ void main(){
185
+
186
+ pc_fragColor = VirtualFetch(vUv);
187
+
188
+ }
189
+
190
+ `
191
+ });
192
+ }
193
+ }
@@ -0,0 +1,203 @@
1
+ import { ClampToEdgeWrapping, DataTexture, NearestFilter, RedIntegerFormat, UnsignedIntType } from "three";
2
+ import { assert } from "../../../../../core/assert.js";
3
+ import { UINT32_MAX } from "../../../../../core/binary/UINT32_MAX.js";
4
+ import { compose_tile_address } from "./tile/compose_tile_address.js";
5
+ import { decompose_finger_print } from "./tile/decompose_finger_print.js";
6
+
7
+ const EMPTY_TILE = UINT32_MAX;
8
+
9
+ /*
10
+ ENCODING:
11
+ 22 bit : slot index
12
+ 10 bit : tile mip
13
+ */
14
+
15
+ /**
16
+ * Represents entire mip pyramid of tiles, where each tile slot points to an actual resident tile
17
+ */
18
+ export class VirtualTextureMemoryMapping {
19
+ /**
20
+ * In tiles
21
+ * @type {number}
22
+ */
23
+ #resolution = 0;
24
+ #max_mip_level = 0;
25
+
26
+ get max_mip_level(){
27
+ return this.#max_mip_level;
28
+ }
29
+
30
+ #pyramid = new Uint32Array(0);
31
+ #texture = new DataTexture(
32
+ new Uint32Array(1),
33
+ 1,
34
+ 1,
35
+ RedIntegerFormat,
36
+ UnsignedIntType
37
+ )
38
+
39
+ get texture() {
40
+ return this.#texture;
41
+ }
42
+
43
+ constructor() {
44
+ const texture = this.#texture;
45
+
46
+ texture.internalFormat = "R32UI";
47
+ texture.generateMipmaps = false;
48
+
49
+ texture.wrapS = ClampToEdgeWrapping;
50
+ texture.wrapT = ClampToEdgeWrapping;
51
+
52
+ texture.magFilter = NearestFilter;
53
+ texture.minFilter = NearestFilter;
54
+ // texture.minFilter = NearestMipMapNearestFilter;
55
+ // texture.magFilter = NearestMipMapNearestFilter;
56
+ }
57
+
58
+ /**
59
+ *
60
+ * @param {number} resolution in tiles
61
+ */
62
+ set resolution(resolution) {
63
+ assert.isNonNegativeInteger(resolution, 'resolution');
64
+
65
+ if (this.#resolution === resolution) {
66
+ return;
67
+ }
68
+
69
+ this.#resolution = resolution;
70
+
71
+ //max mip
72
+ const max_mip_level = Math.log2(resolution);
73
+
74
+ const desired_size = compose_tile_address(max_mip_level + 1, 0, 0);
75
+
76
+ const texture_dimension = Math.ceil(Math.sqrt(desired_size));
77
+
78
+ const size = texture_dimension*texture_dimension;
79
+
80
+ this.#max_mip_level = max_mip_level;
81
+
82
+ const arrayBuffer = new ArrayBuffer(size * 4);
83
+
84
+ this.#pyramid = new Uint32Array(arrayBuffer);
85
+
86
+ const texture = this.#texture;
87
+ texture.dispose();
88
+
89
+
90
+ texture.image.data = this.#pyramid;
91
+ texture.image.width = texture_dimension;
92
+ texture.image.height = texture_dimension;
93
+
94
+ // texture.mipmaps.splice(0, texture.mipmaps.length);
95
+ // let buffer_offset = 0;
96
+ // for (let mip_level = 0; mip_level <= max_mip_level; mip_level++) {
97
+ // const mip_resolution = 1 << mip_level;
98
+ //
99
+ // const texel_count = mip_resolution * mip_resolution;
100
+ // texture.mipmaps[mip_level] = {
101
+ // data: new Uint32Array(arrayBuffer, buffer_offset, texel_count),
102
+ // width: mip_resolution,
103
+ // height: mip_resolution
104
+ // };
105
+ //
106
+ // buffer_offset += texel_count * 4;
107
+ // }
108
+ //
109
+ // texture.mipmaps.reverse();
110
+ //
111
+ // const top_mip = texture.mipmaps[0];
112
+ // texture.image.data = top_mip.data;
113
+ // texture.image.width = top_mip.width;
114
+ // texture.image.height = top_mip.height;
115
+
116
+ texture.needsUpdate = true;
117
+ }
118
+
119
+ get resolution() {
120
+ return this.#resolution;
121
+ }
122
+
123
+ /**
124
+ *
125
+ * @param {VirtualTexturePage} page
126
+ */
127
+ set residency(page) {
128
+
129
+ const tiles = page.resident_tiles;
130
+ const tile_count = tiles.length;
131
+
132
+ // clear pyramid
133
+
134
+ const pyramid = this.#pyramid;
135
+
136
+ pyramid.fill(EMPTY_TILE);
137
+
138
+ // fill by residence
139
+ for (let i = 0; i < tile_count; i++) {
140
+ const tile = tiles[i];
141
+
142
+ const fingerPrint = tile.finder_print;
143
+ const { mip, x, y } = decompose_finger_print(fingerPrint);
144
+ const address = compose_tile_address(mip, x, y);
145
+
146
+ const encoded =
147
+ (tile.page_slot & 0x3FFFFF)
148
+ | ((mip & 0x3FF) << 22) //mip
149
+ ;
150
+
151
+ pyramid[address] = encoded;
152
+ }
153
+
154
+ // back-fill empty slots
155
+ for (let mip = 1; mip <= this.#max_mip_level; mip++) {
156
+
157
+ const mip_resolution = 1 << mip;
158
+
159
+ for (let y = 0; y < mip_resolution; y++) {
160
+ for (let x = 0; x < mip_resolution; x++) {
161
+
162
+ const tile_address = compose_tile_address(mip, x, y);
163
+
164
+ if (pyramid[tile_address] !== EMPTY_TILE) {
165
+ // page is filled, all is good, move on
166
+ continue;
167
+ }
168
+
169
+ const parent_mip = mip - 1;
170
+ const parent_x = x >>> 1;
171
+ const parent_y = y >>> 1;
172
+
173
+ const parent_tile_address = compose_tile_address(parent_mip, parent_x, parent_y);
174
+
175
+ const parent_encoded_tile = pyramid[parent_tile_address];
176
+
177
+ // decode parent tile
178
+ const tile_index = (parent_encoded_tile) & 0x3FFFFF
179
+ const tile_mip = (parent_encoded_tile >> 22) & 0x3FF
180
+
181
+ // swap in parent tile
182
+
183
+
184
+ const encoded_tile =
185
+ (tile_index)
186
+ | (tile_mip << 22)
187
+ ;
188
+
189
+ pyramid[tile_address] = encoded_tile;
190
+ }
191
+ }
192
+
193
+ }
194
+
195
+ // request texture update
196
+ this.#texture.needsUpdate = true;
197
+ }
198
+
199
+
200
+ dispose() {
201
+ this.#texture.dispose();
202
+ }
203
+ }