@woosh/meep-engine 2.109.20 → 2.109.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 (65) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/build/meep.cjs +66 -33
  4. package/build/meep.min.js +1 -1
  5. package/build/meep.module.js +66 -33
  6. package/package.json +1 -1
  7. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray_segment.js +1 -1
  8. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray_segment.js +1 -1
  9. package/src/core/collection/array/isArrayEqualStrict.d.ts.map +1 -1
  10. package/src/core/collection/array/isArrayEqualStrict.js +1 -3
  11. package/src/core/collection/array/isArraysEqualWithComparator.d.ts.map +1 -1
  12. package/src/core/collection/array/isArraysEqualWithComparator.js +3 -2
  13. package/src/core/collection/array/typed/is_typed_array_equals.d.ts +1 -1
  14. package/src/core/collection/array/typed/is_typed_array_equals.d.ts.map +1 -1
  15. package/src/core/collection/array/typed/is_typed_array_equals.js +8 -2
  16. package/src/core/geom/3d/aabb/aabb3_array_near_distance_to_intersection_ray_segment.d.ts +16 -0
  17. package/src/core/geom/3d/aabb/aabb3_array_near_distance_to_intersection_ray_segment.d.ts.map +1 -0
  18. package/src/core/geom/3d/aabb/aabb3_array_near_distance_to_intersection_ray_segment.js +30 -0
  19. package/src/core/geom/3d/aabb/aabb3_intersects_ray_segment.d.ts +4 -5
  20. package/src/core/geom/3d/aabb/aabb3_intersects_ray_segment.d.ts.map +1 -1
  21. package/src/core/geom/3d/aabb/aabb3_intersects_ray_segment.js +6 -9
  22. package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.d.ts +20 -0
  23. package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.d.ts.map +1 -0
  24. package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.js +67 -0
  25. package/src/core/geom/3d/line/line3_compute_nearest_point_to_point.d.ts.map +1 -1
  26. package/src/core/geom/3d/line/line3_compute_nearest_point_to_point.js +15 -1
  27. package/src/core/geom/3d/normal/octahedron/decode_octahedron_to_unit.d.ts +2 -1
  28. package/src/core/geom/3d/normal/octahedron/decode_octahedron_to_unit.d.ts.map +1 -1
  29. package/src/core/geom/3d/normal/octahedron/decode_octahedron_to_unit.js +6 -1
  30. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.d.ts +20 -0
  31. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.d.ts.map +1 -0
  32. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.js +147 -0
  33. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.spec.d.ts +2 -0
  34. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.spec.d.ts.map +1 -0
  35. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.spec.js +100 -0
  36. package/src/core/geom/octahedral_uv_crease_distance.d.ts +8 -0
  37. package/src/core/geom/octahedral_uv_crease_distance.d.ts.map +1 -0
  38. package/src/core/geom/octahedral_uv_crease_distance.js +26 -0
  39. package/src/core/math/random/generate_halton_jitter.js +2 -2
  40. package/src/core/math/statistics/generate_hammersley_jitter.d.ts +7 -0
  41. package/src/core/math/statistics/generate_hammersley_jitter.d.ts.map +1 -0
  42. package/src/core/math/statistics/generate_hammersley_jitter.js +16 -0
  43. package/src/core/math/statistics/hammersley_sequence_2d.js +2 -1
  44. package/src/core/model/object/objectDeepEquals.d.ts +1 -1
  45. package/src/core/model/object/objectDeepEquals.d.ts.map +1 -1
  46. package/src/core/model/object/objectDeepEquals.js +54 -26
  47. package/src/engine/graphics/geometry/buffered/computeGeometryEquality.d.ts.map +1 -1
  48. package/src/engine/graphics/geometry/buffered/computeGeometryEquality.js +4 -0
  49. package/src/engine/graphics/sh3/gi/material/common.glsl +1 -1
  50. package/src/engine/graphics/sh3/lpv/LightProbeVolume.d.ts +0 -7
  51. package/src/engine/graphics/sh3/lpv/LightProbeVolume.d.ts.map +1 -1
  52. package/src/engine/graphics/sh3/lpv/LightProbeVolume.js +0 -11
  53. package/src/engine/graphics/sh3/lpv/LightProbeVolumeBaker.d.ts +7 -0
  54. package/src/engine/graphics/sh3/lpv/LightProbeVolumeBaker.d.ts.map +1 -1
  55. package/src/engine/graphics/sh3/lpv/LightProbeVolumeBaker.js +39 -25
  56. package/src/engine/graphics/sh3/lpv/PathTracerProbeRenderer.js +1 -1
  57. package/src/engine/graphics/sh3/lpv/depth/octahedral/bake_octahedral_depth_map.d.ts.map +1 -1
  58. package/src/engine/graphics/sh3/lpv/depth/octahedral/bake_octahedral_depth_map.js +34 -35
  59. package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.d.ts +18 -0
  60. package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.d.ts.map +1 -1
  61. package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js +421 -21
  62. package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.js +1 -1
  63. package/src/engine/graphics/sh3/path_tracer/prototypePathTracer.js +4 -4
  64. package/src/engine/graphics/sh3/prototypeSH3Probe.js +4 -2
  65. package/src/engine/graphics/texture/virtual/prototype.js +3 -1
@@ -1,15 +1,20 @@
1
+ import { assert } from "../../../../../../core/assert.js";
2
+ import { UINT32_MAX } from "../../../../../../core/binary/UINT32_MAX.js";
1
3
  import { array_copy } from "../../../../../../core/collection/array/array_copy.js";
2
4
  import {
3
5
  decode_octahedron_to_unit
4
6
  } from "../../../../../../core/geom/3d/normal/octahedron/decode_octahedron_to_unit.js";
5
7
  import { Ray3 } from "../../../../../../core/geom/3d/Ray3.js";
6
8
  import { v3_distance } from "../../../../../../core/geom/vec3/v3_distance.js";
7
- import { v3_dot } from "../../../../../../core/geom/vec3/v3_dot.js";
8
9
  import { v3_dot_array_array } from "../../../../../../core/geom/vec3/v3_dot_array_array.js";
9
- import { clamp01 } from "../../../../../../core/math/clamp01.js";
10
10
  import { max2 } from "../../../../../../core/math/max2.js";
11
- import { PI_HALF } from "../../../../../../core/math/PI_HALF.js";
12
- import { generate_halton_jitter } from "../../../../../../core/math/random/generate_halton_jitter.js";
11
+ import { generate_hammersley_jitter } from "../../../../../../core/math/statistics/generate_hammersley_jitter.js";
12
+
13
+ /**
14
+ * Weights of rays that are aligned with the probe direction get exponentially weighted by this exponent
15
+ * @type {number}
16
+ */
17
+ const DEPTH_SHARPNESS = 40;
13
18
 
14
19
  const scratch_ray = new Ray3();
15
20
 
@@ -23,9 +28,8 @@ const ray_hit = [];
23
28
  *
24
29
  * @type {number}
25
30
  */
26
- const SUB_SAMPLE_COUNT = 1;
31
+ const SUB_SAMPLE_COUNT = 4;
27
32
 
28
- const DEPTH_SHARPNESS = 7;
29
33
 
30
34
  /**
31
35
  *
@@ -45,9 +49,16 @@ export function bake_octahedral_depth_map(
45
49
  max_depth
46
50
  ) {
47
51
 
52
+ assert.lessThan(result_offset + resolution * resolution, UINT32_MAX, 'Result overflow');
53
+
48
54
  scratch_ray.tMax = max_depth;
49
55
  array_copy(position, position_offset, scratch_ray, 0, 3);
50
56
 
57
+ /**
58
+ * In radians, how wide of an arc does a single texel cover
59
+ * @type {number}
60
+ */
61
+ const texel_angular_coverage = (Math.PI) / resolution;
51
62
 
52
63
  /**
53
64
  * Resolution bias is to compensate for discontinuities between samples
@@ -58,9 +69,9 @@ export function bake_octahedral_depth_map(
58
69
  * xMath.SQRT2 is to allow for diagonals between pixels
59
70
  * @type {number}
60
71
  */
61
- const RESOLUTION_BIAS = Math.sin(PI_HALF / resolution) * Math.SQRT2;
72
+ const RESOLUTION_BIAS = Math.sin(texel_angular_coverage) * Math.SQRT2 * 2;
62
73
 
63
- const NORMAL_BIAS = max_depth * 0.01 + 1e-6;
74
+ const NORMAL_BIAS = max2(0.01, max_depth * 0.01) + 1e-6;
64
75
 
65
76
  const ray_direction = scratch_ray.direction;
66
77
 
@@ -68,7 +79,7 @@ export function bake_octahedral_depth_map(
68
79
 
69
80
  const texel_uv_scale = 0.5 / (resolution);
70
81
 
71
- const jittered_sub_samples = generate_halton_jitter(SUB_SAMPLE_COUNT);
82
+ const jittered_sub_samples = generate_hammersley_jitter(SUB_SAMPLE_COUNT);
72
83
 
73
84
 
74
85
  for (let oct_x = 0; oct_x < resolution; oct_x++) {
@@ -78,23 +89,18 @@ export function bake_octahedral_depth_map(
78
89
  let weight_sum = 0;
79
90
 
80
91
  // offset position by half a pixel, as we are storing values for pixel centers
81
- const u = (oct_x ) / (resolution);
82
- const v = (oct_y ) / (resolution);
83
-
84
- // const u = (oct_x + 0.5) / (resolution - 1);
85
- // const v = (oct_y + 0.5) / (resolution - 1);
86
-
87
- decode_octahedron_to_unit(
88
- probe_direction, 0,
89
- u * 2 - 1, v * 2 - 1
90
- );
92
+ const u = (oct_x + 0.5) / (resolution);
93
+ const v = (oct_y + 0.5) / (resolution);
91
94
 
92
95
  for (let sub_sample_index = 0; sub_sample_index < SUB_SAMPLE_COUNT; sub_sample_index++) {
93
96
 
94
97
  const sample_index2 = sub_sample_index * 2;
95
98
 
96
- const ray_u = u + jittered_sub_samples[sample_index2] * texel_uv_scale;
97
- const ray_v = v + jittered_sub_samples[sample_index2 + 1] * texel_uv_scale;
99
+ const sample_u = jittered_sub_samples[sample_index2];
100
+ const sample_v = jittered_sub_samples[sample_index2 + 1];
101
+
102
+ const ray_u = u + (sample_u * 2 - 1) * texel_uv_scale;
103
+ const ray_v = v + (sample_v * 2 - 1) * texel_uv_scale;
98
104
 
99
105
  decode_octahedron_to_unit(
100
106
  ray_direction, 0,
@@ -115,27 +121,18 @@ export function bake_octahedral_depth_map(
115
121
  const surface_position_y = ray_hit[1];
116
122
  const surface_position_z = ray_hit[2];
117
123
 
118
- // check for possible miss
119
- const hit_angle_cos = -v3_dot(
120
- surface_normal_x, surface_normal_y, surface_normal_z,
121
- ray_direction[0], ray_direction[1], ray_direction[2]
122
- );
123
-
124
- const resolution_bias_offset = max_depth * RESOLUTION_BIAS;
125
-
126
- const hit_normal_bias = clamp01( 1-hit_angle_cos) * (resolution_bias_offset) + NORMAL_BIAS;
124
+ const hit_normal_bias = -NORMAL_BIAS;
127
125
 
128
126
  // sink the contact into the surface along the hit normal
129
- const biased_hit_x = surface_position_x - surface_normal_x * hit_normal_bias;
130
- const biased_hit_y = surface_position_y - surface_normal_y * hit_normal_bias;
131
- const biased_hit_z = surface_position_z - surface_normal_z * hit_normal_bias;
127
+ const biased_hit_x = surface_position_x + surface_normal_x * hit_normal_bias;
128
+ const biased_hit_y = surface_position_y + surface_normal_y * hit_normal_bias;
129
+ const biased_hit_z = surface_position_z + surface_normal_z * hit_normal_bias;
132
130
 
133
131
  distance = v3_distance(
134
132
  scratch_ray[0], scratch_ray[1], scratch_ray[2],
135
133
  biased_hit_x, biased_hit_y, biased_hit_z
136
134
  );
137
135
 
138
- // distance += resolution_bias_offset;
139
136
  }
140
137
 
141
138
  const direction_dot_product = v3_dot_array_array(ray_direction, 0, probe_direction, 0);
@@ -155,7 +152,9 @@ export function bake_octahedral_depth_map(
155
152
 
156
153
  const address = result_offset + pixel_index;
157
154
 
158
- result[address] = distance_sum / weight_sum;
155
+ const distance = distance_sum / weight_sum;
156
+
157
+ result[address] = distance;
159
158
  }
160
159
  }
161
160
 
@@ -40,6 +40,23 @@ export class BufferedGeometryBVH {
40
40
  * @returns {boolean}
41
41
  */
42
42
  occluded(ray: Ray3): boolean;
43
+ /**
44
+ *
45
+ * @param {SurfacePoint3} result
46
+ * @param {number} x
47
+ * @param {number} y
48
+ * @param {number} z
49
+ * @returns {boolean} true if result is found, only false when geometry is empty
50
+ */
51
+ nearestSurfacePoint(result: SurfacePoint3, x: number, y: number, z: number): boolean;
52
+ /**
53
+ * Code is largely inlined, to avoid extra checks
54
+ * NOTE: raycast is performed in local coordinate space
55
+ * @param {number[]} output
56
+ * @param {number[]|Ray3} ray
57
+ * @returns {number} distance along the ray, negative if no hit
58
+ */
59
+ raycast2(output: number[], ray: number[] | Ray3): number;
43
60
  /**
44
61
  * NOTE: raycast is performed in local coordinate space
45
62
  * @param {number[]} output
@@ -50,4 +67,5 @@ export class BufferedGeometryBVH {
50
67
  #private;
51
68
  }
52
69
  import { AABB3 } from "../../../../core/geom/3d/aabb/AABB3.js";
70
+ import { SurfacePoint3 } from "../../../../core/geom/3d/SurfacePoint3.js";
53
71
  //# sourceMappingURL=BufferedGeometryBVH.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BufferedGeometryBVH.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js"],"names":[],"mappings":"AAyBA;IAkBI;;;OAGG;IACH,eAFW,KAAK,GAAC,MAAM,EAAE,QAIxB;IAIG;;;;OAIG;IACH,mBAAsB;IAEtB;;;;OAIG;IACH,yBAA4B;IAE5B;;;;OAIG;IACH,6BAAgC;IAGhC;;;;OAIG;IACH,yBAAyB;IAG7B;;;OAGG;IACH,WAFW,MAAM,cAAc,QAwC9B;IAED;;;;;OAKG;IACH,qBAFa,OAAO,CAkDnB;IAED;;;;;OAKG;IACH,gBAJW,MAAM,EAAE,OACR,MAAM,EAAE,OAAK,GACX,MAAM,CA2ElB;;CACJ;sBAnQqB,wCAAwC"}
1
+ {"version":3,"file":"BufferedGeometryBVH.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js"],"names":[],"mappings":"AA+CA;IAkBI;;;OAGG;IACH,eAFW,KAAK,GAAC,MAAM,EAAE,QAIxB;IAIG;;;;OAIG;IACH,mBAAsB;IAEtB;;;;OAIG;IACH,yBAA4B;IAE5B;;;;OAIG;IACH,6BAAgC;IAGhC;;;;OAIG;IACH,yBAAyB;IAG7B;;;OAGG;IACH,WAFW,MAAM,cAAc,QAwC9B;IAED;;;;;OAKG;IACH,qBAFa,OAAO,CAmHnB;IAED;;;;;;;OAOG;IACH,4BANW,aAAa,KACb,MAAM,KACN,MAAM,KACN,MAAM,GACJ,OAAO,CAqKnB;IAGD;;;;;;OAMG;IACH,iBAJW,MAAM,EAAE,OACR,MAAM,EAAE,OAAK,GACX,MAAM,CAoIlB;IAED;;;;;OAKG;IACH,gBAJW,MAAM,EAAE,OACR,MAAM,EAAE,OAAK,GACX,MAAM,CA2ElB;;CACJ;sBA3oBqB,wCAAwC;8BAKhC,2CAA2C"}
@@ -1,12 +1,30 @@
1
- import { BVH } from "../../../../core/bvh2/bvh3/BVH.js";
1
+ import {
2
+ BVH,
3
+ COLUMN_CHILD_1,
4
+ COLUMN_CHILD_2,
5
+ COLUMN_USER_DATA,
6
+ ELEMENT_WORD_COUNT,
7
+ NULL_NODE
8
+ } from "../../../../core/bvh2/bvh3/BVH.js";
2
9
  import { ebvh_build_for_geometry_morton } from "../../../../core/bvh2/bvh3/ebvh_build_for_geometry_morton.js";
3
10
  import { bvh_query_user_data_ray_segment } from "../../../../core/bvh2/bvh3/query/bvh_query_user_data_ray_segment.js";
4
11
  import { array_copy } from "../../../../core/collection/array/array_copy.js";
12
+ import { SCRATCH_UINT32_TRAVERSAL_STACK } from "../../../../core/collection/SCRATCH_UINT32_TRAVERSAL_STACK.js";
5
13
  import { AABB3 } from "../../../../core/geom/3d/aabb/AABB3.js";
14
+ import { aabb3_intersects_ray_segment } from "../../../../core/geom/3d/aabb/aabb3_intersects_ray_segment.js";
15
+ import {
16
+ aabb3_unsigned_distance_sqr_to_point
17
+ } from "../../../../core/geom/3d/aabb/aabb3_unsigned_distance_sqr_to_point.js";
18
+ import { SurfacePoint3 } from "../../../../core/geom/3d/SurfacePoint3.js";
19
+ import {
20
+ computeTriangleClosestPointToPointBarycentric
21
+ } from "../../../../core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.js";
6
22
 
7
23
  import {
8
24
  computeTriangleRayIntersectionBarycentricGeometry
9
25
  } from "../../../../core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricGeometry.js";
26
+ import { v3_compute_triangle_normal } from "../../../../core/geom/3d/v3_compute_triangle_normal.js";
27
+ import { v3_distance_sqr } from "../../../../core/geom/vec3/v3_distance_sqr.js";
10
28
  import Vector4 from "../../../../core/geom/Vector4.js";
11
29
  import { makeGeometryIndexed } from "../../geometry/buffered/makeGeometryIndexed.js";
12
30
  import { computeBoundingSphereFromVertexData } from "../../geometry/computeBoundingSphereFromVertexData.js";
@@ -23,6 +41,10 @@ const scratch_uint32_array = new Uint32Array(4096);
23
41
  */
24
42
  const v3_scratch_0 = [];
25
43
 
44
+ const scratch_contact = new SurfacePoint3();
45
+
46
+ const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
47
+
26
48
  export class BufferedGeometryBVH {
27
49
  /**
28
50
  * @type {Uint32Array}
@@ -147,40 +169,418 @@ export class BufferedGeometryBVH {
147
169
 
148
170
  const bvh = this.#bvh;
149
171
 
150
- const count = bvh_query_user_data_ray_segment(
151
- bvh, bvh.root,
152
- scratch_uint32_array, 0,
153
- origin_x, origin_y, origin_z,
154
- direction_x, direction_y, direction_z,
155
- 0, max_distance
156
- );
172
+ const root = bvh.root;
157
173
 
158
- // check triangles found via BVH
159
- for (let i = 0; i < count; i++) {
160
- const triangle_index = scratch_uint32_array[i];
174
+ if (root === NULL_NODE) {
175
+ return -1;
176
+ }
161
177
 
162
- const intersection_found = computeTriangleRayIntersectionBarycentricGeometry(
163
- v3_scratch_0,
178
+ /**
179
+ * Move stack pointer to local variable scope to avoid de-referencing inside the loop
180
+ * @type {number}
181
+ */
182
+ let pointer = stack.pointer;
183
+
184
+ /**
185
+ *
186
+ * @type {number}
187
+ */
188
+ const stack_top = pointer;
189
+
190
+ stack[pointer++] = root;
191
+
192
+
193
+ /*
194
+ For performance, we bind data directly to avoid extra copies required to read out AABB
195
+ */
196
+ const float32 = bvh.__data_float32;
197
+ const uint32 = bvh.__data_uint32;
198
+
199
+ const inv_direction_x = 1 / direction_x;
200
+ const inv_direction_y = 1 / direction_y;
201
+ const inv_direction_z = 1 / direction_z;
202
+
203
+ do {
204
+
205
+ --pointer;
206
+
207
+ /**
208
+ *
209
+ * @type {number}
210
+ */
211
+ const node = stack[pointer];
212
+
213
+ const address = node * ELEMENT_WORD_COUNT;
214
+
215
+ // test node against the ray
216
+ const intersects = aabb3_intersects_ray_segment(
217
+ float32[address], float32[address + 1], float32[address + 2],
218
+ float32[address + 3], float32[address + 4], float32[address + 5],
164
219
  origin_x, origin_y, origin_z,
165
- direction_x, direction_y, direction_z,
166
- indices, triangle_index,
167
- positions
220
+ inv_direction_x, inv_direction_y, inv_direction_z,
221
+ 0, max_distance
168
222
  );
169
223
 
170
- if (!intersection_found) {
224
+ if (!intersects) {
171
225
  continue;
172
226
  }
173
227
 
174
- const t = v3_scratch_0[0];
228
+ // get fist child to check if this is a leaf node or not
229
+ const child_1 = uint32[address + COLUMN_CHILD_1];
230
+
231
+ if (child_1 !== NULL_NODE) {
232
+
233
+ // this is not a leaf node, push children onto traversal stack
234
+ const child_2 = uint32[address + COLUMN_CHILD_2];
235
+
236
+ stack[pointer++] = child_2;
237
+ stack[pointer++] = child_1;
238
+
239
+
240
+ } else {
241
+ // leaf node
242
+
243
+ const triangle_index = uint32[address + COLUMN_USER_DATA];
244
+
245
+ const intersection_found = computeTriangleRayIntersectionBarycentricGeometry(
246
+ v3_scratch_0,
247
+ origin_x, origin_y, origin_z,
248
+ direction_x, direction_y, direction_z,
249
+ indices, triangle_index,
250
+ positions
251
+ );
252
+
253
+ if (!intersection_found) {
254
+ continue;
255
+ }
256
+
257
+ const t = v3_scratch_0[0];
258
+
259
+ if (t < max_distance && t > 0) {
260
+ return true;
261
+
262
+ }
175
263
 
176
- if (t < max_distance && t > 0) {
177
- return true;
178
264
  }
179
- }
265
+ } while (pointer > stack_top);
266
+
180
267
 
181
268
  return false;
182
269
  }
183
270
 
271
+ /**
272
+ *
273
+ * @param {SurfacePoint3} result
274
+ * @param {number} x
275
+ * @param {number} y
276
+ * @param {number} z
277
+ * @returns {boolean} true if result is found, only false when geometry is empty
278
+ */
279
+ nearestSurfacePoint(result, x, y, z) {
280
+
281
+ const indices = this.__geometry_index;
282
+ const positions = this.__geometry_positions;
283
+
284
+ const bvh = this.#bvh;
285
+
286
+ const root = bvh.root;
287
+
288
+ if (root === NULL_NODE) {
289
+ return false;
290
+ }
291
+
292
+ /**
293
+ * Move stack pointer to local variable scope to avoid de-referencing inside the loop
294
+ * @type {number}
295
+ */
296
+ let pointer = stack.pointer;
297
+
298
+ /**
299
+ *
300
+ * @type {number}
301
+ */
302
+ const stack_top = pointer;
303
+
304
+ stack[pointer++] = root;
305
+
306
+ /*
307
+ For performance, we bind data directly to avoid extra copies required to read out AABB
308
+ */
309
+ const float32 = bvh.__data_float32;
310
+ const uint32 = bvh.__data_uint32;
311
+
312
+ let nearest_distance_sqr = Infinity;
313
+
314
+
315
+ do {
316
+ --pointer;
317
+
318
+ /**
319
+ *
320
+ * @type {number}
321
+ */
322
+ const node = stack[pointer];
323
+
324
+ const address = node * ELEMENT_WORD_COUNT;
325
+
326
+ // test node against the ray
327
+
328
+
329
+ // get fist child to check if this is a leaf node or not
330
+ const child_1 = uint32[address + COLUMN_CHILD_1];
331
+
332
+ if (child_1 !== NULL_NODE) {
333
+
334
+ // this is not a leaf node, push children onto traversal stack
335
+ const child_2 = uint32[address + COLUMN_CHILD_2];
336
+
337
+ // to achieve faster convergence, we sort children before adding them to the stack by their proximity to the reference point
338
+ const child_1_address = child_1 * ELEMENT_WORD_COUNT;
339
+
340
+ const distance_sqr_to_child1 = aabb3_unsigned_distance_sqr_to_point(
341
+ float32[child_1_address], float32[child_1_address + 1], float32[child_1_address + 2],
342
+ float32[child_1_address + 3], float32[child_1_address + 4], float32[child_1_address + 5],
343
+ x, y, z
344
+ );
345
+
346
+ const child_2_address = child_2 * ELEMENT_WORD_COUNT;
347
+
348
+ const distance_sqr_to_child2 = aabb3_unsigned_distance_sqr_to_point(
349
+ float32[child_2_address], float32[child_2_address + 1], float32[child_2_address + 2],
350
+ float32[child_2_address + 3], float32[child_2_address + 4], float32[child_2_address + 5],
351
+ x, y, z
352
+ );
353
+
354
+ if (distance_sqr_to_child1 < distance_sqr_to_child2) {
355
+
356
+ if (distance_sqr_to_child2 < nearest_distance_sqr) {
357
+ stack[pointer++] = child_2;
358
+ }
359
+
360
+ if (distance_sqr_to_child1 < nearest_distance_sqr) {
361
+ stack[pointer++] = child_1;
362
+ }
363
+
364
+ } else {
365
+
366
+ if (distance_sqr_to_child1 < nearest_distance_sqr) {
367
+ stack[pointer++] = child_1;
368
+ }
369
+
370
+ if (distance_sqr_to_child2 < nearest_distance_sqr) {
371
+ stack[pointer++] = child_2;
372
+ }
373
+
374
+ }
375
+
376
+
377
+ } else {
378
+ // leaf node
379
+ // read triangle data
380
+ const triangle_index = uint32[address + COLUMN_USER_DATA];
381
+
382
+ const a = indices[triangle_index];
383
+ const b = indices[triangle_index + 1];
384
+ const c = indices[triangle_index + 2];
385
+
386
+
387
+ const a_address = a * 3;
388
+ const b_address = b * 3;
389
+ const c_address = c * 3;
390
+
391
+ const ax = positions[a_address];
392
+ const ay = positions[a_address + 1];
393
+ const az = positions[a_address + 2];
394
+
395
+ const bx = positions[b_address];
396
+ const by = positions[b_address + 1];
397
+ const bz = positions[b_address + 2];
398
+
399
+ const cx = positions[c_address];
400
+ const cy = positions[c_address + 1];
401
+ const cz = positions[c_address + 2];
402
+
403
+ computeTriangleClosestPointToPointBarycentric(
404
+ v3_scratch_0, 0,
405
+ x, y, z,
406
+ ax, ay, az,
407
+ bx, by, bz,
408
+ cx, cy, cz
409
+ );
410
+
411
+ const u = v3_scratch_0[0];
412
+ const v = v3_scratch_0[1];
413
+
414
+ // construct edge
415
+
416
+ const contact_x = (bx - ax) * u + (cx - ax) * v + ax;
417
+ const contact_y = (by - ay) * u + (cy - ay) * v + ay;
418
+ const contact_z = (bz - az) * u + (cz - az) * v + az;
419
+
420
+ const distance_to_triangle_sqr = v3_distance_sqr(contact_x, contact_y, contact_z, x, y, z);
421
+
422
+ if (distance_to_triangle_sqr >= nearest_distance_sqr) {
423
+ continue;
424
+ }
425
+
426
+ nearest_distance_sqr = distance_to_triangle_sqr;
427
+
428
+ result.position.set(contact_x, contact_y, contact_z);
429
+
430
+ v3_compute_triangle_normal(result.normal, 0,
431
+ ax, ay, az,
432
+ bx, by, bz,
433
+ cx, cy, cz
434
+ );
435
+
436
+ result.index = triangle_index;
437
+ }
438
+ } while (pointer > stack_top);
439
+
440
+
441
+ return true;
442
+ }
443
+
444
+
445
+ /**
446
+ * Code is largely inlined, to avoid extra checks
447
+ * NOTE: raycast is performed in local coordinate space
448
+ * @param {number[]} output
449
+ * @param {number[]|Ray3} ray
450
+ * @returns {number} distance along the ray, negative if no hit
451
+ */
452
+ raycast2(output, ray) {
453
+
454
+ const indices = this.__geometry_index;
455
+ const positions = this.__geometry_positions;
456
+
457
+ const origin_x = ray[0];
458
+ const origin_y = ray[1];
459
+ const origin_z = ray[2];
460
+
461
+ const direction_x = ray[3];
462
+ const direction_y = ray[4];
463
+ const direction_z = ray[5];
464
+
465
+ const max_distance = ray[6];
466
+
467
+ const bvh = this.#bvh;
468
+
469
+ const root = bvh.root;
470
+
471
+ if (root === NULL_NODE) {
472
+ return -1;
473
+ }
474
+
475
+ /**
476
+ * Move stack pointer to local variable scope to avoid de-referencing inside the loop
477
+ * @type {number}
478
+ */
479
+ let pointer = stack.pointer;
480
+
481
+ let nearest_hit_distance = max_distance;
482
+
483
+ let best_index = -1;
484
+ let best_u = 0;
485
+ let best_v = 0;
486
+
487
+ /**
488
+ *
489
+ * @type {number}
490
+ */
491
+ const stack_top = pointer;
492
+
493
+ stack[pointer++] = root;
494
+
495
+
496
+ /*
497
+ For performance, we bind data directly to avoid extra copies required to read out AABB
498
+ */
499
+ const float32 = bvh.__data_float32;
500
+ const uint32 = bvh.__data_uint32;
501
+
502
+
503
+ const inv_direction_x = 1 / direction_x;
504
+ const inv_direction_y = 1 / direction_y;
505
+ const inv_direction_z = 1 / direction_z;
506
+
507
+ do {
508
+
509
+ --pointer;
510
+
511
+ /**
512
+ *
513
+ * @type {number}
514
+ */
515
+ const node = stack[pointer];
516
+
517
+ const address = node * ELEMENT_WORD_COUNT;
518
+
519
+ // test node against the ray
520
+ const intersects = aabb3_intersects_ray_segment(
521
+ float32[address], float32[address + 1], float32[address + 2],
522
+ float32[address + 3], float32[address + 4], float32[address + 5],
523
+ origin_x, origin_y, origin_z,
524
+ inv_direction_x, inv_direction_y, inv_direction_z,
525
+ 0, nearest_hit_distance
526
+ );
527
+
528
+ if (!intersects) {
529
+ continue;
530
+ }
531
+
532
+ // get fist child to check if this is a leaf node or not
533
+ const child_1 = uint32[address + COLUMN_CHILD_1];
534
+
535
+ if (child_1 !== NULL_NODE) {
536
+
537
+ // this is not a leaf node, push children onto traversal stack
538
+ const child_2 = uint32[address + COLUMN_CHILD_2];
539
+
540
+ stack[pointer++] = child_2;
541
+ stack[pointer++] = child_1;
542
+
543
+ } else {
544
+ // leaf node
545
+
546
+ const triangle_index = uint32[address + COLUMN_USER_DATA];
547
+
548
+ const intersection_found = computeTriangleRayIntersectionBarycentricGeometry(
549
+ v3_scratch_0,
550
+ origin_x, origin_y, origin_z,
551
+ direction_x, direction_y, direction_z,
552
+ indices, triangle_index,
553
+ positions
554
+ );
555
+
556
+ if (!intersection_found) {
557
+ continue;
558
+ }
559
+
560
+ const t = v3_scratch_0[0];
561
+
562
+ if (t < nearest_hit_distance && t > 0) {
563
+ nearest_hit_distance = t;
564
+
565
+ best_index = triangle_index;
566
+ best_u = v3_scratch_0[1];
567
+ best_v = v3_scratch_0[2];
568
+
569
+ }
570
+
571
+ }
572
+ } while (pointer > stack_top);
573
+
574
+ if (nearest_hit_distance === max_distance) {
575
+ // no hit
576
+ return -1;
577
+ }
578
+
579
+ construct_ray_hit_from_geometry(output, indices, positions, best_index, nearest_hit_distance, best_u, best_v);
580
+
581
+ return nearest_hit_distance;
582
+ }
583
+
184
584
  /**
185
585
  * NOTE: raycast is performed in local coordinate space
186
586
  * @param {number[]} output
@@ -126,7 +126,7 @@ export class PathTracedMesh {
126
126
 
127
127
  local_ray.applyMatrix4(m4);
128
128
 
129
- let distance_to_hit = this.bvh.raycast(out, local_ray);
129
+ let distance_to_hit = this.bvh.raycast2(out, local_ray);
130
130
 
131
131
  if (distance_to_hit >= 0) {
132
132
  // transform output
@@ -67,8 +67,8 @@ vCanvas.css({
67
67
  * How many rays to use per-pixel
68
68
  * @type {number}
69
69
  */
70
- const PIXEL_SAMPLE_COUNT = 1;
71
- const PIXEL_RENDER_RATIO = 0.5;
70
+ const PIXEL_SAMPLE_COUNT = 16;
71
+ const PIXEL_RENDER_RATIO = 1;
72
72
 
73
73
  const scene = new PathTracedScene();
74
74
  const pt = new PathTracer();
@@ -534,8 +534,8 @@ async function start_renderer(camera) {
534
534
  // await prepare_scene_lucy(scene, camera);
535
535
  // await prepare_scene_rtiow(pt, camera);
536
536
  // await prepare_scene_sphere_01(pt, camera);
537
- // await prepare_gi_box_scene(scene, camera);
538
- await prepare_sponza(scene, camera);
537
+ await prepare_gi_box_scene(scene, camera);
538
+ // await prepare_sponza(scene, camera);
539
539
  // await prepare_attic_scene(scene, camera);
540
540
  // await prepare_scene_gltf({scene, camera, path, url: path});
541
541