@woosh/meep-engine 2.43.17 → 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.
- package/core/assert.js +3 -1
- package/core/bvh2/aabb3/aabb3_intersects_ray.js +14 -9
- package/core/bvh2/aabb3/aabb3_intersects_ray_branchless.js +52 -0
- package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.d.ts +2 -0
- package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +162 -10
- 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/query/bvh_query_leaves_ray.js +97 -0
- 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/core/collection/array/typed/typed_array_copy.js +2 -2
- package/core/geom/3d/aabb/compute_aabb_from_points.js +4 -3
- package/core/geom/3d/compute_triangle_normal.js +76 -0
- package/core/geom/3d/topology/samples/sampleFloodFill.js +1 -1
- package/core/geom/3d/topology/simplify/compute_face_normal_change_dot_product.js +1 -1
- package/core/geom/3d/topology/simplify/quadratic/Quadratic3.js +1 -1
- package/core/geom/3d/topology/struct/TopoTriangle.js +1 -57
- package/core/geom/3d/topology/tm_face_normal.js +1 -1
- package/core/geom/3d/topology/tm_vertex_compute_normal.js +1 -1
- package/core/geom/3d/triangle/computeTriangleRayIntersection.js +195 -27
- package/core/geom/Vector3.js +12 -12
- package/core/math/physics/brdf/D_GGX.js +13 -0
- package/editor/tools/v2/prototypeTransformControls.js +14 -2
- package/engine/ecs/parent/EntityNode.js +80 -7
- package/engine/ecs/parent/EntityNodeFlags.js +8 -0
- 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/geometry/AttributeSpec.js +18 -3
- package/engine/graphics/geometry/VertexDataSpec.js +53 -3
- package/engine/graphics/micron/format/VirtualGeometry.js +7 -0
- package/engine/graphics/micron/render/VirtualGeometryBuilder.js +1 -1
- package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +5 -2
- package/engine/graphics/particles/particular/engine/parameter/sample/RGBA_LUT_HEATMAP_IR.js +11 -0
- package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +2 -9
- package/engine/graphics/render/forward_plus/model/DirectionalLight.js +40 -0
- package/engine/graphics/sh3/README.md +1 -0
- package/engine/graphics/sh3/path_tracer/GeometryBVHBatched.js +265 -0
- package/engine/graphics/sh3/path_tracer/PathTracedMesh.js +85 -0
- package/engine/graphics/sh3/path_tracer/PathTracer.js +534 -0
- package/engine/graphics/sh3/path_tracer/apply_texture_clamping_to_coordinate.js +22 -0
- package/engine/graphics/sh3/path_tracer/compute_triangle_group_aabb3.js +36 -0
- package/engine/graphics/sh3/path_tracer/getBiasedNormalSample.js +55 -0
- package/engine/graphics/sh3/path_tracer/make_one_vector3.js +7 -0
- package/engine/graphics/sh3/path_tracer/make_sky_hosek.js +44 -0
- package/engine/graphics/sh3/path_tracer/make_sky_rtiw.js +17 -0
- package/engine/graphics/sh3/path_tracer/make_zero_vector3.js +7 -0
- package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +631 -0
- package/engine/graphics/sh3/path_tracer/random_in_hemisphere.js +39 -0
- package/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +42 -0
- package/engine/graphics/sh3/path_tracer/ray_reflect.js +27 -0
- package/engine/graphics/sh3/path_tracer/sample_triangle_attribute.js +35 -0
- package/engine/graphics/sh3/path_tracer/vec3_uint8_to_float.js +12 -0
- package/engine/graphics/sh3/sky/hosek/README.md +4 -0
- package/engine/graphics/sh3/sky/hosek/prototype_hosek.js +71 -0
- package/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +4171 -0
- package/engine/graphics/texture/sampler/convertTexture2Sampler2D.js +2 -0
- package/package.json +1 -1
- package/view/elements/progress/SmoothProgressBar.js +1 -1
- package/view/task/TaskProgressView.js +6 -8
- package/core/bvh2/bvh3/bvh_query_leaves_ray.js +0 -95
package/core/assert.js
CHANGED
|
@@ -380,7 +380,9 @@ assert.notNaN = function (value, name = "value") {
|
|
|
380
380
|
* @param {string} name
|
|
381
381
|
*/
|
|
382
382
|
assert.isFiniteNumber = function (value, name = "value") {
|
|
383
|
-
|
|
383
|
+
if (!Number.isFinite(value)) {
|
|
384
|
+
throw new Error(`${name} must be a finite number, instead was ${value}`);
|
|
385
|
+
}
|
|
384
386
|
};
|
|
385
387
|
|
|
386
388
|
export {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { fabsf } from "../../math/fabsf.js";
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
/**
|
|
4
5
|
* NOTES:
|
|
5
6
|
* https://web.archive.org/web/20090803054252/http://tog.acm.org/resources/GraphicsGems/gems/RayBox.c
|
|
@@ -20,41 +21,46 @@ import { fabsf } from "../../math/fabsf.js";
|
|
|
20
21
|
* @param {number} dirZ
|
|
21
22
|
* @returns {boolean}
|
|
22
23
|
*/
|
|
23
|
-
function aabb3_intersects_ray(
|
|
24
|
+
export function aabb3_intersects_ray(
|
|
25
|
+
x0, y0, z0,
|
|
26
|
+
x1, y1, z1,
|
|
27
|
+
oX, oY, oZ,
|
|
28
|
+
dirX, dirY, dirZ
|
|
29
|
+
) {
|
|
24
30
|
|
|
25
31
|
// Z Projection
|
|
26
|
-
const boxExtentsX = (x1 - x0)
|
|
32
|
+
const boxExtentsX = (x1 - x0) * 0.5;
|
|
27
33
|
|
|
28
34
|
const centerX = x0 + boxExtentsX;
|
|
29
35
|
|
|
30
36
|
const diffX = oX - centerX;
|
|
31
37
|
|
|
32
38
|
|
|
33
|
-
if (
|
|
39
|
+
if (diffX * dirX >= 0.0 && fabsf(diffX) > boxExtentsX) {
|
|
34
40
|
return false;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
// Y projection
|
|
38
|
-
const boxExtentsY = (y1 - y0)
|
|
44
|
+
const boxExtentsY = (y1 - y0) * 0.5;
|
|
39
45
|
|
|
40
46
|
const centerY = y0 + boxExtentsY;
|
|
41
47
|
|
|
42
48
|
const diffY = oY - centerY;
|
|
43
49
|
|
|
44
50
|
|
|
45
|
-
if (
|
|
51
|
+
if (diffY * dirY >= 0.0 && fabsf(diffY) > boxExtentsY) {
|
|
46
52
|
return false;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
// Z projection
|
|
50
|
-
const boxExtentsZ = (z1 - z0)
|
|
56
|
+
const boxExtentsZ = (z1 - z0) * 0.5;
|
|
51
57
|
|
|
52
58
|
const centerZ = z0 + boxExtentsZ;
|
|
53
59
|
|
|
54
60
|
const diffZ = oZ - centerZ;
|
|
55
61
|
|
|
56
62
|
|
|
57
|
-
if (
|
|
63
|
+
if (diffZ * dirZ >= 0.0 && fabsf(diffZ) > boxExtentsZ) {
|
|
58
64
|
return false;
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -64,9 +70,9 @@ function aabb3_intersects_ray(x0, y0, z0, x1, y1, z1, oX, oY, oZ, dirX, dirY, di
|
|
|
64
70
|
//b = fabsf(Dir.y);
|
|
65
71
|
//if(fabsf(Diff.y)>BoxExtents.y + b) return false;
|
|
66
72
|
|
|
67
|
-
const a = fabsf(dirX);
|
|
68
73
|
const b = fabsf(dirY);
|
|
69
74
|
const c = fabsf(dirZ);
|
|
75
|
+
const a = fabsf(dirX);
|
|
70
76
|
|
|
71
77
|
const f0 = dirY * diffZ - dirZ * diffY;
|
|
72
78
|
|
|
@@ -89,4 +95,3 @@ function aabb3_intersects_ray(x0, y0, z0, x1, y1, z1, oX, oY, oZ, dirX, dirY, di
|
|
|
89
95
|
return true;
|
|
90
96
|
}
|
|
91
97
|
|
|
92
|
-
export { aabb3_intersects_ray };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { min2 } from "../../math/min2.js";
|
|
2
|
+
import { max2 } from "../../math/max2.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SLOW, don't use in production
|
|
6
|
+
* It is branchless on systems where min/max instructions exist.
|
|
7
|
+
* Unfortunately in JS that's not the case, so this is just a curiosity
|
|
8
|
+
* @see https://tavianator.com/2011/ray_box.html
|
|
9
|
+
* @param {number} x0
|
|
10
|
+
* @param {number} y0
|
|
11
|
+
* @param {number} z0
|
|
12
|
+
* @param {number} x1
|
|
13
|
+
* @param {number} y1
|
|
14
|
+
* @param {number} z1
|
|
15
|
+
* @param {number} oX
|
|
16
|
+
* @param {number} oY
|
|
17
|
+
* @param {number} oZ
|
|
18
|
+
* @param {number} dirX
|
|
19
|
+
* @param {number} dirY
|
|
20
|
+
* @param {number} dirZ
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
export function aabb3_intersects_ray_branchless(
|
|
24
|
+
x0, y0, z0,
|
|
25
|
+
x1, y1, z1,
|
|
26
|
+
oX, oY, oZ,
|
|
27
|
+
dirX, dirY, dirZ
|
|
28
|
+
) {
|
|
29
|
+
const inv_dir_x = 1 / dirX;
|
|
30
|
+
const inv_dir_y = 1 / dirY;
|
|
31
|
+
const inv_dir_z = 1 / dirZ;
|
|
32
|
+
|
|
33
|
+
const tx1 = (x0 - oX) * inv_dir_x;
|
|
34
|
+
const tx2 = (x1 - oX) * inv_dir_x;
|
|
35
|
+
|
|
36
|
+
let tmin = min2(tx1, tx2);
|
|
37
|
+
let tmax = max2(tx1, tx2);
|
|
38
|
+
|
|
39
|
+
const ty1 = (y0 - oY) * inv_dir_y;
|
|
40
|
+
const ty2 = (y1 - oY) * inv_dir_y;
|
|
41
|
+
|
|
42
|
+
tmin = max2(tmin, min2(ty1, ty2));
|
|
43
|
+
tmax = min2(tmax, max2(ty1, ty2));
|
|
44
|
+
|
|
45
|
+
const tz1 = (z0 - oZ) * inv_dir_z;
|
|
46
|
+
const tz2 = (z1 - oZ) * inv_dir_z;
|
|
47
|
+
|
|
48
|
+
tmin = max2(tmin, min2(tz1, tz2));
|
|
49
|
+
tmax = min2(tmax, max2(tz1, tz2));
|
|
50
|
+
|
|
51
|
+
return tmax >= tmin;
|
|
52
|
+
}
|
|
@@ -2,11 +2,13 @@ import { aabb3_compute_surface_area } from "../aabb3/aabb3_compute_surface_area.
|
|
|
2
2
|
import { min2 } from "../../math/min2.js";
|
|
3
3
|
import { max2 } from "../../math/max2.js";
|
|
4
4
|
import { assert } from "../../assert.js";
|
|
5
|
+
import { typed_array_copy } from "../../collection/array/typed/typed_array_copy.js";
|
|
6
|
+
import { array_copy } from "../../collection/array/copyArray.js";
|
|
5
7
|
|
|
6
|
-
const COLUMN_PARENT = 6;
|
|
7
|
-
const COLUMN_CHILD_1 = 7;
|
|
8
|
-
const COLUMN_CHILD_2 = 8;
|
|
9
|
-
const COLUMN_HEIGHT = 9;
|
|
8
|
+
export const COLUMN_PARENT = 6;
|
|
9
|
+
export const COLUMN_CHILD_1 = 7;
|
|
10
|
+
export const COLUMN_CHILD_2 = 8;
|
|
11
|
+
export const COLUMN_HEIGHT = 9;
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* A non-leaf node have both CHILD_1 and CHILD_2 set, when CHILD_1 is not set - it's a leaf node
|
|
@@ -40,7 +42,7 @@ const CAPACITY_GROW_MIN_STEP = 64;
|
|
|
40
42
|
* @readonly
|
|
41
43
|
* @type {number}
|
|
42
44
|
*/
|
|
43
|
-
const ELEMENT_WORD_COUNT = 10;
|
|
45
|
+
export const ELEMENT_WORD_COUNT = 10;
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
48
|
* How many nodes can be stored in the newly constructed tree before allocation needs to take place
|
|
@@ -134,6 +136,16 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
|
|
|
134
136
|
this.__capacity + CAPACITY_GROW_MIN_STEP
|
|
135
137
|
));
|
|
136
138
|
|
|
139
|
+
this.__set_capacity(new_capacity);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
*
|
|
144
|
+
* @param {number} new_capacity
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
__set_capacity(new_capacity) {
|
|
148
|
+
|
|
137
149
|
const old_data_uint32 = this.__data_uint32;
|
|
138
150
|
|
|
139
151
|
const new_data_buffer = new ArrayBuffer(new_capacity * ELEMENT_WORD_COUNT * 4);
|
|
@@ -144,11 +156,20 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
|
|
|
144
156
|
this.__data_uint32 = new Uint32Array(new_data_buffer);
|
|
145
157
|
|
|
146
158
|
// copy old data into new buffer
|
|
147
|
-
this.__data_uint32
|
|
159
|
+
typed_array_copy(old_data_uint32, this.__data_uint32);
|
|
148
160
|
|
|
149
161
|
this.__capacity = new_capacity;
|
|
150
162
|
}
|
|
151
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Trim allocated memory region to only contain allocated nodes
|
|
166
|
+
*/
|
|
167
|
+
trim() {
|
|
168
|
+
if (this.__capacity > this.__size) {
|
|
169
|
+
this.__set_capacity(this.__size);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
152
173
|
/**
|
|
153
174
|
*
|
|
154
175
|
* @returns {number}
|
|
@@ -260,6 +281,15 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
|
|
|
260
281
|
return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_CHILD_1];
|
|
261
282
|
}
|
|
262
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
|
+
|
|
263
293
|
/**
|
|
264
294
|
*
|
|
265
295
|
* @param {number} id
|
|
@@ -270,6 +300,54 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
|
|
|
270
300
|
return this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_CHILD_2];
|
|
271
301
|
}
|
|
272
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
|
+
}
|
|
273
351
|
|
|
274
352
|
/**
|
|
275
353
|
*
|
|
@@ -946,10 +1024,84 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
|
|
|
946
1024
|
}
|
|
947
1025
|
|
|
948
1026
|
/**
|
|
949
|
-
*
|
|
950
|
-
*
|
|
1027
|
+
* Update parent and child links of a given node to point to a new location, useful for re-locating nodes
|
|
1028
|
+
* @param {number} node node to update
|
|
1029
|
+
* @param {number} destination Where updated links should point to
|
|
1030
|
+
* @private
|
|
951
1031
|
*/
|
|
952
|
-
|
|
953
|
-
|
|
1032
|
+
__move_node_links(node, destination) {
|
|
1033
|
+
|
|
1034
|
+
const uint32 = this.__data_uint32;
|
|
1035
|
+
|
|
1036
|
+
const source_address = node * ELEMENT_WORD_COUNT;
|
|
1037
|
+
|
|
1038
|
+
// update children of a
|
|
1039
|
+
const child1 = uint32[source_address + COLUMN_CHILD_1];
|
|
1040
|
+
const child2 = uint32[source_address + COLUMN_CHILD_2];
|
|
1041
|
+
|
|
1042
|
+
if (child1 !== NULL_NODE) {
|
|
1043
|
+
|
|
1044
|
+
uint32[child1 * ELEMENT_WORD_COUNT + COLUMN_PARENT] = destination;
|
|
1045
|
+
uint32[child2 * ELEMENT_WORD_COUNT + COLUMN_PARENT] = destination;
|
|
1046
|
+
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// update parent of a
|
|
1050
|
+
const parent = uint32[source_address + COLUMN_PARENT];
|
|
1051
|
+
|
|
1052
|
+
if (parent !== NULL_NODE) {
|
|
1053
|
+
const parent_child1 = uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
|
|
1054
|
+
|
|
1055
|
+
if (parent_child1 === node) {
|
|
1056
|
+
uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = destination;
|
|
1057
|
+
} else {
|
|
1058
|
+
uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = destination;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* Swap two nodes in memory
|
|
1065
|
+
* @param {number} a
|
|
1066
|
+
* @param {number} b
|
|
1067
|
+
* @returns {boolean}
|
|
1068
|
+
*/
|
|
1069
|
+
swap_nodes(a, b) {
|
|
1070
|
+
// console.log(`swap ${a} - ${b}`)
|
|
1071
|
+
|
|
1072
|
+
const uint32 = this.__data_uint32;
|
|
1073
|
+
|
|
1074
|
+
const address_a = a * ELEMENT_WORD_COUNT;
|
|
1075
|
+
const address_b = b * ELEMENT_WORD_COUNT;
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
if (uint32[address_a + COLUMN_PARENT] === b) {
|
|
1079
|
+
// attempting to swap direct parent/child, this is unsupported
|
|
1080
|
+
return false;
|
|
1081
|
+
}
|
|
1082
|
+
if (uint32[address_b + COLUMN_PARENT] === a) {
|
|
1083
|
+
// attempting to swap direct parent/child, this is unsupported
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
this.__move_node_links(a, b);
|
|
1088
|
+
this.__move_node_links(b, a);
|
|
1089
|
+
|
|
1090
|
+
// copy A to temp buffer
|
|
1091
|
+
array_copy(uint32, address_a, this.__free, this.__free_pointer, ELEMENT_WORD_COUNT);
|
|
1092
|
+
|
|
1093
|
+
// write data
|
|
1094
|
+
array_copy(uint32, address_b, uint32, address_a, ELEMENT_WORD_COUNT);
|
|
1095
|
+
array_copy(this.__free, this.__free_pointer, uint32, address_b, ELEMENT_WORD_COUNT);
|
|
1096
|
+
|
|
1097
|
+
// update root as necessary
|
|
1098
|
+
if (this.__root === a) {
|
|
1099
|
+
this.__root = b;
|
|
1100
|
+
} else if (this.__root === b) {
|
|
1101
|
+
this.__root = a;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
return true;
|
|
954
1105
|
}
|
|
955
1106
|
}
|
|
1107
|
+
|
|
@@ -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
|
+
}
|