@woosh/meep-engine 2.43.18 → 2.43.20
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.
- package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +59 -116
- package/core/bvh2/bvh3/ebvh_build_for_geometry_incremental.js +34 -0
- package/core/bvh2/bvh3/ebvh_build_for_geometry_morton.js +175 -0
- package/core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js +122 -0
- package/core/bvh2/bvh3/{bvh_collect_user_data.js → query/bvh_collect_user_data.js} +1 -1
- package/core/bvh2/bvh3/{bvh_query_leaves_generic.js → query/bvh_query_leaves_generic.js} +1 -1
- package/core/bvh2/bvh3/{bvh_query_leaves_ray.js → query/bvh_query_leaves_ray.js} +2 -3
- package/core/bvh2/bvh3/{bvh_query_user_data_generic.js → query/bvh_query_user_data_generic.js} +1 -1
- package/core/bvh2/bvh3/{bvh_query_user_data_nearest_to_point.js → query/bvh_query_user_data_nearest_to_point.js} +3 -3
- 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
- package/core/bvh2/bvh3/{bvh_query_user_data_overlaps_frustum.js → query/bvh_query_user_data_overlaps_frustum.js} +2 -2
- package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js +1 -1
- package/core/collection/array/arrayQuickSort.js +1 -1
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -2
- package/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +9 -1
- package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +1 -1
- package/engine/graphics/render/forward_plus/model/DirectionalLight.js +40 -0
- package/engine/graphics/sh3/path_tracer/GeometryBVHBatched.js +55 -114
- package/engine/graphics/sh3/path_tracer/PathTracer.js +85 -18
- package/engine/graphics/sh3/path_tracer/compute_triangle_group_aabb3.js +1 -1
- package/engine/graphics/sh3/path_tracer/make_one_vector3.js +7 -0
- package/engine/graphics/sh3/path_tracer/make_sky_rtiw.js +2 -0
- package/engine/graphics/sh3/path_tracer/make_zero_vector3.js +7 -0
- package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +18 -6
- 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,10 +1,10 @@
|
|
|
1
|
-
import { aabb3_intersects_ray } from "
|
|
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 "
|
|
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,6 +1,6 @@
|
|
|
1
|
-
import { NULL_NODE } from "
|
|
2
|
-
import { aabb3_signed_distance_sqr_to_point } from "
|
|
3
|
-
import { max2 } from "
|
|
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 "
|
|
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 "
|
|
3
|
-
import { aabb3_array_intersects_frustum_degree } from "
|
|
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 "
|
|
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 = [];
|
|
@@ -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 =
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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.
|
|
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
|
|
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 {
|
|
77
|
+
* @type {AbstractLight[]}
|
|
71
78
|
*/
|
|
72
|
-
this.
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
391
|
+
array_copy(tmp_0, 0, out, 0, 11);
|
|
377
392
|
}
|
|
378
393
|
}
|
|
379
394
|
|
|
@@ -395,9 +410,55 @@ 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
|
-
|
|
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
|
+
// see https://github.com/mrdoob/three.js/blob/f0a9e0cf90a2f1ba5017fcb7fd46f02748b920cf/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js#L172
|
|
451
|
+
|
|
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));
|
|
453
|
+
|
|
454
|
+
for (let j = 0; j < 3; j++) {
|
|
455
|
+
// irradiance
|
|
456
|
+
out[out_offset + j] += dotNL * light.color[j] * light.intensity.getValue();
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return lights_sampled;
|
|
401
462
|
}
|
|
402
463
|
|
|
403
464
|
path_trace(out, ray, max_distance, max_bounce, random = Math.random) {
|
|
@@ -405,9 +466,9 @@ export class PathTracer {
|
|
|
405
466
|
|
|
406
467
|
array_copy(ray, 0, _ray, 0, 6);
|
|
407
468
|
|
|
408
|
-
irradiance[0] =
|
|
409
|
-
irradiance[1] =
|
|
410
|
-
irradiance[2] =
|
|
469
|
+
irradiance[0] = 0;
|
|
470
|
+
irradiance[1] = 0;
|
|
471
|
+
irradiance[2] = 0;
|
|
411
472
|
|
|
412
473
|
let got_emission = false;
|
|
413
474
|
|
|
@@ -421,9 +482,9 @@ export class PathTracer {
|
|
|
421
482
|
|
|
422
483
|
// sample "environment" and terminate path as there is nothing to reflect off of
|
|
423
484
|
|
|
424
|
-
this.sample_background(
|
|
485
|
+
this.sample_background(tmp_0, 0, _ray, 3);
|
|
425
486
|
|
|
426
|
-
vec3.multiply(irradiance, irradiance,
|
|
487
|
+
vec3.multiply(irradiance, irradiance, tmp_0);
|
|
427
488
|
|
|
428
489
|
got_emission = true;
|
|
429
490
|
|
|
@@ -434,16 +495,22 @@ export class PathTracer {
|
|
|
434
495
|
// irradiance[0] += m;
|
|
435
496
|
// irradiance[1] += m;
|
|
436
497
|
// irradiance[2] += m;
|
|
437
|
-
this.sample_material(
|
|
498
|
+
this.sample_material(tmp_0, trace_result);
|
|
499
|
+
|
|
500
|
+
// adjust normal on the hit
|
|
501
|
+
// array_copy(tmp_0, 3, trace_result, 3, 3);
|
|
502
|
+
|
|
503
|
+
// sample light
|
|
504
|
+
this.sample_lights(tmp_1, 0, trace_result, 0);
|
|
505
|
+
vec3.add(irradiance, irradiance, tmp_1);
|
|
438
506
|
|
|
439
507
|
// reflect ray
|
|
440
508
|
array_copy(trace_result, 0, _ray, 0, 3);
|
|
441
509
|
|
|
442
510
|
// getBiasedNormalSample(_ray, 3, tmp, 3, 1, random);
|
|
443
|
-
random_in_hemisphere(random, _ray, 3,
|
|
444
|
-
|
|
445
|
-
vec3.multiply(irradiance, irradiance, tmp);
|
|
511
|
+
random_in_hemisphere(random, _ray, 3, tmp_0, 3);
|
|
446
512
|
|
|
513
|
+
vec3.multiply(irradiance, irradiance, tmp_0);
|
|
447
514
|
}
|
|
448
515
|
|
|
449
516
|
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
|
|
@@ -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: '#
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
8
|
+
"version": "2.43.20",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|