@needle-tools/gltf-build-pipeline 2.13.0 → 2.13.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.
Files changed (103) hide show
  1. package/CHANGELOG.md +520 -517
  2. package/README.md +38 -38
  3. package/dist/cache/cache.d.ts +55 -55
  4. package/dist/cache/cache.js +326 -326
  5. package/dist/cache/index.d.ts +1 -1
  6. package/dist/cache/index.js +1 -1
  7. package/dist/cli/index.d.ts +2 -2
  8. package/dist/cli/index.js +198 -198
  9. package/dist/config/index.d.ts +53 -53
  10. package/dist/config/index.js +152 -152
  11. package/dist/constants.d.ts +7 -7
  12. package/dist/constants.js +7 -7
  13. package/dist/extensions/NEEDLE_compression_texture/NEEDLE_compression_texture.d.ts +24 -24
  14. package/dist/extensions/NEEDLE_compression_texture/NEEDLE_compression_texture.js +54 -54
  15. package/dist/extensions/NEEDLE_compression_texture/index.d.ts +1 -1
  16. package/dist/extensions/NEEDLE_compression_texture/index.js +1 -1
  17. package/dist/extensions/NEEDLE_mesh_compression/NEEDLE_mesh_compression.d.ts +27 -27
  18. package/dist/extensions/NEEDLE_mesh_compression/NEEDLE_mesh_compression.js +88 -88
  19. package/dist/extensions/NEEDLE_mesh_compression/index.d.ts +1 -1
  20. package/dist/extensions/NEEDLE_mesh_compression/index.js +1 -1
  21. package/dist/extensions/NEEDLE_opaque/NEEDLE_opaque.d.ts +27 -27
  22. package/dist/extensions/NEEDLE_opaque/NEEDLE_opaque.js +980 -980
  23. package/dist/extensions/NEEDLE_opaque/index.d.ts +3 -3
  24. package/dist/extensions/NEEDLE_opaque/index.js +30 -30
  25. package/dist/extensions/NEEDLE_progressive/NEEDLE_progressive.d.ts +48 -48
  26. package/dist/extensions/NEEDLE_progressive/NEEDLE_progressive.js +69 -69
  27. package/dist/extensions/NEEDLE_progressive/index.d.ts +1 -1
  28. package/dist/extensions/NEEDLE_progressive/index.js +1 -1
  29. package/dist/extensions/NEEDLE_progressive_mesh_settings/index.d.ts +23 -23
  30. package/dist/extensions/NEEDLE_progressive_mesh_settings/index.js +41 -41
  31. package/dist/extensions/NEEDLE_progressive_texture_settings/index.d.ts +25 -25
  32. package/dist/extensions/NEEDLE_progressive_texture_settings/index.js +38 -38
  33. package/dist/extensions/index.d.ts +7 -7
  34. package/dist/extensions/index.js +17 -17
  35. package/dist/extensions/utils.d.ts +6 -6
  36. package/dist/extensions/utils.js +72 -72
  37. package/dist/functions/weld.3.d.ts +76 -76
  38. package/dist/functions/weld.3.js +414 -414
  39. package/dist/index.d.ts +8 -8
  40. package/dist/index.js +8 -8
  41. package/dist/scripts/index.d.ts +2 -2
  42. package/dist/scripts/index.js +2 -2
  43. package/dist/scripts/pack-gltf.d.ts +15 -15
  44. package/dist/scripts/pack-gltf.js +135 -133
  45. package/dist/scripts/stats.d.ts +2 -2
  46. package/dist/scripts/stats.js +19 -19
  47. package/dist/transforms/index.d.ts +9 -9
  48. package/dist/transforms/index.js +9 -9
  49. package/dist/transforms/needle_animation_transform.d.ts +2 -2
  50. package/dist/transforms/needle_animation_transform.js +5 -5
  51. package/dist/transforms/needle_asset.d.ts +3 -3
  52. package/dist/transforms/needle_asset.js +50 -50
  53. package/dist/transforms/needle_common.d.ts +17 -17
  54. package/dist/transforms/needle_common.js +1 -1
  55. package/dist/transforms/needle_mesh_transform.d.ts +15 -15
  56. package/dist/transforms/needle_mesh_transform.js +223 -223
  57. package/dist/transforms/needle_progressive.d.ts +45 -45
  58. package/dist/transforms/needle_progressive.js +1043 -1043
  59. package/dist/transforms/needle_texture_transform.d.ts +13 -13
  60. package/dist/transforms/needle_texture_transform.js +257 -257
  61. package/dist/transforms/needle_toktx.d.ts +26 -26
  62. package/dist/transforms/needle_toktx.js +243 -243
  63. package/dist/transforms/needle_webp.d.ts +15 -15
  64. package/dist/transforms/needle_webp.js +53 -53
  65. package/dist/transforms/toktx.d.ts +52 -52
  66. package/dist/transforms/toktx.js +221 -221
  67. package/dist/transforms/toktx_types.d.ts +78 -78
  68. package/dist/transforms/toktx_types.js +62 -62
  69. package/dist/transforms/util.d.ts +21 -21
  70. package/dist/transforms/util.js +90 -90
  71. package/dist/utils/compression_utils.d.ts +2 -2
  72. package/dist/utils/compression_utils.js +6 -6
  73. package/dist/utils/fileutils.d.ts +19 -19
  74. package/dist/utils/fileutils.js +177 -177
  75. package/dist/utils/guid.d.ts +1 -1
  76. package/dist/utils/guid.js +5 -5
  77. package/dist/utils/index.d.ts +10 -10
  78. package/dist/utils/index.js +10 -10
  79. package/dist/utils/merge.d.ts +12 -12
  80. package/dist/utils/merge.js +31 -31
  81. package/dist/utils/mesh.d.ts +13 -13
  82. package/dist/utils/mesh.js +124 -124
  83. package/dist/utils/nodeio.d.ts +6 -6
  84. package/dist/utils/nodeio.js +86 -86
  85. package/dist/utils/stats.d.ts +70 -70
  86. package/dist/utils/stats.js +258 -258
  87. package/dist/utils/texture.d.ts +5 -5
  88. package/dist/utils/texture.js +14 -14
  89. package/dist/utils/validate.d.ts +5 -5
  90. package/dist/utils/validate.js +17 -17
  91. package/dist/utils/version.d.ts +1 -1
  92. package/dist/utils/version.gen.d.ts +1 -1
  93. package/dist/utils/version.gen.js +1 -1
  94. package/dist/utils/version.js +4 -4
  95. package/package.json +79 -79
  96. package/scripts/clear-caches.mjs +3 -3
  97. package/scripts/defaults.mjs +11 -11
  98. package/scripts/fileutils.mjs +5 -5
  99. package/scripts/index.mjs +41 -41
  100. package/scripts/limit-caches.mjs +3 -3
  101. package/scripts/make-progressive.mjs +16 -16
  102. package/scripts/pack-gltf.mjs +106 -106
  103. package/tsconfig.json +19 -19
@@ -1,223 +1,223 @@
1
- import { Primitive } from "@gltf-transform/core";
2
- import { meshopt, simplifyPrimitive, SIMPLIFY_DEFAULTS, WELD_DEFAULTS, draco } from "@gltf-transform/functions";
3
- import { MeshoptEncoder, MeshoptSimplifier } from "meshoptimizer";
4
- import { NEEDLE_mesh_compression, NEEDLE_mesh_compression_model_root } from "../extensions/index.js";
5
- import { weldPrimitive } from "../functions/weld.3.js";
6
- import { calculateMeshDensity } from "../utils/mesh.js";
7
- import { isMeshLOD } from "./needle_progressive.js";
8
- const NAME = 'needle_mesh_transform';
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
11
- */
12
- export function determineMeshCompression(document, filePath) {
13
- const rootExtension = document.getRoot().getExtension(NEEDLE_mesh_compression.EXTENSION_NAME);
14
- if (rootExtension) {
15
- const rootModel = rootExtension.model;
16
- if (rootModel) {
17
- if (!rootModel.compression) {
18
- throw new Error(`[${NAME}]: Invalid mesh compression model extension on root node of ${filePath}`);
19
- }
20
- return rootModel;
21
- }
22
- }
23
- if (filePath?.toLowerCase().endsWith(".vrm")) {
24
- const hasBlendshapes = document.getRoot().listMeshes().some((mesh) => mesh.listPrimitives().some((prim) => prim.listTargets().length > 0));
25
- if (hasBlendshapes) {
26
- return new NEEDLE_mesh_compression_model_root("meshopt");
27
- }
28
- }
29
- // heuristic to detect if we should choose meshopt over draco
30
- // because meshopt can compress blendshapes while draco can not
31
- // we currently only consider the vertex count of the blendshapes
32
- let sizeOfBlendshapes = 0;
33
- let sizeOfMeshes = 0;
34
- for (const mesh of document.getRoot().listMeshes()) {
35
- for (const prim of mesh.listPrimitives()) {
36
- for (const target of prim.listTargets()) {
37
- sizeOfBlendshapes += target.getAttribute('POSITION').getArray().length;
38
- }
39
- sizeOfMeshes += prim.getAttribute('POSITION').getArray().length;
40
- }
41
- }
42
- // TODO: make this more robus with progressive loading: when progressive loading is enabled we need to run this check on the root mesh and enforce the same mesh compression for all children. Until we can do that we can not use the code for choosing compression based on blendshape size
43
- if (sizeOfBlendshapes > 0) {
44
- document.getLogger().info(`[${NAME}]: will apply meshopt mesh compression because the mesh contains blendshapes`);
45
- return new NEEDLE_mesh_compression_model_root("meshopt");
46
- }
47
- // consider meshopt if blendshape size is > 256 KB
48
- // const sizeThreshold = 256 * 1024;
49
- // if (sizeOfBlendshapes > sizeThreshold) {
50
- // /** How big are the blendshapes compared to the overall mesh size */
51
- // const blendshapeRatio = sizeOfBlendshapes / sizeOfMeshes;
52
- // /** 25% of the size are blendshapres */
53
- // const ratioThreshold = 0.1;
54
- // if (blendshapeRatio >= ratioThreshold) {
55
- // document.getLogger().info(`[${NAME}]: will apply meshopt mesh compression because blendshapes are ${(blendshapeRatio * 100).toFixed(2)}% of the mesh size`);
56
- // const transform = meshopt({ encoder: MeshoptEncoder });
57
- // return transform(document, transform_context);
58
- // }
59
- // }
60
- return new NEEDLE_mesh_compression_model_root("draco");
61
- }
62
- export const needle_mesh_transform = function (context) {
63
- return async (document, _context) => {
64
- // TODO: needs to be moved into NEEDLE_progressive
65
- await simplifierTransform(document, _context, context);
66
- await applyMeshCompression(document, _context, context);
67
- };
68
- };
69
- async function applyMeshCompression(document, transform_context, context) {
70
- const isLOD = isMeshLOD(context.file);
71
- const compressionModel = determineMeshCompression(document, context.file); // document.getRoot().getExtension(NEEDLE_mesh_compression.EXTENSION_NAME) as NEEDLE_mesh_compression_property;
72
- if (compressionModel) {
73
- // const rootModel = rootExtension.model as NEEDLE_mesh_compression_model_root;
74
- if (compressionModel.compression) {
75
- if (compressionModel.compression === "none") {
76
- document.getLogger().info(`[${NAME}]: NONE → will not apply mesh compression`);
77
- return;
78
- }
79
- document.getLogger().info(`[${NAME}]: ${compressionModel.compression.toUpperCase()} → will apply ${compressionModel.compression} mesh compression`);
80
- if (compressionModel.compression === "draco") {
81
- const transform = draco({
82
- encodeSpeed: 8,
83
- decodeSpeed: 8,
84
- quantizationVolume: isLOD ? "scene" : undefined
85
- });
86
- return transform(document, transform_context);
87
- }
88
- else if (compressionModel.compression === "meshopt") {
89
- const transform = meshopt({ encoder: MeshoptEncoder });
90
- return transform(document, transform_context);
91
- }
92
- }
93
- }
94
- // by default we still want to run draco if the root doesnt have a compression model configured
95
- document.getLogger().info(`[${NAME}]: → will apply draco mesh compression (default)`);
96
- const transform = draco({
97
- encodeSpeed: 8,
98
- decodeSpeed: 8,
99
- quantizationVolume: isLOD ? "scene" : undefined
100
- });
101
- return transform(document, transform_context);
102
- }
103
- async function simplifierTransform(document, _context, meshContext) {
104
- const simplifier = MeshoptSimplifier;
105
- if (!simplifier) {
106
- console.log(`WARN: [${NAME}] simplifier dependency required — install "meshoptimizer".`);
107
- return false;
108
- }
109
- const logger = document.getLogger();
110
- const isVRM = meshContext.file?.toLowerCase().endsWith(".vrm");
111
- const options = {
112
- ...SIMPLIFY_DEFAULTS,
113
- simplifier: simplifier,
114
- ratio: 0.75,
115
- error: 0.0005,
116
- };
117
- const weldOptions = {
118
- ...WELD_DEFAULTS,
119
- overwrite: true,
120
- };
121
- // let meshInformation: MeshInformation | null = null;
122
- // if (isVRM) {
123
- // meshInformation = getMeshInformation(document);
124
- // }
125
- for (const mesh of document.getRoot().listMeshes()) {
126
- let i = 0;
127
- for (const prim of mesh.listPrimitives()) {
128
- const index = i++;
129
- // TODO: mesh compression caching
130
- // const key = getKey(prim);
131
- // const cached = tryGetFromCache(key);
132
- // if (cached) {
133
- // logger.debug(`[${NAME}]: Skipping primitive of mesh "${mesh.getName()}": Already cached.`);
134
- // continue;
135
- // }
136
- // updatePrimitive(prim, simplifier, null, prim.getIndices());
137
- // continue;
138
- const ext = prim.getExtension(NEEDLE_mesh_compression.EXTENSION_NAME);
139
- const mesh_compression = ext?.model;
140
- const simplifyOpts = { ...options };
141
- if (isVRM) {
142
- const density = calculateMeshDensity(prim);
143
- if (density > 0) {
144
- const targetDensity = 2_000;
145
- const t = (1 / density) * targetDensity;
146
- simplifyOpts.ratio = Math.min(Math.max(.1, t), .95);
147
- simplifyOpts.error = 15 / density;
148
- simplifyOpts.lockBorder = true;
149
- }
150
- else {
151
- simplifyOpts.ratio = 0.8;
152
- simplifyOpts.error = 0.001;
153
- simplifyOpts.lockBorder = true;
154
- }
155
- logger.info(`${mesh.getName()}: Density: ${density.toFixed(2)}, Ratio: ${simplifyOpts.ratio.toFixed(2)}, Error: ${simplifyOpts.error}`);
156
- }
157
- if (!ext && !isVRM) {
158
- // If no extension exists on the primitive dont apply any simplification
159
- // AND if the file is not a VRM file
160
- continue;
161
- }
162
- if (mesh_compression) {
163
- // Dont run if useSimplifier is turned off explictly
164
- if (mesh_compression.useSimplifier === false) {
165
- logger.debug(`[${NAME}]: Skipping primitive of mesh "${mesh.getName()}": useSimplifier is false.`);
166
- continue;
167
- }
168
- if (mesh_compression.error >= 0)
169
- simplifyOpts.error = mesh_compression.error;
170
- if (mesh_compression.ratio >= 0)
171
- simplifyOpts.ratio = mesh_compression.ratio;
172
- simplifyOpts.lockBorder = mesh_compression.lockBorder;
173
- }
174
- if (prim.getMode() !== Primitive.Mode.TRIANGLES) {
175
- logger.warn(`[${NAME}]: Skipping primitive of mesh "${mesh.getName()}": Requires TRIANGLES draw mode.`);
176
- continue;
177
- }
178
- compressPrimitive(document, mesh, prim, index, weldOptions, simplifyOpts);
179
- }
180
- }
181
- return true;
182
- }
183
- export async function compressPrimitive(document, mesh, prim, index, weldOptions, meshOptions) {
184
- const logger = document.getLogger();
185
- logger.debug(`weld: primitive[${index}] of mesh "${mesh.getName()}"`);
186
- weldPrimitive(prim, weldOptions);
187
- // make sure we wait for the mesh simplifier to be ready
188
- if (meshOptions.simplifier && meshOptions.simplifier?.ready instanceof Promise) {
189
- const res = await meshOptions.simplifier?.ready
190
- .then(() => {
191
- // ready!
192
- return true;
193
- })
194
- .catch((err) => {
195
- console.error(`[${NAME}]: Simplifier failed to load...\n${err}`);
196
- return false;
197
- });
198
- if (!res)
199
- return;
200
- }
201
- // TODO: enable regularize once it's available: https://github.com/zeux/meshoptimizer/pull/914/files
202
- logger.debug(`simplify: primitive[${index}] of mesh "${mesh.getName()}" → ratio: ${meshOptions.ratio}, error: ${meshOptions.error}, lockBorder: ${meshOptions.lockBorder}, regularize: ${meshOptions.regularize}, normalWeight: ${meshOptions.normalWeight}, colorWeight: ${meshOptions.colorWeight}, textureWeight: ${meshOptions.textureWeight}`);
203
- return simplifyPrimitive(prim, meshOptions);
204
- }
205
- // function updatePrimitive(prim: Primitive, simplifier: any, positionArray, dstIndicesArray) {
206
- // const position = prim.getAttribute('POSITION')!;
207
- // const srcIndices = prim.getIndices()!;
208
- // const srcVertexCount = position.getCount();
209
- // const [remap, unique] = simplifier.compactMesh(dstIndicesArray);
210
- // // logger.debug(`${NAME}: ${formatDeltaOp(position.getCount(), unique)} vertices, error: ${error.toFixed(4)}.`);
211
- // // (3) Write vertex attributes.
212
- // for (const srcAttribute of deepListAttributes(prim)) {
213
- // const dstAttribute = srcAttribute.clone();
214
- // remapAttribute(dstAttribute, remap, unique);
215
- // deepSwapAttribute(prim, srcAttribute, dstAttribute);
216
- // if (srcAttribute.listParents().length === 1) srcAttribute.dispose();
217
- // }
218
- // // (4) Write indices.
219
- // const dstIndices = srcIndices.clone();
220
- // dstIndices.setArray(srcVertexCount <= 65534 ? new Uint16Array(dstIndicesArray) : dstIndicesArray);
221
- // prim.setIndices(dstIndices);
222
- // if (srcIndices.listParents().length === 1) srcIndices.dispose();
223
- // }
1
+ import { Primitive } from "@gltf-transform/core";
2
+ import { meshopt, simplifyPrimitive, SIMPLIFY_DEFAULTS, WELD_DEFAULTS, draco } from "@gltf-transform/functions";
3
+ import { MeshoptEncoder, MeshoptSimplifier } from "meshoptimizer";
4
+ import { NEEDLE_mesh_compression, NEEDLE_mesh_compression_model_root } from "../extensions/index.js";
5
+ import { weldPrimitive } from "../functions/weld.3.js";
6
+ import { calculateMeshDensity } from "../utils/mesh.js";
7
+ import { isMeshLOD } from "./needle_progressive.js";
8
+ const NAME = 'needle_mesh_transform';
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
11
+ */
12
+ export function determineMeshCompression(document, filePath) {
13
+ const rootExtension = document.getRoot().getExtension(NEEDLE_mesh_compression.EXTENSION_NAME);
14
+ if (rootExtension) {
15
+ const rootModel = rootExtension.model;
16
+ if (rootModel) {
17
+ if (!rootModel.compression) {
18
+ throw new Error(`[${NAME}]: Invalid mesh compression model extension on root node of ${filePath}`);
19
+ }
20
+ return rootModel;
21
+ }
22
+ }
23
+ if (filePath?.toLowerCase().endsWith(".vrm")) {
24
+ const hasBlendshapes = document.getRoot().listMeshes().some((mesh) => mesh.listPrimitives().some((prim) => prim.listTargets().length > 0));
25
+ if (hasBlendshapes) {
26
+ return new NEEDLE_mesh_compression_model_root("meshopt");
27
+ }
28
+ }
29
+ // heuristic to detect if we should choose meshopt over draco
30
+ // because meshopt can compress blendshapes while draco can not
31
+ // we currently only consider the vertex count of the blendshapes
32
+ let sizeOfBlendshapes = 0;
33
+ let sizeOfMeshes = 0;
34
+ for (const mesh of document.getRoot().listMeshes()) {
35
+ for (const prim of mesh.listPrimitives()) {
36
+ for (const target of prim.listTargets()) {
37
+ sizeOfBlendshapes += target.getAttribute('POSITION').getArray().length;
38
+ }
39
+ sizeOfMeshes += prim.getAttribute('POSITION').getArray().length;
40
+ }
41
+ }
42
+ // TODO: make this more robus with progressive loading: when progressive loading is enabled we need to run this check on the root mesh and enforce the same mesh compression for all children. Until we can do that we can not use the code for choosing compression based on blendshape size
43
+ if (sizeOfBlendshapes > 0) {
44
+ document.getLogger().info(`[${NAME}]: will apply meshopt mesh compression because the mesh contains blendshapes`);
45
+ return new NEEDLE_mesh_compression_model_root("meshopt");
46
+ }
47
+ // consider meshopt if blendshape size is > 256 KB
48
+ // const sizeThreshold = 256 * 1024;
49
+ // if (sizeOfBlendshapes > sizeThreshold) {
50
+ // /** How big are the blendshapes compared to the overall mesh size */
51
+ // const blendshapeRatio = sizeOfBlendshapes / sizeOfMeshes;
52
+ // /** 25% of the size are blendshapres */
53
+ // const ratioThreshold = 0.1;
54
+ // if (blendshapeRatio >= ratioThreshold) {
55
+ // document.getLogger().info(`[${NAME}]: will apply meshopt mesh compression because blendshapes are ${(blendshapeRatio * 100).toFixed(2)}% of the mesh size`);
56
+ // const transform = meshopt({ encoder: MeshoptEncoder });
57
+ // return transform(document, transform_context);
58
+ // }
59
+ // }
60
+ return new NEEDLE_mesh_compression_model_root("draco");
61
+ }
62
+ export const needle_mesh_transform = function (context) {
63
+ return async (document, _context) => {
64
+ // TODO: needs to be moved into NEEDLE_progressive
65
+ await simplifierTransform(document, _context, context);
66
+ await applyMeshCompression(document, _context, context);
67
+ };
68
+ };
69
+ async function applyMeshCompression(document, transform_context, context) {
70
+ const isLOD = isMeshLOD(context.file);
71
+ const compressionModel = determineMeshCompression(document, context.file); // document.getRoot().getExtension(NEEDLE_mesh_compression.EXTENSION_NAME) as NEEDLE_mesh_compression_property;
72
+ if (compressionModel) {
73
+ // const rootModel = rootExtension.model as NEEDLE_mesh_compression_model_root;
74
+ if (compressionModel.compression) {
75
+ if (compressionModel.compression === "none") {
76
+ document.getLogger().info(`[${NAME}]: NONE → will not apply mesh compression`);
77
+ return;
78
+ }
79
+ document.getLogger().info(`[${NAME}]: ${compressionModel.compression.toUpperCase()} → will apply ${compressionModel.compression} mesh compression`);
80
+ if (compressionModel.compression === "draco") {
81
+ const transform = draco({
82
+ encodeSpeed: 8,
83
+ decodeSpeed: 8,
84
+ quantizationVolume: isLOD ? "scene" : undefined
85
+ });
86
+ return transform(document, transform_context);
87
+ }
88
+ else if (compressionModel.compression === "meshopt") {
89
+ const transform = meshopt({ encoder: MeshoptEncoder });
90
+ return transform(document, transform_context);
91
+ }
92
+ }
93
+ }
94
+ // by default we still want to run draco if the root doesnt have a compression model configured
95
+ document.getLogger().info(`[${NAME}]: → will apply draco mesh compression (default)`);
96
+ const transform = draco({
97
+ encodeSpeed: 8,
98
+ decodeSpeed: 8,
99
+ quantizationVolume: isLOD ? "scene" : undefined
100
+ });
101
+ return transform(document, transform_context);
102
+ }
103
+ async function simplifierTransform(document, _context, meshContext) {
104
+ const simplifier = MeshoptSimplifier;
105
+ if (!simplifier) {
106
+ console.log(`WARN: [${NAME}] simplifier dependency required — install "meshoptimizer".`);
107
+ return false;
108
+ }
109
+ const logger = document.getLogger();
110
+ const isVRM = meshContext.file?.toLowerCase().endsWith(".vrm");
111
+ const options = {
112
+ ...SIMPLIFY_DEFAULTS,
113
+ simplifier: simplifier,
114
+ ratio: 0.75,
115
+ error: 0.0005,
116
+ };
117
+ const weldOptions = {
118
+ ...WELD_DEFAULTS,
119
+ overwrite: true,
120
+ };
121
+ // let meshInformation: MeshInformation | null = null;
122
+ // if (isVRM) {
123
+ // meshInformation = getMeshInformation(document);
124
+ // }
125
+ for (const mesh of document.getRoot().listMeshes()) {
126
+ let i = 0;
127
+ for (const prim of mesh.listPrimitives()) {
128
+ const index = i++;
129
+ // TODO: mesh compression caching
130
+ // const key = getKey(prim);
131
+ // const cached = tryGetFromCache(key);
132
+ // if (cached) {
133
+ // logger.debug(`[${NAME}]: Skipping primitive of mesh "${mesh.getName()}": Already cached.`);
134
+ // continue;
135
+ // }
136
+ // updatePrimitive(prim, simplifier, null, prim.getIndices());
137
+ // continue;
138
+ const ext = prim.getExtension(NEEDLE_mesh_compression.EXTENSION_NAME);
139
+ const mesh_compression = ext?.model;
140
+ const simplifyOpts = { ...options };
141
+ if (isVRM) {
142
+ const density = calculateMeshDensity(prim);
143
+ if (density > 0) {
144
+ const targetDensity = 2_000;
145
+ const t = (1 / density) * targetDensity;
146
+ simplifyOpts.ratio = Math.min(Math.max(.1, t), .95);
147
+ simplifyOpts.error = 15 / density;
148
+ simplifyOpts.lockBorder = true;
149
+ }
150
+ else {
151
+ simplifyOpts.ratio = 0.8;
152
+ simplifyOpts.error = 0.001;
153
+ simplifyOpts.lockBorder = true;
154
+ }
155
+ logger.info(`${mesh.getName()}: Density: ${density.toFixed(2)}, Ratio: ${simplifyOpts.ratio.toFixed(2)}, Error: ${simplifyOpts.error}`);
156
+ }
157
+ if (!ext && !isVRM) {
158
+ // If no extension exists on the primitive dont apply any simplification
159
+ // AND if the file is not a VRM file
160
+ continue;
161
+ }
162
+ if (mesh_compression) {
163
+ // Dont run if useSimplifier is turned off explictly
164
+ if (mesh_compression.useSimplifier === false) {
165
+ logger.debug(`[${NAME}]: Skipping primitive of mesh "${mesh.getName()}": useSimplifier is false.`);
166
+ continue;
167
+ }
168
+ if (mesh_compression.error >= 0)
169
+ simplifyOpts.error = mesh_compression.error;
170
+ if (mesh_compression.ratio >= 0)
171
+ simplifyOpts.ratio = mesh_compression.ratio;
172
+ simplifyOpts.lockBorder = mesh_compression.lockBorder;
173
+ }
174
+ if (prim.getMode() !== Primitive.Mode.TRIANGLES) {
175
+ logger.warn(`[${NAME}]: Skipping primitive of mesh "${mesh.getName()}": Requires TRIANGLES draw mode.`);
176
+ continue;
177
+ }
178
+ compressPrimitive(document, mesh, prim, index, weldOptions, simplifyOpts);
179
+ }
180
+ }
181
+ return true;
182
+ }
183
+ export async function compressPrimitive(document, mesh, prim, index, weldOptions, meshOptions) {
184
+ const logger = document.getLogger();
185
+ logger.debug(`weld: primitive[${index}] of mesh "${mesh.getName()}"`);
186
+ weldPrimitive(prim, weldOptions);
187
+ // make sure we wait for the mesh simplifier to be ready
188
+ if (meshOptions.simplifier && meshOptions.simplifier?.ready instanceof Promise) {
189
+ const res = await meshOptions.simplifier?.ready
190
+ .then(() => {
191
+ // ready!
192
+ return true;
193
+ })
194
+ .catch((err) => {
195
+ console.error(`[${NAME}]: Simplifier failed to load...\n${err}`);
196
+ return false;
197
+ });
198
+ if (!res)
199
+ return;
200
+ }
201
+ // TODO: enable regularize once it's available: https://github.com/zeux/meshoptimizer/pull/914/files
202
+ logger.debug(`simplify: primitive[${index}] of mesh "${mesh.getName()}" → ratio: ${meshOptions.ratio}, error: ${meshOptions.error}, lockBorder: ${meshOptions.lockBorder}, regularize: ${meshOptions.regularize}, normalWeight: ${meshOptions.normalWeight}, colorWeight: ${meshOptions.colorWeight}, textureWeight: ${meshOptions.textureWeight}`);
203
+ return simplifyPrimitive(prim, meshOptions);
204
+ }
205
+ // function updatePrimitive(prim: Primitive, simplifier: any, positionArray, dstIndicesArray) {
206
+ // const position = prim.getAttribute('POSITION')!;
207
+ // const srcIndices = prim.getIndices()!;
208
+ // const srcVertexCount = position.getCount();
209
+ // const [remap, unique] = simplifier.compactMesh(dstIndicesArray);
210
+ // // logger.debug(`${NAME}: ${formatDeltaOp(position.getCount(), unique)} vertices, error: ${error.toFixed(4)}.`);
211
+ // // (3) Write vertex attributes.
212
+ // for (const srcAttribute of deepListAttributes(prim)) {
213
+ // const dstAttribute = srcAttribute.clone();
214
+ // remapAttribute(dstAttribute, remap, unique);
215
+ // deepSwapAttribute(prim, srcAttribute, dstAttribute);
216
+ // if (srcAttribute.listParents().length === 1) srcAttribute.dispose();
217
+ // }
218
+ // // (4) Write indices.
219
+ // const dstIndices = srcIndices.clone();
220
+ // dstIndices.setArray(srcVertexCount <= 65534 ? new Uint16Array(dstIndicesArray) : dstIndicesArray);
221
+ // prim.setIndices(dstIndices);
222
+ // if (srcIndices.listParents().length === 1) srcIndices.dispose();
223
+ // }
@@ -1,45 +1,45 @@
1
- import { ILogger, Texture, Transform, vec2 } from '@gltf-transform/core';
2
- import { Config } from '../config/index.js';
3
- declare interface ProgressiveOptions {
4
- path: string | Array<string>;
5
- /** Directory to output progressive assets to */
6
- outpath?: string;
7
- results: Array<string>;
8
- config: Config;
9
- debug?: boolean;
10
- verbose?: boolean;
11
- useCache?: boolean;
12
- logger: ILogger;
13
- }
14
- export declare function make_progressive(opts: ProgressiveOptions): Promise<boolean>;
15
- export declare function createTransform(name: string, fn: Transform): Transform;
16
- export declare function make_progressive_textures(filePath: string, _options: TextureResizeOptions, opts: ProgressiveOptions): Transform;
17
- /** Options for the {@link textureResize} function. */
18
- export interface TextureResizeOptions {
19
- /**
20
- * Maximum width/height to enforce, preserving aspect ratio. For example,
21
- * a 4096x8192 texture, resized with limit [2048, 2048] will be reduced
22
- * to 1024x2048.
23
- */
24
- size: vec2;
25
- /** Resampling filter method. LANCZOS3 is sharper, LANCZOS2 is smoother. */
26
- filter?: TextureResizeFilter;
27
- /** Pattern identifying textures to resize, matched to name or URI. */
28
- pattern?: RegExp | null;
29
- /** Pattern to match slots usage for resizing. */
30
- slots?: RegExp | null;
31
- }
32
- /** Resampling filter methods. LANCZOS3 is sharper, LANCZOS2 is smoother. */
33
- export declare enum TextureResizeFilter {
34
- /** Lanczos3 (sharp) */
35
- LANCZOS3 = "lanczos3",
36
- /** Lanczos2 (smooth) */
37
- LANCZOS2 = "lanczos2"
38
- }
39
- export declare const TEXTURE_RESIZE_DEFAULTS: TextureResizeOptions;
40
- export declare function resizeTextureToMax(texture: Texture, maxSize: number, logger: ILogger): Promise<void>;
41
- export declare function make_progressive_meshes(filePath: string, _options: any, opts?: ProgressiveOptions): Transform;
42
- export declare function isMeshLOD(filepath: string): boolean;
43
- export declare function isImageLOD(filepath: string): boolean;
44
- export declare function isLOD(filepath: string): boolean;
45
- export {};
1
+ import { ILogger, Texture, Transform, vec2 } from '@gltf-transform/core';
2
+ import { Config } from '../config/index.js';
3
+ declare interface ProgressiveOptions {
4
+ path: string | Array<string>;
5
+ /** Directory to output progressive assets to */
6
+ outpath?: string;
7
+ results: Array<string>;
8
+ config: Config;
9
+ debug?: boolean;
10
+ verbose?: boolean;
11
+ useCache?: boolean;
12
+ logger: ILogger;
13
+ }
14
+ export declare function make_progressive(opts: ProgressiveOptions): Promise<boolean>;
15
+ export declare function createTransform(name: string, fn: Transform): Transform;
16
+ export declare function make_progressive_textures(filePath: string, _options: TextureResizeOptions, opts: ProgressiveOptions): Transform;
17
+ /** Options for the {@link textureResize} function. */
18
+ export interface TextureResizeOptions {
19
+ /**
20
+ * Maximum width/height to enforce, preserving aspect ratio. For example,
21
+ * a 4096x8192 texture, resized with limit [2048, 2048] will be reduced
22
+ * to 1024x2048.
23
+ */
24
+ size: vec2;
25
+ /** Resampling filter method. LANCZOS3 is sharper, LANCZOS2 is smoother. */
26
+ filter?: TextureResizeFilter;
27
+ /** Pattern identifying textures to resize, matched to name or URI. */
28
+ pattern?: RegExp | null;
29
+ /** Pattern to match slots usage for resizing. */
30
+ slots?: RegExp | null;
31
+ }
32
+ /** Resampling filter methods. LANCZOS3 is sharper, LANCZOS2 is smoother. */
33
+ export declare enum TextureResizeFilter {
34
+ /** Lanczos3 (sharp) */
35
+ LANCZOS3 = "lanczos3",
36
+ /** Lanczos2 (smooth) */
37
+ LANCZOS2 = "lanczos2"
38
+ }
39
+ export declare const TEXTURE_RESIZE_DEFAULTS: TextureResizeOptions;
40
+ export declare function resizeTextureToMax(texture: Texture, maxSize: number, logger: ILogger): Promise<void>;
41
+ export declare function make_progressive_meshes(filePath: string, _options: any, opts?: ProgressiveOptions): Transform;
42
+ export declare function isMeshLOD(filepath: string): boolean;
43
+ export declare function isImageLOD(filepath: string): boolean;
44
+ export declare function isLOD(filepath: string): boolean;
45
+ export {};