@woosh/meep-engine 2.71.0 → 2.73.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 (37) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/build/meep.cjs +36 -187
  3. package/build/meep.min.js +1 -1
  4. package/build/meep.module.js +36 -187
  5. package/package.json +3 -2
  6. package/src/core/collection/array/array_get_index_in_range.js +1 -1
  7. package/src/core/collection/array/array_swap.js +2 -2
  8. package/src/core/geom/3d/v3_compute_triangle_normal.js +7 -1
  9. package/src/core/geom/vec3/v3_normalize_array.js +27 -0
  10. package/src/engine/ecs/terrain/BufferedGeometryArraysBuilder.js +2 -2
  11. package/src/engine/graphics/ecs/camera/FrustumProjector.js +31 -182
  12. package/src/engine/graphics/geometry/buffered/ComputeNormals.js +11 -26
  13. package/src/engine/graphics/texture/virtual/v2/NOTES.md +27 -0
  14. package/src/engine/graphics/texture/virtual/v2/ShaderUsage.js +51 -26
  15. package/src/engine/graphics/texture/virtual/v2/SparseTexture.js +182 -0
  16. package/src/engine/graphics/texture/virtual/v2/TextureTile.js +31 -0
  17. package/src/engine/graphics/texture/virtual/v2/TileLoader.js +215 -0
  18. package/src/engine/graphics/texture/virtual/v2/UsageDebugView.js +64 -0
  19. package/src/engine/graphics/texture/virtual/v2/UsageMetadata.js +153 -0
  20. package/src/engine/graphics/texture/virtual/v2/UsagePyramidDebugView.js +252 -0
  21. package/src/engine/graphics/texture/virtual/v2/VirtualTextureManager.js +257 -0
  22. package/src/engine/graphics/texture/virtual/v2/compose_finger_print.js +13 -0
  23. package/src/engine/graphics/texture/virtual/v2/finger_print_to_tile_index.js +37 -0
  24. package/src/engine/graphics/texture/virtual/v2/prototype.js +122 -31
  25. package/src/engine/graphics/texture/virtual/v2/tile_index_to_finger_print.js +31 -0
  26. package/src/core/geom/2d/quad-tree/PointQuadTree.js +0 -478
  27. package/src/core/math/random/makeRangedRandom.js +0 -19
  28. package/src/engine/ecs/terrain/TerrainGeometryBuilder.js +0 -152
  29. package/src/engine/ecs/terrain/ecs/PromiseSamplerHeight.js +0 -66
  30. package/src/engine/ecs/terrain/ecs/TerrainClassifier.js +0 -125
  31. package/src/engine/graphics/clouds/MaterialTransformer.js +0 -0
  32. package/src/engine/graphics/clouds/TerrainCloudsPlugin.js +0 -0
  33. package/src/engine/graphics/clouds/cs_build_fragment_shader.js +0 -0
  34. package/src/engine/graphics/clouds/cs_build_vertex_shader.js +0 -0
  35. package/src/engine/graphics/ecs/camera/TiltCameraController.js +0 -69
  36. package/src/engine/graphics/ecs/camera/is_valid_distance_value.js +0 -11
  37. package/src/engine/graphics/texture/sampler/SampleTraverser.js +0 -165
@@ -0,0 +1,153 @@
1
+ import { compose_finger_print } from "./compose_finger_print.js";
2
+ import { finger_print_to_tile_index } from "./finger_print_to_tile_index.js";
3
+
4
+ export class UsageMetadata {
5
+
6
+ #counts_intrinsic = new Uint32Array(0);
7
+
8
+ get counts() {
9
+ return this.#counts_intrinsic;
10
+ }
11
+
12
+ #max_mip_level = 0;
13
+
14
+ set max_mip_level(v) {
15
+ this.#max_mip_level = v;
16
+
17
+ this.#ensureCapacity();
18
+ }
19
+
20
+ get max_mip_level() {
21
+ return this.#max_mip_level;
22
+ }
23
+
24
+ /**
25
+ *
26
+ */
27
+ #ensureCapacity() {
28
+
29
+ const finger_print = compose_finger_print(this.#max_mip_level + 1, 0, 0);
30
+
31
+ const size = finger_print_to_tile_index(finger_print);
32
+
33
+ if (this.#counts_intrinsic.length >= size) {
34
+ // already large enough
35
+ return;
36
+ }
37
+
38
+ this.#counts_intrinsic = new Uint32Array(size);
39
+ }
40
+
41
+ #getIndexBy(mip, x, y) {
42
+ const finger_print = compose_finger_print(mip, x, y);
43
+
44
+ return finger_print_to_tile_index(finger_print);
45
+ }
46
+
47
+ /**
48
+ *
49
+ * @param {number} fingerprint
50
+ * @returns {number}
51
+ */
52
+ getCountByFingerprint(fingerprint) {
53
+ const index = finger_print_to_tile_index(fingerprint);
54
+
55
+
56
+ return this.#counts_intrinsic[index];
57
+ }
58
+
59
+ /**
60
+ *
61
+ * @param {number} mip
62
+ * @param {number} x
63
+ * @param {number} y
64
+ * @returns {number}
65
+ */
66
+ getCountBy(mip, x, y) {
67
+ const finger_print = compose_finger_print(mip, x, y);
68
+
69
+ return this.getCountByFingerprint(finger_print);
70
+ }
71
+
72
+ clear() {
73
+ this.#counts_intrinsic.fill(0);
74
+ }
75
+
76
+ /**
77
+ * Given existing usage, award same usage to all tiles up the chain all the way to the root
78
+ * This helps avoid popping and ensures that when there's not enough space - at least the higher level LOD tiles are available
79
+ * @param {number} bias added to the ancestor tiles to make them more likely to be loaded
80
+ */
81
+ promoteAncestors(bias = 1) {
82
+
83
+ // traverse mip pyramid in reverse, so we can push counts to parents one level at a time
84
+ for (let mip = this.#max_mip_level - 1; mip > 0; mip--) {
85
+
86
+ const mip_resolution = 1 << mip;
87
+
88
+ for (let y = 0; y < mip_resolution; y++) {
89
+ for (let x = 0; x < mip_resolution; x++) {
90
+
91
+ const count = this.getCountBy(mip, x, y);
92
+
93
+ if (count <= 0) {
94
+ continue;
95
+ }
96
+
97
+ // get higher-level lod
98
+ const parent_level = mip - 1;
99
+ const parent_x = x >>> 1;
100
+ const parent_y = y >>> 1;
101
+
102
+ const parent_index = this.#getIndexBy(parent_level, parent_x, parent_y);
103
+
104
+ const parent_count = this.#counts_intrinsic[parent_index];
105
+
106
+ const expected_count = count + bias;
107
+
108
+ if (parent_count < expected_count) {
109
+ this.#counts_intrinsic[parent_index] = expected_count;
110
+ }
111
+
112
+ }
113
+ }
114
+
115
+ }
116
+ }
117
+
118
+
119
+ /**
120
+ *
121
+ * @param {Uint8Array} data usage texture data
122
+ */
123
+ fromTexture(data) {
124
+ this.clear();
125
+
126
+ const data_size = data.length;
127
+
128
+ this.#ensureCapacity(data_size >> 2);
129
+
130
+ for (let offset = 0; offset < data_size; offset += 4) {
131
+
132
+ if (data[offset + 3] === 0) {
133
+ // no data
134
+ continue;
135
+ }
136
+
137
+ const mip_level = data[offset];
138
+
139
+ // we create a mask to make sure we don't end up with invalid tile position values
140
+ const max_tile_value = (1 << mip_level) - 1;
141
+
142
+ const tile_x = data[offset + 1] & max_tile_value;
143
+ const tile_y = data[offset + 2] & max_tile_value;
144
+
145
+ const finger_print = compose_finger_print(mip_level, tile_x, tile_y);
146
+
147
+ const index0 = finger_print_to_tile_index(finger_print);
148
+
149
+ this.#counts_intrinsic[index0]++;
150
+ }
151
+
152
+ }
153
+ }
@@ -0,0 +1,252 @@
1
+ import { assert } from "../../../../../core/assert.js";
2
+ import LabelView from "../../../../../view/common/LabelView.js";
3
+ import EmptyView from "../../../../../view/elements/EmptyView.js";
4
+
5
+ const ABSOLUTE_CSS = {
6
+ position: "absolute",
7
+ top: 0,
8
+ left: 0
9
+ };
10
+
11
+ class TextureLODView extends EmptyView {
12
+
13
+ /**
14
+ *
15
+ * @type {View[]}
16
+ */
17
+ #tiles = [];
18
+
19
+ #resolution = [1, 1];
20
+
21
+ constructor({
22
+ resolution = [1, 1],
23
+ tile_size = 8
24
+ }) {
25
+ super({ css: ABSOLUTE_CSS });
26
+
27
+ this.css({
28
+ background: "rgba(255,255,255,0.05)"
29
+ });
30
+
31
+ this.#resolution = resolution;
32
+
33
+ const width = resolution[0];
34
+ const height = resolution[1];
35
+
36
+ assert.isNonNegativeInteger(width, 'width');
37
+ assert.isNonNegativeInteger(height, 'height');
38
+
39
+ for (let j = 0; j < height; j++) {
40
+ for (let i = 0; i < width; i++) {
41
+ const tileView = new EmptyView({ css: ABSOLUTE_CSS });
42
+
43
+ // const flipped_y = height - j - 1;
44
+ tileView.position.set(i * tile_size, j * tile_size);
45
+ tileView.size.set(tile_size, tile_size);
46
+
47
+ this.clearTile(tileView);
48
+
49
+ this.#tiles.push(tileView)
50
+
51
+ this.addChild(tileView);
52
+ }
53
+ }
54
+
55
+ this.size.set(width * tile_size, height * tile_size);
56
+
57
+
58
+ const label = new LabelView(`${width} x ${height}`, {
59
+ css: {
60
+ ...ABSOLUTE_CSS,
61
+ color: '#FFFFFF',
62
+ textShadow: '0 0 2px black',
63
+ whiteSpace: "pre"
64
+ }
65
+ });
66
+
67
+ label.position.set(this.size.x + 4, (this.size.y - 20) * 0.5);
68
+
69
+ this.addChild(label);
70
+ }
71
+
72
+ clear() {
73
+ const resolution = this.#resolution;
74
+ const count = resolution[0] * resolution[1];
75
+ for (let i = 0; i < count; i++) {
76
+
77
+ const view = this.#tiles[i];
78
+
79
+ this.clearTile(view);
80
+ }
81
+ }
82
+
83
+ /**
84
+ *
85
+ * @param {View} view
86
+ */
87
+ clearTile(view) {
88
+
89
+ view.css({
90
+ background: "none"
91
+ });
92
+
93
+ view.visible = false;
94
+ }
95
+
96
+ getTile(x, y) {
97
+ assert.isNonNegativeInteger(x, 'x');
98
+ assert.isNonNegativeInteger(y, 'y');
99
+
100
+ const resolution = this.#resolution;
101
+
102
+ // assert.lessThan(x, resolution[0]);
103
+ // assert.lessThan(y, resolution[1]);
104
+
105
+ const width = resolution[0];
106
+
107
+ const view = this.#tiles[x + width * y];
108
+
109
+ if (view === undefined) {
110
+ debugger
111
+ }
112
+
113
+ return view;
114
+
115
+ }
116
+
117
+ }
118
+
119
+ export class UsagePyramidDebugView extends EmptyView {
120
+ #levels = [];
121
+ #resolution = [1, 1];
122
+ #tile_size = 0;
123
+
124
+ #used_set = [];
125
+ #used_set_size = 0;
126
+
127
+ #image_url = null
128
+
129
+ setImageURL(url) {
130
+ this.#image_url = url;
131
+
132
+ this.#applyImageURL();
133
+ }
134
+
135
+ #applyImageURL() {
136
+
137
+ const background = {
138
+ backgroundImage: this.#image_url === null ? "none" : `url(${this.#image_url})`,
139
+ backgroundSize: "contain"
140
+ };
141
+
142
+ this.#levels.forEach(l => {
143
+
144
+ l.css(background);
145
+
146
+ });
147
+ }
148
+
149
+ setTextureParameters(resolution, tile_size) {
150
+ if (this.#resolution[0] === resolution && this.#resolution[1] === resolution && this.#tile_size === tile_size) {
151
+ return;
152
+ }
153
+
154
+ this.#tile_size = tile_size;
155
+ this.#resolution = [resolution, resolution];
156
+
157
+ this.#levels.splice(0, this.#levels.length);
158
+ this.removeAllChildren();
159
+
160
+ let size = resolution / tile_size;
161
+
162
+ assert.isNonNegativeInteger(size, 'size');
163
+
164
+ while (size > 0) {
165
+ const lod = new TextureLODView({
166
+ resolution: [size, size],
167
+ });
168
+
169
+ lod.css({
170
+ border: "1px solid rgba(0,0,0,0.7)"
171
+ });
172
+
173
+ this.#levels.push(lod);
174
+
175
+ this.addChild(lod);
176
+
177
+ size >>>= 1;
178
+ }
179
+
180
+ this.#levels.reverse();
181
+
182
+ let y = 0;
183
+ for (let i = 0; i < this.#levels.length; i++) {
184
+ const level = this.#levels[i];
185
+
186
+ level.position.set(2, y);
187
+
188
+ y += level.size.y + 4;
189
+ }
190
+
191
+ this.#applyImageURL();
192
+ }
193
+
194
+ /**
195
+ *
196
+ * @param {UsageMetadata} usage
197
+ */
198
+ set usage(usage) {
199
+
200
+ for (let i = 0; i < this.#used_set_size; i++) {
201
+ const finger_print = this.#used_set[i];
202
+
203
+
204
+ const mip = (finger_print >> 24) & 0xFF;
205
+ const x = (finger_print >> 16) & 0xFF;
206
+ const y = (finger_print >> 8) & 0xFF;
207
+
208
+ const mipView = this.#levels[mip];
209
+
210
+ const tile = mipView.getTile(x, y);
211
+
212
+ mipView.clearTile(tile);
213
+ }
214
+ this.#used_set_size = 0;
215
+
216
+
217
+ const mip_count = this.#levels.length;
218
+
219
+ for (let mip = 0; mip < mip_count; mip++) {
220
+ const size = 1 << mip;
221
+
222
+ for (let y = 0; y < size; y++) {
223
+ for (let x = 0; x < size; x++) {
224
+ const count = usage.getCountBy(mip,x,y);
225
+
226
+ if(count === 0){
227
+ continue;
228
+ }
229
+
230
+ const mipView = this.#levels[mip];
231
+
232
+ const tile = mipView.getTile(x, y);
233
+
234
+ tile.css({
235
+ background: "rgba(255,0,0,0.8)"
236
+ });
237
+
238
+ tile.visible = true;
239
+
240
+ const finger_print = (mip << 24)
241
+ | (x << 16)
242
+ | (y << 8)
243
+ ;
244
+
245
+ this.#used_set[this.#used_set_size] = finger_print;
246
+ this.#used_set_size++;
247
+ }
248
+ }
249
+ }
250
+
251
+ }
252
+ }
@@ -0,0 +1,257 @@
1
+ import {
2
+ ClampToEdgeWrapping,
3
+ GLSL3,
4
+ Matrix4,
5
+ NearestFilter,
6
+ NoBlending,
7
+ RawShaderMaterial,
8
+ RGBAIntegerFormat,
9
+ UnsignedByteType,
10
+ Vector2,
11
+ Vector4,
12
+ WebGLRenderTarget
13
+ } from "three";
14
+ import { assert } from "../../../../../core/assert.js";
15
+ import { clamp } from "../../../../../core/math/clamp.js";
16
+ import { max2 } from "../../../../../core/math/max2.js";
17
+ import { renderScreenSpace } from "../../../render/utils/renderScreenSpace.js";
18
+ import { Sampler2D } from "../../sampler/Sampler2D.js";
19
+ import { fragment, vertex } from "./ShaderUsage.js";
20
+ import { UsageMetadata } from "./UsageMetadata.js";
21
+
22
+ const usage_material_uniforms = {
23
+ "u_mt_params": { value: new Vector2(0.1, 1) },
24
+ /**
25
+ * Format: VT_WIDTH, VT_HEIGHT, VT_TILE_SIZE, VT_ID
26
+ * - VT_WIDTH : width of the virtual texture
27
+ * - VT_HEIGHT : height of the virtual texture
28
+ * - VT_TILE_SIZE : resolution of a single tile
29
+ * - VT_ID : multiple different virtual textures may be used, this identifies current texture
30
+ */
31
+ "u_mt_tex": { value: new Vector4(1, 1, 1, 3) },
32
+ "u_mt_viewport_size": { value: new Vector2(1, 1) },
33
+ "modelViewMatrix": { value: new Matrix4() },
34
+ "projectionMatrix": { value: new Matrix4() }
35
+ };
36
+ const usage_material = new RawShaderMaterial({
37
+
38
+ uniforms: usage_material_uniforms,
39
+ vertexShader: vertex(),
40
+ fragmentShader: fragment(),
41
+ glslVersion: GLSL3
42
+
43
+ });
44
+ usage_material.blending = NoBlending;
45
+
46
+
47
+ const clear_usage_material = new RawShaderMaterial({
48
+ vertexShader: `
49
+ precision mediump float;
50
+ in vec2 uv;
51
+ void main() {
52
+
53
+ gl_Position = vec4( (uv - 0.5)*2.0, 0.0, 1.0 );
54
+
55
+ }`,
56
+ fragmentShader: `
57
+
58
+ precision mediump int;
59
+ out uvec4 out_decoded;
60
+
61
+ void main(){
62
+ out_decoded = uvec4(0u);
63
+ }
64
+ `,
65
+ glslVersion: GLSL3
66
+ });
67
+
68
+ export class VirtualTextureManager {
69
+
70
+ #usage_buffer = new WebGLRenderTarget(1, 1, {
71
+ wrapT: ClampToEdgeWrapping,
72
+ wrapS: ClampToEdgeWrapping,
73
+ generateMipmaps: false,
74
+ minFilter: NearestFilter,
75
+ magFilter: NearestFilter,
76
+ format: RGBAIntegerFormat,
77
+ depthBuffer: true,
78
+ stencilBuffer: false,
79
+ type: UnsignedByteType,
80
+ internalFormat: "RGBA8UI"
81
+ });
82
+
83
+ #usage_pixel_data = Sampler2D.uint8(4, 1, 1);
84
+
85
+ #viewport_resolution = [1, 1];
86
+
87
+ /**
88
+ *
89
+ * @type {number}
90
+ */
91
+ #usage_resolution_scale = 32;
92
+
93
+ #usage_metadata = new UsageMetadata();
94
+
95
+ #texture_id = 3;
96
+ #texture_resolution = 65536;
97
+ #tile_resolution = 256;
98
+ #max_mip_level = 0;
99
+
100
+ get texture_resolution() {
101
+ return this.#texture_resolution;
102
+ }
103
+
104
+ get tile_resolution() {
105
+ return this.#tile_resolution;
106
+ }
107
+
108
+ get max_mip_level() {
109
+ return this.#max_mip_level;
110
+ }
111
+
112
+ /**
113
+ * Used to fetch higher resolution tiles
114
+ * @type {number}
115
+ */
116
+ #usage_texture_bias = 0.1;
117
+
118
+ /**
119
+ *
120
+ * @param {number} resolution
121
+ * @param {number} tile_size
122
+ * @param {number} texture_id
123
+ */
124
+ setTextureParameters(
125
+ resolution,
126
+ tile_size,
127
+ texture_id
128
+ ) {
129
+ this.#texture_id = texture_id;
130
+ this.#texture_resolution = resolution;
131
+ this.#tile_resolution = tile_size;
132
+ this.#max_mip_level = Math.log2(this.#texture_resolution / this.#tile_resolution);
133
+
134
+ if (!Number.isInteger(this.#max_mip_level)) {
135
+ throw new Error(`texture resolution must be a power-of-two multiple of tile resolution`);
136
+ }
137
+
138
+ this.#initialize_usage_uniforms();
139
+
140
+ this.#usage_metadata.max_mip_level = this.#max_mip_level;
141
+ }
142
+
143
+ get usage_metadata() {
144
+ return this.#usage_metadata;
145
+ }
146
+
147
+ get usage_data() {
148
+ return this.#usage_pixel_data;
149
+ }
150
+
151
+ constructor() {
152
+ this.#initialize_usage_uniforms();
153
+ }
154
+
155
+ #initialize_usage_uniforms() {
156
+
157
+ const uniforms = usage_material.uniforms;
158
+
159
+ const max_mip_level = this.#max_mip_level;
160
+
161
+
162
+ uniforms.u_mt_params.value.set(
163
+ this.#usage_texture_bias,
164
+ max_mip_level
165
+ );
166
+
167
+ uniforms.u_mt_tex.value.set(
168
+ this.#texture_resolution,
169
+ this.#texture_resolution,
170
+ this.#tile_resolution,
171
+ this.#texture_id
172
+ );
173
+ }
174
+
175
+ /**
176
+ *
177
+ * @param {number} x
178
+ * @param {number} y
179
+ */
180
+ setViewportResolution(x, y) {
181
+ assert.isNumber(x, 'x');
182
+ assert.isNumber(y, 'y');
183
+
184
+ assert.notNaN(x, 'x');
185
+ assert.notNaN(y, 'y');
186
+
187
+ const _x = max2(1, Math.floor(x));
188
+ const _y = max2(1, Math.floor(y));
189
+
190
+ const viewport = this.#viewport_resolution;
191
+
192
+ if (_x === viewport[0] && _y === viewport[1]) {
193
+ // no change, do nothing
194
+ return;
195
+ }
196
+
197
+ viewport[0] = _x;
198
+ viewport[1] = _y;
199
+
200
+ const usage_resolution_x = clamp(Math.floor(_x / this.#usage_resolution_scale), 1, _x);
201
+ const usage_resolution_y = clamp(Math.floor(_y / this.#usage_resolution_scale), 1, _y);
202
+
203
+ this.#usage_buffer.setSize(usage_resolution_x, usage_resolution_y);
204
+ this.#usage_pixel_data.resize(usage_resolution_x, usage_resolution_y, true);
205
+
206
+ const usage_material_uniforms = usage_material.uniforms;
207
+ usage_material_uniforms.u_mt_viewport_size.value.set(_x / usage_resolution_x, _y / usage_resolution_y);
208
+
209
+ }
210
+
211
+ /**
212
+ *
213
+ * @param {WebGLRenderer} renderer
214
+ * @param {Scene} scene
215
+ * @param {Camera} camera
216
+ */
217
+ updateUsage(renderer, scene, camera) {
218
+
219
+ const usage_buffer = this.#usage_buffer;
220
+
221
+ const uniforms = usage_material.uniforms;
222
+
223
+ uniforms.modelViewMatrix.value.multiplyMatrices(camera.matrixWorldInverse, camera.matrixWorld);
224
+ uniforms.projectionMatrix.value.copy(camera.projectionMatrix);
225
+
226
+ // remember existing state
227
+ const _old_render_target = renderer.getRenderTarget();
228
+ const _auto_clear = renderer.autoClear;
229
+
230
+ const _old_material = scene.overrideMaterial;
231
+
232
+ renderer.autoClear = false;
233
+ renderer.setRenderTarget(usage_buffer);
234
+ scene.overrideMaterial = usage_material;
235
+
236
+ // clear out usage texture
237
+ renderScreenSpace(renderer, clear_usage_material);
238
+
239
+ renderer.clearDepth();
240
+
241
+ renderer.render(scene, camera);
242
+
243
+ scene.overrideMaterial = _old_material;
244
+
245
+ renderer.readRenderTargetPixels(usage_buffer, 0, 0, usage_buffer.width, usage_buffer.height, this.#usage_pixel_data.data);
246
+
247
+ renderer.setRenderTarget(_old_render_target);
248
+ renderer.autoClear = _auto_clear;
249
+
250
+ this.#usage_metadata.fromTexture(this.#usage_pixel_data.data);
251
+ this.#usage_metadata.promoteAncestors(1);
252
+ }
253
+
254
+ dispose() {
255
+ this.#usage_buffer.dispose();
256
+ }
257
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ *
3
+ * @param {number} mip
4
+ * @param {number} x
5
+ * @param {number} y
6
+ * @returns {number}
7
+ */
8
+ export function compose_finger_print(mip, x, y) {
9
+ return (mip << 24)
10
+ | (x << 16)
11
+ | (y << 8)
12
+ ;
13
+ }
@@ -0,0 +1,37 @@
1
+ import { split_by_2 } from "../../../../../core/geom/3d/morton/split_by_2.js";
2
+
3
+ /**
4
+ *
5
+ * @param {number} finger_print
6
+ * @returns {{mip: number, x: number, y: number}}
7
+ */
8
+ export function decompose_finger_print(finger_print) {
9
+ const mip = (finger_print >> 24) & 0xFF;
10
+ const x = (finger_print >> 16) & 0xFF;
11
+ const y = (finger_print >> 8) & 0xFF;
12
+
13
+ return { mip, x, y };
14
+ }
15
+
16
+ /**
17
+ *
18
+ * @param {number} finger_print
19
+ * @returns {number}
20
+ */
21
+ export function finger_print_to_tile_index(finger_print) {
22
+ // decode fingerprint
23
+ const mip = (finger_print >> 24) & 0xFF;
24
+ const x = (finger_print >> 16) & 0xFF;
25
+ const y = (finger_print >> 8) & 0xFF;
26
+
27
+ // figure out resolution of this mip level
28
+ const mip_resolution = 1 << mip;
29
+
30
+ // this is basically converting something like 0100 to 0011;
31
+ const mip_mask = mip_resolution - 1;
32
+
33
+ // where data for this mip starts
34
+ const index_offset = split_by_2(mip_mask);
35
+
36
+ return index_offset + x + y * mip_resolution;
37
+ }