@woosh/meep-engine 2.43.19 → 2.43.21

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.
@@ -1,6 +1,6 @@
1
1
  //
2
2
 
3
- import { NULL_NODE } from "../ExplicitBinaryBoundingVolumeHierarchy.d.ts";
3
+ import { NULL_NODE } from "../ExplicitBinaryBoundingVolumeHierarchy.js";
4
4
 
5
5
  const traversal_stack = [];
6
6
  /**
@@ -16,12 +16,21 @@ export class SGMeshHighlightSystem extends System {
16
16
  * @param {number} entity
17
17
  * @private
18
18
  */
19
- __apply_highlight( event, entity) {
19
+ __apply_highlight(event, entity) {
20
20
  const ecd = this.entityManager.dataset;
21
21
 
22
22
  const sg_mesh = ecd.getComponent(entity, SGMesh);
23
+
24
+ if (sg_mesh === undefined) {
25
+ return;
26
+ }
27
+
23
28
  const highlight = ecd.getComponent(entity, Highlight);
24
29
 
30
+ if (highlight === undefined) {
31
+ return;
32
+ }
33
+
25
34
  const node = sg_mesh.__node;
26
35
 
27
36
  node.traverse(t => {
@@ -35,7 +44,7 @@ export class SGMeshHighlightSystem extends System {
35
44
  * @param {number} entity
36
45
  * @private
37
46
  */
38
- __remove_highlight( event, entity) {
47
+ __remove_highlight(event, entity) {
39
48
 
40
49
  const ecd = this.entityManager.dataset;
41
50
 
@@ -56,7 +65,7 @@ export class SGMeshHighlightSystem extends System {
56
65
  */
57
66
  link(highlight, mesh, entity) {
58
67
  if (mesh.__node !== null) {
59
- this.__apply_highlight(undefined, entity);
68
+ this.__apply_highlight(undefined, entity);
60
69
  }
61
70
 
62
71
  const ecd = this.entityManager.dataset;
@@ -1,3 +1,3 @@
1
1
  export class MicronGeometryPatch {
2
-
2
+ allocate_metadata(buffer: ArrayBuffer, offset: number): number
3
3
  }
@@ -35,7 +35,7 @@ export class MicronGeometryPatch {
35
35
  this.group_bounding_box = null;
36
36
 
37
37
  /**
38
- *
38
+ * TODO get rid of this, use bounding box instead for smaller memory footprint
39
39
  * @type {number[]|Float32Array}
40
40
  */
41
41
  this.group_bounding_box_corners = null;
@@ -124,6 +124,7 @@ export class MicronGeometryPatch {
124
124
  * @returns {number}
125
125
  */
126
126
  allocate_metadata(buffer, offset) {
127
+ assert.isNonNegativeInteger(offset, 'offset');
127
128
  assert.greaterThanOrEqual(buffer.byteLength, offset + 144, 'buffer underflow');
128
129
 
129
130
  this.bounding_box = new Float32Array(buffer, offset, 6);
@@ -54,6 +54,10 @@ export class PathTracedMesh {
54
54
  this.__local_scale_inverse = Math.hypot(scale_x, scale_y, scale_z);
55
55
  }
56
56
 
57
+ get transform() {
58
+ return this.__transform;
59
+ }
60
+
57
61
  /**
58
62
  *
59
63
  * @param {number[]} out
@@ -23,14 +23,12 @@ 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";
34
32
 
35
33
  /*
36
34
  Ray hit data layout:
@@ -49,13 +47,22 @@ const texture_uv = [0, 0];
49
47
  const color = [];
50
48
 
51
49
  const irradiance = [0, 0, 0];
50
+
51
+ /**
52
+ *
53
+ * @type {number[]|vec3}
54
+ */
55
+ const throughputColor = [1, 1, 1];
52
56
  const trace_result = [];
53
57
 
54
- const _ray = [];
58
+ const _ray_0 = [];
59
+ const _ray_1 = [];
55
60
 
56
61
  const tmp_0 = [];
57
62
  const tmp_1 = [];
58
63
 
64
+ const null_output = [];
65
+
59
66
 
60
67
  export class PathTracer {
61
68
  constructor() {
@@ -320,6 +327,9 @@ export class PathTracer {
320
327
  const normal_array = normal_attribute.array;
321
328
 
322
329
  sample_triangle_attribute(out, 3, index_0, index_1, index_2, normal_array, 3, u, v);
330
+
331
+ // apply transform
332
+ transform_normal_m4(out, 3, out, 3, mesh.transform);
323
333
  } else {
324
334
  // copy hit normal
325
335
  array_copy(hit, 3, out, 3, 3);
@@ -417,16 +427,17 @@ export class PathTracer {
417
427
  *
418
428
  * @param {number[]} out
419
429
  * @param {number} out_offset
420
- * @param {number[]} hit_info
421
- * @param {number} hit_info_address
430
+ * @param {number[]} ray
431
+ * @param {number} ray_address
422
432
  */
423
- sample_lights(out, out_offset, hit_info, hit_info_address) {
433
+ sample_lights(out, out_offset, ray, ray_address) {
424
434
  let lights_sampled = false;
425
435
 
426
436
  const lights = this.__lights;
427
437
  const light_count = lights.length;
428
438
 
429
439
  for (let i = 0; i < 3; i++) {
440
+ // initialize contribution to 0
430
441
  out[out_offset + i] = 0;
431
442
  }
432
443
 
@@ -436,23 +447,47 @@ export class PathTracer {
436
447
  if (light.isDirectionalLight === true) {
437
448
  const dir = light.direction;
438
449
 
450
+ // see https://github.com/mrdoob/three.js/blob/f0a9e0cf90a2f1ba5017fcb7fd46f02748b920cf/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js#L172
451
+
452
+ const ray_direction_x = ray[ray_address + 3];
453
+ const ray_direction_y = ray[ray_address + 4];
454
+ const ray_direction_z = ray[ray_address + 5];
455
+
456
+ const light_dir_inv_x = -dir.x;
457
+ const light_dir_inv_y = -dir.y;
458
+ const light_dir_inv_z = -dir.z;
459
+
460
+ 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);
461
+
462
+ if (dotNL <= 0) {
463
+ // no contribution, facing away from the light
464
+ continue;
465
+ }
466
+
467
+ const ray_origin_x = ray[ray_address + 0];
468
+ const ray_origin_y = ray[ray_address + 1];
469
+ const ray_origin_z = ray[ray_address + 2];
470
+
471
+ ray3_array_compose(_ray_1,
472
+ ray_origin_x, ray_origin_y, ray_origin_z,
473
+ light_dir_inv_x, light_dir_inv_y, light_dir_inv_z
474
+ );
475
+
439
476
  // 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) {
477
+ if (this.trace(null_output, _ray_1, 0.0001, Infinity) > 0) {
446
478
  // light is occluded
447
- break;
479
+ continue;
448
480
  }
449
481
 
450
- 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));
482
+
483
+ const intensity = dotNL * light.intensity.getValue();
451
484
 
452
485
  for (let j = 0; j < 3; j++) {
453
486
  // irradiance
454
- out[out_offset + j] += dotNL * light.color[j] * light.intensity.getValue();
487
+ out[out_offset + j] += light.color[j] * intensity;
455
488
  }
489
+
490
+ lights_sampled = true;
456
491
  }
457
492
  }
458
493
 
@@ -462,28 +497,30 @@ export class PathTracer {
462
497
  path_trace(out, ray, max_distance, max_bounce, random = Math.random) {
463
498
  //TODO add importance sampling, see https://raytracing.github.io/books/RayTracingTheRestOfYourLife.html#lightscattering/thescatteringpdf
464
499
 
465
- array_copy(ray, 0, _ray, 0, 6);
500
+ array_copy(ray, 0, _ray_0, 0, 6);
466
501
 
467
- irradiance[0] = 0;
468
- irradiance[1] = 0;
469
- irradiance[2] = 0;
502
+ irradiance[0] = 1;
503
+ irradiance[1] = 1;
504
+ irradiance[2] = 1;
470
505
 
471
506
  let got_emission = false;
472
507
 
473
508
  let i;
474
509
  for (i = 0; i < max_bounce; i++) {
475
510
 
476
- const hit_distance = this.trace(trace_result, _ray, 0.0001, max_distance);
511
+ const hit_distance = this.trace(trace_result, _ray_0, 0.0001, max_distance);
512
+
477
513
 
478
514
  if (hit_distance < 0) {
479
515
  // ray didn't hit anything
480
516
 
481
517
  // sample "environment" and terminate path as there is nothing to reflect off of
482
518
 
483
- this.sample_background(tmp_0, 0, _ray, 3);
519
+ this.sample_background(tmp_0, 0, _ray_0, 3);
484
520
 
485
521
  vec3.multiply(irradiance, irradiance, tmp_0);
486
522
 
523
+
487
524
  got_emission = true;
488
525
 
489
526
  break;
@@ -496,28 +533,36 @@ export class PathTracer {
496
533
  this.sample_material(tmp_0, trace_result);
497
534
 
498
535
  // adjust normal on the hit
499
- // array_copy(tmp_0, 3, trace_result, 3, 3);
536
+ array_copy(tmp_0, 3, trace_result, 3, 3);
500
537
 
501
- // sample light
502
- this.sample_lights(tmp_1, 0, trace_result, 0);
503
- vec3.add(irradiance, irradiance, tmp_1);
504
538
 
505
539
  // reflect ray
506
- array_copy(trace_result, 0, _ray, 0, 3);
540
+ array_copy(trace_result, 0, _ray_0, 0, 3);
507
541
 
508
542
  // getBiasedNormalSample(_ray, 3, tmp, 3, 1, random);
509
- random_in_hemisphere(random, _ray, 3, tmp_0, 3);
543
+ random_in_hemisphere(random, _ray_0, 3, tmp_0, 3);
510
544
 
545
+ // accumulate
511
546
  vec3.multiply(irradiance, irradiance, tmp_0);
512
547
  }
513
548
 
514
- if (got_emission === false) {
515
- // no light source was hit, propagate shadow(darkness) along the ray
516
- irradiance[0] = 0;
517
- irradiance[1] = 0;
518
- irradiance[2] = 0;
549
+
550
+ if (i > 0) {
551
+
552
+ // directly sample light at the end of the path
553
+ this.sample_lights(tmp_1, 0, trace_result, 0);
554
+
555
+ vec3.multiply(irradiance, irradiance, tmp_1);
519
556
  }
520
557
 
558
+ //
559
+ // if (got_emission === false) {
560
+ // // no light source was hit, propagate shadow(darkness) along the ray
561
+ // irradiance[0] = 0;
562
+ // irradiance[1] = 0;
563
+ // irradiance[2] = 0;
564
+ // }
565
+
521
566
  array_copy(irradiance, 0, out, 0, 3);
522
567
  }
523
568
  }
@@ -532,3 +577,13 @@ function compute_hit_reaction_standard_material(material, hit) {
532
577
  }
533
578
 
534
579
 
580
+ /**
581
+ *
582
+ * @param {number[]|vec3} out
583
+ * @param {number} value
584
+ */
585
+ function vec3_set_scalar(out, value) {
586
+ out[0] = value;
587
+ out[1] = value;
588
+ out[2] = value;
589
+ }
@@ -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) {
@@ -189,6 +216,8 @@ async function prepare_scene_gltf(pt, camera, url) {
189
216
 
190
217
  box3.getBoundingSphere(sphere);
191
218
 
219
+ pt.addLight(make_sun());
220
+
192
221
  camera.position.set(1, 1.3, 1)
193
222
  .normalize()
194
223
  .multiplyScalar(sphere.radius * 2.6);
@@ -226,6 +255,8 @@ async function prepare_scene_lucy(pt, camera) {
226
255
  }).matrix);
227
256
 
228
257
 
258
+ pt.addLight(make_sun());
259
+
229
260
  camera.position.set(0, 0, -2300);
230
261
  camera.lookAt(0, 0, 0);
231
262
  }
@@ -336,6 +367,8 @@ async function prepare_scene_rtiow(pt, camera) {
336
367
  }
337
368
 
338
369
 
370
+ pt.addLight(make_sun());
371
+
339
372
  camera.position.set(13, 2, 3);
340
373
  camera.lookAt(0, 0, 0);
341
374
 
@@ -376,7 +409,7 @@ function* render(target, pt, camera, progress = { current: 0, total: 0 }) {
376
409
  const pixel_scale_x = 1 / (width - 1);
377
410
  const pixel_scale_y = 1 / (height - 1);
378
411
 
379
- const pixel_sample_count = 64;
412
+ const pixel_sample_count = 500;
380
413
 
381
414
  progress.total = width * height;
382
415
 
@@ -426,7 +459,7 @@ function* render(target, pt, camera, progress = { current: 0, total: 0 }) {
426
459
  ray_direction.x, ray_direction.y, ray_direction.z
427
460
  );
428
461
 
429
- pt.path_trace(out, ray, Infinity, 4, random);
462
+ pt.path_trace(out, ray, Infinity, 16, random);
430
463
 
431
464
  out2[0] += out[0];
432
465
  out2[1] += out[1];
@@ -476,12 +509,12 @@ const camera = new PerspectiveCamera();
476
509
  async function start_renderer(camera) {
477
510
  camera.aspect = vCanvas.size.x / vCanvas.size.y;
478
511
 
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
512
+ const path = 'data/models/LowPolyTownshipSet/Small_house/Small_house.gltf';
513
+ // const path = 'data/models/road_bike/road_bike.gltf'; //large CAD-type model
481
514
 
482
515
  // await prepare_scene_lucy(pt, camera);
483
- // await prepare_scene_rtiow(pt, camera);
484
- await prepare_scene_sphere_01(pt, camera);
516
+ await prepare_scene_rtiow(pt, camera);
517
+ // await prepare_scene_sphere_01(pt, camera);
485
518
  // await prepare_scene_gltf(pt, camera, path);
486
519
 
487
520
  pt.build();
@@ -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
  }
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.19",
8
+ "version": "2.43.21",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",