@woosh/meep-engine 2.43.18 → 2.43.19

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/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +59 -116
  2. package/core/bvh2/bvh3/ebvh_build_for_geometry_incremental.js +34 -0
  3. package/core/bvh2/bvh3/ebvh_build_for_geometry_morton.js +175 -0
  4. package/core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js +122 -0
  5. package/core/bvh2/bvh3/{bvh_collect_user_data.js → query/bvh_collect_user_data.js} +1 -1
  6. package/core/bvh2/bvh3/{bvh_query_leaves_generic.js → query/bvh_query_leaves_generic.js} +1 -1
  7. package/core/bvh2/bvh3/{bvh_query_leaves_ray.js → query/bvh_query_leaves_ray.js} +2 -3
  8. package/core/bvh2/bvh3/{bvh_query_user_data_generic.js → query/bvh_query_user_data_generic.js} +1 -1
  9. package/core/bvh2/bvh3/{bvh_query_user_data_nearest_to_point.js → query/bvh_query_user_data_nearest_to_point.js} +3 -3
  10. package/core/bvh2/bvh3/{bvh_query_user_data_nearest_to_point.spec.js → query/bvh_query_user_data_nearest_to_point.spec.js} +1 -1
  11. package/core/bvh2/bvh3/{bvh_query_user_data_overlaps_frustum.js → query/bvh_query_user_data_overlaps_frustum.js} +2 -2
  12. package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js +1 -1
  13. package/core/collection/array/arrayQuickSort.js +1 -1
  14. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -2
  15. package/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +9 -1
  16. package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +1 -1
  17. package/engine/graphics/render/forward_plus/model/DirectionalLight.js +40 -0
  18. package/engine/graphics/sh3/path_tracer/GeometryBVHBatched.js +55 -114
  19. package/engine/graphics/sh3/path_tracer/PathTracer.js +83 -18
  20. package/engine/graphics/sh3/path_tracer/compute_triangle_group_aabb3.js +1 -1
  21. package/engine/graphics/sh3/path_tracer/make_one_vector3.js +7 -0
  22. package/engine/graphics/sh3/path_tracer/make_sky_rtiw.js +2 -0
  23. package/engine/graphics/sh3/path_tracer/make_zero_vector3.js +7 -0
  24. package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +18 -6
  25. package/package.json +1 -1
@@ -5,10 +5,10 @@ import { assert } from "../../assert.js";
5
5
  import { typed_array_copy } from "../../collection/array/typed/typed_array_copy.js";
6
6
  import { array_copy } from "../../collection/array/copyArray.js";
7
7
 
8
- const COLUMN_PARENT = 6;
8
+ export const COLUMN_PARENT = 6;
9
9
  export const COLUMN_CHILD_1 = 7;
10
10
  export const COLUMN_CHILD_2 = 8;
11
- const COLUMN_HEIGHT = 9;
11
+ export const COLUMN_HEIGHT = 9;
12
12
 
13
13
  /**
14
14
  * A non-leaf node have both CHILD_1 and CHILD_2 set, when CHILD_1 is not set - it's a leaf node
@@ -281,6 +281,15 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
281
281
  return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_CHILD_1];
282
282
  }
283
283
 
284
+ /**
285
+ *
286
+ * @param {number} node
287
+ * @param {number} child1
288
+ */
289
+ node_set_child1(node, child1) {
290
+ this.__data_uint32[ELEMENT_WORD_COUNT * node + COLUMN_CHILD_1] = child1;
291
+ }
292
+
284
293
  /**
285
294
  *
286
295
  * @param {number} id
@@ -291,6 +300,54 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
291
300
  return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_CHILD_2];
292
301
  }
293
302
 
303
+ /**
304
+ *
305
+ * @param {number} node
306
+ * @param {number} child2
307
+ */
308
+ node_set_child2(node, child2) {
309
+ this.__data_uint32[ELEMENT_WORD_COUNT * node + COLUMN_CHILD_2] = child2;
310
+ }
311
+
312
+ /**
313
+ *
314
+ * @param {number} id
315
+ * @returns {number}
316
+ */
317
+ node_get_parent(id) {
318
+ assert.isNonNegativeInteger(id, 'id');
319
+ return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_PARENT];
320
+ }
321
+
322
+ /**
323
+ *
324
+ * @param {number} node
325
+ * @param {number} parent
326
+ */
327
+ node_set_parent(node, parent) {
328
+ this.__data_uint32[ELEMENT_WORD_COUNT * node + COLUMN_PARENT] = parent;
329
+ }
330
+
331
+
332
+ /**
333
+ *
334
+ * @param {number} id
335
+ * @returns {number}
336
+ */
337
+ node_get_height(id) {
338
+ assert.isNonNegativeInteger(id, 'id');
339
+ return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_HEIGHT];
340
+ }
341
+
342
+ /**
343
+ *
344
+ * @param {number} id
345
+ * @param {number} height
346
+ */
347
+ node_set_height(id, height) {
348
+ assert.isNonNegativeInteger(id, 'id');
349
+ this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_HEIGHT] = height;
350
+ }
294
351
 
295
352
  /**
296
353
  *
@@ -1046,119 +1103,5 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
1046
1103
 
1047
1104
  return true;
1048
1105
  }
1049
-
1050
-
1051
- /**
1052
- * Sort live nodes in the traversal order.
1053
- * This makes most queries have linear access, resulting in near-optimal cache usage
1054
- * NOTE: assumes that there are no free nodes
1055
- */
1056
- sort_for_traversal_depth_first() {
1057
- const stack = [];
1058
-
1059
- stack[0] = 0;
1060
- stack[1] = this.__size - 1;
1061
-
1062
- let stack_pointer = 2;
1063
- let i, j;
1064
-
1065
- const uint32 = this.__data_uint32;
1066
- const float32 = this.__data_float32;
1067
-
1068
- while (stack_pointer > 0) {
1069
- stack_pointer -= 2;
1070
-
1071
- const right = stack[stack_pointer + 1];
1072
- const left = stack[stack_pointer];
1073
-
1074
- i = left;
1075
- j = right;
1076
-
1077
- const pivotIndex = (left + right) >> 1;
1078
-
1079
- /* partition */
1080
- while (i <= j) {
1081
-
1082
- while (traversal_sort_compare_nodes(uint32, float32, i, pivotIndex) > 0) {
1083
- i++;
1084
- }
1085
-
1086
- while (traversal_sort_compare_nodes(uint32, float32, j, pivotIndex) < 0) {
1087
- j--;
1088
- }
1089
-
1090
- if (i <= j) {
1091
-
1092
- if (i !== j) {
1093
- //do swap
1094
- this.swap_nodes(i, j);
1095
- }
1096
-
1097
- i++;
1098
- j--;
1099
- }
1100
- }
1101
-
1102
- /* recursion */
1103
- if (left < j) {
1104
- stack[stack_pointer++] = left;
1105
- stack[stack_pointer++] = j;
1106
- }
1107
- if (i < right) {
1108
- stack[stack_pointer++] = i;
1109
- stack[stack_pointer++] = right;
1110
- }
1111
- }
1112
- }
1113
1106
  }
1114
1107
 
1115
- /**
1116
- *
1117
- * @param {Uint32Array} uint32
1118
- * @param {number} node_index
1119
- * @return {number}
1120
- */
1121
- function compute_traversal_index_upper_bound(uint32, node_index) {
1122
- let n = node_index;
1123
-
1124
- let parent = uint32[node_index * ELEMENT_WORD_COUNT + COLUMN_PARENT];
1125
-
1126
- let result = 0;
1127
- let x = 0;
1128
-
1129
- while (parent !== NULL_NODE) {
1130
-
1131
- if (uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] === n) {
1132
- result += Math.pow(2, x);
1133
- }
1134
-
1135
- x++;
1136
-
1137
- n = parent;
1138
- parent = uint32[n * ELEMENT_WORD_COUNT + COLUMN_PARENT];
1139
- }
1140
-
1141
- return result;
1142
- }
1143
-
1144
- /**
1145
- *
1146
- * @param {Uint32Array} uint32
1147
- * @param {Float32Array} float32
1148
- * @param {number} a
1149
- * @param {number} b
1150
- * @returns {number}
1151
- */
1152
- function traversal_sort_compare_nodes(uint32, float32, a, b) {
1153
- const a_height = uint32[a * ELEMENT_WORD_COUNT + COLUMN_HEIGHT];
1154
- const b_height = uint32[b * ELEMENT_WORD_COUNT + COLUMN_HEIGHT];
1155
-
1156
- const height_delta = a_height - b_height;
1157
-
1158
- if (height_delta !== 0) {
1159
- return height_delta;
1160
- }
1161
-
1162
- return compute_traversal_index_upper_bound(uint32, a) - compute_traversal_index_upper_bound(uint32, b);
1163
-
1164
- }
@@ -0,0 +1,34 @@
1
+ import { min2 } from "../../math/min2.js";
2
+ import { compute_triangle_group_aabb3 } from "../../../engine/graphics/sh3/path_tracer/compute_triangle_group_aabb3.js";
3
+
4
+ /**
5
+ * Build the BVH by relying on BVH's own incremental insertion mechanism
6
+ * @param {ExplicitBinaryBoundingVolumeHierarchy} bvh
7
+ * @param {number[]|Float32Array} index_array
8
+ * @param {number[]|Float32Array} positions_array
9
+ * @param {number} [batch_size] can batch triangles in groups of up-to this many triangles per BVH leaf
10
+ */
11
+ export function ebvh_build_for_geometry_incremental(bvh, index_array, positions_array, batch_size = 1) {
12
+ bvh.release_all();
13
+ const triangle_count = index_array.length / 3;
14
+
15
+ const leaf_node_count = Math.ceil(triangle_count / batch_size);
16
+
17
+ const leaf_aabb = [];
18
+
19
+ for (let i = 0; i < leaf_node_count; i++) {
20
+
21
+ const index_start = i * batch_size;
22
+ const index_end = min2(triangle_count, index_start + batch_size);
23
+
24
+ const group_size = index_end - index_start;
25
+
26
+ compute_triangle_group_aabb3(leaf_aabb, 0, index_array, positions_array, index_start * 3, group_size, 3);
27
+
28
+ const node_index = bvh.allocate_node();
29
+ bvh.node_set_aabb(node_index, leaf_aabb);
30
+ bvh.node_set_user_data(node_index, i);
31
+
32
+ bvh.insert_leaf(node_index);
33
+ }
34
+ }
@@ -0,0 +1,175 @@
1
+ import { AABB3 } from "../aabb3/AABB3.js";
2
+ import { aabb3_from_v3_array } from "../aabb3/aabb3_from_v3_array.js";
3
+ import morton from "../../geom/3d/morton/Morton.js";
4
+ import { arrayQuickSort } from "../../collection/array/arrayQuickSort.js";
5
+ import { compute_triangle_group_aabb3 } from "../../../engine/graphics/sh3/path_tracer/compute_triangle_group_aabb3.js";
6
+ import { NULL_NODE } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
7
+ import { max2 } from "../../math/max2.js";
8
+
9
+ /**
10
+ * Build the BVH bottom-up using spatial hash sorting
11
+ * Resulting BVH is produced very quickly, has perfect balance and is of decent quality
12
+ * @param {ExplicitBinaryBoundingVolumeHierarchy} bvh
13
+ * @param {number[]|Float32Array} index_array
14
+ * @param {number[]|Float32Array} position_array
15
+ */
16
+ export function ebvh_build_for_geometry_morton(bvh, index_array, position_array) {
17
+ // clear out existing BVH
18
+ bvh.release_all();
19
+
20
+ const aabb3 = new AABB3();
21
+
22
+ // get bounds for the entire geometry
23
+ aabb3_from_v3_array(aabb3, position_array, position_array.length);
24
+
25
+ const aabb_x0 = aabb3.x0;
26
+ const aabb_y0 = aabb3.y0;
27
+ const aabb_z0 = aabb3.z0;
28
+
29
+ const aabb_size_x = aabb3.getExtentsX();
30
+ const aabb_size_y = aabb3.getExtentsY();
31
+ const aabb_size_z = aabb3.getExtentsZ();
32
+
33
+ const morton_scale_x = aabb_size_x === 0 ? 0 : 1023 / aabb_size_x;
34
+ const morton_scale_y = aabb_size_y === 0 ? 0 : 1023 / aabb_size_y;
35
+ const morton_scale_z = aabb_size_z === 0 ? 0 : 1023 / aabb_size_z;
36
+
37
+ // allocate nodes
38
+ const tri_count = index_array.length / 3;
39
+ const node_leaf_count = tri_count;
40
+ const node_bin_count = Math.ceil(node_leaf_count / 2) * 2 - 1;
41
+
42
+ const node_total_count = node_leaf_count + node_bin_count;
43
+
44
+ const nodes = new Uint32Array(node_total_count);
45
+
46
+ // skip allocation calls, allocate exactly as many nodes as we need
47
+ bvh.__set_capacity(node_total_count);
48
+ bvh.__size = node_total_count;
49
+
50
+ for (let i = 0; i < node_total_count; i++) {
51
+ // store nodes in reverse order so that top-level nodes end up on top
52
+ nodes[i] = (node_total_count - 1) - i;
53
+ }
54
+
55
+ // for (let i = node_total_count - 1; i >= 0; i--) {
56
+ // // store nodes in reverse order so that top-level nodes end up on top
57
+ // nodes[i] = bvh.allocate_node();
58
+ // }
59
+
60
+ const morton_codes = new Uint32Array(tri_count);
61
+ const sorted_triangle_order = new Uint32Array(tri_count);
62
+
63
+ // compute morton codes
64
+ for (let i = 0; i < tri_count; i++) {
65
+
66
+ sorted_triangle_order[i] = i;
67
+
68
+ const i3 = i * 3;
69
+
70
+ const a = index_array[i3];
71
+ const b = index_array[i3 + 1];
72
+ const c = index_array[i3 + 2];
73
+
74
+ const a_address = a * 3;
75
+ const ax = position_array[a_address];
76
+ const ay = position_array[a_address + 1];
77
+ const az = position_array[a_address + 2];
78
+
79
+ const b_address = b * 3;
80
+ const bx = position_array[b_address];
81
+ const by = position_array[b_address + 1];
82
+ const bz = position_array[b_address + 2];
83
+
84
+ const c_address = c * 3;
85
+ const cx = position_array[c_address];
86
+ const cy = position_array[c_address + 1];
87
+ const cz = position_array[c_address + 2];
88
+
89
+ const center_x = (ax + bx + cx) * 0.333333333;
90
+ const center_y = (ay + by + cy) * 0.333333333;
91
+ const center_z = (az + bz + cz) * 0.333333333;
92
+
93
+
94
+ // normalize to bounds
95
+ const ncx = (center_x - aabb_x0) * morton_scale_x;
96
+ const ncy = (center_y - aabb_y0) * morton_scale_y;
97
+ const ncz = (center_z - aabb_z0) * morton_scale_z;
98
+
99
+ const morton_code = morton(
100
+ Math.round(ncx),
101
+ Math.round(ncy),
102
+ Math.round(ncz)
103
+ );
104
+
105
+ morton_codes[i] = morton_code;
106
+ }
107
+
108
+ // sort leaves by morton codes
109
+ arrayQuickSort(sorted_triangle_order, (triangle_index) => morton_codes[triangle_index], null, 0, tri_count - 1);
110
+
111
+ let used_index = 0;
112
+ const unprocessed_nodes = new Uint32Array(tri_count);
113
+ // assign leaves
114
+ const aabb_array = new Float32Array(6);
115
+ for (let i = 0; i < tri_count; i++) {
116
+ const node = nodes[used_index++];
117
+
118
+ const triangle_index = sorted_triangle_order[i];
119
+
120
+ bvh.node_set_child1(node, NULL_NODE);
121
+ bvh.node_set_user_data(node, triangle_index);
122
+
123
+ bvh.node_set_height(node, 0);
124
+
125
+ compute_triangle_group_aabb3(aabb_array, 0, index_array, position_array, triangle_index * 3, 1, 3);
126
+
127
+ bvh.node_set_aabb(node, aabb_array);
128
+
129
+ unprocessed_nodes[i] = node;
130
+ }
131
+
132
+ // Assemble hierarchy
133
+ let unprocessed_node_count = tri_count;
134
+
135
+
136
+ while (used_index < node_total_count) {
137
+
138
+ let added_nodes = 0;
139
+ let cursor = 0;
140
+
141
+ while (cursor + 1 < unprocessed_node_count) {
142
+ const child_1 = unprocessed_nodes[cursor++];
143
+ const child_2 = unprocessed_nodes[cursor++];
144
+
145
+ const parent = nodes[used_index++];
146
+
147
+ bvh.node_set_combined_aabb(parent, child_1, child_2);
148
+
149
+ bvh.node_set_parent(child_1, parent);
150
+ bvh.node_set_parent(child_2, parent);
151
+
152
+ bvh.node_set_child1(parent, child_1);
153
+ bvh.node_set_child2(parent, child_2);
154
+
155
+ bvh.node_set_height(parent,
156
+ 1 + max2(
157
+ bvh.node_get_height(child_1),
158
+ bvh.node_get_height(child_2)
159
+ )
160
+ );
161
+
162
+ unprocessed_nodes[added_nodes++] = parent;
163
+ }
164
+
165
+ while (cursor < unprocessed_node_count) {
166
+ // dangling nodes, push them onto the next level
167
+ unprocessed_nodes[added_nodes++] = unprocessed_nodes[cursor++];
168
+ }
169
+
170
+ unprocessed_node_count = added_nodes;
171
+ }
172
+
173
+ // assign root
174
+ bvh.__root = nodes[nodes.length - 1];
175
+ }
@@ -0,0 +1,122 @@
1
+ import {
2
+ COLUMN_CHILD_1,
3
+ COLUMN_HEIGHT,
4
+ COLUMN_PARENT,
5
+ ELEMENT_WORD_COUNT,
6
+ NULL_NODE
7
+ } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
8
+
9
+ /**
10
+ * Sort live nodes in the traversal order.
11
+ * This makes most queries have linear access, resulting in near-optimal cache usage
12
+ * NOTE: assumes that there are no free nodes
13
+ * @param {ExplicitBinaryBoundingVolumeHierarchy} bvh
14
+ */
15
+ export function ebvh_sort_for_traversal_depth_first(bvh) {
16
+ const stack = [];
17
+
18
+ stack[0] = 0;
19
+ stack[1] = bvh.__size - 1;
20
+
21
+ let stack_pointer = 2;
22
+ let i, j;
23
+
24
+ const uint32 = bvh.__data_uint32;
25
+ const float32 = bvh.__data_float32;
26
+
27
+ while (stack_pointer > 0) {
28
+ stack_pointer -= 2;
29
+
30
+ const right = stack[stack_pointer + 1];
31
+ const left = stack[stack_pointer];
32
+
33
+ i = left;
34
+ j = right;
35
+
36
+ const pivotIndex = (left + right) >> 1;
37
+
38
+ /* partition */
39
+ while (i <= j) {
40
+
41
+ while (traversal_sort_compare_nodes(uint32, float32, i, pivotIndex) > 0) {
42
+ i++;
43
+ }
44
+
45
+ while (traversal_sort_compare_nodes(uint32, float32, j, pivotIndex) < 0) {
46
+ j--;
47
+ }
48
+
49
+ if (i <= j) {
50
+
51
+ if (i !== j) {
52
+ //do swap
53
+ bvh.swap_nodes(i, j);
54
+ }
55
+
56
+ i++;
57
+ j--;
58
+ }
59
+ }
60
+
61
+ /* recursion */
62
+ if (left < j) {
63
+ stack[stack_pointer++] = left;
64
+ stack[stack_pointer++] = j;
65
+ }
66
+ if (i < right) {
67
+ stack[stack_pointer++] = i;
68
+ stack[stack_pointer++] = right;
69
+ }
70
+ }
71
+ }
72
+
73
+ /**
74
+ *
75
+ * @param {Uint32Array} uint32
76
+ * @param {number} node_index
77
+ * @return {number}
78
+ */
79
+ function compute_traversal_index_upper_bound(uint32, node_index) {
80
+ let n = node_index;
81
+
82
+ let parent = uint32[node_index * ELEMENT_WORD_COUNT + COLUMN_PARENT];
83
+
84
+ let result = 0;
85
+ let x = 0;
86
+
87
+ while (parent !== NULL_NODE) {
88
+
89
+ if (uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] === n) {
90
+ result += Math.pow(2, x);
91
+ }
92
+
93
+ x++;
94
+
95
+ n = parent;
96
+ parent = uint32[n * ELEMENT_WORD_COUNT + COLUMN_PARENT];
97
+ }
98
+
99
+ return result;
100
+ }
101
+
102
+ /**
103
+ *
104
+ * @param {Uint32Array} uint32
105
+ * @param {Float32Array} float32
106
+ * @param {number} a
107
+ * @param {number} b
108
+ * @returns {number}
109
+ */
110
+ function traversal_sort_compare_nodes(uint32, float32, a, b) {
111
+ const a_height = uint32[a * ELEMENT_WORD_COUNT + COLUMN_HEIGHT];
112
+ const b_height = uint32[b * ELEMENT_WORD_COUNT + COLUMN_HEIGHT];
113
+
114
+ const height_delta = a_height - b_height;
115
+
116
+ if (height_delta !== 0) {
117
+ return height_delta;
118
+ }
119
+
120
+ return compute_traversal_index_upper_bound(uint32, a) - compute_traversal_index_upper_bound(uint32, b);
121
+
122
+ }
@@ -1,6 +1,6 @@
1
1
  //
2
2
 
3
- import { NULL_NODE } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
3
+ import { NULL_NODE } from "../ExplicitBinaryBoundingVolumeHierarchy.d.ts";
4
4
 
5
5
  const traversal_stack = [];
6
6
  /**
@@ -1,4 +1,4 @@
1
- import { NULL_NODE } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
1
+ import { NULL_NODE } from "../ExplicitBinaryBoundingVolumeHierarchy.js";
2
2
 
3
3
  /**
4
4
  *
@@ -1,10 +1,10 @@
1
- import { aabb3_intersects_ray } from "../aabb3/aabb3_intersects_ray.js";
1
+ import { aabb3_intersects_ray } from "../../aabb3/aabb3_intersects_ray.js";
2
2
  import {
3
3
  COLUMN_CHILD_1,
4
4
  COLUMN_CHILD_2,
5
5
  ELEMENT_WORD_COUNT,
6
6
  NULL_NODE
7
- } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
7
+ } from "../ExplicitBinaryBoundingVolumeHierarchy.js";
8
8
 
9
9
  /**
10
10
  *
@@ -93,6 +93,5 @@ export function bvh_query_leaves_ray(
93
93
  }
94
94
  } while (traversal_cursor > 0);
95
95
 
96
- // drop stack frame
97
96
  return result_cursor - result_offset;
98
97
  }
@@ -1,4 +1,4 @@
1
- import { NULL_NODE } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
1
+ import { NULL_NODE } from "../ExplicitBinaryBoundingVolumeHierarchy.js";
2
2
 
3
3
  /**
4
4
  *
@@ -1,6 +1,6 @@
1
- import { NULL_NODE } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
2
- import { aabb3_signed_distance_sqr_to_point } from "../aabb3/aabb3_signed_distance_sqr_to_point.js";
3
- import { max2 } from "../../math/max2.js";
1
+ import { NULL_NODE } from "../ExplicitBinaryBoundingVolumeHierarchy.js";
2
+ import { aabb3_signed_distance_sqr_to_point } from "../../aabb3/aabb3_signed_distance_sqr_to_point.js";
3
+ import { max2 } from "../../../math/max2.js";
4
4
 
5
5
  /**
6
6
  *
@@ -1,4 +1,4 @@
1
- import { ExplicitBinaryBoundingVolumeHierarchy } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
1
+ import { ExplicitBinaryBoundingVolumeHierarchy } from "../ExplicitBinaryBoundingVolumeHierarchy.js";
2
2
  import { bvh_query_user_data_nearest_to_point } from "./bvh_query_user_data_nearest_to_point.js";
3
3
 
4
4
  /**
@@ -1,6 +1,6 @@
1
1
  //
2
- import { NULL_NODE } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
3
- import { aabb3_array_intersects_frustum_degree } from "../aabb3/aabb3_array_intersects_frustum_degree.js";
2
+ import { NULL_NODE } from "../ExplicitBinaryBoundingVolumeHierarchy.js";
3
+ import { aabb3_array_intersects_frustum_degree } from "../../aabb3/aabb3_array_intersects_frustum_degree.js";
4
4
  import { bvh_collect_user_data } from "./bvh_collect_user_data.js";
5
5
 
6
6
  const traversal_stack = [];
@@ -4,7 +4,7 @@
4
4
  import { array_copy } from "../../../collection/array/copyArray.js";
5
5
  import { NULL_NODE } from "../ExplicitBinaryBoundingVolumeHierarchy.js";
6
6
  import { aabb3_array_intersects_frustum_degree } from "../../aabb3/aabb3_array_intersects_frustum_degree.js";
7
- import { bvh_collect_user_data } from "../bvh_collect_user_data.js";
7
+ import { bvh_collect_user_data } from "./bvh_collect_user_data.js";
8
8
  import { v3_distance_above_plane } from "../../../geom/v3_distance_above_plane.js";
9
9
 
10
10
  const traversal_stack = [];
@@ -4,7 +4,7 @@ const stack = [];
4
4
 
5
5
  /**
6
6
  * @template T
7
- * @param {T[]} data
7
+ * @param {T[]|ArrayLike<number>|Uint32Array} data
8
8
  * @param {function(T):number} score_function
9
9
  * @param {*} score_function_context
10
10
  * @param {number} start
@@ -11,7 +11,7 @@ import { ray3_array_compose } from "../../../../core/geom/3d/ray/ray3_array_comp
11
11
  import {
12
12
  ExplicitBinaryBoundingVolumeHierarchy
13
13
  } from "../../../../core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js";
14
- import { bvh_query_leaves_generic } from "../../../../core/bvh2/bvh3/bvh_query_leaves_generic.js";
14
+ import { bvh_query_leaves_generic } from "../../../../core/bvh2/bvh3/query/bvh_query_leaves_generic.js";
15
15
  import { BVHQueryIntersectsRay } from "../../../../core/bvh2/bvh3/query/BVHQueryIntersectsRay.js";
16
16
  import Task from "../../../../core/process/task/Task.js";
17
17
  import { RuntimeDrawMethodOptimizer } from "./render/optimization/RuntimeDrawMethodOptimizer.js";
@@ -19,7 +19,7 @@ import { TaskSignal } from "../../../../core/process/task/TaskSignal.js";
19
19
  import TaskState from "../../../../core/process/task/TaskState.js";
20
20
  import {
21
21
  bvh_query_user_data_overlaps_frustum
22
- } from "../../../../core/bvh2/bvh3/bvh_query_user_data_overlaps_frustum.js";
22
+ } from "../../../../core/bvh2/bvh3/query/bvh_query_user_data_overlaps_frustum.js";
23
23
 
24
24
 
25
25
  /**
@@ -7,6 +7,14 @@ export const SGMeshFlags = {
7
7
  Loaded: 4
8
8
  };
9
9
 
10
+ /**
11
+ * @readonly
12
+ * @type {number}
13
+ */
14
+ const DEFAULT_FLAGS = SGMeshFlags.CastShadow
15
+ | SGMeshFlags.ReceiveShadow
16
+ ;
17
+
10
18
  export class SGMesh {
11
19
  constructor() {
12
20
  /**
@@ -33,7 +41,7 @@ export class SGMesh {
33
41
  *
34
42
  * @type {number}
35
43
  */
36
- this.flags = 0;
44
+ this.flags = DEFAULT_FLAGS;
37
45
  }
38
46
 
39
47
  /**
@@ -5,7 +5,7 @@ import { assert } from "../../../../../core/assert.js";
5
5
  import { ShadedGeometry } from "../ShadedGeometry.js";
6
6
  import {
7
7
  bvh_query_user_data_overlaps_frustum
8
- } from "../../../../../core/bvh2/bvh3/bvh_query_user_data_overlaps_frustum.js";
8
+ } from "../../../../../core/bvh2/bvh3/query/bvh_query_user_data_overlaps_frustum.js";
9
9
  import { ShadedGeometryFlags } from "../ShadedGeometryFlags.js";
10
10
 
11
11
  /**
@@ -0,0 +1,40 @@
1
+ import { AbstractLight } from "./AbstractLight.js";
2
+ import Vector3 from "../../../../../core/geom/Vector3.js";
3
+ import { Color } from "../../../../../core/color/Color.js";
4
+ import Vector1 from "../../../../../core/geom/Vector1.js";
5
+
6
+ export class DirectionalLight extends AbstractLight {
7
+ constructor() {
8
+ super();
9
+
10
+ /**
11
+ *
12
+ * @type {Vector3}
13
+ */
14
+ this.position = new Vector3();
15
+
16
+ /**
17
+ *
18
+ * @type {Vector3}
19
+ */
20
+ this.direction = new Vector3();
21
+
22
+ /**
23
+ * @readonly
24
+ * @type {Color}
25
+ */
26
+ this.color = new Color(1, 1, 1);
27
+
28
+ /**
29
+ *
30
+ * @type {Vector1}
31
+ */
32
+ this.intensity = new Vector1(1);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @readonly
38
+ * @type {boolean}
39
+ */
40
+ DirectionalLight.prototype.isDirectionalLight = true;
@@ -1,13 +1,12 @@
1
1
  import {
2
2
  ExplicitBinaryBoundingVolumeHierarchy
3
3
  } from "../../../../core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js";
4
- import { min2 } from "../../../../core/math/min2.js";
5
- import { compute_triangle_group_aabb3 } from "./compute_triangle_group_aabb3.js";
6
- import { bvh_query_leaves_ray } from "../../../../core/bvh2/bvh3/bvh_query_leaves_ray.js";
4
+ import { bvh_query_leaves_ray } from "../../../../core/bvh2/bvh3/query/bvh_query_leaves_ray.js";
7
5
  import { aabb3_signed_distance_sqr_to_point } from "../../../../core/bvh2/aabb3/aabb3_signed_distance_sqr_to_point.js";
8
6
  import {
9
7
  computeTriangleRayIntersectionBarycentric
10
8
  } from "../../../../core/geom/3d/triangle/computeTriangleRayIntersection.js";
9
+ import { ebvh_build_for_geometry_morton } from "../../../../core/bvh2/bvh3/ebvh_build_for_geometry_morton.js";
11
10
 
12
11
  /**
13
12
  *
@@ -19,10 +18,10 @@ const leaf_buffer = [];
19
18
  * @type {number[]}
20
19
  */
21
20
  const v3_scratch_0 = [];
22
- const BVH_BATCH_SIZE = 1;
23
21
 
24
22
  const temp_aabb3 = new Float32Array(6);
25
23
 
24
+
26
25
  export class GeometryBVHBatched {
27
26
  constructor() {
28
27
  /**
@@ -69,10 +68,6 @@ export class GeometryBVHBatched {
69
68
  build(geometry) {
70
69
  this.__geometry = geometry;
71
70
 
72
- const bvh = this.__bvh;
73
-
74
- bvh.release_all();
75
-
76
71
  const index = geometry.getIndex();
77
72
  const index_array = index.array;
78
73
 
@@ -83,32 +78,15 @@ export class GeometryBVHBatched {
83
78
  this.__geometry_positions = array_positions;
84
79
  this.__geometry_index = index_array;
85
80
 
86
- const triangle_count = index_array.length / 3;
87
-
88
- this.__triangle_count = triangle_count;
89
-
90
- const leaf_node_count = Math.ceil(triangle_count / BVH_BATCH_SIZE);
91
-
92
- const leaf_aabb = [];
93
-
94
- for (let i = 0; i < leaf_node_count; i++) {
81
+ this.__triangle_count = index_array.length / 3;
95
82
 
96
- const index_start = i * BVH_BATCH_SIZE;
97
- const index_end = min2(triangle_count, index_start + BVH_BATCH_SIZE);
98
-
99
- const group_size = index_end - index_start;
100
-
101
- compute_triangle_group_aabb3(leaf_aabb, 0, index_array, array_positions, index_start * 3, group_size, 3);
102
-
103
- const node_index = bvh.allocate_node();
104
- bvh.node_set_aabb(node_index, leaf_aabb);
105
- bvh.node_set_user_data(node_index, i);
83
+ const bvh = this.__bvh;
106
84
 
107
- bvh.insert_leaf(node_index);
108
- }
85
+ ebvh_build_for_geometry_morton(bvh, index_array, array_positions);
86
+ //
87
+ // ebvh_build_for_geometry_incremental(bvh, index_array, array_positions, 1);
109
88
 
110
89
  bvh.trim();
111
- bvh.sort_for_traversal_depth_first();
112
90
  }
113
91
 
114
92
  /**
@@ -119,7 +97,7 @@ export class GeometryBVHBatched {
119
97
  * @param {number} u
120
98
  * @param {number} v
121
99
  */
122
- construct_output(output, face_index, t, u, v) {
100
+ construct_ray_hit(output, face_index, t, u, v) {
123
101
  const indices = this.__geometry_index;
124
102
  const positions = this.__geometry_positions;
125
103
 
@@ -129,7 +107,6 @@ export class GeometryBVHBatched {
129
107
  const b = indices[index_offset + 1];
130
108
  const c = indices[index_offset + 2];
131
109
 
132
-
133
110
  const a_address = a * 3;
134
111
  const b_address = b * 3;
135
112
  const c_address = c * 3;
@@ -146,15 +123,7 @@ export class GeometryBVHBatched {
146
123
  const cy = positions[c_address + 1];
147
124
  const cz = positions[c_address + 2];
148
125
 
149
- // read out interpolated buffer values
150
- // const w = 1 - u - v;
151
-
152
- // output[0] = ax * u + bx * v + cx * w;
153
- // output[1] = ay * u + by * v + cy * w;
154
- // output[2] = az * u + bz * v + cz * w;
155
-
156
126
  // normal
157
- // v3_compute_triangle_normal(output, 3, ax, ay, ax, bx, by, bz, cx, cy, cz);
158
127
 
159
128
  // edge1 = a - b
160
129
  const edge1_x = bx - ax;
@@ -173,7 +142,6 @@ export class GeometryBVHBatched {
173
142
  const normal_y = edge1_z * edge2_x - edge1_x * edge2_z;
174
143
  const normal_z = edge1_x * edge2_y - edge1_y * edge2_x
175
144
 
176
-
177
145
  output[3] = normal_x;
178
146
  output[4] = normal_y;
179
147
  output[5] = normal_z;
@@ -207,6 +175,7 @@ export class GeometryBVHBatched {
207
175
 
208
176
  const bvh = this.__bvh;
209
177
 
178
+ // TODO we can unify BVH traversal with ray checks to reduce number of checks
210
179
  const count = bvh_query_leaves_ray(
211
180
  bvh, leaf_buffer, 0,
212
181
  origin_x, origin_y, origin_z,
@@ -220,18 +189,13 @@ export class GeometryBVHBatched {
220
189
  let best_v = 0;
221
190
 
222
191
  for (let i = 0; i < count; i++) {
223
- const leaf_node_id = leaf_buffer[i];
224
-
225
- const triangle_batch_index = bvh.node_get_user_data(leaf_node_id);
192
+ const node_id = leaf_buffer[i];
226
193
 
227
- const index_start = triangle_batch_index * BVH_BATCH_SIZE;
228
-
229
- const index_end = min2(index_start + BVH_BATCH_SIZE, this.__triangle_count);
230
194
 
231
195
  if (best_t < max_distance) {
232
196
  // early bail-out
233
197
 
234
- bvh.node_get_aabb(leaf_node_id, temp_aabb3);
198
+ bvh.node_get_aabb(node_id, temp_aabb3);
235
199
 
236
200
  const distance = aabb3_signed_distance_sqr_to_point(temp_aabb3[0], temp_aabb3[1], temp_aabb3[2], temp_aabb3[3], temp_aabb3[4], temp_aabb3[5], ray[0], ray[1], ray[2]);
237
201
 
@@ -241,71 +205,48 @@ export class GeometryBVHBatched {
241
205
  }
242
206
  }
243
207
 
244
- for (let j = index_start; j < index_end; j++) {
245
- const index_offset = j * 3;
246
-
247
- const a = indices[index_offset];
248
- const b = indices[index_offset + 1];
249
- const c = indices[index_offset + 2];
250
-
251
-
252
- const a_address = a * 3;
253
- const b_address = b * 3;
254
- const c_address = c * 3;
255
-
256
-
257
- const ax = positions[a_address];
258
- const ay = positions[a_address + 1];
259
- const az = positions[a_address + 2];
260
-
261
- const bx = positions[b_address];
262
- const by = positions[b_address + 1];
263
- const bz = positions[b_address + 2];
264
-
265
- const cx = positions[c_address];
266
- const cy = positions[c_address + 1];
267
- const cz = positions[c_address + 2];
268
-
269
- // const sp3 = new SurfacePoint3();
270
- //
271
- // const intersection_found = computeTriangleRayIntersection(
272
- // sp3,
273
- // origin_x, origin_y, origin_z,
274
- // direction_x, direction_y, direction_z,
275
- // ax, ay, az,
276
- // bx, by, bz,
277
- // cx, cy, cz);
278
- //
279
- // v3_scratch_0[0] = sp3.position.distanceTo(new Vector3(origin_x, origin_y, origin_z));
280
-
281
- const intersection_found = computeTriangleRayIntersectionBarycentric(
282
- v3_scratch_0,
283
- origin_x, origin_y, origin_z,
284
- direction_x, direction_y, direction_z,
285
- ax, ay, az,
286
- bx, by, bz,
287
- cx, cy, cz
288
- );
289
-
290
- if (intersection_found && v3_scratch_0[0] < best_t && v3_scratch_0[0] > min_distance) {
291
- best_t = v3_scratch_0[0];
292
-
293
- best_index = j;
294
- best_u = v3_scratch_0[1];
295
- best_v = v3_scratch_0[2];
296
-
297
-
298
- // output[0] = v3_scratch_0[0];
299
- // output[1] = v3_scratch_0[1];
300
- // output[2] = v3_scratch_0[2];
301
- //
302
- // output[3] = v3_scratch_0[3];
303
- // output[4] = v3_scratch_0[4];
304
- // output[5] = v3_scratch_0[5];
305
-
306
- // uv
307
- //TODO
308
- }
208
+ const triangle_index = bvh.node_get_user_data(node_id);
209
+
210
+ const index_offset = triangle_index * 3;
211
+
212
+ const a = indices[index_offset];
213
+ const b = indices[index_offset + 1];
214
+ const c = indices[index_offset + 2];
215
+
216
+
217
+ const a_address = a * 3;
218
+ const b_address = b * 3;
219
+ const c_address = c * 3;
220
+
221
+
222
+ const ax = positions[a_address];
223
+ const ay = positions[a_address + 1];
224
+ const az = positions[a_address + 2];
225
+
226
+ const bx = positions[b_address];
227
+ const by = positions[b_address + 1];
228
+ const bz = positions[b_address + 2];
229
+
230
+ const cx = positions[c_address];
231
+ const cy = positions[c_address + 1];
232
+ const cz = positions[c_address + 2];
233
+
234
+ const intersection_found = computeTriangleRayIntersectionBarycentric(
235
+ v3_scratch_0,
236
+ origin_x, origin_y, origin_z,
237
+ direction_x, direction_y, direction_z,
238
+ ax, ay, az,
239
+ bx, by, bz,
240
+ cx, cy, cz
241
+ );
242
+
243
+ if (intersection_found && v3_scratch_0[0] < best_t && v3_scratch_0[0] > min_distance) {
244
+ best_t = v3_scratch_0[0];
245
+
246
+ best_index = triangle_index;
247
+ best_u = v3_scratch_0[1];
248
+ best_v = v3_scratch_0[2];
249
+
309
250
  }
310
251
  }
311
252
 
@@ -317,7 +258,7 @@ export class GeometryBVHBatched {
317
258
  output[1] = origin_y + direction_y * best_t;
318
259
  output[2] = origin_z + direction_z * best_t;
319
260
 
320
- this.construct_output(output, best_index, best_t, best_u, best_v);
261
+ this.construct_ray_hit(output, best_index, best_t, best_u, best_v);
321
262
 
322
263
  return best_t;
323
264
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ExplicitBinaryBoundingVolumeHierarchy
3
3
  } from "../../../../core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js";
4
- import { bvh_query_leaves_ray } from "../../../../core/bvh2/bvh3/bvh_query_leaves_ray.js";
4
+ import { bvh_query_leaves_ray } from "../../../../core/bvh2/bvh3/query/bvh_query_leaves_ray.js";
5
5
  import { aabb3_matrix4_project } from "../../../../core/geom/3d/aabb/aabb3_matrix4_project.js";
6
6
  import { array_copy } from "../../../../core/collection/array/copyArray.js";
7
7
  import { mat4, vec3 } from "gl-matrix";
@@ -25,6 +25,12 @@ import { sample_triangle_attribute } from "./sample_triangle_attribute.js";
25
25
  import { vec3_uint8_to_float } from "./vec3_uint8_to_float.js";
26
26
  import { make_sky_rtiw } from "./make_sky_rtiw.js";
27
27
  import { apply_texture_clamping_to_coordinate } from "./apply_texture_clamping_to_coordinate.js";
28
+ 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
+ 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
+ import { make_one_vector3 } from "./make_one_vector3.js";
28
34
 
29
35
  /*
30
36
  Ray hit data layout:
@@ -47,7 +53,8 @@ const trace_result = [];
47
53
 
48
54
  const _ray = [];
49
55
 
50
- const tmp = [];
56
+ const tmp_0 = [];
57
+ const tmp_1 = [];
51
58
 
52
59
 
53
60
  export class PathTracer {
@@ -67,9 +74,9 @@ export class PathTracer {
67
74
 
68
75
  /**
69
76
  *
70
- * @type {Map<number, Light>}
77
+ * @type {AbstractLight[]}
71
78
  */
72
- this.lights = new Map();
79
+ this.__lights = [];
73
80
 
74
81
  /**
75
82
  *
@@ -88,7 +95,7 @@ export class PathTracer {
88
95
  * @type {function}
89
96
  * @private
90
97
  */
91
- this.__background_sampler = make_sky_rtiw();
98
+ this.__background_sampler = make_one_vector3();
92
99
  }
93
100
 
94
101
  /**
@@ -114,7 +121,7 @@ export class PathTracer {
114
121
 
115
122
  optimize() {
116
123
  this.bvh_top_level.trim();
117
- this.bvh_top_level.sort_for_traversal_depth_first();
124
+ ebvh_sort_for_traversal_depth_first(this.bvh_top_level);
118
125
  }
119
126
 
120
127
  async build() {
@@ -155,6 +162,14 @@ export class PathTracer {
155
162
  }
156
163
  }
157
164
 
165
+ /**
166
+ *
167
+ * @param {THREE.Light} light
168
+ */
169
+ addLight(light) {
170
+ this.__lights.push(light);
171
+ }
172
+
158
173
  /**
159
174
  *
160
175
  * @param {THREE.BufferGeometry} geo
@@ -366,14 +381,14 @@ export class PathTracer {
366
381
 
367
382
  const mesh = this.meshes.get(node_user_data);
368
383
 
369
- const distance_to_hit = mesh.hit(tmp, ray, min_distance, nearest_hit_distance);
384
+ const distance_to_hit = mesh.hit(tmp_0, ray, min_distance, nearest_hit_distance);
370
385
 
371
386
  if (distance_to_hit >= 0) {
372
387
  // since raycast in leaf nodes is already bound by maximum distance, any hit we get is necessarily a closer hit than before
373
388
  nearest_hit_distance = distance_to_hit;
374
389
  nearest_mesh = mesh;
375
390
 
376
- array_copy(tmp, 0, out, 0, 11);
391
+ array_copy(tmp_0, 0, out, 0, 11);
377
392
  }
378
393
  }
379
394
 
@@ -395,9 +410,53 @@ export class PathTracer {
395
410
  * @param {number} direction_offset
396
411
  */
397
412
  sample_background(out, out_offset, direction, direction_offset) {
413
+ this.__background_sampler(out, out_offset, direction, direction_offset);
414
+ }
398
415
 
416
+ /**
417
+ *
418
+ * @param {number[]} out
419
+ * @param {number} out_offset
420
+ * @param {number[]} hit_info
421
+ * @param {number} hit_info_address
422
+ */
423
+ sample_lights(out, out_offset, hit_info, hit_info_address) {
424
+ let lights_sampled = false;
399
425
 
400
- this.__background_sampler(out, out_offset, direction, direction_offset);
426
+ const lights = this.__lights;
427
+ const light_count = lights.length;
428
+
429
+ for (let i = 0; i < 3; i++) {
430
+ out[out_offset + i] = 0;
431
+ }
432
+
433
+ for (let i = 0; i < light_count; i++) {
434
+ const light = lights[i];
435
+
436
+ if (light.isDirectionalLight === true) {
437
+ const dir = light.direction;
438
+
439
+ // 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) {
446
+ // light is occluded
447
+ break;
448
+ }
449
+
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));
451
+
452
+ for (let j = 0; j < 3; j++) {
453
+ // irradiance
454
+ out[out_offset + j] += dotNL * light.color[j] * light.intensity.getValue();
455
+ }
456
+ }
457
+ }
458
+
459
+ return lights_sampled;
401
460
  }
402
461
 
403
462
  path_trace(out, ray, max_distance, max_bounce, random = Math.random) {
@@ -405,9 +464,9 @@ export class PathTracer {
405
464
 
406
465
  array_copy(ray, 0, _ray, 0, 6);
407
466
 
408
- irradiance[0] = 1;
409
- irradiance[1] = 1;
410
- irradiance[2] = 1;
467
+ irradiance[0] = 0;
468
+ irradiance[1] = 0;
469
+ irradiance[2] = 0;
411
470
 
412
471
  let got_emission = false;
413
472
 
@@ -421,9 +480,9 @@ export class PathTracer {
421
480
 
422
481
  // sample "environment" and terminate path as there is nothing to reflect off of
423
482
 
424
- this.sample_background(tmp, 0, _ray, 3);
483
+ this.sample_background(tmp_0, 0, _ray, 3);
425
484
 
426
- vec3.multiply(irradiance, irradiance, tmp);
485
+ vec3.multiply(irradiance, irradiance, tmp_0);
427
486
 
428
487
  got_emission = true;
429
488
 
@@ -434,16 +493,22 @@ export class PathTracer {
434
493
  // irradiance[0] += m;
435
494
  // irradiance[1] += m;
436
495
  // irradiance[2] += m;
437
- this.sample_material(tmp, trace_result);
496
+ this.sample_material(tmp_0, trace_result);
497
+
498
+ // adjust normal on the hit
499
+ // array_copy(tmp_0, 3, trace_result, 3, 3);
500
+
501
+ // sample light
502
+ this.sample_lights(tmp_1, 0, trace_result, 0);
503
+ vec3.add(irradiance, irradiance, tmp_1);
438
504
 
439
505
  // reflect ray
440
506
  array_copy(trace_result, 0, _ray, 0, 3);
441
507
 
442
508
  // getBiasedNormalSample(_ray, 3, tmp, 3, 1, random);
443
- random_in_hemisphere(random, _ray, 3, tmp, 3);
444
-
445
- vec3.multiply(irradiance, irradiance, tmp);
509
+ random_in_hemisphere(random, _ray, 3, tmp_0, 3);
446
510
 
511
+ vec3.multiply(irradiance, irradiance, tmp_0);
447
512
  }
448
513
 
449
514
  if (got_emission === false) {
@@ -3,7 +3,7 @@ import { max2 } from "../../../../core/math/max2.js";
3
3
 
4
4
  /**
5
5
  * Compute AABB for a group of triangles based on index-buffer
6
- * @param {number[]} output
6
+ * @param {number[]|Float32Array} output
7
7
  * @param {number} output_offset
8
8
  * @param {number[]|ArrayLike<number>} indices
9
9
  * @param {number[]|ArrayLike<number>} positions
@@ -0,0 +1,7 @@
1
+ export function make_one_vector3() {
2
+ return (out, out_offset, direction, direction_offset) => {
3
+ out[out_offset] = 1;
4
+ out[out_offset + 1] = 1;
5
+ out[out_offset + 2] = 1;
6
+ }
7
+ }
@@ -13,3 +13,5 @@ export function make_sky_rtiw() {
13
13
  out[out_offset + 2] = lerp(1, 1, t);
14
14
  }
15
15
  }
16
+
17
+
@@ -0,0 +1,7 @@
1
+ export function make_zero_vector3() {
2
+ return (out, out_offset, direction, direction_offset) => {
3
+ out[out_offset] = 0;
4
+ out[out_offset + 1] = 0;
5
+ out[out_offset + 2] = 0;
6
+ }
7
+ }
@@ -37,6 +37,7 @@ import { noop } from "../../../../core/function/Functions.js";
37
37
  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
+ import { DirectionalLight } from "../../render/forward_plus/model/DirectionalLight.js";
40
41
 
41
42
  document.body.style.margin = 0;
42
43
  document.body.style.overflow = "hidden";
@@ -108,20 +109,30 @@ function prepare_scene_sphere_01(pt, camera) {
108
109
  camera.lookAt(0, 0, 0);
109
110
  pt.addMesh(
110
111
  new SphereBufferGeometry(1, 16, 16),
111
- new MeshStandardMaterial({ color: '#FF0000' }),
112
+ // new MeshStandardMaterial({ color: '#FF0000' }),
113
+ new MeshStandardMaterial({ color: '#FFFFFF' }),
112
114
  Transform.fromJSON({
113
115
  scale: 0.5
114
116
  }).matrix
115
117
  );
116
118
 
117
119
  const ground_material = new MeshStandardMaterial({
118
- color: '#00FF00'
120
+ color: '#FFFFFF'
119
121
  });
120
122
  pt.addMesh(new PlaneBufferGeometry(), ground_material, Transform.fromJSON({
121
123
  position: new Vector3(0, -0.5, 0),
122
124
  scale: 50,
123
125
  rotation: Quaternion.fromEulerAngles(-Math.PI / 2, 0, 0)
124
126
  }).matrix);
127
+
128
+
129
+ const light = new DirectionalLight();
130
+ light.color.setRGB(1, 0, 0);
131
+ light.intensity.set(1);
132
+ light.direction.set(-1, -1, 0);
133
+ light.direction.normalize();
134
+
135
+ pt.addLight(light);
125
136
  }
126
137
 
127
138
  function promise_ply(url) {
@@ -365,7 +376,7 @@ function* render(target, pt, camera, progress = { current: 0, total: 0 }) {
365
376
  const pixel_scale_x = 1 / (width - 1);
366
377
  const pixel_scale_y = 1 / (height - 1);
367
378
 
368
- const pixel_sample_count = 500;
379
+ const pixel_sample_count = 64;
369
380
 
370
381
  progress.total = width * height;
371
382
 
@@ -465,11 +476,12 @@ const camera = new PerspectiveCamera();
465
476
  async function start_renderer(camera) {
466
477
  camera.aspect = vCanvas.size.x / vCanvas.size.y;
467
478
 
468
- const path = 'data/models/LowPolyTownshipSet/Small_house/Small_house.gltf';
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
469
481
 
470
482
  // await prepare_scene_lucy(pt, camera);
471
- await prepare_scene_rtiow(pt, camera);
472
- // await prepare_scene_sphere_01(pt, camera);
483
+ // await prepare_scene_rtiow(pt, camera);
484
+ await prepare_scene_sphere_01(pt, camera);
473
485
  // await prepare_scene_gltf(pt, camera, path);
474
486
 
475
487
  pt.build();
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.18",
8
+ "version": "2.43.19",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",