@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
@@ -23,14 +23,13 @@ import { linear_to_sRGB } from "../../../../core/color/linear_to_sRGB.js";
23
23
  import { random_in_hemisphere } from "./random_in_hemisphere.js";
24
24
  import { sample_triangle_attribute } from "./sample_triangle_attribute.js";
25
25
  import { vec3_uint8_to_float } from "./vec3_uint8_to_float.js";
26
- import { make_sky_rtiw } from "./make_sky_rtiw.js";
27
26
  import { apply_texture_clamping_to_coordinate } from "./apply_texture_clamping_to_coordinate.js";
28
27
  import { ebvh_sort_for_traversal_depth_first } from "../../../../core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js";
29
- import { clamp01 } from "../../../../core/math/clamp01.js";
30
28
  import { v3_dot } from "../../../../core/geom/v3_dot.js";
31
- import { LightType } from "../../ecs/light/LightType.js";
32
- import { make_zero_vector3 } from "./make_zero_vector3.js";
33
29
  import { make_one_vector3 } from "./make_one_vector3.js";
30
+ import { transform_normal_m4 } from "./ray_hit_apply_transform.js";
31
+ import { ray3_array_compose } from "../../../../core/geom/3d/ray/ray3_array_compose.js";
32
+ import { getBiasedNormalSample } from "./getBiasedNormalSample.js";
34
33
 
35
34
  /*
36
35
  Ray hit data layout:
@@ -49,13 +48,22 @@ const texture_uv = [0, 0];
49
48
  const color = [];
50
49
 
51
50
  const irradiance = [0, 0, 0];
51
+
52
+ /**
53
+ *
54
+ * @type {number[]|vec3}
55
+ */
56
+ const throughputColor = [1, 1, 1];
52
57
  const trace_result = [];
53
58
 
54
- const _ray = [];
59
+ const _ray_0 = [];
60
+ const _ray_1 = [];
55
61
 
56
62
  const tmp_0 = [];
57
63
  const tmp_1 = [];
58
64
 
65
+ const null_output = [];
66
+
59
67
 
60
68
  export class PathTracer {
61
69
  constructor() {
@@ -320,6 +328,9 @@ export class PathTracer {
320
328
  const normal_array = normal_attribute.array;
321
329
 
322
330
  sample_triangle_attribute(out, 3, index_0, index_1, index_2, normal_array, 3, u, v);
331
+
332
+ // apply transform
333
+ transform_normal_m4(out, 3, out, 3, mesh.transform);
323
334
  } else {
324
335
  // copy hit normal
325
336
  array_copy(hit, 3, out, 3, 3);
@@ -417,16 +428,17 @@ export class PathTracer {
417
428
  *
418
429
  * @param {number[]} out
419
430
  * @param {number} out_offset
420
- * @param {number[]} hit_info
421
- * @param {number} hit_info_address
431
+ * @param {number[]} ray
432
+ * @param {number} ray_address
422
433
  */
423
- sample_lights(out, out_offset, hit_info, hit_info_address) {
434
+ sample_lights(out, out_offset, ray, ray_address) {
424
435
  let lights_sampled = false;
425
436
 
426
437
  const lights = this.__lights;
427
438
  const light_count = lights.length;
428
439
 
429
440
  for (let i = 0; i < 3; i++) {
441
+ // initialize contribution to 0
430
442
  out[out_offset + i] = 0;
431
443
  }
432
444
 
@@ -436,25 +448,47 @@ export class PathTracer {
436
448
  if (light.isDirectionalLight === true) {
437
449
  const dir = light.direction;
438
450
 
451
+ // see https://github.com/mrdoob/three.js/blob/f0a9e0cf90a2f1ba5017fcb7fd46f02748b920cf/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js#L172
452
+
453
+ const ray_direction_x = ray[ray_address + 3];
454
+ const ray_direction_y = ray[ray_address + 4];
455
+ const ray_direction_z = ray[ray_address + 5];
456
+
457
+ const light_dir_inv_x = -dir.x;
458
+ const light_dir_inv_y = -dir.y;
459
+ const light_dir_inv_z = -dir.z;
460
+
461
+ const dotNL = v3_dot(ray_direction_x, ray_direction_y, ray_direction_z, light_dir_inv_x, light_dir_inv_y, light_dir_inv_z);
462
+
463
+ if (dotNL <= 0) {
464
+ // no contribution, facing away from the light
465
+ continue;
466
+ }
467
+
468
+ const ray_origin_x = ray[ray_address + 0];
469
+ const ray_origin_y = ray[ray_address + 1];
470
+ const ray_origin_z = ray[ray_address + 2];
471
+
472
+ ray3_array_compose(_ray_1,
473
+ ray_origin_x, ray_origin_y, ray_origin_z,
474
+ light_dir_inv_x, light_dir_inv_y, light_dir_inv_z
475
+ );
476
+
439
477
  // check if there are any obstacles in the direction of the light
440
- if (this.trace([], [
441
- hit_info[hit_info_address + 0],
442
- hit_info[hit_info_address + 1],
443
- hit_info[hit_info_address + 2],
444
- -dir.x, -dir.y, -dir.z
445
- ], 0.0001, Infinity) > 0) {
478
+ if (this.trace(null_output, _ray_1, 0.0001, Infinity) > 0) {
446
479
  // light is occluded
447
- break;
480
+ continue;
448
481
  }
449
482
 
450
- // see https://github.com/mrdoob/three.js/blob/f0a9e0cf90a2f1ba5017fcb7fd46f02748b920cf/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js#L172
451
483
 
452
- const dotNL = clamp01(v3_dot(hit_info[hit_info_address + 3], hit_info[hit_info_address + 4], hit_info[hit_info_address + 5], -dir.x, -dir.y, -dir.z));
484
+ const intensity = dotNL * light.intensity.getValue();
453
485
 
454
486
  for (let j = 0; j < 3; j++) {
455
487
  // irradiance
456
- out[out_offset + j] += dotNL * light.color[j] * light.intensity.getValue();
488
+ out[out_offset + j] += light.color[j] * intensity;
457
489
  }
490
+
491
+ lights_sampled = true;
458
492
  }
459
493
  }
460
494
 
@@ -464,28 +498,30 @@ export class PathTracer {
464
498
  path_trace(out, ray, max_distance, max_bounce, random = Math.random) {
465
499
  //TODO add importance sampling, see https://raytracing.github.io/books/RayTracingTheRestOfYourLife.html#lightscattering/thescatteringpdf
466
500
 
467
- array_copy(ray, 0, _ray, 0, 6);
501
+ array_copy(ray, 0, _ray_0, 0, 6);
468
502
 
469
- irradiance[0] = 0;
470
- irradiance[1] = 0;
471
- irradiance[2] = 0;
503
+ irradiance[0] = 1;
504
+ irradiance[1] = 1;
505
+ irradiance[2] = 1;
472
506
 
473
507
  let got_emission = false;
474
508
 
475
509
  let i;
476
510
  for (i = 0; i < max_bounce; i++) {
477
511
 
478
- const hit_distance = this.trace(trace_result, _ray, 0.0001, max_distance);
512
+ const hit_distance = this.trace(trace_result, _ray_0, 0.0001, max_distance);
513
+
479
514
 
480
515
  if (hit_distance < 0) {
481
516
  // ray didn't hit anything
482
517
 
483
518
  // sample "environment" and terminate path as there is nothing to reflect off of
484
519
 
485
- this.sample_background(tmp_0, 0, _ray, 3);
520
+ this.sample_background(tmp_0, 0, _ray_0, 3);
486
521
 
487
522
  vec3.multiply(irradiance, irradiance, tmp_0);
488
523
 
524
+
489
525
  got_emission = true;
490
526
 
491
527
  break;
@@ -498,28 +534,36 @@ export class PathTracer {
498
534
  this.sample_material(tmp_0, trace_result);
499
535
 
500
536
  // adjust normal on the hit
501
- // array_copy(tmp_0, 3, trace_result, 3, 3);
537
+ array_copy(tmp_0, 3, trace_result, 3, 3);
502
538
 
503
- // sample light
504
- this.sample_lights(tmp_1, 0, trace_result, 0);
505
- vec3.add(irradiance, irradiance, tmp_1);
506
539
 
507
540
  // reflect ray
508
- array_copy(trace_result, 0, _ray, 0, 3);
541
+ array_copy(trace_result, 0, _ray_0, 0, 3);
509
542
 
510
- // getBiasedNormalSample(_ray, 3, tmp, 3, 1, random);
511
- random_in_hemisphere(random, _ray, 3, tmp_0, 3);
543
+ getBiasedNormalSample(_ray_0, 3, tmp_0, 3, 1, random);
544
+ // random_in_hemisphere(random, _ray_0, 3, tmp_0, 3);
512
545
 
546
+ // accumulate
513
547
  vec3.multiply(irradiance, irradiance, tmp_0);
514
548
  }
515
549
 
516
- if (got_emission === false) {
517
- // no light source was hit, propagate shadow(darkness) along the ray
518
- irradiance[0] = 0;
519
- irradiance[1] = 0;
520
- irradiance[2] = 0;
550
+
551
+ if (i > 0) {
552
+
553
+ // directly sample light at the end of the path
554
+ this.sample_lights(tmp_1, 0, trace_result, 0);
555
+
556
+ vec3.multiply(irradiance, irradiance, tmp_1);
521
557
  }
522
558
 
559
+ //
560
+ // if (got_emission === false) {
561
+ // // no light source was hit, propagate shadow(darkness) along the ray
562
+ // irradiance[0] = 0;
563
+ // irradiance[1] = 0;
564
+ // irradiance[2] = 0;
565
+ // }
566
+
523
567
  array_copy(irradiance, 0, out, 0, 3);
524
568
  }
525
569
  }
@@ -534,3 +578,13 @@ function compute_hit_reaction_standard_material(material, hit) {
534
578
  }
535
579
 
536
580
 
581
+ /**
582
+ *
583
+ * @param {number[]|vec3} out
584
+ * @param {number} value
585
+ */
586
+ function vec3_set_scalar(out, value) {
587
+ out[0] = value;
588
+ out[1] = value;
589
+ out[2] = value;
590
+ }
@@ -8,7 +8,7 @@
8
8
  * @param {number} power
9
9
  * @param {function():number} random
10
10
  */
11
- function getBiasedNormalSample(out, out_offset, normal, normal_offset, power, random) {
11
+ export function getBiasedNormalSample(out, out_offset, normal, normal_offset, power, random) {
12
12
  const dir_x = normal[normal_offset];
13
13
  const dir_y = normal[normal_offset + 1];
14
14
  const dir_z = normal[normal_offset + 2];
@@ -21,7 +21,7 @@ import { Camera } from "../../ecs/camera/Camera.js";
21
21
  import Vector3 from "../../../../core/geom/Vector3.js";
22
22
  import { scaleSampler2D } from "../../texture/sampler/scaleSampler2D.js";
23
23
  import { seededRandom } from "../../../../core/math/random/seededRandom.js";
24
- import { mat4 } from "gl-matrix";
24
+ import { mat4, vec3 } from "gl-matrix";
25
25
  import { v3_distance } from "../../../../core/geom/v3_distance.js";
26
26
  import ConcurrentExecutor from "../../../../core/process/executor/ConcurrentExecutor.js";
27
27
  import Task from "../../../../core/process/task/Task.js";
@@ -38,6 +38,7 @@ import { Color } from "../../../../core/color/Color.js";
38
38
  import { min2 } from "../../../../core/math/min2.js";
39
39
  import { makeGeometryIndexed } from "../../geometry/buffered/makeGeometryIndexed.js";
40
40
  import { DirectionalLight } from "../../render/forward_plus/model/DirectionalLight.js";
41
+ import { kelvin_to_rgb } from "../../../../core/color/kelvin_to_rgb.js";
41
42
 
42
43
  document.body.style.margin = 0;
43
44
  document.body.style.overflow = "hidden";
@@ -103,6 +104,32 @@ const gltfLoader = new GLTFLoader();
103
104
  // );
104
105
  //
105
106
 
107
+ function make_sun({
108
+ temperature = 5000, // around clear-sky day
109
+ color = '#FFFFFF',
110
+ intensity = 1,
111
+ direction = new Vector3(-0.5, -1, -0.05)
112
+ } = {}) {
113
+
114
+ const temp_c = new Color();
115
+
116
+ kelvin_to_rgb(temp_c, 0, temperature);
117
+
118
+ const light = new DirectionalLight();
119
+
120
+ light.color.parse(color);
121
+ vec3.multiply(light.color, light.color, temp_c);
122
+
123
+ const n = vec3.length(light.color);
124
+ vec3.scale(light.color, light.color, 1 / n); // normalize color vector
125
+
126
+ light.intensity.set(intensity * n);
127
+ light.direction.copy(direction);
128
+ light.direction.normalize();
129
+
130
+
131
+ return light;
132
+ }
106
133
 
107
134
  function prepare_scene_sphere_01(pt, camera) {
108
135
  camera.position.set(0, 0, -2.1);
@@ -132,7 +159,7 @@ function prepare_scene_sphere_01(pt, camera) {
132
159
  light.direction.set(-1, -1, 0);
133
160
  light.direction.normalize();
134
161
 
135
- pt.addLight(light);
162
+ pt.addLight(make_sun());
136
163
  }
137
164
 
138
165
  function promise_ply(url) {
@@ -158,9 +185,10 @@ function promise_gltf(url) {
158
185
  * @param {PathTracer} pt
159
186
  * @param {THREE.Camera} camera
160
187
  * @param {string} url
188
+ * @param zoom
161
189
  * @return {Promise<void>}
162
190
  */
163
- async function prepare_scene_gltf(pt, camera, url) {
191
+ async function prepare_scene_gltf(pt, camera, url, zoom = 1) {
164
192
  const gltf = await promise_gltf(url);
165
193
 
166
194
 
@@ -189,9 +217,11 @@ async function prepare_scene_gltf(pt, camera, url) {
189
217
 
190
218
  box3.getBoundingSphere(sphere);
191
219
 
220
+ pt.addLight(make_sun());
221
+
192
222
  camera.position.set(1, 1.3, 1)
193
223
  .normalize()
194
- .multiplyScalar(sphere.radius * 2.6);
224
+ .multiplyScalar(sphere.radius * 2.6 * (1 / zoom));
195
225
 
196
226
  camera.lookAt(sphere.center);
197
227
  }
@@ -226,6 +256,8 @@ async function prepare_scene_lucy(pt, camera) {
226
256
  }).matrix);
227
257
 
228
258
 
259
+ pt.addLight(make_sun());
260
+
229
261
  camera.position.set(0, 0, -2300);
230
262
  camera.lookAt(0, 0, 0);
231
263
  }
@@ -336,6 +368,8 @@ async function prepare_scene_rtiow(pt, camera) {
336
368
  }
337
369
 
338
370
 
371
+ pt.addLight(make_sun());
372
+
339
373
  camera.position.set(13, 2, 3);
340
374
  camera.lookAt(0, 0, 0);
341
375
 
@@ -376,7 +410,7 @@ function* render(target, pt, camera, progress = { current: 0, total: 0 }) {
376
410
  const pixel_scale_x = 1 / (width - 1);
377
411
  const pixel_scale_y = 1 / (height - 1);
378
412
 
379
- const pixel_sample_count = 64;
413
+ const pixel_sample_count = 500;
380
414
 
381
415
  progress.total = width * height;
382
416
 
@@ -426,7 +460,7 @@ function* render(target, pt, camera, progress = { current: 0, total: 0 }) {
426
460
  ray_direction.x, ray_direction.y, ray_direction.z
427
461
  );
428
462
 
429
- pt.path_trace(out, ray, Infinity, 4, random);
463
+ pt.path_trace(out, ray, Infinity, 16, random);
430
464
 
431
465
  out2[0] += out[0];
432
466
  out2[1] += out[1];
@@ -476,13 +510,13 @@ const camera = new PerspectiveCamera();
476
510
  async function start_renderer(camera) {
477
511
  camera.aspect = vCanvas.size.x / vCanvas.size.y;
478
512
 
479
- // const path = 'data/models/LowPolyTownshipSet/Small_house/Small_house.gltf';
480
- const path = 'data/models/road_bike/road_bike.gltf'; //large CAD-type model
513
+ const path = 'data/models/LowPolyTownshipSet/Small_house/Small_house.gltf';
514
+ // const path = 'data/models/road_bike/road_bike.gltf'; //large CAD-type model
481
515
 
482
516
  // await prepare_scene_lucy(pt, camera);
483
517
  // await prepare_scene_rtiow(pt, camera);
484
- await prepare_scene_sphere_01(pt, camera);
485
- // await prepare_scene_gltf(pt, camera, path);
518
+ // await prepare_scene_sphere_01(pt, camera);
519
+ await prepare_scene_gltf(pt, camera, path);
486
520
 
487
521
  pt.build();
488
522
  pt.optimize();
@@ -1,5 +1,32 @@
1
1
  import { v3_length } from "../../../../core/geom/v3_length.js";
2
2
 
3
+ /**
4
+ *
5
+ * @param {number[]} output
6
+ * @param {number} output_offset
7
+ * @param {number[]} normal
8
+ * @param {number} normal_offset
9
+ * @param {number[]} m 4x4 matrix
10
+ */
11
+ export function transform_normal_m4(output, output_offset, normal, normal_offset, m) {
12
+
13
+ const n_x = normal[normal_offset];
14
+ const n_y = normal[normal_offset + 1];
15
+ const n_z = normal[normal_offset + 2];
16
+
17
+ const result_n_x = m[0] * n_x + m[4] * n_y + m[8] * n_z;
18
+ const result_n_y = m[1] * n_x + m[5] * n_y + m[9] * n_z;
19
+ const result_n_z = m[2] * n_x + m[6] * n_y + m[10] * n_z;
20
+
21
+ const vec_length = v3_length(result_n_x, result_n_y, result_n_z);
22
+
23
+ const normal_multiplier = vec_length === 0 ? 0 : 1 / vec_length;
24
+
25
+ output[output_offset] = result_n_x * normal_multiplier;
26
+ output[output_offset + 1] = result_n_y * normal_multiplier;
27
+ output[output_offset + 2] = result_n_z * normal_multiplier;
28
+ }
29
+
3
30
  /**
4
31
  *
5
32
  * @param {number[]} output
@@ -25,18 +52,6 @@ export function ray_hit_apply_transform(output, hit, m) {
25
52
  output[2] = result_p_z;
26
53
 
27
54
  // transform normal
28
- const n_x = hit[3];
29
- const n_y = hit[4];
30
- const n_z = hit[5];
31
-
32
- const result_n_x = m[0] * n_x + m[4] * n_y + m[8] * n_z;
33
- const result_n_y = m[1] * n_x + m[5] * n_y + m[9] * n_z;
34
- const result_n_z = m[2] * n_x + m[6] * n_y + m[10] * n_z;
35
-
36
- const normal_multiplier = 1 / v3_length(result_n_x, result_n_y, result_n_z);
37
-
38
- output[3] = result_n_x * normal_multiplier;
39
- output[4] = result_n_y * normal_multiplier;
40
- output[5] = result_n_z * normal_multiplier;
55
+ transform_normal_m4(output, 3, hit, 3, m);
41
56
 
42
57
  }
@@ -29,6 +29,7 @@ export class IndexedDBStorage extends Storage {
29
29
  const request = indexedDB.open(name, 1);
30
30
 
31
31
  request.addEventListener('success', function (event) {
32
+ // TODO check if the storage with the given name "MAIN_STORE_NAME" exists, deal with with the case if it's missing
32
33
  resolve(request.result);
33
34
  });
34
35
 
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "productName": "Meep",
6
6
  "description": "production-ready JavaScript game engine based on Entity Component System Architecture",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.43.20",
8
+ "version": "2.43.22",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",