@woosh/meep-engine 2.43.16 → 2.43.18

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 (53) hide show
  1. package/core/assert.js +3 -1
  2. package/core/bvh2/aabb3/aabb3_intersects_ray.js +14 -9
  3. package/core/bvh2/aabb3/aabb3_intersects_ray_branchless.js +52 -0
  4. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.d.ts +2 -0
  5. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +214 -5
  6. package/core/bvh2/bvh3/bvh_query_leaves_ray.js +32 -29
  7. package/core/collection/array/typed/typed_array_copy.js +2 -2
  8. package/core/geom/3d/aabb/compute_aabb_from_points.js +4 -3
  9. package/core/geom/3d/compute_triangle_normal.js +76 -0
  10. package/core/geom/3d/topology/samples/sampleFloodFill.js +1 -1
  11. package/core/geom/3d/topology/simplify/compute_face_normal_change_dot_product.js +1 -1
  12. package/core/geom/3d/topology/simplify/quadratic/Quadratic3.js +1 -1
  13. package/core/geom/3d/topology/struct/TopoTriangle.js +1 -57
  14. package/core/geom/3d/topology/tm_face_normal.js +1 -1
  15. package/core/geom/3d/topology/tm_vertex_compute_normal.js +1 -1
  16. package/core/geom/3d/triangle/computeTriangleRayIntersection.js +195 -27
  17. package/core/geom/Vector3.js +12 -12
  18. package/core/math/physics/brdf/D_GGX.js +13 -0
  19. package/editor/tools/v2/prototypeTransformControls.js +14 -2
  20. package/engine/ecs/parent/EntityNode.js +80 -7
  21. package/engine/ecs/parent/EntityNodeFlags.js +8 -0
  22. package/engine/ecs/terrain/tiles/TerrainTile.js +7 -9
  23. package/engine/graphics/ecs/path/PathDisplaySystem.d.ts +3 -0
  24. package/engine/graphics/ecs/path/PathDisplaySystem.js +10 -0
  25. package/engine/graphics/geometry/AttributeSpec.js +18 -3
  26. package/engine/graphics/geometry/VertexDataSpec.js +53 -3
  27. package/engine/graphics/micron/format/VirtualGeometry.js +7 -0
  28. package/engine/graphics/micron/render/VirtualGeometryBuilder.js +1 -1
  29. package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +5 -2
  30. package/engine/graphics/particles/particular/engine/parameter/sample/RGBA_LUT_HEATMAP_IR.js +11 -0
  31. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +2 -9
  32. package/engine/graphics/sh3/README.md +1 -0
  33. package/engine/graphics/sh3/path_tracer/GeometryBVHBatched.js +324 -0
  34. package/engine/graphics/sh3/path_tracer/PathTracedMesh.js +85 -0
  35. package/engine/graphics/sh3/path_tracer/PathTracer.js +469 -0
  36. package/engine/graphics/sh3/path_tracer/apply_texture_clamping_to_coordinate.js +22 -0
  37. package/engine/graphics/sh3/path_tracer/compute_triangle_group_aabb3.js +36 -0
  38. package/engine/graphics/sh3/path_tracer/getBiasedNormalSample.js +55 -0
  39. package/engine/graphics/sh3/path_tracer/make_sky_hosek.js +44 -0
  40. package/engine/graphics/sh3/path_tracer/make_sky_rtiw.js +15 -0
  41. package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +619 -0
  42. package/engine/graphics/sh3/path_tracer/random_in_hemisphere.js +39 -0
  43. package/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +42 -0
  44. package/engine/graphics/sh3/path_tracer/ray_reflect.js +27 -0
  45. package/engine/graphics/sh3/path_tracer/sample_triangle_attribute.js +35 -0
  46. package/engine/graphics/sh3/path_tracer/vec3_uint8_to_float.js +12 -0
  47. package/engine/graphics/sh3/sky/hosek/README.md +4 -0
  48. package/engine/graphics/sh3/sky/hosek/prototype_hosek.js +71 -0
  49. package/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +4171 -0
  50. package/engine/graphics/texture/sampler/convertTexture2Sampler2D.js +2 -0
  51. package/package.json +1 -1
  52. package/view/elements/progress/SmoothProgressBar.js +1 -1
  53. package/view/task/TaskProgressView.js +6 -8
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
- assert.ok(Number.isFinite(value), `${name} must be a finite number, instead was ${value}`);
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(x0, y0, z0, x1, y1, z1, oX, oY, oZ, dirX, dirY, dirZ) {
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) / 2;
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 (fabsf(diffX) > boxExtentsX && diffX * dirX >= 0.0) {
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) / 2;
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 (fabsf(diffY) > boxExtentsY && diffY * dirY >= 0.0) {
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) / 2;
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 (fabsf(diffZ) > boxExtentsZ && diffZ * dirZ >= 0.0) {
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
+ }
@@ -20,4 +20,6 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
20
20
  node_get_user_data(node: number): number
21
21
 
22
22
  release_all(): void
23
+
24
+ trim(): void
23
25
  }
@@ -2,10 +2,12 @@ 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
8
  const COLUMN_PARENT = 6;
7
- const COLUMN_CHILD_1 = 7;
8
- const COLUMN_CHILD_2 = 8;
9
+ export const COLUMN_CHILD_1 = 7;
10
+ export const COLUMN_CHILD_2 = 8;
9
11
  const COLUMN_HEIGHT = 9;
10
12
 
11
13
  /**
@@ -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.set(old_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}
@@ -945,11 +966,199 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
945
966
  return i - destination_offset;
946
967
  }
947
968
 
969
+ /**
970
+ * Update parent and child links of a given node to point to a new location, useful for re-locating nodes
971
+ * @param {number} node node to update
972
+ * @param {number} destination Where updated links should point to
973
+ * @private
974
+ */
975
+ __move_node_links(node, destination) {
976
+
977
+ const uint32 = this.__data_uint32;
978
+
979
+ const source_address = node * ELEMENT_WORD_COUNT;
980
+
981
+ // update children of a
982
+ const child1 = uint32[source_address + COLUMN_CHILD_1];
983
+ const child2 = uint32[source_address + COLUMN_CHILD_2];
984
+
985
+ if (child1 !== NULL_NODE) {
986
+
987
+ uint32[child1 * ELEMENT_WORD_COUNT + COLUMN_PARENT] = destination;
988
+ uint32[child2 * ELEMENT_WORD_COUNT + COLUMN_PARENT] = destination;
989
+
990
+ }
991
+
992
+ // update parent of a
993
+ const parent = uint32[source_address + COLUMN_PARENT];
994
+
995
+ if (parent !== NULL_NODE) {
996
+ const parent_child1 = uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
997
+
998
+ if (parent_child1 === node) {
999
+ uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = destination;
1000
+ } else {
1001
+ uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_2] = destination;
1002
+ }
1003
+ }
1004
+ }
1005
+
1006
+ /**
1007
+ * Swap two nodes in memory
1008
+ * @param {number} a
1009
+ * @param {number} b
1010
+ * @returns {boolean}
1011
+ */
1012
+ swap_nodes(a, b) {
1013
+ // console.log(`swap ${a} - ${b}`)
1014
+
1015
+ const uint32 = this.__data_uint32;
1016
+
1017
+ const address_a = a * ELEMENT_WORD_COUNT;
1018
+ const address_b = b * ELEMENT_WORD_COUNT;
1019
+
1020
+
1021
+ if (uint32[address_a + COLUMN_PARENT] === b) {
1022
+ // attempting to swap direct parent/child, this is unsupported
1023
+ return false;
1024
+ }
1025
+ if (uint32[address_b + COLUMN_PARENT] === a) {
1026
+ // attempting to swap direct parent/child, this is unsupported
1027
+ return false;
1028
+ }
1029
+
1030
+ this.__move_node_links(a, b);
1031
+ this.__move_node_links(b, a);
1032
+
1033
+ // copy A to temp buffer
1034
+ array_copy(uint32, address_a, this.__free, this.__free_pointer, ELEMENT_WORD_COUNT);
1035
+
1036
+ // write data
1037
+ array_copy(uint32, address_b, uint32, address_a, ELEMENT_WORD_COUNT);
1038
+ array_copy(this.__free, this.__free_pointer, uint32, address_b, ELEMENT_WORD_COUNT);
1039
+
1040
+ // update root as necessary
1041
+ if (this.__root === a) {
1042
+ this.__root = b;
1043
+ } else if (this.__root === b) {
1044
+ this.__root = a;
1045
+ }
1046
+
1047
+ return true;
1048
+ }
1049
+
1050
+
948
1051
  /**
949
1052
  * Sort live nodes in the traversal order.
950
1053
  * This makes most queries have linear access, resulting in near-optimal cache usage
1054
+ * NOTE: assumes that there are no free nodes
951
1055
  */
952
1056
  sort_for_traversal_depth_first() {
953
- throw new Error('Not Implemented');
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
+ }
954
1112
  }
955
1113
  }
1114
+
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
+ }
@@ -1,23 +1,16 @@
1
1
  import { aabb3_intersects_ray } from "../aabb3/aabb3_intersects_ray.js";
2
- import { NULL_NODE } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
2
+ import {
3
+ COLUMN_CHILD_1,
4
+ COLUMN_CHILD_2,
5
+ ELEMENT_WORD_COUNT,
6
+ NULL_NODE
7
+ } from "./ExplicitBinaryBoundingVolumeHierarchy.js";
3
8
 
4
9
  /**
5
10
  *
6
11
  * @type {number[]}
7
12
  */
8
- const traversal_stack = [];
9
-
10
- /**
11
- *
12
- * @type {number[]}
13
- */
14
- const scratch_aabb = [];
15
-
16
- /**
17
- *
18
- * @type {number}
19
- */
20
- let traversal_cursor = 0;
13
+ const traversal_stack = new Uint32Array(2048);
21
14
 
22
15
 
23
16
  /**
@@ -39,19 +32,29 @@ export function bvh_query_leaves_ray(
39
32
  origin_x, origin_y, origin_z,
40
33
  direction_x, direction_y, direction_z
41
34
  ) {
42
- const stack_frame_address = traversal_cursor;
43
-
44
35
  const root = bvh.root;
45
36
 
46
37
  if (root === NULL_NODE) {
47
38
  return 0;
48
39
  }
49
40
 
50
- traversal_stack[traversal_cursor++] = root;
51
41
 
52
- let result_cursor = result_offset;
42
+ traversal_stack[0] = root;
53
43
 
54
- while (traversal_cursor > stack_frame_address) {
44
+ let result_cursor = result_offset;
45
+ /**
46
+ *
47
+ * @type {number}
48
+ */
49
+ let traversal_cursor = 1;
50
+
51
+ /*
52
+ For performance, we bind data directly to avoid extra copies required to read out AABB
53
+ */
54
+ const float32 = bvh.__data_float32;
55
+ const uint32 = bvh.__data_uint32;
56
+
57
+ do {
55
58
  traversal_cursor--;
56
59
 
57
60
  /**
@@ -60,12 +63,12 @@ export function bvh_query_leaves_ray(
60
63
  */
61
64
  const node = traversal_stack[traversal_cursor];
62
65
 
63
- bvh.node_get_aabb(node, scratch_aabb);
66
+ const address = node * ELEMENT_WORD_COUNT;
64
67
 
65
68
  // test node against the ray
66
69
  const intersects = aabb3_intersects_ray(
67
- scratch_aabb[0], scratch_aabb[1], scratch_aabb[2],
68
- scratch_aabb[3], scratch_aabb[4], scratch_aabb[5],
70
+ float32[address], float32[address + 1], float32[address + 2],
71
+ float32[address + 3], float32[address + 4], float32[address + 5],
69
72
  origin_x, origin_y, origin_z,
70
73
  direction_x, direction_y, direction_z
71
74
  );
@@ -74,22 +77,22 @@ export function bvh_query_leaves_ray(
74
77
  continue;
75
78
  }
76
79
 
77
- const node_is_leaf = bvh.node_is_leaf(node);
80
+ // get fist child to check if this is a leaf node or not
81
+ const child_1 = uint32[address + COLUMN_CHILD_1];
78
82
 
79
- if (!node_is_leaf) {
83
+ if (child_1 !== NULL_NODE) {
80
84
 
81
- traversal_stack[traversal_cursor++] = bvh.node_get_child1(node);
82
- traversal_stack[traversal_cursor++] = bvh.node_get_child2(node);
85
+ // this is not a leaf node, push children onto traversal stack
86
+ traversal_stack[traversal_cursor++] = child_1;
87
+ traversal_stack[traversal_cursor++] = uint32[address + COLUMN_CHILD_2];
83
88
 
84
89
  } else {
85
90
  // leaf node
86
91
 
87
92
  result[result_cursor++] = node;
88
93
  }
89
- }
94
+ } while (traversal_cursor > 0);
90
95
 
91
96
  // drop stack frame
92
- traversal_cursor = stack_frame_address;
93
-
94
97
  return result_cursor - result_offset;
95
98
  }
@@ -3,8 +3,8 @@ import { array_copy } from "../copyArray.js";
3
3
 
4
4
  /**
5
5
  * Assumes arrays have the same type
6
- * @param {TypedArray|Float32Array|Uint8Array} source
7
- * @param {TypedArray|Float32Array|Uint8Array} destination
6
+ * @param {TypedArray|Float32Array|Uint8Array|Uint32Array} source
7
+ * @param {TypedArray|Float32Array|Uint8Array|Uint32Array} destination
8
8
  */
9
9
  export function typed_array_copy(source, destination) {
10
10
  const destination_size = destination.length;
@@ -4,11 +4,12 @@ import { max2 } from "../../../math/max2.js";
4
4
  /**
5
5
  * Multidimensional axis-aligned bounding box calculation
6
6
  * @param {number[]} result
7
- * @param {number[]} input
7
+ * @param {number[]|ArrayLike<number>} input
8
+ * @param {number} input_offset
8
9
  * @param {number} input_count
9
10
  * @param {number} d number of dimensions
10
11
  */
11
- export function compute_aabb_from_points(result, input, input_count, d) {
12
+ export function compute_aabb_from_points(result, input, input_offset, input_count, d) {
12
13
  let i = 0;
13
14
  let j = 0;
14
15
 
@@ -19,7 +20,7 @@ export function compute_aabb_from_points(result, input, input_count, d) {
19
20
 
20
21
  for (i = 0; i < input_count; i++) {
21
22
  for (j = 0; j < d; j++) {
22
- const v = input[i*d + j];
23
+ const v = input[input_count + i * d + j];
23
24
 
24
25
  result[j] = min2(result[j], v);
25
26
  result[j + d] = max2(result[j + d], v);
@@ -0,0 +1,76 @@
1
+ import { v3_length_sqr } from "../v3_length_sqr.js";
2
+ import { vec3 } from "gl-matrix";
3
+
4
+ /**
5
+ *
6
+ * @param {number[]} result
7
+ * @param {number} result_offset
8
+ * @param {number} vAx
9
+ * @param {number} vAy
10
+ * @param {number} vAz
11
+ * @param {number} vBx
12
+ * @param {number} vBy
13
+ * @param {number} vBz
14
+ * @param {number} vCx
15
+ * @param {number} vCy
16
+ * @param {number} vCz
17
+ */
18
+ export function v3_compute_triangle_normal(result, result_offset, vAx, vAy, vAz, vBx, vBy, vBz, vCx, vCy, vCz) {
19
+ //compute CB and AB vectors
20
+ const vCBx = vCx - vBx;
21
+ const vCBy = vCy - vBy;
22
+ const vCBz = vCz - vBz;
23
+
24
+ const vABx = vAx - vBx;
25
+ const vABy = vAy - vBy;
26
+ const vABz = vAz - vBz;
27
+
28
+ //compute triangle normal
29
+ const crossX = vCBy * vABz - vCBz * vABy;
30
+ const crossY = vCBz * vABx - vCBx * vABz;
31
+ const crossZ = vCBx * vABy - vCBy * vABx;
32
+
33
+ const l_sqr = v3_length_sqr(crossX, crossY, crossZ);
34
+
35
+ if (l_sqr === 0) {
36
+ // degenerate triangle, provide arbitrary invalid result
37
+ result[result_offset] = 0;
38
+ result[result_offset + 1] = 1;
39
+ result[result_offset + 2] = 0;
40
+ return;
41
+ }
42
+
43
+ const l_inv = 1 / Math.sqrt(l_sqr);
44
+
45
+ const x = crossX * l_inv;
46
+ const y = crossY * l_inv;
47
+ const z = crossZ * l_inv;
48
+
49
+ result[result_offset] = x;
50
+ result[result_offset + 1] = y;
51
+ result[result_offset + 2] = z;
52
+ }
53
+
54
+ /**
55
+ *
56
+ * @param {number[]|vec3} result
57
+ * @param {number[]} vA
58
+ * @param {number[]} vB
59
+ * @param {number[]} vC
60
+ */
61
+ export function compute_triangle_normal(result, vA, vB, vC) {
62
+
63
+ const vAx = vA[0];
64
+ const vAy = vA[1];
65
+ const vAz = vA[2];
66
+
67
+ const vBx = vB[0];
68
+ const vBy = vB[1];
69
+ const vBz = vB[2];
70
+
71
+ const vCx = vC[0];
72
+ const vCy = vC[1];
73
+ const vCz = vC[2];
74
+
75
+ v3_compute_triangle_normal(result, 0, vAx, vAy, vAz, vBx, vBy, vBz, vCx, vCy, vCz);
76
+ }
@@ -211,7 +211,7 @@ async function main(engine) {
211
211
 
212
212
  const aabb_array = [];
213
213
  const position_attribute = geometry.getAttribute('position');
214
- compute_aabb_from_points(aabb_array, position_attribute.array, position_attribute.count, 3);
214
+ compute_aabb_from_points(aabb_array, position_attribute.array, 0, position_attribute.count, 3);
215
215
  const aabb3 = new AABB3(...aabb_array);
216
216
  //
217
217
  // weld_duplicate_vertices(topo, aabb3);
@@ -1,5 +1,5 @@
1
1
  import { v3_dot } from "../../../v3_dot.js";
2
- import { compute_triangle_normal } from "../struct/TopoTriangle.js";
2
+ import { compute_triangle_normal } from "../../compute_triangle_normal.js";
3
3
 
4
4
  const scratch_normal = new Float32Array(3);
5
5
 
@@ -1,8 +1,8 @@
1
1
  import { v3_dot } from "../../../../v3_dot.js";
2
2
  import { fabsf } from "../../../../../math/fabsf.js";
3
3
  import { vec3 } from "gl-matrix";
4
- import { compute_triangle_normal } from "../../struct/TopoTriangle.js";
5
4
  import { EPSILON } from "../../../../../math/EPSILON.js";
5
+ import { compute_triangle_normal } from "../../../compute_triangle_normal.js";
6
6
 
7
7
  const scratch_v3 = new Float32Array(3);
8
8