@woosh/meep-engine 2.48.20 → 2.48.22
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/package.json +1 -1
- package/src/{engine/graphics/micron/build/clustering → core/geom/3d/topology/util}/compute_face_connection_weight.js +40 -35
- package/src/core/graph/build_face_graph_from_mesh.js +1 -1
- package/src/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +0 -3
- package/src/engine/graphics/micron/MICRON_GEOMETRY_FIELD.js +0 -1
- package/src/engine/graphics/micron/MICRON_URI_FIELD.js +0 -1
- package/src/engine/graphics/micron/build/MICRON_PATCH_SIZE_MAX.js +0 -5
- package/src/engine/graphics/micron/build/PatchRepresentation.js +0 -424
- package/src/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +0 -311
- package/src/engine/graphics/micron/build/build_geometry_info.js +0 -21
- package/src/engine/graphics/micron/build/clustering/assignInitialPatchNeighbours.js +0 -63
- package/src/engine/graphics/micron/build/clustering/assign_patch_neighbours_from_topology.js +0 -46
- package/src/engine/graphics/micron/build/clustering/build_clustering_2.js +0 -37
- package/src/engine/graphics/micron/build/clustering/build_leaf_patches.js +0 -326
- package/src/engine/graphics/micron/build/clustering/build_leaf_patches_metis.js +0 -76
- package/src/engine/graphics/micron/build/clustering/computeBorderLengthChange.js +0 -59
- package/src/engine/graphics/micron/build/clustering/computeFaceCurvatureScore.js +0 -57
- package/src/engine/graphics/micron/build/clustering/computeOpenFaceFreedom.js +0 -35
- package/src/engine/graphics/micron/build/clustering/populateOpenFaceNeighboursForPatch.js +0 -56
- package/src/engine/graphics/micron/build/clustering/validate_leaf_patch_connectivity.js +0 -44
- package/src/engine/graphics/micron/build/compute_micron_buffer_array_constructor.js +0 -24
- package/src/engine/graphics/micron/build/compute_vertex_remap_cost.js +0 -29
- package/src/engine/graphics/micron/build/debug/build_clustering_info.js +0 -58
- package/src/engine/graphics/micron/build/debug/build_graph_info.js +0 -59
- package/src/engine/graphics/micron/build/fill_patch_geometry_data.js +0 -270
- package/src/engine/graphics/micron/build/hierarchy/buildAbstractPatchHierarchy.js +0 -196
- package/src/engine/graphics/micron/build/hierarchy/build_intermediate_patch_topology.js +0 -162
- package/src/engine/graphics/micron/build/hierarchy/build_merge_graph.js +0 -89
- package/src/engine/graphics/micron/build/hierarchy/computePatchMergeScore.js +0 -146
- package/src/engine/graphics/micron/build/hierarchy/compute_patches_shared_vertex_count.js +0 -32
- package/src/engine/graphics/micron/build/hierarchy/compute_patches_shared_vertices.js +0 -34
- package/src/engine/graphics/micron/build/hierarchy/merge_patches.js +0 -581
- package/src/engine/graphics/micron/build/hierarchy/metis_cluster_clusters.js +0 -53
- package/src/engine/graphics/micron/build/hierarchy/optimize_graph_partitioning_balance.js +0 -515
- package/src/engine/graphics/micron/build/hierarchy/patch_combine.js +0 -180
- package/src/engine/graphics/micron/build/hierarchy/patch_fill_holes_by_uncollapse.js +0 -60
- package/src/engine/graphics/micron/build/hierarchy/patch_stitch_parent_border.js +0 -320
- package/src/engine/graphics/micron/build/hierarchy/qvdr_build_simplified_clusters.js +0 -547
- package/src/engine/graphics/micron/build/hierarchy/qvdr_build_tree.js +0 -140
- package/src/engine/graphics/micron/buildPatchwork.js +0 -68
- package/src/engine/graphics/micron/convert_three_object_to_micron.js +0 -179
- package/src/engine/graphics/micron/debug/VirtualGeometryStats.js +0 -42
- package/src/engine/graphics/micron/format/MICRON_GEOMETRY_PROPERTY_NAME.js +0 -1
- package/src/engine/graphics/micron/format/MicronGeometry.d.ts +0 -21
- package/src/engine/graphics/micron/format/MicronGeometry.js +0 -334
- package/src/engine/graphics/micron/format/MicronGeometryPatch.d.ts +0 -3
- package/src/engine/graphics/micron/format/MicronGeometryPatch.js +0 -205
- package/src/engine/graphics/micron/format/MicronGeometryPatchOccurance.js +0 -50
- package/src/engine/graphics/micron/format/ThreeMicronMesh.d.ts +0 -10
- package/src/engine/graphics/micron/format/ThreeMicronMesh.js +0 -45
- package/src/engine/graphics/micron/format/VirtualGeometry.js +0 -158
- package/src/engine/graphics/micron/format/micron_build_proxy_geometry.js +0 -205
- package/src/engine/graphics/micron/format/serialization/MicronGeometryBinarySerializationAdapter.js +0 -123
- package/src/engine/graphics/micron/format/serialization/MicronGeometryBinarySerializationAdapter.spec.js +0 -63
- package/src/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.js +0 -83
- package/src/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.spec.js +0 -51
- package/src/engine/graphics/micron/format/serialization/deserialize_attribute_spec.js +0 -25
- package/src/engine/graphics/micron/format/serialization/deserialize_patch.js +0 -106
- package/src/engine/graphics/micron/format/serialization/serialize_attribute_spec.js +0 -18
- package/src/engine/graphics/micron/format/serialization/serialize_patch.js +0 -84
- package/src/engine/graphics/micron/format/validate_patch_bounds.js +0 -69
- package/src/engine/graphics/micron/plugin/GLTFAssetTransformer.js +0 -265
- package/src/engine/graphics/micron/plugin/GLTF_MICRON_ID_FIELD.js +0 -5
- package/src/engine/graphics/micron/plugin/MicronRenderPlugin.d.ts +0 -8
- package/src/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -150
- package/src/engine/graphics/micron/plugin/serialization/BufferGeometrySerializationAdapter.js +0 -176
- package/src/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +0 -216
- package/src/engine/graphics/micron/prototypeMicronProxyBuild.js +0 -227
- package/src/engine/graphics/micron/prototypeVirtualGeometry.js +0 -911
- package/src/engine/graphics/micron/render/PatchCacheKey.js +0 -79
- package/src/engine/graphics/micron/render/VIRTUAL_MESH_FLAG.d.ts +0 -1
- package/src/engine/graphics/micron/render/VIRTUAL_MESH_FLAG.js +0 -1
- package/src/engine/graphics/micron/render/VirtualGeometryBuilder.js +0 -207
- package/src/engine/graphics/micron/render/approximateTriangleArea.js +0 -25
- package/src/engine/graphics/micron/render/culling/PatchCullingSystem.js +0 -129
- package/src/engine/graphics/micron/render/instanced/PatchDataTextures.js +0 -329
- package/src/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +0 -175
- package/src/engine/graphics/micron/render/instanced/shader/constants.js +0 -3
- package/src/engine/graphics/micron/render/instanced/shader/gen_micron_vertex_attribute_texture_name.js +0 -8
- package/src/engine/graphics/micron/render/instanced/shader/shader_rewrite_standard.js +0 -250
- package/src/engine/graphics/micron/render/makeThreeMeshFromVirtualGeometry.js +0 -37
- package/src/engine/graphics/micron/render/refinement/ActivePatchFlags.js +0 -8
- package/src/engine/graphics/micron/render/refinement/ActivePatchList.js +0 -241
- package/src/engine/graphics/micron/render/refinement/ActivePatchRecord.js +0 -154
- package/src/engine/graphics/micron/render/refinement/RefinementSpec.js +0 -84
- package/src/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +0 -133
- package/src/engine/graphics/micron/render/refinement/is_patch_facing_back.js +0 -43
- package/src/engine/graphics/micron/render/refinement/is_patch_visible.js +0 -37
- package/src/engine/graphics/micron/render/v1/MaterialContext.js +0 -150
- package/src/engine/graphics/micron/render/v1/MaterialVertexSpec.js +0 -115
- package/src/engine/graphics/micron/render/v1/MicronRenderContext.js +0 -145
- package/src/engine/graphics/micron/render/v1/ThreeVirtualGeometryAdapter.js +0 -255
- package/src/engine/graphics/micron/render/v1/VGThreeRenderer.js +0 -142
- package/src/engine/graphics/micron/render/v1/getTransformedPositionsCached.js +0 -54
- package/src/engine/graphics/micron/simplifyGeometry.js +0 -26
- package/src/engine/graphics/micron/util/patchToBufferGeometry.js +0 -19
|
@@ -1,547 +0,0 @@
|
|
|
1
|
-
import { array_push_if_unique } from "../../../../../core/collection/array/array_push_if_unique.js";
|
|
2
|
-
import FastBinaryHeap from "../../../../../core/collection/heap/FastBinaryHeap.js";
|
|
3
|
-
import {
|
|
4
|
-
build_vertex_quadratics
|
|
5
|
-
} from "../../../../../core/geom/3d/topology/simplify/quadratic/build_vertex_quadratics.js";
|
|
6
|
-
import { compute_set_of_border_vertex_indices } from "./build_intermediate_patch_topology.js";
|
|
7
|
-
import { compute_patches_shared_vertices } from "./compute_patches_shared_vertices.js";
|
|
8
|
-
import { max2 } from "../../../../../core/math/max2.js";
|
|
9
|
-
import { MICRON_PATCH_SIZE_MAX } from "../MICRON_PATCH_SIZE_MAX.js";
|
|
10
|
-
import { collapse_edges, extract_edge_cost } from "../../../../../core/geom/3d/topology/simplify/simplifyTopoMesh.js";
|
|
11
|
-
import {
|
|
12
|
-
build_edge_collapse_candidates
|
|
13
|
-
} from "../../../../../core/geom/3d/topology/simplify/build_edge_collapse_candidates.js";
|
|
14
|
-
import {
|
|
15
|
-
collapse_all_degenerate_edges
|
|
16
|
-
} from "../../../../../core/geom/3d/topology/simplify/collapse_all_degenerate_edges.js";
|
|
17
|
-
import { compute_set_intersection } from "../../../../../core/collection/set/compute_set_intersection.js";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Cluster must share at least this many vertices for the vertex boundary constraint to be relaxed and allow them to be made dependent on each other
|
|
21
|
-
* @type {number}
|
|
22
|
-
*/
|
|
23
|
-
const BOUNDARY_RELAXATION_SHARED_VERTEX_THRESHOLD = 5;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Common ancestor between patches can be found in a given depth (up-traversal)
|
|
27
|
-
* @param {PatchRepresentation} a
|
|
28
|
-
* @param {PatchRepresentation} b
|
|
29
|
-
* @param {number} depth
|
|
30
|
-
*/
|
|
31
|
-
function patches_share_ancestor_within_depth(a, b, depth) {
|
|
32
|
-
let n0 = a.parent;
|
|
33
|
-
let n1;
|
|
34
|
-
|
|
35
|
-
let d0 = 1;
|
|
36
|
-
let d1 = 1;
|
|
37
|
-
|
|
38
|
-
while (n0 !== null) {
|
|
39
|
-
|
|
40
|
-
n1 = b.parent;
|
|
41
|
-
|
|
42
|
-
while (n1 !== null) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (n0 === n1) {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
n1 = n1.parent;
|
|
50
|
-
|
|
51
|
-
d1++
|
|
52
|
-
|
|
53
|
-
if (d1 >= depth) {
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
n0 = n0.parent;
|
|
59
|
-
|
|
60
|
-
d0++;
|
|
61
|
-
|
|
62
|
-
if (d0 >= depth) {
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// no common ancestor
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* normalized difference in screen-space error between two patches
|
|
73
|
-
* @param {PatchRepresentation} a
|
|
74
|
-
* @param {PatchRepresentation} b
|
|
75
|
-
* @returns {number}
|
|
76
|
-
*/
|
|
77
|
-
function compute_patch_screen_error_difference(a, b) {
|
|
78
|
-
|
|
79
|
-
const a_radius = a.bounding_sphere[3];
|
|
80
|
-
const b_radius = b.bounding_sphere[3];
|
|
81
|
-
|
|
82
|
-
const abs_delta = Math.abs(a_radius - b_radius);
|
|
83
|
-
|
|
84
|
-
return abs_delta / max2(a_radius, b_radius);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* A dependency between A and B will not introduce a chain (unless all the clusters in the chain share vertices)
|
|
89
|
-
* @param {PatchRepresentation} a
|
|
90
|
-
* @param {PatchRepresentation} b
|
|
91
|
-
* @returns {boolean}
|
|
92
|
-
*/
|
|
93
|
-
function creates_dependency_chain(a, b) {
|
|
94
|
-
const a_dependencies = a.dependencies;
|
|
95
|
-
const a_dependencies_count = a_dependencies.length;
|
|
96
|
-
if (a_dependencies_count === 0 && b.dependencies.length === 0) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// get all dependencies
|
|
101
|
-
const open = [a, b];
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
*
|
|
105
|
-
* @type {Set<number>}
|
|
106
|
-
*/
|
|
107
|
-
let shared_neighbours = compute_patches_shared_vertices(a, b);
|
|
108
|
-
|
|
109
|
-
const dependencies_in_chain = new Set();
|
|
110
|
-
|
|
111
|
-
while (open.length > 0) {
|
|
112
|
-
const link = open.pop();
|
|
113
|
-
|
|
114
|
-
dependencies_in_chain.add(link);
|
|
115
|
-
|
|
116
|
-
const link_dependencies = link.dependencies;
|
|
117
|
-
|
|
118
|
-
const link_dependency_count = link_dependencies.length;
|
|
119
|
-
for (let i = 0; i < link_dependency_count; i++) {
|
|
120
|
-
const dep = link_dependencies[i];
|
|
121
|
-
|
|
122
|
-
if (dependencies_in_chain.has(dep)) {
|
|
123
|
-
// already processed
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (open.indexOf(dep) !== -1) {
|
|
128
|
-
// already in the queue
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// check dependency vertices
|
|
133
|
-
shared_neighbours = compute_set_intersection(shared_neighbours, dep.vertices_shared);
|
|
134
|
-
|
|
135
|
-
if (shared_neighbours.size === 0) {
|
|
136
|
-
// creates a chain, not all dependencies share vertices
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
open.push(dep);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return false;
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* @param {TopoMesh} t
|
|
150
|
-
*/
|
|
151
|
-
function cleanup_disconnected_faces(t) {
|
|
152
|
-
|
|
153
|
-
const faces = t.getFaces();
|
|
154
|
-
|
|
155
|
-
for (let face of faces) {
|
|
156
|
-
|
|
157
|
-
if (face.edges.length < 3) {
|
|
158
|
-
// cut
|
|
159
|
-
faces.delete(face);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
*
|
|
166
|
-
* @param {PatchRepresentation} patch
|
|
167
|
-
*/
|
|
168
|
-
function build_patch_topology_mask_from_children(patch) {
|
|
169
|
-
const t = patch.topology_mask;
|
|
170
|
-
|
|
171
|
-
const child_a = patch.children[0];
|
|
172
|
-
const mask_a = child_a.topology_mask;
|
|
173
|
-
t.injectManyFaces(mask_a.getFaces());
|
|
174
|
-
|
|
175
|
-
const child_b = patch.children[1];
|
|
176
|
-
const mask_b = child_b.topology_mask;
|
|
177
|
-
t.injectManyFaces(mask_b.getFaces());
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
// cleanup disconnected faces
|
|
181
|
-
cleanup_disconnected_faces(t);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
*
|
|
186
|
-
* @param {PatchRepresentation} root
|
|
187
|
-
* @param {Map<number,PatchRepresentation[]>} result
|
|
188
|
-
*/
|
|
189
|
-
function map_hierarchy_vertices(root, result) {
|
|
190
|
-
const leaves = root.collectNodes(p => p.lod === 0);
|
|
191
|
-
|
|
192
|
-
for (let i = 0; i < leaves.length; i++) {
|
|
193
|
-
const leaf = leaves[i];
|
|
194
|
-
|
|
195
|
-
const vertices = leaf.topology_mask.vertices;
|
|
196
|
-
|
|
197
|
-
for (let j = 0; j < vertices.length; j++) {
|
|
198
|
-
const vertex = vertices[j];
|
|
199
|
-
|
|
200
|
-
const vertex_index = vertex.index;
|
|
201
|
-
|
|
202
|
-
let participants = result.get(vertex_index);
|
|
203
|
-
|
|
204
|
-
if (participants === undefined) {
|
|
205
|
-
participants = [];
|
|
206
|
-
result.set(vertex_index, participants);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
let p = leaf;
|
|
210
|
-
|
|
211
|
-
while (p !== null) {
|
|
212
|
-
array_push_if_unique(participants, p);
|
|
213
|
-
|
|
214
|
-
p.vertices_included.add(vertex_index);
|
|
215
|
-
|
|
216
|
-
p = p.parent;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
*
|
|
225
|
-
* @param {PatchRepresentation} patch
|
|
226
|
-
* @param {Map<number, PatchRepresentation[]>} vertex_patch_map
|
|
227
|
-
*/
|
|
228
|
-
function compute_border_vertices(patch, vertex_patch_map) {
|
|
229
|
-
|
|
230
|
-
main:for (const v of patch.vertices_included) {
|
|
231
|
-
|
|
232
|
-
const containing_patches = vertex_patch_map.get(v);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
for (const c of containing_patches) {
|
|
236
|
-
if (c.lod > patch.lod) {
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (!c.isDescendantOf(patch)) {
|
|
241
|
-
// interior
|
|
242
|
-
continue main;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
//
|
|
247
|
-
patch.vertices_shared.add(v);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
*
|
|
253
|
-
* @param {PatchRepresentation} root
|
|
254
|
-
* @param {TopoMesh} mesh
|
|
255
|
-
*/
|
|
256
|
-
export function build_simplified_clusters(root, mesh) {
|
|
257
|
-
/**
|
|
258
|
-
*
|
|
259
|
-
* @type {PatchRepresentation[]}
|
|
260
|
-
*/
|
|
261
|
-
const intermediate_nodes = [];
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
*
|
|
265
|
-
* @type {PatchRepresentation[][]}
|
|
266
|
-
*/
|
|
267
|
-
const lod_buckets = [];
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
*
|
|
271
|
-
* @type {Map<number, PatchRepresentation[]>}
|
|
272
|
-
*/
|
|
273
|
-
const vertex_patch_map = new Map();
|
|
274
|
-
|
|
275
|
-
map_hierarchy_vertices(root, vertex_patch_map);
|
|
276
|
-
|
|
277
|
-
root.traverse(n => {
|
|
278
|
-
const lod = n.lod;
|
|
279
|
-
if (lod > 0) {
|
|
280
|
-
intermediate_nodes.push(n);
|
|
281
|
-
|
|
282
|
-
compute_border_vertices(n, vertex_patch_map);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const bucket = lod_buckets[lod];
|
|
286
|
-
|
|
287
|
-
if (bucket !== undefined) {
|
|
288
|
-
bucket.push(n);
|
|
289
|
-
} else {
|
|
290
|
-
lod_buckets[lod] = [n];
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
*
|
|
298
|
-
* @param {PatchRepresentation} patch
|
|
299
|
-
* @returns {number} lower = better fitness
|
|
300
|
-
*/
|
|
301
|
-
function score_by_border(patch) {
|
|
302
|
-
let total_vertex_count = 0;
|
|
303
|
-
const shared_vertices = new Set();
|
|
304
|
-
|
|
305
|
-
for (let i = 0; i < patch.children.length; i++) {
|
|
306
|
-
const child = patch.children[i];
|
|
307
|
-
|
|
308
|
-
child.vertices_shared.forEach(s => {
|
|
309
|
-
if (shared_vertices.has(s)) {
|
|
310
|
-
shared_vertices.delete(s);
|
|
311
|
-
} else {
|
|
312
|
-
shared_vertices.add(s);
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
total_vertex_count += child.topology_mask.vertices.length;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// compute proportion of inner to outer vertices in the combined patch
|
|
320
|
-
const shared_vertex_count = shared_vertices.size;
|
|
321
|
-
const interior_vertices = total_vertex_count - shared_vertex_count;
|
|
322
|
-
|
|
323
|
-
return -shared_vertex_count / (interior_vertices + 0.01);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
*
|
|
328
|
-
* @type {BinaryHeap<PatchRepresentation>}
|
|
329
|
-
*/
|
|
330
|
-
const heap = new FastBinaryHeap(score_by_border);
|
|
331
|
-
|
|
332
|
-
// TODO perform simplification directly on the source mesh
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
// keep global quadratics set
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
*
|
|
339
|
-
* @type {Map<number, Quadratic3>}
|
|
340
|
-
*/
|
|
341
|
-
const vertex_quadratics = new Map();
|
|
342
|
-
|
|
343
|
-
build_vertex_quadratics({ mesh: mesh, quadratics: vertex_quadratics });
|
|
344
|
-
|
|
345
|
-
let total_relaxed_vertex_count = 0;
|
|
346
|
-
let rejection_count_shared_vertices = 0;
|
|
347
|
-
let rejection_count_depth = 0;
|
|
348
|
-
let rejection_count_error = 0;
|
|
349
|
-
let rejection_count_chain = 0;
|
|
350
|
-
|
|
351
|
-
// build simplified meshes for each level of LOD
|
|
352
|
-
for (let i = 1; i < lod_buckets.length; i++) {
|
|
353
|
-
const lod_bucket = lod_buckets[i];
|
|
354
|
-
|
|
355
|
-
const bucket_size = lod_bucket.length;
|
|
356
|
-
|
|
357
|
-
// push children's topology
|
|
358
|
-
for (let j = 0; j < bucket_size; j++) {
|
|
359
|
-
const patch = lod_bucket[j];
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
// build face mask
|
|
363
|
-
build_patch_topology_mask_from_children(patch);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
// push all patches to the queue
|
|
368
|
-
for (let j = 0; j < bucket_size; j++) {
|
|
369
|
-
const patch = lod_bucket[j];
|
|
370
|
-
|
|
371
|
-
heap.push(patch);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
while (!heap.isEmpty()) {
|
|
375
|
-
/**
|
|
376
|
-
*
|
|
377
|
-
* @type {PatchRepresentation}
|
|
378
|
-
*/
|
|
379
|
-
const patch = heap.pop();
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
*
|
|
384
|
-
* @type {Set<number>}
|
|
385
|
-
*/
|
|
386
|
-
const restricted_vertices = new Set();
|
|
387
|
-
|
|
388
|
-
compute_set_of_border_vertex_indices(restricted_vertices, patch.topology_mask);
|
|
389
|
-
|
|
390
|
-
const initial_restricted_vertex_count = restricted_vertices.size;
|
|
391
|
-
|
|
392
|
-
/*
|
|
393
|
-
consider relaxing boundary constraints for this patch
|
|
394
|
-
Relaxing constraints forces both patches to be refined together to assure crack-free boundaries,
|
|
395
|
-
which degrades runtime performance but allows for better mesh simplification
|
|
396
|
-
*/
|
|
397
|
-
const neighbours = patch.neighbours;
|
|
398
|
-
const neighbour_count = neighbours.length;
|
|
399
|
-
const patch_dependencies = patch.dependencies;
|
|
400
|
-
for (let j = 0; j < neighbour_count; j++) {
|
|
401
|
-
// continue;
|
|
402
|
-
const neighbour = neighbours[j];
|
|
403
|
-
|
|
404
|
-
if (!heap.contains(neighbour)) {
|
|
405
|
-
// neighbour is not at the same level in the hierarchy
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (patch.dependencies.indexOf(neighbour) !== -1) {
|
|
410
|
-
// already dependent
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if (compute_patch_screen_error_difference(patch, neighbour) > 0.2) {
|
|
416
|
-
/*
|
|
417
|
-
A and B have similar error-range
|
|
418
|
-
condition discourages dependencies between those clusters that are
|
|
419
|
-
unlikely to be simultaneously present in the ACL at runtime
|
|
420
|
-
*/
|
|
421
|
-
rejection_count_error++;
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (patches_share_ancestor_within_depth(patch, neighbour, 2)) {
|
|
426
|
-
/*
|
|
427
|
-
A and B will not be merged within a small number of levels up the cluster hierarchy.
|
|
428
|
-
This condition avoids creating dependencies between clusters that are resolved within only a few additional hierarchy levels.
|
|
429
|
-
*/
|
|
430
|
-
rejection_count_depth++;
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const shared_vertices = compute_patches_shared_vertices(patch, neighbour);
|
|
436
|
-
const shared_vertex_count = shared_vertices.size;
|
|
437
|
-
|
|
438
|
-
if (shared_vertex_count < BOUNDARY_RELAXATION_SHARED_VERTEX_THRESHOLD) {
|
|
439
|
-
// too few vertices are shared, relaxing boundary constraint is not worth while
|
|
440
|
-
rejection_count_shared_vertices++;
|
|
441
|
-
continue;
|
|
442
|
-
}
|
|
443
|
-
if (creates_dependency_chain(patch, neighbour)) {
|
|
444
|
-
// condition prevents long dependency chains and preserves selective refinement at the cluster level
|
|
445
|
-
rejection_count_chain++;
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// register dependency
|
|
450
|
-
array_push_if_unique(patch_dependencies, neighbour);
|
|
451
|
-
array_push_if_unique(neighbour.dependencies, patch);
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
const dependency_count = patch_dependencies.length;
|
|
456
|
-
|
|
457
|
-
if (dependency_count > 0) {
|
|
458
|
-
// relax vertex constraints
|
|
459
|
-
const vs = [];
|
|
460
|
-
vertex_loop:for (const v of restricted_vertices) {
|
|
461
|
-
const participating_patches = vertex_patch_map.get(v);
|
|
462
|
-
|
|
463
|
-
for (let k = 0; k < dependency_count; k++) {
|
|
464
|
-
const dependency = patch_dependencies[k];
|
|
465
|
-
|
|
466
|
-
if (participating_patches.indexOf(dependency) === -1) {
|
|
467
|
-
continue vertex_loop;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
// vertex is fully dependent, can relax
|
|
473
|
-
restricted_vertices.delete(v);
|
|
474
|
-
vs.push(v);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
for (let j = 0; j < vs.length; j++) {
|
|
478
|
-
const v = vs[j];
|
|
479
|
-
|
|
480
|
-
restricted_vertices.delete(v);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const relaxed_restricted_vertices_count = restricted_vertices.size;
|
|
486
|
-
|
|
487
|
-
const relaxed_vertex_count = initial_restricted_vertex_count - relaxed_restricted_vertices_count;
|
|
488
|
-
|
|
489
|
-
total_relaxed_vertex_count += relaxed_vertex_count;
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const num_desired_faces_to_remove = max2(0, patch.topology_mask.getFaces().size - MICRON_PATCH_SIZE_MAX);
|
|
493
|
-
|
|
494
|
-
if (num_desired_faces_to_remove > 0) {
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
*
|
|
498
|
-
* @type {BinaryHeap<EdgeCollapseCandidate>}
|
|
499
|
-
*/
|
|
500
|
-
const open_set = new FastBinaryHeap(extract_edge_cost);
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
*
|
|
504
|
-
* @type {Map<TopoEdge, EdgeCollapseCandidate>}
|
|
505
|
-
*/
|
|
506
|
-
const edge_to_collapse_map = new Map();
|
|
507
|
-
|
|
508
|
-
build_edge_collapse_candidates(
|
|
509
|
-
patch.topology_mask,
|
|
510
|
-
open_set,
|
|
511
|
-
edge_to_collapse_map,
|
|
512
|
-
vertex_quadratics,
|
|
513
|
-
restricted_vertices
|
|
514
|
-
);
|
|
515
|
-
|
|
516
|
-
// execute mesh simplification
|
|
517
|
-
|
|
518
|
-
collapse_edges(
|
|
519
|
-
num_desired_faces_to_remove,
|
|
520
|
-
open_set,
|
|
521
|
-
[],
|
|
522
|
-
mesh,
|
|
523
|
-
edge_to_collapse_map,
|
|
524
|
-
vertex_quadratics,
|
|
525
|
-
restricted_vertices
|
|
526
|
-
);
|
|
527
|
-
collapse_all_degenerate_edges(patch.topology_mask.getEdges(), mesh);
|
|
528
|
-
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// snapshot patch topology
|
|
534
|
-
for (let j = 0; j < bucket_size; j++) {
|
|
535
|
-
const patch = lod_bucket[j];
|
|
536
|
-
|
|
537
|
-
// cleanup mask
|
|
538
|
-
cleanup_disconnected_faces(patch.topology_mask);
|
|
539
|
-
collapse_all_degenerate_edges(patch.topology_mask.getEdges(), patch.topology_mask);
|
|
540
|
-
|
|
541
|
-
// snapshot the mask
|
|
542
|
-
patch.snapshotTopology();
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { computePatchMergeScore } from "./computePatchMergeScore.js";
|
|
2
|
-
import { array_remove_first } from "../../../../../core/collection/array/array_remove_first.js";
|
|
3
|
-
import { patch_combine } from "./patch_combine.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Ok, so there is no neighbour. No connection between patches,
|
|
7
|
-
* but we need to build a single hierarchy,
|
|
8
|
-
* so let's find the most suitable candidate to merge with
|
|
9
|
-
* @param {PatchRepresentation} source
|
|
10
|
-
* @param {PatchRepresentation[]} pool
|
|
11
|
-
* @returns {number} index of the best candidate
|
|
12
|
-
*/
|
|
13
|
-
function synthesizeNeighbour(source, pool) {
|
|
14
|
-
const open_set_size = pool.length;
|
|
15
|
-
|
|
16
|
-
let best_score = Number.NEGATIVE_INFINITY;
|
|
17
|
-
let best_candidate = -1;
|
|
18
|
-
|
|
19
|
-
for (let i = 0; i < open_set_size; i++) {
|
|
20
|
-
const other = pool[i];
|
|
21
|
-
|
|
22
|
-
const score = computePatchMergeScore(source, other);
|
|
23
|
-
|
|
24
|
-
if (score > best_score) {
|
|
25
|
-
best_candidate = i;
|
|
26
|
-
best_score = score;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return best_candidate;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
* @param {PatchRepresentation} source
|
|
37
|
-
* @param {PatchRepresentation[]} open_set
|
|
38
|
-
* @returns {PatchRepresentation}
|
|
39
|
-
*/
|
|
40
|
-
function pickBestNeighbourForMerge(source, open_set) {
|
|
41
|
-
const neighbours = source.neighbours;
|
|
42
|
-
const neighbour_count = neighbours.length;
|
|
43
|
-
|
|
44
|
-
let best_neighbour_score = Number.NEGATIVE_INFINITY;
|
|
45
|
-
let best_neighbour = undefined;
|
|
46
|
-
|
|
47
|
-
for (let i = 0; i < neighbour_count; i++) {
|
|
48
|
-
const neighbour = neighbours[i];
|
|
49
|
-
|
|
50
|
-
if (open_set.indexOf(neighbour) === -1) {
|
|
51
|
-
// not in open set, already part of the hierarchy
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const score = computePatchMergeScore(source, neighbour);
|
|
56
|
-
|
|
57
|
-
if (score > best_neighbour_score) {
|
|
58
|
-
best_neighbour = neighbour;
|
|
59
|
-
best_neighbour_score = score;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return best_neighbour;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @see QVDR paper
|
|
68
|
-
* @returns {PatchRepresentation}
|
|
69
|
-
* @param {PatchRepresentation[]} leaves
|
|
70
|
-
*/
|
|
71
|
-
function build_tree(leaves) {
|
|
72
|
-
if (leaves.length === 0) {
|
|
73
|
-
throw new Error('Number of leaves must be at least 1, none were given');
|
|
74
|
-
}
|
|
75
|
-
//
|
|
76
|
-
// console.time('build merge graph');
|
|
77
|
-
// const leaf_cluster_weight_graph = build_merge_graph(leaves);
|
|
78
|
-
// console.timeEnd('build merge graph');
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const open_set = leaves.slice();
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
*
|
|
86
|
-
* @type {PatchRepresentation[]}
|
|
87
|
-
*/
|
|
88
|
-
const neighbourless = [];
|
|
89
|
-
|
|
90
|
-
while (open_set.length > 1) {
|
|
91
|
-
const candidate_0 = open_set.shift();
|
|
92
|
-
|
|
93
|
-
// pick neighbour to combine with
|
|
94
|
-
const candidate_1 = pickBestNeighbourForMerge(candidate_0, open_set);
|
|
95
|
-
|
|
96
|
-
if (candidate_1 === undefined) {
|
|
97
|
-
// no neighbour
|
|
98
|
-
neighbourless.push(candidate_0);
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// cut second node from open set
|
|
103
|
-
array_remove_first(open_set, candidate_1);
|
|
104
|
-
|
|
105
|
-
const merged = patch_combine(candidate_0, candidate_1, true);
|
|
106
|
-
|
|
107
|
-
// add to set
|
|
108
|
-
open_set.push(merged);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (neighbourless.length > 0 && open_set.length > 0) {
|
|
112
|
-
// move conventional root to the neighbourless set
|
|
113
|
-
neighbourless.push(open_set[0]);
|
|
114
|
-
|
|
115
|
-
// handle patch groups without neighbours
|
|
116
|
-
while (neighbourless.length > 1) {
|
|
117
|
-
const candidate_0 = neighbourless.shift();
|
|
118
|
-
|
|
119
|
-
const candidate_1_index = synthesizeNeighbour(candidate_0, neighbourless);
|
|
120
|
-
|
|
121
|
-
if (candidate_1_index === -1) {
|
|
122
|
-
// this should never happen
|
|
123
|
-
throw new Error('No candidate found');
|
|
124
|
-
}
|
|
125
|
-
const candidate_1 = neighbourless[candidate_1_index];
|
|
126
|
-
|
|
127
|
-
neighbourless.splice(candidate_1_index, 1);
|
|
128
|
-
|
|
129
|
-
const merged = patch_combine(candidate_0, candidate_1, false);
|
|
130
|
-
|
|
131
|
-
open_set.push(merged);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// move to open set for return
|
|
135
|
-
open_set[0] = neighbourless[0];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return open_set[0];
|
|
140
|
-
}
|