@needle-tools/gltf-build-pipeline 2.8.0-alpha.2 → 2.8.1-alpha.1
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
|
+
## [2.8.1-alpha.0] - 2024-03-26
|
|
8
|
+
- Fix: Issue where cache key gets longer depending on the primitive count
|
|
9
|
+
- Fix: Cache key not taking index buffer into account
|
|
10
|
+
- Fix: Caching of mesh LODs did clone shared mesh attributes for every primitive instead of re-using previous clones
|
|
11
|
+
|
|
7
12
|
## [2.8.0-alpha.2] - 2024-03-04
|
|
8
13
|
- change: use webp compression for textures smaller than 32x32
|
|
9
14
|
- fix: webp compression for small textures (64x64)
|
package/dist/cache/cache.js
CHANGED
|
@@ -202,8 +202,9 @@ export function getHash(property, level = 0) {
|
|
|
202
202
|
const weights = property.getWeights();
|
|
203
203
|
hash += hashObject(weights);
|
|
204
204
|
const primitives = property.listPrimitives();
|
|
205
|
-
for (const prim of primitives)
|
|
206
|
-
|
|
205
|
+
for (const prim of primitives) {
|
|
206
|
+
hash += hashString(getHash(prim, level + 1));
|
|
207
|
+
}
|
|
207
208
|
}
|
|
208
209
|
else if (property instanceof Primitive) {
|
|
209
210
|
const indices = property.getIndices();
|
|
@@ -222,11 +223,19 @@ export function getHash(property, level = 0) {
|
|
|
222
223
|
targets,
|
|
223
224
|
semantics: semanticsNames
|
|
224
225
|
});
|
|
226
|
+
if (indices) {
|
|
227
|
+
const indexBuffer = indices.getArray();
|
|
228
|
+
if (indexBuffer) {
|
|
229
|
+
hash += hashBuffer(indexBuffer.buffer, indexBuffer.byteOffset, indexBuffer.byteLength);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
225
232
|
// We can not pass the whole buffer into hashObject since very large meshes will cause errors when trying to stringify
|
|
226
233
|
// See https://linear.app/needle/issue/NE-6414
|
|
227
234
|
for (const sem of semantics) {
|
|
228
|
-
const arr = property.getAttribute(sem)
|
|
229
|
-
|
|
235
|
+
const arr = property.getAttribute(sem)?.getArray();
|
|
236
|
+
if (arr) {
|
|
237
|
+
hash += hashBuffer(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
238
|
+
}
|
|
230
239
|
}
|
|
231
240
|
}
|
|
232
241
|
}
|
|
@@ -4,6 +4,7 @@ import { MeshoptEncoder, MeshoptSimplifier } from "meshoptimizer";
|
|
|
4
4
|
import { NEEDLE_mesh_compression, NEEDLE_mesh_compression_model_root } from "../extensions/index.js";
|
|
5
5
|
import { weldPrimitive } from "../functions/weld.3.js";
|
|
6
6
|
import { calculateMeshDensity } from "../utils/mesh.js";
|
|
7
|
+
import { isMeshLOD } from "./needle_progressive.js";
|
|
7
8
|
const NAME = 'needle_mesh_transform';
|
|
8
9
|
/**
|
|
9
10
|
* Which compression type for a model should be used. This should be respected when creating LOD meshes and the appropriate extension should be added to the LOD mesh so it receives the same mesh compression as the parent mesh
|
|
@@ -66,6 +67,7 @@ export const needle_mesh_transform = function (context) {
|
|
|
66
67
|
};
|
|
67
68
|
};
|
|
68
69
|
async function applyMeshCompression(document, transform_context, context) {
|
|
70
|
+
const isLOD = isMeshLOD(context.file);
|
|
69
71
|
const compressionModel = determineMeshCompression(document, context.file); // document.getRoot().getExtension(NEEDLE_mesh_compression.EXTENSION_NAME) as NEEDLE_mesh_compression_property;
|
|
70
72
|
if (compressionModel) {
|
|
71
73
|
// const rootModel = rootExtension.model as NEEDLE_mesh_compression_model_root;
|
|
@@ -76,7 +78,9 @@ async function applyMeshCompression(document, transform_context, context) {
|
|
|
76
78
|
}
|
|
77
79
|
document.getLogger().info(`[${NAME}]: ${compressionModel.compression.toUpperCase()} → will apply ${compressionModel.compression} mesh compression`);
|
|
78
80
|
if (compressionModel.compression === "draco") {
|
|
79
|
-
const transform = draco(
|
|
81
|
+
const transform = draco({
|
|
82
|
+
quantizationVolume: isLOD ? "scene" : undefined
|
|
83
|
+
});
|
|
80
84
|
return transform(document, transform_context);
|
|
81
85
|
}
|
|
82
86
|
else if (compressionModel.compression === "meshopt") {
|
|
@@ -87,7 +91,10 @@ async function applyMeshCompression(document, transform_context, context) {
|
|
|
87
91
|
}
|
|
88
92
|
// by default we still want to run draco if the root doesnt have a compression model configured
|
|
89
93
|
document.getLogger().info(`[${NAME}]: → will apply draco mesh compression (default)`);
|
|
90
|
-
|
|
94
|
+
const transform = draco({
|
|
95
|
+
quantizationVolume: isLOD ? "scene" : undefined
|
|
96
|
+
});
|
|
97
|
+
return transform(document, transform_context);
|
|
91
98
|
}
|
|
92
99
|
async function simplifierTransform(document, _context, meshContext) {
|
|
93
100
|
const simplifier = MeshoptSimplifier;
|
|
@@ -660,7 +660,7 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
660
660
|
const vertexDifference = lastMeshInfo.totalVertexCount - meshInfo.totalVertexCount;
|
|
661
661
|
const diff01 = vertexDifference / lastMeshInfo.totalVertexCount;
|
|
662
662
|
if (vertexDifference < 0) {
|
|
663
|
-
console.log(`WARN:
|
|
663
|
+
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)}`);
|
|
664
664
|
shouldOmitLevel = true;
|
|
665
665
|
}
|
|
666
666
|
else if (vertexDifference < 32 || diff01 < 0.05) {
|
|
@@ -850,27 +850,13 @@ async function createMeshLod(inputFileName, doc, basePath, level, mesh, meshInfo
|
|
|
850
850
|
}
|
|
851
851
|
const tasks = new Array();
|
|
852
852
|
// let didRunSimplification = false;
|
|
853
|
+
const clonedAttributes = new Map();
|
|
853
854
|
for (const prim of primitives) {
|
|
854
855
|
const newPrim = newDoc.createPrimitive();
|
|
855
856
|
newMesh.addPrimitive(newPrim);
|
|
856
857
|
newPrim.setName(prim.getName());
|
|
857
858
|
newPrim.setMode(prim.getMode());
|
|
858
859
|
newPrim.setMaterial(newMat);
|
|
859
|
-
const semantics = prim.listSemantics();
|
|
860
|
-
for (let i = 0; i < semantics.length; i++) {
|
|
861
|
-
const attr = prim.getAttribute(semantics[i]);
|
|
862
|
-
if (attr) {
|
|
863
|
-
const newAttr = newDoc.createAccessor();
|
|
864
|
-
newAttr.setArray(attr.getArray());
|
|
865
|
-
newAttr.setType(attr.getType());
|
|
866
|
-
newAttr.setSparse(attr.getSparse());
|
|
867
|
-
newAttr.setNormalized(attr.getNormalized());
|
|
868
|
-
newPrim.setAttribute(semantics[i], newAttr);
|
|
869
|
-
}
|
|
870
|
-
else {
|
|
871
|
-
logger.warn(`[${NAME}] Missing attribute ${semantics[i]} in mesh LOD ${level}`);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
860
|
const indices = prim.getIndices();
|
|
875
861
|
if (indices) {
|
|
876
862
|
const newIndices = newDoc.createAccessor();
|
|
@@ -880,6 +866,28 @@ async function createMeshLod(inputFileName, doc, basePath, level, mesh, meshInfo
|
|
|
880
866
|
newIndices.setNormalized(indices.getNormalized());
|
|
881
867
|
newPrim.setIndices(newIndices);
|
|
882
868
|
}
|
|
869
|
+
const semantics = prim.listSemantics();
|
|
870
|
+
for (let i = 0; i < semantics.length; i++) {
|
|
871
|
+
const attr = prim.getAttribute(semantics[i]);
|
|
872
|
+
if (attr) {
|
|
873
|
+
if (clonedAttributes.has(attr)) {
|
|
874
|
+
const cloned = clonedAttributes.get(attr);
|
|
875
|
+
newPrim.setAttribute(semantics[i], cloned);
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
const newAttr = newDoc.createAccessor();
|
|
879
|
+
newAttr.setArray(attr.getArray());
|
|
880
|
+
newAttr.setType(attr.getType());
|
|
881
|
+
newAttr.setSparse(attr.getSparse());
|
|
882
|
+
newAttr.setNormalized(attr.getNormalized());
|
|
883
|
+
newPrim.setAttribute(semantics[i], newAttr);
|
|
884
|
+
clonedAttributes.set(attr, newAttr);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
logger.warn(`[${NAME}] Missing attribute ${semantics[i]} in mesh LOD ${level}`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
883
891
|
const targets = prim.listTargets();
|
|
884
892
|
for (const target of targets) {
|
|
885
893
|
const newTarget = newDoc.createPrimitiveTarget(target.getName());
|
|
@@ -964,7 +972,8 @@ async function createMeshLod(inputFileName, doc, basePath, level, mesh, meshInfo
|
|
|
964
972
|
// add to cache
|
|
965
973
|
if (cacheKey != undefined) {
|
|
966
974
|
const bytes = readFileSync(outPath);
|
|
967
|
-
|
|
975
|
+
const sizeInMB = (bytes.byteLength / 1024 / 1024).toFixed(2);
|
|
976
|
+
logger.info(`[${NAME}] Add LOD ${level} to cache (${sizeInMB} MB) for \"${mesh.getName()}\" as ${cacheKey}`);
|
|
968
977
|
addToCache(cacheKey, bytes);
|
|
969
978
|
addMetaToCache(cacheKey, { meshInfo: newMeshInformation, density: density });
|
|
970
979
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.8.
|
|
1
|
+
export declare const version = "2.8.1-alpha.1";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "2.8.
|
|
1
|
+
export const version = "2.8.1-alpha.1";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-build-pipeline",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.1-alpha.1",
|
|
4
4
|
"description": "Pipeline and tools for optimizing gltf files using gltf-transform and compression settings within glTF extensions",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|