@woosh/meep-engine 2.76.4 → 2.77.0
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/build/meep.cjs +197 -616
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +197 -616
- package/editor/view/ecs/components/TerrainController.js +9 -16
- package/package.json +1 -1
- package/src/core/collection/heap/Uint32Heap.js +10 -1
- package/src/core/graph/Edge.js +20 -0
- package/src/core/graph/Graph.js +1 -0
- package/src/core/graph/SquareMatrix.js +4 -2
- package/src/core/graph/WeightedEdge.js +5 -9
- package/src/core/graph/coloring/validateGraphColoring.js +1 -1
- package/src/core/graph/eigen/matrix_eigenvalues_in_place.js +21 -0
- package/src/core/graph/eigen/{eigen.spec.js → matrix_eigenvalues_in_place.spec.js} +2 -2
- package/src/core/graph/eigen/matrix_householder_in_place.js +92 -0
- package/src/core/graph/eigen/{eigen.js → matrix_qr_in_place.js} +2 -113
- package/src/core/graph/v2/Graph.js +16 -9
- package/src/core/graph/v2/NodeContainer.js +120 -22
- package/src/engine/ecs/storage/binary/BinarySerializationRegistry.js +8 -6
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModuleRegistry.js +1 -1
- package/src/engine/navigation/grid/find_path_on_grid_astar.js +25 -22
- package/src/engine/navigation/grid/find_path_on_grid_astar.spec.js +2 -2
- package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +17 -33
- package/src/core/graph/GraphUtils.js +0 -284
- package/src/engine/ecs/terrain/ecs/splat/SplatMapMaterialPatch.js +0 -464
- package/src/engine/ecs/terrain/ecs/splat/SplatMapOptimizer.js +0 -622
- package/src/engine/ecs/terrain/ecs/splat/SplatMapOptimizerDebugger.js +0 -383
|
@@ -1,622 +0,0 @@
|
|
|
1
|
-
import Graph from "../../../../../core/graph/Graph.js";
|
|
2
|
-
import { QuadTreeNode } from "../../../../../core/geom/2d/quad-tree/QuadTreeNode.js";
|
|
3
|
-
import { BitSet } from "../../../../../core/binary/BitSet.js";
|
|
4
|
-
import { SplatMapMaterialPatch } from "./SplatMapMaterialPatch.js";
|
|
5
|
-
import { colorizeGraphGreedyWeight } from "../../../../../core/graph/coloring/colorizeGraphGreedyWeight.js";
|
|
6
|
-
import { validateGraphColoring } from "../../../../../core/graph/coloring/validateGraphColoring.js";
|
|
7
|
-
import ConcurrentExecutor from "../../../../../core/process/executor/ConcurrentExecutor.js";
|
|
8
|
-
import Task from "../../../../../core/process/task/Task.js";
|
|
9
|
-
import { TaskSignal } from "../../../../../core/process/task/TaskSignal.js";
|
|
10
|
-
import { actionTask } from "../../../../../core/process/task/util/actionTask.js";
|
|
11
|
-
import { countTask } from "../../../../../core/process/task/util/countTask.js";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* We convert splat mapping into a graph of connecting material patches and solve the overlaps as a "Graph Coloring" problem
|
|
15
|
-
*/
|
|
16
|
-
export class SplatMapOptimizer {
|
|
17
|
-
constructor() {
|
|
18
|
-
/**
|
|
19
|
-
*
|
|
20
|
-
* @type {Graph<SplatMapMaterialPatch>}
|
|
21
|
-
*/
|
|
22
|
-
this.graph = new Graph();
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Contains patches to speed up lookup
|
|
26
|
-
* @type {QuadTreeNode<SplatMapMaterialPatch>}
|
|
27
|
-
*/
|
|
28
|
-
this.quadTree = new QuadTreeNode();
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
32
|
-
* @type {Uint8Array}
|
|
33
|
-
*/
|
|
34
|
-
this.ranking = null;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
*
|
|
38
|
-
* @type {SplatMapping}
|
|
39
|
-
*/
|
|
40
|
-
this.mapping = null;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
44
|
-
* @type {BitSet}
|
|
45
|
-
*/
|
|
46
|
-
this.marks = new BitSet();
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
-
* @param {SplatMapping} mapping
|
|
53
|
-
* @return {Promise}
|
|
54
|
-
*/
|
|
55
|
-
static optimizeSynchronous(mapping) {
|
|
56
|
-
|
|
57
|
-
const optimizer = new SplatMapOptimizer();
|
|
58
|
-
|
|
59
|
-
optimizer.mapping = mapping;
|
|
60
|
-
|
|
61
|
-
const tasks = optimizer.optimize();
|
|
62
|
-
|
|
63
|
-
const executor = new ConcurrentExecutor(0, Number.POSITIVE_INFINITY);
|
|
64
|
-
|
|
65
|
-
executor.runMany(tasks);
|
|
66
|
-
|
|
67
|
-
return Task.promiseAll(tasks);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @returns {Task[]}
|
|
72
|
-
*/
|
|
73
|
-
optimize() {
|
|
74
|
-
|
|
75
|
-
const result = [];
|
|
76
|
-
|
|
77
|
-
const tInitialize = this.initialize();
|
|
78
|
-
|
|
79
|
-
Array.prototype.push.apply(result, tInitialize);
|
|
80
|
-
|
|
81
|
-
// optimizer.removePatchesSmallerThan(100);
|
|
82
|
-
|
|
83
|
-
const self = this;
|
|
84
|
-
|
|
85
|
-
const tMergePatches = new Task({
|
|
86
|
-
name: 'Merge redundant patches',
|
|
87
|
-
cycleFunction() {
|
|
88
|
-
const removedPatchCount = self.mergeRedundantPatches();
|
|
89
|
-
|
|
90
|
-
if (removedPatchCount === 0) {
|
|
91
|
-
return TaskSignal.EndSuccess;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return TaskSignal.Continue;
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
tMergePatches.addDependencies(tInitialize);
|
|
99
|
-
|
|
100
|
-
result.push(tMergePatches);
|
|
101
|
-
|
|
102
|
-
const tSolve = actionTask(() => this.solve());
|
|
103
|
-
|
|
104
|
-
tSolve.addDependency(tMergePatches);
|
|
105
|
-
tSolve.addDependencies(tInitialize);
|
|
106
|
-
|
|
107
|
-
result.push(tSolve);
|
|
108
|
-
|
|
109
|
-
return result;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
*
|
|
115
|
-
* @param {SplatMapMaterialPatch} patch
|
|
116
|
-
*/
|
|
117
|
-
removePatch(patch) {
|
|
118
|
-
|
|
119
|
-
this.graph.removeNode(patch);
|
|
120
|
-
patch.quad.disconnect();
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
removeNoisePatches(threshold = 0.05) {
|
|
125
|
-
|
|
126
|
-
const nodes = this.graph.nodes;
|
|
127
|
-
const n = nodes.length;
|
|
128
|
-
|
|
129
|
-
const garbage = [];
|
|
130
|
-
|
|
131
|
-
const depth = this.mapping.depth;
|
|
132
|
-
const weightData = this.mapping.weightData;
|
|
133
|
-
|
|
134
|
-
for (let i = 0; i < n; i++) {
|
|
135
|
-
const node = nodes[i];
|
|
136
|
-
|
|
137
|
-
const contribution = node.computeMaxWeightContribution(weightData, depth);
|
|
138
|
-
|
|
139
|
-
if (contribution < threshold) {
|
|
140
|
-
garbage.push(node);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
for (let i = 0; i < garbage.length; i++) {
|
|
145
|
-
this.removePatch(garbage[i]);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
*
|
|
152
|
-
* @param {number} area
|
|
153
|
-
* @param {number} contributionThreshold
|
|
154
|
-
*/
|
|
155
|
-
removePatchesSmallerThan(area, contributionThreshold = 0.9) {
|
|
156
|
-
|
|
157
|
-
const nodes = this.graph.nodes;
|
|
158
|
-
const n = nodes.length;
|
|
159
|
-
|
|
160
|
-
const garbage = [];
|
|
161
|
-
|
|
162
|
-
const depth = this.mapping.depth;
|
|
163
|
-
const weightData = this.mapping.weightData;
|
|
164
|
-
|
|
165
|
-
for (let i = 0; i < n; i++) {
|
|
166
|
-
|
|
167
|
-
const node = nodes[i];
|
|
168
|
-
|
|
169
|
-
if (node.area >= area) {
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const contribution = node.computeMaxWeightContribution(weightData, depth);
|
|
174
|
-
|
|
175
|
-
if (contribution < contributionThreshold) {
|
|
176
|
-
garbage.push(node);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
for (let i = 0; i < garbage.length; i++) {
|
|
181
|
-
this.removePatch(garbage[i]);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* NOTE: uses flooding algorithm to build a patch
|
|
188
|
-
* @param {number} materialIndex
|
|
189
|
-
* @param {number} start_x
|
|
190
|
-
* @param {number} start_y
|
|
191
|
-
* @returns {SplatMapMaterialPatch}
|
|
192
|
-
*/
|
|
193
|
-
buildPatch(materialIndex, start_x, start_y) {
|
|
194
|
-
|
|
195
|
-
const mapping = this.mapping;
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
*
|
|
199
|
-
* @type {Uint8Array}
|
|
200
|
-
*/
|
|
201
|
-
const ranking = this.ranking;
|
|
202
|
-
|
|
203
|
-
const width = mapping.size.x;
|
|
204
|
-
const height = mapping.size.y;
|
|
205
|
-
|
|
206
|
-
const layerSize = width * height;
|
|
207
|
-
|
|
208
|
-
const targetLayerAddress = layerSize * materialIndex;
|
|
209
|
-
|
|
210
|
-
const patch = new SplatMapMaterialPatch(width, height);
|
|
211
|
-
|
|
212
|
-
patch.materialIndex = materialIndex;
|
|
213
|
-
patch.mask.reset();
|
|
214
|
-
patch.aabb.setNegativelyInfiniteBounds();
|
|
215
|
-
|
|
216
|
-
const weightData = mapping.weightData;
|
|
217
|
-
|
|
218
|
-
const open = [start_y * width + start_x];
|
|
219
|
-
|
|
220
|
-
const closed = new BitSet();
|
|
221
|
-
|
|
222
|
-
const marks = this.marks;
|
|
223
|
-
|
|
224
|
-
main: while (open.length > 0) {
|
|
225
|
-
const index = open.pop();
|
|
226
|
-
closed.set(index, true);
|
|
227
|
-
|
|
228
|
-
const address = index + targetLayerAddress;
|
|
229
|
-
|
|
230
|
-
//sample ranking of the patch texel
|
|
231
|
-
const rank = ranking[address];
|
|
232
|
-
|
|
233
|
-
if (rank > 3) {
|
|
234
|
-
//will not fit in the material map, ignore
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const weight = weightData[address];
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
marks.set(address, true);
|
|
242
|
-
|
|
243
|
-
const x = index % width;
|
|
244
|
-
const y = (index / width) | 0;
|
|
245
|
-
|
|
246
|
-
patch.aabb._expandToFit(x, y, x, y);
|
|
247
|
-
patch.mask.set(index, true);
|
|
248
|
-
patch.area++;
|
|
249
|
-
|
|
250
|
-
if (weight === 0) {
|
|
251
|
-
//tile has weight of 0, don't bother about its neighbours
|
|
252
|
-
continue main;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
//build neighbours
|
|
256
|
-
if (y > 0) {
|
|
257
|
-
const n2 = index - width;
|
|
258
|
-
|
|
259
|
-
if (!closed.get(n2)) {
|
|
260
|
-
open.push(n2);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (y < height - 1) {
|
|
265
|
-
const n3 = index + width;
|
|
266
|
-
|
|
267
|
-
if (!closed.get(n3)) {
|
|
268
|
-
open.push(n3);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (x > 0) {
|
|
273
|
-
const n0 = index - 1;
|
|
274
|
-
|
|
275
|
-
if (!closed.get(n0)) {
|
|
276
|
-
open.push(n0);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (x < width - 1) {
|
|
281
|
-
const n1 = index + 1;
|
|
282
|
-
|
|
283
|
-
if (!closed.get(n1)) {
|
|
284
|
-
open.push(n1);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
continue main;
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return patch;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
mergeRedundantPatches() {
|
|
296
|
-
const graph = this.graph;
|
|
297
|
-
const nodes = graph.nodes;
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
*
|
|
301
|
-
* @type {SplatMapMaterialPatch[][]}
|
|
302
|
-
*/
|
|
303
|
-
const chains = [];
|
|
304
|
-
|
|
305
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
306
|
-
const patch = nodes[i];
|
|
307
|
-
|
|
308
|
-
const chain = [patch];
|
|
309
|
-
|
|
310
|
-
graph.traverseSuccessors(patch, n => {
|
|
311
|
-
if (n === patch) {
|
|
312
|
-
//skip edges to self
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (n.materialIndex === patch.materialIndex) {
|
|
317
|
-
|
|
318
|
-
for (let j = 0; j < chains.length; j++) {
|
|
319
|
-
const chain = chains[j];
|
|
320
|
-
|
|
321
|
-
if (chain.indexOf(n) !== -1) {
|
|
322
|
-
//already a part of a chain, skip
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
chain.push(n);
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
if (chain.length > 1) {
|
|
333
|
-
chains.push(chain);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
//merge chains
|
|
338
|
-
let chainCount = chains.length;
|
|
339
|
-
for (let i = 0; i < chainCount; i++) {
|
|
340
|
-
const chain_0 = chains[i];
|
|
341
|
-
|
|
342
|
-
let chain_0_length = chain_0.length;
|
|
343
|
-
|
|
344
|
-
for (let j = 0; j < chain_0_length; j++) {
|
|
345
|
-
const link_0 = chain_0[j];
|
|
346
|
-
|
|
347
|
-
for (let k = i + 1; k < chainCount; k++) {
|
|
348
|
-
const chain_1 = chains[k];
|
|
349
|
-
|
|
350
|
-
const link_1_index = chain_1.indexOf(link_0);
|
|
351
|
-
|
|
352
|
-
if (link_1_index === -1) {
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
//found a match, lets merge the chains
|
|
357
|
-
for (let m = 0; m < chain_1.length; m++) {
|
|
358
|
-
const link_1 = chain_1[m];
|
|
359
|
-
|
|
360
|
-
if (chain_0.indexOf(link_1) === -1) {
|
|
361
|
-
chain_0.push(link_1);
|
|
362
|
-
chain_0_length++;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
//remove the chain
|
|
367
|
-
chains.splice(k, 1);
|
|
368
|
-
|
|
369
|
-
//update iterators
|
|
370
|
-
k--;
|
|
371
|
-
chainCount--;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
*
|
|
378
|
-
* @type {SplatMapMaterialPatch[]}
|
|
379
|
-
*/
|
|
380
|
-
const garbage = [];
|
|
381
|
-
|
|
382
|
-
//execute merge
|
|
383
|
-
for (let i = 0; i < chainCount; i++) {
|
|
384
|
-
const chain = chains[i];
|
|
385
|
-
|
|
386
|
-
const target = chain[0];
|
|
387
|
-
|
|
388
|
-
for (let j = 1; j < chain.length; j++) {
|
|
389
|
-
|
|
390
|
-
const source = chain[j];
|
|
391
|
-
|
|
392
|
-
if (garbage.indexOf(target) !== -1) {
|
|
393
|
-
//desired merge target has already been marked as garbage, skip merge
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
target.add(source);
|
|
398
|
-
|
|
399
|
-
//take over connections of the source
|
|
400
|
-
const neighbours = graph.getNeighbours(source);
|
|
401
|
-
|
|
402
|
-
const neighboursCount = neighbours.length;
|
|
403
|
-
|
|
404
|
-
for (let j = 0; j < neighboursCount; j++) {
|
|
405
|
-
const neighbour = neighbours[j];
|
|
406
|
-
|
|
407
|
-
if (neighbour !== target || !graph.edgeExists(target, neighbour)) {
|
|
408
|
-
graph.createEdge(target, neighbour);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
garbage.push(source);
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
//cleanup
|
|
418
|
-
for (let i = 0; i < garbage.length; i++) {
|
|
419
|
-
const patch = garbage[i];
|
|
420
|
-
this.removePatch(patch);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return garbage.length;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
*
|
|
428
|
-
* @param {SplatMapMaterialPatch} patch
|
|
429
|
-
*/
|
|
430
|
-
insertPatch(patch) {
|
|
431
|
-
/**
|
|
432
|
-
*
|
|
433
|
-
* @type {SplatMapMaterialPatch[]}
|
|
434
|
-
*/
|
|
435
|
-
const connectedPatches = [];
|
|
436
|
-
|
|
437
|
-
this.graph.addNode(patch);
|
|
438
|
-
|
|
439
|
-
const bb = patch.aabb;
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
*
|
|
443
|
-
* @param {QuadTreeDatum<SplatMapMaterialPatch>} container
|
|
444
|
-
* @param {number} x
|
|
445
|
-
* @param {number} y
|
|
446
|
-
*/
|
|
447
|
-
function visitOverlap(container, x, y) {
|
|
448
|
-
const otherPatch = container.data;
|
|
449
|
-
|
|
450
|
-
const overlaps = otherPatch.test(x, y);
|
|
451
|
-
|
|
452
|
-
if (overlaps && connectedPatches.indexOf(otherPatch) === -1) {
|
|
453
|
-
connectedPatches.push(otherPatch);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
//traverse the patch to find overlaps with other patches
|
|
458
|
-
for (let y = bb.y0; y <= bb.y1; y++) {
|
|
459
|
-
|
|
460
|
-
for (let x = bb.x0; x <= bb.x1; x++) {
|
|
461
|
-
|
|
462
|
-
if (!patch.test(x, y)) {
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
this.quadTree.traversePointIntersections(x, y, visitOverlap);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (patch.quad !== null) {
|
|
472
|
-
patch.quad.disconnect();
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const quadTreeDatum = this.quadTree.add(patch, bb.x0, bb.y0, bb.x1, bb.y1);
|
|
476
|
-
patch.quad = quadTreeDatum;
|
|
477
|
-
|
|
478
|
-
//insert edges to connecting patches
|
|
479
|
-
for (let i = 0; i < connectedPatches.length; i++) {
|
|
480
|
-
const other = connectedPatches[i];
|
|
481
|
-
|
|
482
|
-
this.graph.createEdge(patch, other);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
*
|
|
489
|
-
* @return {Task[]}
|
|
490
|
-
*/
|
|
491
|
-
initialize() {
|
|
492
|
-
this.graph.clear();
|
|
493
|
-
|
|
494
|
-
//build patches
|
|
495
|
-
|
|
496
|
-
const mapping = this.mapping;
|
|
497
|
-
|
|
498
|
-
const width = mapping.size.x;
|
|
499
|
-
const height = mapping.size.y;
|
|
500
|
-
const depth = mapping.depth;
|
|
501
|
-
|
|
502
|
-
const weightData = mapping.weightData;
|
|
503
|
-
|
|
504
|
-
this.ranking = new Uint8Array(width * height * depth);
|
|
505
|
-
|
|
506
|
-
//build ranking map
|
|
507
|
-
const tComputeRankings = mapping.computeWeightRankingMap(this.ranking);
|
|
508
|
-
|
|
509
|
-
const marks = this.marks;
|
|
510
|
-
marks.reset();
|
|
511
|
-
|
|
512
|
-
const layerSize = width * height;
|
|
513
|
-
|
|
514
|
-
const tBuildPatches = countTask(0, layerSize * depth, address => {
|
|
515
|
-
|
|
516
|
-
if (marks.get(address)) {
|
|
517
|
-
//already processed, skip
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
if (weightData[address] === 0) {
|
|
522
|
-
//tile has 0 weight, don't make a patch for it
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
const d = (address / layerSize) | 0;
|
|
527
|
-
const layerIndex = address % layerSize;
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const y = (layerIndex / width) | 0;
|
|
531
|
-
const x = layerIndex % width;
|
|
532
|
-
|
|
533
|
-
const patch = this.buildPatch(d, x, y);
|
|
534
|
-
|
|
535
|
-
if (patch.area === 0) {
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
this.insertPatch(patch);
|
|
540
|
-
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
tBuildPatches.addDependency(tComputeRankings);
|
|
544
|
-
|
|
545
|
-
return [tComputeRankings, tBuildPatches];
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
solve() {
|
|
549
|
-
const nodes = this.graph.nodes;
|
|
550
|
-
|
|
551
|
-
let nodeCount = nodes.length;
|
|
552
|
-
|
|
553
|
-
const weightData = this.mapping.weightData;
|
|
554
|
-
const materialSampler = this.mapping.materialSampler;
|
|
555
|
-
|
|
556
|
-
for (let i = 0; i < nodeCount; i++) {
|
|
557
|
-
const patch = nodes[i];
|
|
558
|
-
|
|
559
|
-
patch.readWeights(weightData);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
nodeCount = nodes.length;
|
|
563
|
-
|
|
564
|
-
const colors = colorizeGraphGreedyWeight(this.graph);
|
|
565
|
-
|
|
566
|
-
if (!validateGraphColoring(colors, this.graph)) {
|
|
567
|
-
console.error('Created invalid graph coloring');
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
//split colored sets into those that fit the channel set and those that don't
|
|
571
|
-
const colorMappableNodes = [];
|
|
572
|
-
const colorFloatingNodes = [];
|
|
573
|
-
|
|
574
|
-
for (let i = 0; i < nodeCount; i++) {
|
|
575
|
-
const c = colors[i];
|
|
576
|
-
|
|
577
|
-
if (c >= materialSampler.itemSize) {
|
|
578
|
-
colorFloatingNodes.push(i);
|
|
579
|
-
} else {
|
|
580
|
-
colorMappableNodes.push(i);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
//purge materials
|
|
586
|
-
const materialData = materialSampler.data;
|
|
587
|
-
materialData.fill(255);
|
|
588
|
-
|
|
589
|
-
const occupancy = new BitSet();
|
|
590
|
-
|
|
591
|
-
for (let i = 0; i < colorMappableNodes.length; i++) {
|
|
592
|
-
const nodeIndex = colorMappableNodes[i];
|
|
593
|
-
|
|
594
|
-
const patch = nodes[nodeIndex];
|
|
595
|
-
|
|
596
|
-
const color = colors[nodeIndex];
|
|
597
|
-
|
|
598
|
-
patch.write(color, materialSampler, occupancy);
|
|
599
|
-
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
for (let i = 0; i < colorFloatingNodes.length; i++) {
|
|
603
|
-
const nodeIndex = colorFloatingNodes[i];
|
|
604
|
-
|
|
605
|
-
const patch = nodes[nodeIndex];
|
|
606
|
-
|
|
607
|
-
patch.writeByOccupancy(materialSampler, occupancy);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
this.mapping.materialTexture.needsUpdate = true;
|
|
611
|
-
this.mapping.weightTexture.needsUpdate = true;
|
|
612
|
-
|
|
613
|
-
// const d = new SplatMapOptimizerDebugger();
|
|
614
|
-
//
|
|
615
|
-
// const v = d.build(this.graph);
|
|
616
|
-
//
|
|
617
|
-
// v.link();
|
|
618
|
-
// window.document.body.appendChild(v.el);
|
|
619
|
-
|
|
620
|
-
return colors;
|
|
621
|
-
}
|
|
622
|
-
}
|