@woosh/meep-engine 2.43.20 → 2.43.22

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 (25) hide show
  1. package/core/collection/HashMap.d.ts +2 -2
  2. package/core/collection/HashMap.js +22 -2
  3. package/core/geom/3d/morton/mortonEncode_magicbits.js +18 -0
  4. package/core/geom/3d/topology/struct/BinaryElementPool.js +292 -0
  5. package/core/geom/3d/topology/struct/BinaryElementPool.spec.js +36 -0
  6. package/core/geom/3d/topology/struct/BinaryTopology.js +454 -66
  7. package/core/geom/3d/topology/struct/BinaryTopology.spec.js +16 -0
  8. package/core/geom/3d/topology/struct/TopoEdge.js +4 -0
  9. package/core/geom/3d/topology/struct/TopoMesh.js +25 -0
  10. package/core/geom/3d/topology/struct/TopoTriangle.js +4 -0
  11. package/core/geom/3d/topology/struct/TopoVertex.js +8 -0
  12. package/core/geom/3d/topology/struct/bt_index_geometry_to_topology.js +329 -0
  13. package/core/geom/3d/topology/struct/bt_index_geometry_to_topology.spec.js +26 -0
  14. package/core/geom/3d/topology/struct/prototypeBinaryTopology.js +55 -0
  15. package/engine/graphics/ecs/mesh-v2/aggregate/SGMeshHighlightSystem.js +12 -3
  16. package/engine/graphics/micron/format/MicronGeometryPatch.d.ts +1 -1
  17. package/engine/graphics/micron/format/MicronGeometryPatch.js +2 -1
  18. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +89 -64
  19. package/engine/graphics/sh3/path_tracer/PathTracedMesh.js +4 -0
  20. package/engine/graphics/sh3/path_tracer/PathTracer.js +90 -36
  21. package/engine/graphics/sh3/path_tracer/getBiasedNormalSample.js +1 -1
  22. package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +44 -10
  23. package/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +28 -13
  24. package/engine/save/storage/IndexedDBStorage.js +1 -0
  25. package/package.json +1 -1
@@ -1,112 +1,500 @@
1
- import { max2 } from "../../../../math/max2.js";
1
+ import { BinaryElementPool } from "./BinaryElementPool.js";
2
+ import { array_copy } from "../../../../collection/array/copyArray.js";
3
+ import { assert } from "../../../../assert.js";
2
4
 
3
- const INITIAL_CAPACITY = 128;
5
+ /**
6
+ * Byte size of FLOAT_32
7
+ * @readonly
8
+ * @type {number}
9
+ */
10
+ const FLOAT_32_SIZE = 4;
4
11
 
5
12
  /**
13
+ * Byte size of UINT_32
6
14
  * @readonly
7
15
  * @type {number}
8
16
  */
9
- const CAPACITY_GROW_MULTIPLIER = 1.2;
17
+ const UINT_32_SIZE = 4;
10
18
 
11
19
  /**
12
20
  * @readonly
13
21
  * @type {number}
14
22
  */
15
- const CAPACITY_GROW_MIN_STEP = 32;
23
+ export const NULL_POINTER = 0xFFFFFFFF;
16
24
 
17
- class BinaryPool {
25
+ /**
26
+ * Heavily influenced by blender's internal mesh structure
27
+ * @see https://github.com/blender/blender/blob/master/source/blender/bmesh/bmesh_class.h
28
+ */
29
+ export class BinaryTopology {
30
+ /**
31
+ * structure:
32
+ * coordinate: float32[3] // vertex coordinates
33
+ * normal: float32[3] // vertex normal
34
+ * edge_pointer: uint32 // Pointer to (any) edge using this vertex (for disk cycles)
35
+ *
36
+ * @type {BinaryElementPool}
37
+ * @private
38
+ */
39
+ __vertex_pool = new BinaryElementPool(3 * FLOAT_32_SIZE + 3 * FLOAT_32_SIZE + UINT_32_SIZE);
40
+ /**
41
+ * structure:
42
+ * v1: uint32 // Vertices (unordered)
43
+ * v2: uint32 // Vertices (unordered)
44
+ * l: uint32 // The list of loops around the edge points at a Loop
45
+ * v1_disk_link: uint32[2] // Disk Cycle Pointers edge around vertex v1 and d2 does the same for v2.
46
+ * v2_disk_link: uint32[2] // see above
47
+ * @type {BinaryElementPool}
48
+ * @private
49
+ */
50
+ __edge_pool = new BinaryElementPool(UINT_32_SIZE * 7);
51
+ /**
52
+ * Loop represents a corner of the face
53
+ * structure:
54
+ * v: uint32 // The vertex this loop points to. This vertex must be unique within the cycle
55
+ * e: uint32 // The edge this loop uses.
56
+ * f: uint32 // The face this loop is part of.
57
+ * radial_next: uint32 // Other loops connected to this edge.
58
+ * radial_prev: uint32
59
+ * next: uint32 // Other loops that are part of this face.
60
+ * prev: uint32
61
+ * @type {BinaryElementPool}
62
+ * @private
63
+ */
64
+ __loop_pool = new BinaryElementPool(UINT_32_SIZE * 7);
65
+ /**
66
+ * structure:
67
+ * l_first: uint32 // first loop pointer
68
+ * no: float32[3] // Face normal
69
+ * @type {BinaryElementPool}
70
+ * @private
71
+ */
72
+ __face_pool = new BinaryElementPool(UINT_32_SIZE + FLOAT_32_SIZE * 3);
18
73
 
19
- constructor(item_size, initial_capacity = INITIAL_CAPACITY) {
20
- /**
21
- * Size of a single pool item in bytes
22
- * @type {number}
23
- * @private
24
- */
25
- this.__item_size = item_size;
26
- /**
27
- * Unused slots
28
- * @type {number[]}
29
- * @private
30
- */
31
- this.__free = [];
32
74
 
33
- /**
34
- *
35
- * @type {number}
36
- * @private
37
- */
38
- this.__free_pointer = 0;
75
+ /**
76
+ * Total (approximate) size of this structure in memory, in bytes
77
+ * @return {number}
78
+ */
79
+ get byteSize() {
80
+ return this.__loop_pool.byteSize
81
+ + this.__vertex_pool.byteSize
82
+ + this.__edge_pool.byteSize
83
+ + this.__face_pool.byteSize
84
+ ;
85
+ }
39
86
 
40
- this.__data_buffer = new ArrayBuffer(initial_capacity * item_size);
41
- this.__data_uint8 = new Uint8Array(this.__data_buffer);
87
+ trim() {
88
+ this.__loop_pool.trim();
89
+ this.__vertex_pool.trim();
90
+ this.__edge_pool.trim();
91
+ this.__face_pool.trim();
92
+ }
42
93
 
43
- this.__capacity = initial_capacity;
94
+ get vertices() {
95
+ return this.__vertex_pool;
96
+ }
44
97
 
45
- this.__used_end = 0;
98
+ /**
99
+ * Edges are shared among faces, edges point to 2 unordered vertices
100
+ * @return {BinaryElementPool}
101
+ */
102
+ get edges() {
103
+ return this.__edge_pool;
46
104
  }
47
105
 
48
- __grow_capacity() {
49
- const new_capacity = Math.ceil(max2(
50
- this.__capacity * CAPACITY_GROW_MULTIPLIER,
51
- this.__capacity + CAPACITY_GROW_MIN_STEP
52
- ));
106
+ /**
107
+ * Loops are corners of faces, a single vertex can be associated with many loops, one per connected face
108
+ * @return {BinaryElementPool}
109
+ */
110
+ get loops() {
111
+ return this.__loop_pool;
112
+ }
53
113
 
54
- const old_data_uint8 = this.__data_uint8;
114
+ get faces() {
115
+ return this.__face_pool;
116
+ }
55
117
 
56
- const new_data_buffer = new ArrayBuffer(new_capacity * this.__item_size);
57
118
 
58
- this.__data_buffer = new_data_buffer;
59
- this.__data_uint8 = new Uint8Array(new_data_buffer);
119
+ /**
120
+ * Clear the topology, removed all data
121
+ */
122
+ clear() {
123
+ this.__vertex_pool.clear();
124
+ this.__edge_pool.clear();
125
+ this.__loop_pool.clear();
126
+ this.__face_pool.clear();
127
+ }
60
128
 
61
- // copy old data
62
- this.__data_uint8.set(old_data_uint8);
129
+ /**
130
+ *
131
+ * @param {number[]|ArrayLike<number>|Float32Array} result
132
+ * @param {number} result_offset
133
+ * @param {number} id vertex ID
134
+ */
135
+ vertex_read_coordinate(result, result_offset, id) {
136
+ const pool = this.__vertex_pool;
63
137
 
64
- this.__capacity = new_capacity;
138
+ const v_address = pool.element_address(id);
139
+ const v_offset = v_address >> 2; // get 4-byte boundary
140
+ array_copy(pool.data_float32, v_offset, result, result_offset, 3);
65
141
  }
66
142
 
143
+ /**
144
+ *
145
+ * @param {number} id
146
+ * @param {number[]} value
147
+ * @param {number} value_offset
148
+ */
149
+ vertex_write_coordinate(id, value, value_offset) {
150
+ const pool = this.__vertex_pool;
151
+
152
+ const v_address = pool.element_address(id);
153
+ const v_offset = v_address >> 2; // get 4-byte boundary
154
+ array_copy(value, value_offset, pool.data_float32, v_offset, 3);
155
+ }
67
156
 
68
157
  /**
69
158
  *
70
- * @return {number}
159
+ * @param {number[]|ArrayLike<number>|Float32Array} result
160
+ * @param {number} result_offset
161
+ * @param {number} id vertex ID
71
162
  */
72
- allocate() {
73
- if (this.__free_pointer > 0) {
74
- // get unused slot
75
- this.__free_pointer--;
76
- return this.__free[this.__free_pointer];
77
- }
163
+ vertex_read_normal(result, result_offset, id) {
164
+ const pool = this.__vertex_pool;
165
+
166
+ const v_address = pool.element_address(id) + 3 * FLOAT_32_SIZE;
167
+ const v_offset = v_address >> 2; // get 4-byte boundary
168
+ array_copy(pool.data_float32, v_offset, result, result_offset, 3);
169
+ }
78
170
 
79
- // allocate new
171
+ /**
172
+ *
173
+ * @param {number} id
174
+ * @param {number[]} value
175
+ * @param {number} value_offset
176
+ */
177
+ vertex_write_normal(id, value, value_offset) {
178
+ const pool = this.__vertex_pool;
80
179
 
81
- let result = this.__used_end;
180
+ const v_address = pool.element_address(id) + 3 * FLOAT_32_SIZE;
181
+ const v_offset = v_address >> 2; // get 4-byte boundary
182
+ array_copy(value, value_offset, pool.data_float32, v_offset, 3);
183
+ }
82
184
 
83
- if (result >= this.__capacity) {
84
- this.__grow_capacity();
85
- }
185
+ /**
186
+ * @param {number} id
187
+ * @returns {number}
188
+ */
189
+ vertex_read_edge(id) {
190
+ const pool = this.__vertex_pool;
86
191
 
87
- this.__used_end++;
192
+ const address = pool.element_address(id);
193
+ return pool.data_view.getUint32(address + 6 * UINT_32_SIZE);
88
194
 
89
- return result;
90
195
  }
91
196
 
92
197
  /**
93
198
  *
199
+ * @param {number} edge_id
94
200
  * @param {number} id
95
201
  */
96
- release(id) {
97
- this.__free[this.__free_pointer++] = id;
202
+ vertex_write_edge(id, edge_id) {
203
+ const pool = this.__vertex_pool;
204
+
205
+ const address = pool.element_address(id);
206
+ pool.data_view.setUint32(address + 6 * UINT_32_SIZE, edge_id);
98
207
  }
99
- }
100
208
 
101
- /**
102
- * Heavily influenced by blender's internal mesh structure
103
- * @see https://github.com/blender/blender/blob/master/source/blender/bmesh/bmesh_class.h
104
- */
105
- export class BinaryTopology {
106
- __vertex_pool = new BinaryPool();
107
- __edge_pool = new BinaryPool();
108
- __loop_pool = new BinaryPool();
109
- __face_pool = new BinaryPool();
209
+ /**
210
+ * @param {number} id edge ID
211
+ * @returns {number}
212
+ */
213
+ edge_read_vertex1(id) {
214
+ const pool = this.__edge_pool;
215
+
216
+ const address = pool.element_address(id);
217
+ return pool.data_view.getUint32(address);
218
+
219
+ }
220
+
221
+ /**
222
+ *
223
+ * @param {number} value
224
+ * @param {number} id edge ID
225
+ */
226
+ edge_write_vertex1(id, value) {
227
+ const pool = this.__edge_pool;
228
+
229
+ const address = pool.element_address(id);
230
+ pool.data_view.setUint32(address, value);
231
+ }
232
+
233
+ /**
234
+ * @param {number} id edge ID
235
+ * @returns {number}
236
+ */
237
+ edge_read_vertex2(id) {
238
+ const pool = this.__edge_pool;
239
+
240
+ const address = pool.element_address(id);
241
+ return pool.data_view.getUint32(address + UINT_32_SIZE);
242
+
243
+ }
244
+
245
+ /**
246
+ *
247
+ * @param {number} value
248
+ * @param {number} id edge ID
249
+ */
250
+ edge_write_vertex2(id, value) {
251
+ const pool = this.__edge_pool;
252
+
253
+ const address = pool.element_address(id);
254
+ pool.data_view.setUint32(address + UINT_32_SIZE, value);
255
+ }
256
+
257
+ /**
258
+ * @param {number} id edge ID
259
+ * @returns {number}
260
+ */
261
+ edge_read_loop(id) {
262
+ assert.isNonNegativeInteger(id, 'id');
263
+ const pool = this.__edge_pool;
264
+
265
+ assert.equal(pool.is_allocated(id), true, 'element is not allocated');
266
+
267
+ const address = pool.element_address(id);
268
+ return pool.data_view.getUint32(address + 2 * UINT_32_SIZE);
269
+
270
+ }
271
+
272
+ /**
273
+ *
274
+ * @param {number} value
275
+ * @param {number} id edge ID
276
+ */
277
+ edge_write_loop(id, value) {
278
+ const pool = this.__edge_pool;
279
+
280
+ const address = pool.element_address(id);
281
+ pool.data_view.setUint32(address + 2 * UINT_32_SIZE, value);
282
+ }
283
+
284
+ loop_create() {
285
+ const id = this.__loop_pool.allocate();
286
+
287
+ this.loop_initialize(id);
288
+
289
+ return id;
290
+ }
110
291
 
292
+ /**
293
+ * Put loop into valid initial state, set all pointers to NULL
294
+ * @param {number} id
295
+ */
296
+ loop_initialize(id) {
297
+ this.loop_write_radial_next(id, NULL_POINTER);
298
+ this.loop_write_radial_prev(id, NULL_POINTER);
299
+ this.loop_write_next(id, NULL_POINTER);
300
+ this.loop_write_prev(id, NULL_POINTER);
301
+ }
302
+
303
+ /**
304
+ * @param {number} id loop ID
305
+ * @returns {number}
306
+ */
307
+ loop_read_vertex(id) {
308
+ const pool = this.__loop_pool;
309
+
310
+ const address = pool.element_address(id);
311
+ return pool.data_view.getUint32(address);
312
+
313
+ }
314
+
315
+ /**
316
+ *
317
+ * @param {number} value
318
+ * @param {number} id loop ID
319
+ */
320
+ loop_write_vertex(id, value) {
321
+ const pool = this.__loop_pool;
322
+
323
+ const address = pool.element_address(id);
324
+ pool.data_view.setUint32(address, value);
325
+ }
326
+
327
+ /**
328
+ *
329
+ * @param {number} id loop ID
330
+ * @returns {number}
331
+ */
332
+ loop_read_edge(id) {
333
+ const pool = this.__loop_pool;
334
+
335
+ const address = pool.element_address(id);
336
+ return pool.data_view.getUint32(address + UINT_32_SIZE);
337
+ }
338
+
339
+ /**
340
+ *
341
+ * @param {number} value
342
+ * @param {number} id loop ID
343
+ */
344
+ loop_write_edge(id, value) {
345
+ const pool = this.__loop_pool;
346
+
347
+ const address = pool.element_address(id);
348
+ pool.data_view.setUint32(address + UINT_32_SIZE, value);
349
+ }
350
+
351
+ /**
352
+ *
353
+ * @param {number} id loop ID
354
+ * @returns {number}
355
+ */
356
+ loop_read_face(id) {
357
+ const pool = this.__loop_pool;
358
+
359
+ const address = pool.element_address(id);
360
+ return pool.data_view.getUint32(address + 2 * UINT_32_SIZE);
361
+ }
362
+
363
+ /**
364
+ *
365
+ * @param {number} value
366
+ * @param {number} id loop ID
367
+ */
368
+ loop_write_face(id, value) {
369
+ assert.isNonNegativeInteger(id, 'id');
370
+
371
+ const pool = this.__loop_pool;
372
+
373
+ const address = pool.element_address(id);
374
+ pool.data_view.setUint32(address + 2 * UINT_32_SIZE, value);
375
+ }
376
+
377
+ /**
378
+ *
379
+ * @param {number} id loop ID
380
+ * @returns {number}
381
+ */
382
+ loop_read_radial_next(id) {
383
+ assert.isNonNegativeInteger(id, 'id');
384
+ const pool = this.__loop_pool;
385
+
386
+ const address = pool.element_address(id);
387
+ return pool.data_view.getUint32(address + 3 * UINT_32_SIZE);
388
+ }
389
+
390
+ /**
391
+ *
392
+ * @param {number} value
393
+ * @param {number} id loop ID
394
+ */
395
+ loop_write_radial_next(id, value) {
396
+ assert.isNonNegativeInteger(id, 'id');
397
+ const pool = this.__loop_pool;
398
+
399
+ const address = pool.element_address(id);
400
+ pool.data_view.setUint32(address + 3 * UINT_32_SIZE, value);
401
+ }
402
+
403
+ /**
404
+ *
405
+ * @param {number} id loop ID
406
+ * @returns {number}
407
+ */
408
+ loop_read_radial_prev(id) {
409
+ assert.isNonNegativeInteger(id, 'id');
410
+ const pool = this.__loop_pool;
411
+
412
+ const address = pool.element_address(id);
413
+ return pool.data_view.getUint32(address + 4 * UINT_32_SIZE);
414
+ }
415
+
416
+ /**
417
+ *
418
+ * @param {number} value
419
+ * @param {number} id loop ID
420
+ */
421
+ loop_write_radial_prev(id, value) {
422
+ assert.isNonNegativeInteger(id, 'id');
423
+ const pool = this.__loop_pool;
424
+
425
+ const address = pool.element_address(id);
426
+ pool.data_view.setUint32(address + 4 * UINT_32_SIZE, value);
427
+ }
428
+
429
+ /**
430
+ *
431
+ * @param {number} id loop ID
432
+ * @returns {number}
433
+ */
434
+ loop_read_next(id) {
435
+ const pool = this.__loop_pool;
436
+
437
+ const address = pool.element_address(id);
438
+ return pool.data_view.getUint32(address + 5 * UINT_32_SIZE);
439
+ }
440
+
441
+ /**
442
+ *
443
+ * @param {number} value
444
+ * @param {number} id loop ID
445
+ */
446
+ loop_write_next(id, value) {
447
+ const pool = this.__loop_pool;
111
448
 
449
+ const address = pool.element_address(id);
450
+ pool.data_view.setUint32(address + 5 * UINT_32_SIZE, value);
451
+ }
452
+
453
+ /**
454
+ *
455
+ * @param {number} id loop ID
456
+ * @returns {number}
457
+ */
458
+ loop_read_prev(id) {
459
+ const pool = this.__loop_pool;
460
+
461
+ const address = pool.element_address(id);
462
+ return pool.data_view.getUint32(address + 6 * UINT_32_SIZE);
463
+ }
464
+
465
+ /**
466
+ *
467
+ * @param {number} value
468
+ * @param {number} id loop ID
469
+ */
470
+ loop_write_prev(id, value) {
471
+ const pool = this.__loop_pool;
472
+
473
+ const address = pool.element_address(id);
474
+ pool.data_view.setUint32(address + 6 * UINT_32_SIZE, value);
475
+ }
476
+
477
+ /**
478
+ *
479
+ * @param {number} id face ID
480
+ * @returns {number}
481
+ */
482
+ face_read_loop(id) {
483
+ const pool = this.__face_pool;
484
+
485
+ const address = pool.element_address(id);
486
+ return pool.data_view.getUint32(address);
487
+ }
488
+
489
+ /**
490
+ *
491
+ * @param {number} value
492
+ * @param {number} id face ID
493
+ */
494
+ face_write_loop(id, value) {
495
+ const pool = this.__face_pool;
496
+
497
+ const address = pool.element_address(id);
498
+ pool.data_view.setUint32(address, value);
499
+ }
112
500
  }
@@ -0,0 +1,16 @@
1
+ import { BinaryTopology } from "./BinaryTopology.js";
2
+
3
+ test('create and set vertex', () => {
4
+
5
+ const topo = new BinaryTopology();
6
+
7
+ const v = topo.vertices.allocate();
8
+
9
+ topo.vertex_write_coordinate(v, [1, 2, 0], 0);
10
+
11
+ const coordinate = [];
12
+
13
+ topo.vertex_read_coordinate(coordinate, 0, v);
14
+
15
+ expect(coordinate).toEqual([1, 2, 0]);
16
+ });
@@ -41,6 +41,10 @@ export class TopoEdge {
41
41
  this.lengthSqr = -1;
42
42
  }
43
43
 
44
+ get byteSize() {
45
+ return 80+5*4 + 4 + 8 + 8 + 8 + this.faces.length * 8 + 10;
46
+ }
47
+
44
48
  /**
45
49
  *
46
50
  * @param {number} i
@@ -35,6 +35,31 @@ export class TopoMesh {
35
35
  this.__faces = new Set();
36
36
  }
37
37
 
38
+ /**
39
+ * Approximation of memory footprint of this object
40
+ * NOTE: this is highly speculative and will differ from reality depending on VM running the code as well as many other factors
41
+ * @return {number}
42
+ */
43
+ get byteSize() {
44
+ let r = 0;
45
+
46
+ for (let i = 0; i < this.vertices.length; i++) {
47
+ const v = this.vertices[i];
48
+
49
+ r += v.byteSize;
50
+ }
51
+
52
+ for (const edge of this.__edges) {
53
+ r += edge.byteSize;
54
+ }
55
+
56
+ for (const face of this.__faces) {
57
+ r += face.byteSize;
58
+ }
59
+
60
+ return r;
61
+ }
62
+
38
63
  /**
39
64
  *
40
65
  * @returns {Set<TopoEdge>}
@@ -32,6 +32,10 @@ export class TopoTriangle {
32
32
  this.normal = [0, 0, 0];
33
33
  }
34
34
 
35
+ get byteSize() {
36
+ return 80 + 4 * 4 + 4 + this.vertices.length * 8 + 10 + this.edges.length * 8 + 10 + 8 * 3 + 10;
37
+ }
38
+
35
39
  /**
36
40
  *
37
41
  * @param {TopoTriangle} other
@@ -52,6 +52,14 @@ export class TopoVertex {
52
52
  return this.z;
53
53
  }
54
54
 
55
+ /**
56
+ * Estimate of this object's size in RAM, in bytes
57
+ * @return {number}
58
+ */
59
+ get byteSize() {
60
+ return 80 + 6 * 4 + 8 + this.edges.length * 8 + 10 + this.faces.length * 8 + 10;
61
+ }
62
+
55
63
  /**
56
64
  *
57
65
  * @param {TopoVertex} other