@needle-tools/gltf-build-pipeline 1.4.1-beta.10 → 1.4.1-beta.11
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/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,11 @@ All notable changes to this package will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [1.4.1-beta.11] - 2023-03-20
|
|
8
|
+
- add: NEEDLE_progressive LOD meshes morph targets data
|
|
9
|
+
- fix: NEEDLE_progressive should check if simplifcation did result in improved meshes (> 32 verts removed && > 5 % improvement) and otherwise discard LODs (and the whole extension if no improvement was made)
|
|
10
|
+
- change: Unity NEEDLE_progressive logs
|
|
11
|
+
|
|
7
12
|
## [1.4.1-beta.10] - 2023-03-19
|
|
8
13
|
- change: NEEDLE_progressive calculates density per mesh level
|
|
9
14
|
- change: NEEDLE_progressive `uri` field is removed and added `lod` array of objects
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Transform, Document, Primitive, Mesh } from "@gltf-transform/core";
|
|
2
2
|
import { SimplifyOptions, WeldOptions } from "@gltf-transform/functions";
|
|
3
3
|
export declare const needle_mesh_transform: (_options: SimplifyOptions) => Transform;
|
|
4
|
-
export declare function compressPrimitive(document: Document, mesh: Mesh, prim: Primitive, index: number, weldOptions: Required<WeldOptions>, meshOptions: Required<SimplifyOptions>): Promise<
|
|
4
|
+
export declare function compressPrimitive(document: Document, mesh: Mesh, prim: Primitive, index: number, weldOptions: Required<WeldOptions>, meshOptions: Required<SimplifyOptions>): Promise<Primitive>;
|
|
@@ -93,7 +93,7 @@ async function simplifierTransform(document, _context, _options) {
|
|
|
93
93
|
}
|
|
94
94
|
export async function compressPrimitive(document, mesh, prim, index, weldOptions, meshOptions) {
|
|
95
95
|
const logger = document.getLogger();
|
|
96
|
-
logger.debug(`
|
|
96
|
+
logger.debug(`weld: primitive[${index}] of mesh "${mesh.getName()}" → tolerance: ${weldOptions.tolerance}`);
|
|
97
97
|
weldPrimitive(document, prim, weldOptions);
|
|
98
98
|
// make sure we wait for the mesh simplifier to be ready
|
|
99
99
|
if (meshOptions.simplifier && meshOptions.simplifier?.ready instanceof Promise) {
|
|
@@ -109,8 +109,8 @@ export async function compressPrimitive(document, mesh, prim, index, weldOptions
|
|
|
109
109
|
if (!res)
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
|
-
logger.debug(`
|
|
113
|
-
simplifyPrimitive(document, prim, meshOptions);
|
|
112
|
+
logger.debug(`simplify: primitive[${index}] of mesh "${mesh.getName()}" → ratio: ${meshOptions.ratio}, error: ${meshOptions.error}, lockBorder: ${meshOptions.lockBorder}`);
|
|
113
|
+
return simplifyPrimitive(document, prim, meshOptions);
|
|
114
114
|
}
|
|
115
115
|
// function updatePrimitive(prim: Primitive, simplifier: any, positionArray, dstIndicesArray) {
|
|
116
116
|
// const position = prim.getAttribute('POSITION')!;
|
|
@@ -306,7 +306,7 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
306
306
|
await MeshoptSimplifier.ready;
|
|
307
307
|
const logger = doc.getLogger();
|
|
308
308
|
const filename = path.basename(filePath);
|
|
309
|
-
console.log(
|
|
309
|
+
console.log(`[${NAME}]: Begin generating progressive meshes for \"${filename}\"`);
|
|
310
310
|
let index = 0;
|
|
311
311
|
// the lowest LOD (last entry) will be saved in the original file
|
|
312
312
|
const MAX_LODS = 6;
|
|
@@ -333,12 +333,13 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
333
333
|
const visited = new Map();
|
|
334
334
|
const meshes = doc.getRoot().listMeshes();
|
|
335
335
|
for (const mesh of meshes) {
|
|
336
|
-
|
|
337
|
-
const
|
|
336
|
+
/** The size and vertex count of the original mesh */
|
|
337
|
+
const originalMeshInformation = getMeshInformation(mesh);
|
|
338
|
+
const { totalVertexCount: originalTotalVertexCount, maxVertexCount: originalMaxVertexCount } = originalMeshInformation;
|
|
338
339
|
let settings = mesh.getExtension(NEEDLE_progressive_mesh_settings.EXTENSION_NAME);
|
|
339
340
|
if (!settings) {
|
|
340
|
-
if (
|
|
341
|
-
logger.debug(`[${NAME}] Skipping, no settings found and no primitive with more than 32 vertices. mesh[${index}] has ${
|
|
341
|
+
if (originalMaxVertexCount < 32) {
|
|
342
|
+
logger.debug(`[${NAME}] Skipping, no settings found and no primitive with more than 32 vertices. mesh[${index}] has ${originalTotalVertexCount} vertices (max: ${originalMaxVertexCount})`);
|
|
342
343
|
continue;
|
|
343
344
|
}
|
|
344
345
|
else {
|
|
@@ -346,7 +347,7 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
346
347
|
guid: generateGuid(path + "_mesh_" + index.toString()),
|
|
347
348
|
generateLods: true,
|
|
348
349
|
};
|
|
349
|
-
logger.debug(`[${NAME}] No settings found for mesh[${index}] with ${
|
|
350
|
+
logger.debug(`[${NAME}] No settings found for mesh[${index}] with ${originalTotalVertexCount} vertices (max: ${originalMaxVertexCount}) - will use default settings (guid: ${settings.guid})`);
|
|
350
351
|
}
|
|
351
352
|
}
|
|
352
353
|
else if (settings.generateLods === false) {
|
|
@@ -371,12 +372,13 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
371
372
|
/** async simplification tasks... */
|
|
372
373
|
const tasks = new Array();
|
|
373
374
|
if (needsToGenerateLODs) {
|
|
375
|
+
console.log(`[${NAME}] Begin generating mesh LODs for \"${mesh.getName()}\"`);
|
|
374
376
|
// produce LODS, skip the last level because it will be saved in the original file
|
|
375
377
|
for (let level = 0; level < LODS.length - 1; level++) {
|
|
376
378
|
const lod = LODS[level];
|
|
377
379
|
const guid = settings.guid;
|
|
378
380
|
// TODO: check if the LOD level did actually change the mesh - otherwise we can just skip generating lower resolutions
|
|
379
|
-
const lodTask = createMeshLod(doc, outputDirectory, level, mesh,
|
|
381
|
+
const lodTask = createMeshLod(doc, outputDirectory, level, mesh, originalMeshInformation, guid, lod, opts);
|
|
380
382
|
lodTask.then(res => {
|
|
381
383
|
if (res?.success) {
|
|
382
384
|
generatedLODInfos[level] = res;
|
|
@@ -406,11 +408,11 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
406
408
|
let shouldOmitLevel = false;
|
|
407
409
|
// if the mesh size on disc is identical to the last level, we can omit this level
|
|
408
410
|
if (sizeOnDisc <= 0) {
|
|
409
|
-
console.log(`[${NAME}] mesh LOD ${level} size on disc is 0
|
|
411
|
+
console.log(`[${NAME}] Flag mesh LOD ${level}: size on disc is 0 → level will be omitted (if this mesh is is taken from cache it can mean that the LOD level was omitted before). \"${mesh.getName()}\" at \"${lodInfo.path}\"`);
|
|
410
412
|
shouldOmitLevel = true;
|
|
411
413
|
}
|
|
412
414
|
else if (diff == 0) {
|
|
413
|
-
console.log(`[${NAME}] mesh LOD ${level} size on disc did not change compared to previous level
|
|
415
|
+
console.log(`[${NAME}] Flag mesh LOD ${level}: size on disc did not change compared to previous level → level will be omitted. \"${mesh.getName()}\" at \"${lodInfo.path}\"`);
|
|
414
416
|
shouldOmitLevel = true;
|
|
415
417
|
}
|
|
416
418
|
else if (!shouldOmitLevel) {
|
|
@@ -419,13 +421,13 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
419
421
|
const lastMeshInfo = generatedLODInfos[i - 1]?.lodMeshInfo;
|
|
420
422
|
if (meshInfo && lastMeshInfo) {
|
|
421
423
|
const vertexDifference = lastMeshInfo.totalVertexCount - meshInfo.totalVertexCount;
|
|
422
|
-
const
|
|
424
|
+
const diff01 = vertexDifference / lastMeshInfo.totalVertexCount;
|
|
423
425
|
if (vertexDifference < 0) {
|
|
424
|
-
console.log(`WARN: [${NAME}] mesh LOD ${level} did increase
|
|
426
|
+
console.log(`WARN: ❌ [${NAME}] Flag mesh LOD ${level}: vertex count did increase (${-vertexDifference.toFixed(0)} vertices, ${(-diff01 * 100).toFixed(4)}%) → LOD ${level} will be omitted. \"${mesh.getName()}\" at \"${lodInfo.path}\" ${JSON.stringify(lastMeshInfo)} → ${JSON.stringify(meshInfo)}`);
|
|
425
427
|
shouldOmitLevel = true;
|
|
426
428
|
}
|
|
427
|
-
else if (vertexDifference < 32 ||
|
|
428
|
-
console.log(`[${NAME}] mesh LOD ${level}
|
|
429
|
+
else if (vertexDifference < 32 || diff01 < 0.05) {
|
|
430
|
+
console.log(`[${NAME}] Flag mesh LOD ${level}: vertex count difference below threshold (removed ${vertexDifference.toFixed(0)} vertices, ${(-diff01 * 100).toFixed(4)}%) → LOD ${level} will be omitted. \"${mesh.getName()}\" at \"${lodInfo.path}\"`);
|
|
429
431
|
shouldOmitLevel = true;
|
|
430
432
|
}
|
|
431
433
|
}
|
|
@@ -446,10 +448,9 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
446
448
|
}
|
|
447
449
|
let maxDensity = 0;
|
|
448
450
|
let primitiveIndex = 0;
|
|
451
|
+
const lodLevelInFile = generatedLODInfos.length;
|
|
449
452
|
for (const prim of mesh.listPrimitives()) {
|
|
450
|
-
const
|
|
451
|
-
const lastLod = LODS[lodsCount];
|
|
452
|
-
console.log(`[${NAME}] ${mesh.getName()} LOD ${lodsCount}`);
|
|
453
|
+
const lastLod = LODS[lodLevelInFile];
|
|
453
454
|
// TODO: this should be run after simplification but then we need to change the runtime to use the LOD 0 density
|
|
454
455
|
const density = calculateMeshDensity(prim);
|
|
455
456
|
maxDensity = Math.max(maxDensity, density);
|
|
@@ -459,8 +460,23 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
459
460
|
await Promise.all(tasks);
|
|
460
461
|
tasks.length = 0;
|
|
461
462
|
const { totalVertexCount: totalVertexCountAfter } = getMeshInformation(mesh);
|
|
463
|
+
const difference = originalTotalVertexCount - totalVertexCountAfter;
|
|
464
|
+
const diff01 = difference / originalTotalVertexCount;
|
|
462
465
|
if (totalVertexCountAfter <= 0) {
|
|
463
|
-
logger.error(`ERR: [${NAME}] Mesh[${index}] has no vertices after simplification: ${mesh.getName()}`);
|
|
466
|
+
logger.error(`ERR: [${NAME}]: Mesh[${index}] has no vertices after simplification: ${mesh.getName()}`);
|
|
467
|
+
}
|
|
468
|
+
else if (difference < 32 || diff01 <= 0.05) {
|
|
469
|
+
// the lowest LOD has the same vertex count as the original mesh → we don't need to write the extension and can also delete previously generated LODs
|
|
470
|
+
console.log(`[${NAME}]: Flag mesh LOD ${generatedLODInfos.length}: vertex count difference below threshold (removed ${difference} vertices, ${(-diff01 * 100).toFixed(2)} %) → will remove LODs. \"${mesh.getName()}\"`);
|
|
471
|
+
try {
|
|
472
|
+
for (const lodInfo of generatedLODInfos) {
|
|
473
|
+
rmSync(outputDirectory + "/" + lodInfo.path);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
catch (err) {
|
|
477
|
+
logger.error(`ERR: [${NAME}] Failed to remove LOD files for \"${mesh.getName()}\"`);
|
|
478
|
+
}
|
|
479
|
+
continue;
|
|
464
480
|
}
|
|
465
481
|
if (generatedLODInfos.length > 0) {
|
|
466
482
|
const model = progressiveExt.create({
|
|
@@ -483,7 +499,7 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
483
499
|
}
|
|
484
500
|
}
|
|
485
501
|
}
|
|
486
|
-
console.log(
|
|
502
|
+
console.log(`[${NAME}]: Finished generating progressive meshes for \"${filename}\"`);
|
|
487
503
|
});
|
|
488
504
|
}
|
|
489
505
|
/**
|
|
@@ -527,6 +543,10 @@ async function createMeshLod(doc, basePath, level, mesh, meshInfo, guid, lodLeve
|
|
|
527
543
|
newMesh.setName(mesh.getName() + " LOD " + level);
|
|
528
544
|
newNode.setMesh(newMesh);
|
|
529
545
|
const newMat = newDoc.createMaterial();
|
|
546
|
+
const weights = mesh.getWeights();
|
|
547
|
+
if (weights) {
|
|
548
|
+
newMesh.setWeights(weights);
|
|
549
|
+
}
|
|
530
550
|
const tasks = new Array();
|
|
531
551
|
// let didRunSimplification = false;
|
|
532
552
|
for (const prim of primitives) {
|
|
@@ -544,6 +564,9 @@ async function createMeshLod(doc, basePath, level, mesh, meshInfo, guid, lodLeve
|
|
|
544
564
|
newAttr.setType(attr.getType());
|
|
545
565
|
newPrim.setAttribute(semantics[i], newAttr);
|
|
546
566
|
}
|
|
567
|
+
else {
|
|
568
|
+
console.warn(`[${NAME}] Missing attribute ${semantics[i]} in mesh LOD ${level}`);
|
|
569
|
+
}
|
|
547
570
|
}
|
|
548
571
|
const indices = prim.getIndices();
|
|
549
572
|
if (indices) {
|
|
@@ -552,6 +575,21 @@ async function createMeshLod(doc, basePath, level, mesh, meshInfo, guid, lodLeve
|
|
|
552
575
|
newIndices.setType(indices.getType());
|
|
553
576
|
newPrim.setIndices(newIndices);
|
|
554
577
|
}
|
|
578
|
+
const targets = prim.listTargets();
|
|
579
|
+
for (const target of targets) {
|
|
580
|
+
const newTarget = newDoc.createPrimitiveTarget(target.getName());
|
|
581
|
+
newPrim.addTarget(newTarget);
|
|
582
|
+
for (const attr of target.listSemantics()) {
|
|
583
|
+
const accessor = target.getAttribute(attr);
|
|
584
|
+
if (accessor) {
|
|
585
|
+
const newAccessor = newDoc.createAccessor();
|
|
586
|
+
newAccessor.setArray(accessor.getArray());
|
|
587
|
+
newAccessor.setType(accessor.getType());
|
|
588
|
+
newTarget.setAttribute(attr, newAccessor);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
;
|
|
555
593
|
const extensions = prim.listExtensions();
|
|
556
594
|
for (const ext of extensions) {
|
|
557
595
|
const PropertyClass = ext.constructor;
|
|
@@ -603,7 +641,7 @@ async function createMeshLod(doc, basePath, level, mesh, meshInfo, guid, lodLeve
|
|
|
603
641
|
}
|
|
604
642
|
const newIo = new NodeIO();
|
|
605
643
|
registerExtensions(newIo);
|
|
606
|
-
console.log(`[${NAME}] Write mesh LOD ${level} with
|
|
644
|
+
console.log(`[${NAME}] Write mesh LOD ${level} with to \"${fileName}\"`);
|
|
607
645
|
await newIo.write(outPath, newDoc);
|
|
608
646
|
// const hasChanged = meshInfo.maxVertexCount == newMeshInformation.maxVertexCount && meshInfo.totalVertexCount == newMeshInformation.totalVertexCount;
|
|
609
647
|
// add to cache
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-build-pipeline",
|
|
3
|
-
"version": "1.4.1-beta.
|
|
3
|
+
"version": "1.4.1-beta.11",
|
|
4
4
|
"description": "Pipeline and tools for optimizing gltf files using gltf-transform and compression settings within glTF extensions",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"type": "module",
|