@woosh/meep-engine 2.33.37 → 2.33.41
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/binary/2/BinaryUint32BVH.js +3 -3
- package/core/collection/array/typed/typed_array_copy.js +2 -2
- package/core/geom/3d/aabb/aabb3_matrix4_project_by_corners.js +1 -1
- package/core/geom/3d/morton/mortonEncode_magicbits.js +7 -6
- package/core/geom/3d/morton/v3_morton_encode_transformed.js +15 -11
- package/core/geom/3d/plane/orient3d_fast.js +15 -9
- package/core/geom/3d/tetrahedra/GetDepthForTetrahedronProbe.glsl +43 -0
- package/core/geom/3d/tetrahedra/compute_delaunay_tetrahedral_mesh.js +20 -169
- package/core/geom/3d/tetrahedra/compute_delaunay_tetrahedral_mesh.spec.js +9 -0
- package/core/geom/3d/tetrahedra/tetrahedra_collection.js +454 -0
- package/core/geom/3d/tetrahedra/tetrahedron_contains_point.js +2 -1
- package/core/process/undo/ActionProcessor.js +8 -5
- package/engine/asset/loaders/GLTFAssetLoader.js +4 -8
- package/engine/ecs/System.d.ts +24 -1
- package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +1 -1
- package/engine/graphics/micron/prototypeVirtualGeometry.js +4 -1
- package/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +20 -24
- package/engine/graphics/particles/particular/engine/shader/ShaderManager.js +1 -1
- package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +2 -2
- package/engine/graphics/render/buffer/simple-fx/DepthLimitedBlur.js +9 -4
- package/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +7 -1
- package/engine/graphics/render/forward_plus/LightManager.js +295 -51
- package/engine/graphics/render/forward_plus/LightManager.spec.js +1 -1
- package/engine/graphics/render/forward_plus/PointLightData.js +9 -13
- package/engine/graphics/render/forward_plus/data/TextureBackedMemoryRegion.js +10 -1
- package/engine/graphics/render/forward_plus/debug/LightClusterVolumeMaterial.js +1 -1
- package/engine/graphics/render/forward_plus/debug/buildLightClusterWidget.js +3 -1
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +31 -18
- package/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +1 -0
- package/engine/graphics/render/forward_plus/model/AbstractLight.js +50 -0
- package/engine/graphics/render/forward_plus/model/Decal.js +68 -0
- package/engine/graphics/render/forward_plus/{PointLight.js → model/PointLight.js} +19 -11
- package/engine/graphics/render/forward_plus/{SpotLight.js → model/SpotLight.js} +20 -12
- package/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +6 -5
- package/engine/graphics/render/forward_plus/{prototypeLightManager.js → prototype/prototypeLightManager.js} +100 -25
- package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +29 -20
- package/engine/graphics/render/visibility/IncrementalDeltaSet.d.ts +2 -0
- package/engine/graphics/render/visibility/IncrementalDeltaSet.js +1 -0
- package/engine/graphics/texture/atlas/AbstractTextureAtlas.js +19 -0
- package/engine/graphics/texture/atlas/CachingTextureAtlas.js +99 -0
- package/engine/graphics/texture/atlas/ManagedTextureAtlas.js +19 -39
- package/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +75 -0
- package/engine/graphics/texture/atlas/TextureAtlas.js +22 -2
- package/engine/reference/v2/Reference.d.ts +4 -0
- package/engine/reference/v2/Reference.js +3 -1
- package/package.json +1 -1
|
@@ -434,11 +434,11 @@ export class BinaryUint32BVH {
|
|
|
434
434
|
* Assemble leaf nodes into hierarchy, set binary node bounds iteratively bottom up
|
|
435
435
|
*/
|
|
436
436
|
build() {
|
|
437
|
-
const
|
|
437
|
+
const binary_node_count = this.__node_count_binary;
|
|
438
438
|
|
|
439
|
-
const leaf_node_block_address =
|
|
439
|
+
const leaf_node_block_address = binary_node_count * BVH_BINARY_NODE_SIZE;
|
|
440
440
|
|
|
441
|
-
let level = Math.floor(Math.log(
|
|
441
|
+
let level = Math.floor(Math.log(binary_node_count) / Math.log(2));
|
|
442
442
|
|
|
443
443
|
let i, offset, level_node_count;
|
|
444
444
|
//NOTE: building first level separately allows to avoid some switching logic needed to determine what is the type of lower level node
|
|
@@ -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} source
|
|
7
|
-
* @param {TypedArray|Float32Array} destination
|
|
6
|
+
* @param {TypedArray|Float32Array|Uint8Array} source
|
|
7
|
+
* @param {TypedArray|Float32Array|Uint8Array} destination
|
|
8
8
|
*/
|
|
9
9
|
export function typed_array_copy(source, destination) {
|
|
10
10
|
const destination_size = destination.length;
|
|
@@ -4,7 +4,7 @@ import { assert } from "../../../assert.js";
|
|
|
4
4
|
*
|
|
5
5
|
* @param {number[]|Float32Array} result
|
|
6
6
|
* @param {number[]|Float32Array|Float64Array} aabb_corners
|
|
7
|
-
* @param {number[]|Float32Array} projection_matrix
|
|
7
|
+
* @param {number[]|Float32Array|mat4} projection_matrix
|
|
8
8
|
*/
|
|
9
9
|
export function aabb3_matrix4_project_by_corners(result, aabb_corners, projection_matrix) {
|
|
10
10
|
assert.greaterThanOrEqual(result.length, 6, 'result.length must >= 6');
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* method to separate bits from a given integer 3 positions apart
|
|
3
3
|
* we only look at the first 10 bits
|
|
4
|
+
*
|
|
5
|
+
* @example when input is ABC, output bits are A00B00C
|
|
4
6
|
* @param {number} a
|
|
5
7
|
* @returns {number}
|
|
6
8
|
*/
|
|
7
|
-
function
|
|
9
|
+
function split_by_3(a) {
|
|
8
10
|
let x = a;
|
|
9
11
|
x = (x | x << 16) & 0x30000ff;
|
|
10
12
|
x = (x | x << 8) & 0x0300f00f;
|
|
@@ -25,10 +27,9 @@ function splitBy3(a) {
|
|
|
25
27
|
*/
|
|
26
28
|
export function mortonEncode_magicbits(x, y, z) {
|
|
27
29
|
|
|
28
|
-
const x_bits =
|
|
29
|
-
const y_bits =
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
return x_bits | y_bits | z_bit;
|
|
30
|
+
const x_bits = split_by_3(x);
|
|
31
|
+
const y_bits = split_by_3(y) << 1;
|
|
32
|
+
const z_bits = split_by_3(z) << 2;
|
|
33
33
|
|
|
34
|
+
return x_bits | y_bits | z_bits;
|
|
34
35
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mortonEncode_magicbits } from "./mortonEncode_magicbits.js";
|
|
2
|
+
import { clamp } from "../../../math/clamp.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Assumes that coordinates will be mapped to -1..1 range after transform
|
|
@@ -16,18 +17,21 @@ export function v3_morton_encode_transformed(x, y, z, matrix) {
|
|
|
16
17
|
const _z = matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14];
|
|
17
18
|
const _w = matrix[3] * x + matrix[7] * y + matrix[11] * z + matrix[15];
|
|
18
19
|
|
|
19
|
-
const d =
|
|
20
|
+
const d = 511.5 / _w;
|
|
20
21
|
|
|
21
|
-
// apply perspective divide
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
22
|
+
// apply perspective divide as well as scaling to 10 bits
|
|
23
|
+
const ndc_x = _x * d;
|
|
24
|
+
const ndc_y = _y * d;
|
|
25
|
+
const ndc_z = _z * d;
|
|
25
26
|
|
|
26
|
-
//
|
|
27
|
+
// Normalize values (0,1023) and clamp to prevent wrapping
|
|
28
|
+
const px = clamp(ndc_x + 511.5, 0, 1023);
|
|
29
|
+
const py = clamp(ndc_y + 511.5, 0, 1023);
|
|
30
|
+
const pz = clamp(ndc_z + 511.5, 0, 1023);
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
return mortonEncode_magicbits(
|
|
33
|
+
Math.round(px),
|
|
34
|
+
Math.round(py),
|
|
35
|
+
Math.round(pz)
|
|
36
|
+
);
|
|
33
37
|
}
|
|
@@ -21,23 +21,29 @@ export function orient3d_fast(points, a, b, c, d) {
|
|
|
21
21
|
const a3 = a * 3;
|
|
22
22
|
const d3 = d * 3;
|
|
23
23
|
|
|
24
|
-
const
|
|
24
|
+
const d_x = points[d3];
|
|
25
|
+
|
|
26
|
+
const adx = points[a3] - d_x;
|
|
25
27
|
|
|
26
28
|
const b3 = b * 3;
|
|
27
29
|
|
|
28
|
-
const bdx = points[b3] -
|
|
30
|
+
const bdx = points[b3] - d_x;
|
|
29
31
|
|
|
30
32
|
const c3 = c * 3;
|
|
31
33
|
|
|
32
|
-
const cdx = points[c3] -
|
|
34
|
+
const cdx = points[c3] - d_x;
|
|
35
|
+
|
|
36
|
+
const d_y = points[d3 + 1];
|
|
37
|
+
|
|
38
|
+
const ady = points[a3 + 1] - d_y;
|
|
39
|
+
const bdy = points[b3 + 1] - d_y;
|
|
40
|
+
const cdy = points[c3 + 1] - d_y;
|
|
33
41
|
|
|
34
|
-
const
|
|
35
|
-
const bdy = points[b3 + 1] - points[d3 + 1];
|
|
36
|
-
const cdy = points[c3 + 1] - points[d3 + 1];
|
|
42
|
+
const d_z = points[d3 + 2];
|
|
37
43
|
|
|
38
|
-
const adz = points[a3 + 2] -
|
|
39
|
-
const bdz = points[b3 + 2] -
|
|
40
|
-
const cdz = points[c3 + 2] -
|
|
44
|
+
const adz = points[a3 + 2] - d_z;
|
|
45
|
+
const bdz = points[b3 + 2] - d_z;
|
|
46
|
+
const cdz = points[c3 + 2] - d_z;
|
|
41
47
|
|
|
42
48
|
return adx * (bdy * cdz - bdz * cdy)
|
|
43
49
|
+ bdx * (cdy * adz - cdz * ady)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Sample triangular depth map
|
|
2
|
+
// see CoD SIGGRAPH 2017 "Advances in Real-Time Graphics in Games" course
|
|
3
|
+
float GetDepthForTetrahedronProbe(float2 bary, uint4 encodedDepth)
|
|
4
|
+
{
|
|
5
|
+
float sampleY = floor(bary.y * 4.0f);
|
|
6
|
+
float sampleX = floor(bary.x * 4.0f);
|
|
7
|
+
|
|
8
|
+
int baseIndex = (int)(((11.0f - sampleY) * sampleY) * 0.5f + sampleX + 0.5f);
|
|
9
|
+
int lineLength = 5 - (int)sampleY;
|
|
10
|
+
|
|
11
|
+
float2 baseBary = float2(sampleX * 0.25f, sampleY * 0.25f);
|
|
12
|
+
|
|
13
|
+
int3 indices = int3(baseIndex + 1, baseIndex + lineLength, baseIndex);
|
|
14
|
+
|
|
15
|
+
float3 sampleBary;
|
|
16
|
+
sampleBary.x = (bary.x - baseBary.x) * 4.0f;
|
|
17
|
+
sampleBary.y = (bary.y - baseBary.y) * 4.0f;
|
|
18
|
+
|
|
19
|
+
if (sampleBary.x + sampleBary.y > 1.0f)
|
|
20
|
+
{
|
|
21
|
+
// above the diagonal
|
|
22
|
+
indices = int3(baseIndex + lineLength, baseIndex + 1, baseIndex + lineLength + 1);
|
|
23
|
+
|
|
24
|
+
float3 newSampleBary;
|
|
25
|
+
newSampleBary.x = -(sampleBary.y - 1.0f);
|
|
26
|
+
newSampleBary.y = -(sampleBary.x - 1.0f);
|
|
27
|
+
sampleBary = newSampleBary;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
sampleBary.z = 1.0f - sampleBary.x - sampleBary.y;
|
|
31
|
+
|
|
32
|
+
float result = 0.0f;
|
|
33
|
+
for (int i=0; i<3; ++i)
|
|
34
|
+
{
|
|
35
|
+
uint currIndex = indices[i];
|
|
36
|
+
int intDepth = (encodedDepth[currIndex / 4] >> ((currIndex % 4) * 8)) & 0xFF;
|
|
37
|
+
float depth = (float)intDepth / 255.0f;
|
|
38
|
+
|
|
39
|
+
result += sampleBary[i] * depth;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
@@ -1,199 +1,50 @@
|
|
|
1
1
|
//
|
|
2
2
|
|
|
3
3
|
import { compute_bounding_simplex_3d } from "./compute_bounding_simplex_3d.js";
|
|
4
|
-
import {
|
|
5
|
-
import { tetrahedron_contains_point } from "./tetrahedron_contains_point.js";
|
|
6
|
-
import { assert } from "../../../assert.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @readonly
|
|
10
|
-
* @type {number}
|
|
11
|
-
*/
|
|
12
|
-
const LAYOUT_TETRA_BYTE_SIZE = 9 * 4;
|
|
13
|
-
const INVALID_NEIGHBOUR = 0xFFFFFFFF;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
*
|
|
17
|
-
* Layout:
|
|
18
|
-
* vertex_id_a :: uint
|
|
19
|
-
* vertex_id_b :: uint
|
|
20
|
-
* vertex_id_c :: uint
|
|
21
|
-
* vertex_id_d :: uint
|
|
22
|
-
* neighbour_a :: uint - neighbour tetrahedron, opposite to vertex A
|
|
23
|
-
* neighbour_b :: uint - neighbour tetrahedron, opposite to vertex B
|
|
24
|
-
* neighbour_c :: uint - neighbour tetrahedron, opposite to vertex C
|
|
25
|
-
* neighbour_d :: uint - neighbour tetrahedron, opposite to vertex D
|
|
26
|
-
* sub_determinant :: float - ?
|
|
27
|
-
* Layout is similar to [1], but is interleaved for better cache locality
|
|
28
|
-
*/
|
|
29
|
-
class TetrahedraCollection {
|
|
30
|
-
constructor(initial_size) {
|
|
31
|
-
this.__buffer = new ArrayBuffer(initial_size * 4 * 9);
|
|
32
|
-
|
|
33
|
-
this.__view = new DataView(this.__buffer);
|
|
34
|
-
|
|
35
|
-
this.__capacity = initial_size;
|
|
36
|
-
this.__length = 0;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
* @param {number} tetra_index
|
|
42
|
-
* @param {number} neighbour_index
|
|
43
|
-
* @returns {number}
|
|
44
|
-
*/
|
|
45
|
-
getNeighbour(tetra_index, neighbour_index) {
|
|
46
|
-
const tetra_address = LAYOUT_TETRA_BYTE_SIZE * tetra_index;
|
|
47
|
-
return this.__view.getUint32(tetra_address + (4 + neighbour_index) * 4);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
-
* @param {number[]|Float32Array} points
|
|
53
|
-
* @param {number} a
|
|
54
|
-
* @param {number} b
|
|
55
|
-
* @param {number} c
|
|
56
|
-
* @param {number} d
|
|
57
|
-
*/
|
|
58
|
-
append(points, a, b, c, d) {
|
|
59
|
-
const address = this.__length * LAYOUT_TETRA_BYTE_SIZE;
|
|
60
|
-
|
|
61
|
-
// TODO check if there's enough room
|
|
62
|
-
|
|
63
|
-
const view = this.__view;
|
|
64
|
-
|
|
65
|
-
view.setUint32(address, a);
|
|
66
|
-
view.setUint32(address + 4, b);
|
|
67
|
-
view.setUint32(address + 8, c);
|
|
68
|
-
view.setUint32(address + 12, d);
|
|
69
|
-
|
|
70
|
-
// set neighbours
|
|
71
|
-
view.setUint32(address + 16, INVALID_NEIGHBOUR);
|
|
72
|
-
view.setUint32(address + 20, INVALID_NEIGHBOUR);
|
|
73
|
-
view.setUint32(address + 24, INVALID_NEIGHBOUR);
|
|
74
|
-
view.setUint32(address + 28, INVALID_NEIGHBOUR);
|
|
75
|
-
|
|
76
|
-
this.__length++;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
*
|
|
81
|
-
* @param {number[]} points
|
|
82
|
-
* @param {number} index
|
|
83
|
-
* @returns {number} index of tetra or -1 if no containing tetra found
|
|
84
|
-
*/
|
|
85
|
-
findTetraContainingPoint(points, index) {
|
|
86
|
-
const data_view = this.__view;
|
|
87
|
-
|
|
88
|
-
// TODO improve this
|
|
89
|
-
const n = this.__length;
|
|
90
|
-
for (let i = 0; i < n; i++) {
|
|
91
|
-
|
|
92
|
-
const tet_address = i * LAYOUT_TETRA_BYTE_SIZE;
|
|
93
|
-
|
|
94
|
-
const a = data_view.getUint32(tet_address);
|
|
95
|
-
const b = data_view.getUint32(tet_address + 4);
|
|
96
|
-
const c = data_view.getUint32(tet_address + 8);
|
|
97
|
-
const d = data_view.getUint32(tet_address + 12);
|
|
98
|
-
|
|
99
|
-
if (tetrahedron_contains_point(points, a, b, c, d, index)) {
|
|
100
|
-
return i;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return -1;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
*
|
|
109
|
-
* @param {number[]|Float32Array} points
|
|
110
|
-
* @param {number} index
|
|
111
|
-
*/
|
|
112
|
-
insertPoint(points, index) {
|
|
113
|
-
|
|
114
|
-
// 1. walk to tetrahedron that contains this point
|
|
115
|
-
const containing_tetra = this.findTetraContainingPoint(points, index);
|
|
116
|
-
|
|
117
|
-
assert.isNonNegativeInteger(containing_tetra, 'containing_tetra');
|
|
118
|
-
|
|
119
|
-
const cavity = [];
|
|
120
|
-
const open_set = [
|
|
121
|
-
containing_tetra
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
const data_view = this.__view;
|
|
125
|
-
|
|
126
|
-
// 2. find the Delaunay "cavity", a set of tetrahedrons who's circumsphere overlarlaps the point
|
|
127
|
-
// perform breadth-first expansion on the containing tetra to identify the cavity
|
|
128
|
-
while (open_set.length > 0) {
|
|
129
|
-
const tetra = open_set.pop();
|
|
130
|
-
cavity.push(tetra);
|
|
131
|
-
|
|
132
|
-
// get neighbours
|
|
133
|
-
for (let i = 0; i < 4; i++) {
|
|
134
|
-
const neighbour = this.getNeighbour(tetra, i);
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
(neighbour === INVALID_NEIGHBOUR) // no neighbour
|
|
138
|
-
|| cavity.includes(neighbour) // already visited
|
|
139
|
-
|| open_set.includes(neighbour) // already in open set
|
|
140
|
-
) {
|
|
141
|
-
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const neighbour_address = neighbour * LAYOUT_TETRA_BYTE_SIZE;
|
|
146
|
-
const a = data_view.getUint32(neighbour_address);
|
|
147
|
-
const b = data_view.getUint32(neighbour_address + 4);
|
|
148
|
-
const c = data_view.getUint32(neighbour_address + 8);
|
|
149
|
-
const d = data_view.getUint32(neighbour_address + 12);
|
|
150
|
-
|
|
151
|
-
// check neighbour
|
|
152
|
-
const is_in_sphere = point_in_tetrahedron_circumsphere(points, a, b, c, d, index);
|
|
153
|
-
|
|
154
|
-
if (is_in_sphere) {
|
|
155
|
-
open_set.push(neighbour);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// 3. remove the cavity tetrahedrons
|
|
161
|
-
|
|
162
|
-
// 4. fill the cavity by creating new tetrahedrons connecting the point to the faces of cavity boundary
|
|
163
|
-
}
|
|
164
|
-
}
|
|
4
|
+
import { TetrahedraCollection } from "./tetrahedra_collection.js";
|
|
165
5
|
|
|
166
6
|
/**
|
|
167
7
|
* @see https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm
|
|
168
8
|
* @see [1] "One machine, one minute, three billion tetrahedra" by Célestin Marot et al
|
|
169
9
|
* @param {number[]} input serialized set of 3d points (x,y,z)
|
|
170
|
-
* @param {number}
|
|
10
|
+
* @param {number} n number of points in the input
|
|
171
11
|
*/
|
|
172
|
-
export function compute_delaunay_tetrahedral_mesh(input,
|
|
12
|
+
export function compute_delaunay_tetrahedral_mesh(input, n) {
|
|
13
|
+
if (n < 4) {
|
|
14
|
+
// no mesh can be created, too few points
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
173
18
|
/**
|
|
174
19
|
* According to [1] number of tetrahedra is around 6 times greater than number of input vertices, we use that fact to pre-allocate memory
|
|
20
|
+
* From the book of Cheng et al. 'Delaunay Mesh Generation' A triangulation of n vertices in R^3 has at most `ntet=(n^2 − 3n − 2)/2` tetrahedra,
|
|
21
|
+
* at most nedg=(n^2 − n)/2` edges (this is simply the 2-combination of n)
|
|
22
|
+
* and at most `ntri=n^2 − 3n = 2*ntet+2` triangles
|
|
23
|
+
*
|
|
24
|
+
* The +7 is for the initial "super" tetrahedron and 6 tetras for intermediate triangulation
|
|
175
25
|
*/
|
|
176
|
-
const
|
|
26
|
+
const tetrahedron_count_upper_bound = Math.ceil((n * n - 3 * n - 2) / 2) + 7;
|
|
27
|
+
const tetrahedrons = new TetrahedraCollection(tetrahedron_count_upper_bound);
|
|
177
28
|
// TODO apply spatial sort to point, to get better cache locality
|
|
178
29
|
|
|
179
30
|
// compute bounds over the input
|
|
180
|
-
const points = new Float32Array((
|
|
31
|
+
const points = new Float32Array((n + 4) * 3);
|
|
181
32
|
|
|
182
33
|
points.set(input);
|
|
183
34
|
|
|
184
35
|
/*
|
|
185
36
|
create super bounding volume
|
|
186
37
|
*/
|
|
187
|
-
compute_bounding_simplex_3d(points,
|
|
38
|
+
compute_bounding_simplex_3d(points, n * 3, points, n, 1);
|
|
188
39
|
|
|
189
|
-
tetrahedrons.append(points,
|
|
40
|
+
tetrahedrons.append(points, n, n + 1, n + 2, n + 3);
|
|
190
41
|
|
|
191
42
|
// add all points to the mesh
|
|
192
|
-
for (let i = 0; i <
|
|
43
|
+
for (let i = 0; i < n; i++) {
|
|
193
44
|
tetrahedrons.insertPoint(points, i);
|
|
194
45
|
}
|
|
195
46
|
|
|
196
47
|
// remove tetras formed by with the super bounding volume
|
|
197
|
-
|
|
48
|
+
tetrahedrons.removeTetrasConnectedToPoints(points, n, n + 3);
|
|
198
49
|
}
|
|
199
50
|
|
|
@@ -3,3 +3,12 @@ import { compute_delaunay_tetrahedral_mesh } from "./compute_delaunay_tetrahedra
|
|
|
3
3
|
test('single point', () => {
|
|
4
4
|
compute_delaunay_tetrahedral_mesh([1, 2, 3], 1);
|
|
5
5
|
});
|
|
6
|
+
|
|
7
|
+
test('exactly 4 points', () => {
|
|
8
|
+
compute_delaunay_tetrahedral_mesh([
|
|
9
|
+
-1, 0, 0,
|
|
10
|
+
1, 0, 0,
|
|
11
|
+
0.5, 1, 0,
|
|
12
|
+
0.5, 0, 1
|
|
13
|
+
], 4);
|
|
14
|
+
});
|