@needle-tools/gltf-build-pipeline 2.6.0-alpha.1 โ 2.7.0-alpha
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 +7 -0
- package/dist/extensions/NEEDLE_opaque/NEEDLE_opaque.js +64 -8
- package/dist/scripts/pack-gltf.js +3 -2
- package/dist/transforms/needle_mesh_transform.d.ts +1 -1
- package/dist/transforms/needle_mesh_transform.js +34 -10
- package/dist/transforms/needle_progressive.js +16 -3
- package/dist/utils/mesh.d.ts +2 -2
- package/dist/utils/mesh.js +29 -12
- package/dist/utils/nodeio.js +6 -0
- package/dist/utils/version.gen.d.ts +1 -0
- package/dist/utils/version.gen.js +1 -0
- package/dist/utils/version.js +2 -3
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@ 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.7.0-alpha] - 2024-01-13
|
|
8
|
+
- add: VRM mesh simplification support
|
|
9
|
+
- fix: issue where the output directory was not created if it didn't exist yet
|
|
10
|
+
- fix: try resolve deduplicated meshes
|
|
11
|
+
- change: update gltf-transform to 4.1.1
|
|
12
|
+
- change: modify mesh LOD settings for more vertices
|
|
13
|
+
|
|
7
14
|
## [2.6.0-alpha] - 2024-12-16
|
|
8
15
|
- add: create placeholder textures for missing jpg, png, webp, gif, tif on disc
|
|
9
16
|
- fix: compressing a gltf file to a different output directory does not falsely delete old external textures on disc in the input directory
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Extension, ExtensionProperty, PropertyType, TextureChannel } from '@gltf-transform/core';
|
|
1
|
+
import { Extension, ExtensionProperty, PropertyType, Node, TextureChannel } from '@gltf-transform/core';
|
|
2
2
|
import { getTexture, tryGetInArray, writeExtension } from '../utils.js';
|
|
3
3
|
import { isLightmap } from '../../utils/texture.js';
|
|
4
4
|
const ALL_PROPERTY_TYPES = [];
|
|
@@ -369,7 +369,7 @@ export function createOpaqueExtension(name, types, opts) {
|
|
|
369
369
|
writeExtension(result, EXTENSION_NAME, this.ext);
|
|
370
370
|
if (debugLog) {
|
|
371
371
|
// console.log("WRITE", EXTENSION_NAME, "to", this.gltfField, "at index", index);
|
|
372
|
-
console.log(result);
|
|
372
|
+
// console.log("WRITE RESULT", result);
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
375
|
}
|
|
@@ -646,6 +646,26 @@ class JsonPointerHandler {
|
|
|
646
646
|
pointer?.register(obj, key, fullPath);
|
|
647
647
|
}
|
|
648
648
|
}
|
|
649
|
+
if (value.startsWith("/meshes/")) {
|
|
650
|
+
if (this.debug)
|
|
651
|
+
console.log("Read pointer", value);
|
|
652
|
+
const index = parseInt(value.substring("/meshes/".length));
|
|
653
|
+
const object = context.meshes[index];
|
|
654
|
+
if (object) {
|
|
655
|
+
let pointer = this.map.get(value);
|
|
656
|
+
if (!pointer) {
|
|
657
|
+
const definition = context.jsonDoc.json.meshes[index];
|
|
658
|
+
const res = new MeshIndexResolver(this.document, object, definition, prop);
|
|
659
|
+
res.debug = this.debug;
|
|
660
|
+
res.init(context);
|
|
661
|
+
pointer = new JsonPointer("/meshes/", res, value);
|
|
662
|
+
pointer.debug = this.debug;
|
|
663
|
+
this.map.set(value, pointer);
|
|
664
|
+
prop.setReference(value, object);
|
|
665
|
+
}
|
|
666
|
+
pointer?.register(obj, key, fullPath);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
649
669
|
}
|
|
650
670
|
break;
|
|
651
671
|
case 'number':
|
|
@@ -770,14 +790,15 @@ class JsonPointer {
|
|
|
770
790
|
else {
|
|
771
791
|
this.newPointer = index;
|
|
772
792
|
}
|
|
773
|
-
if (this.debug)
|
|
774
|
-
console.log(`RESOLVED pointer ${this.oldPointer} to โ ${this.newPointer}`);
|
|
775
793
|
return true;
|
|
776
794
|
}
|
|
777
795
|
else {
|
|
778
796
|
// If the pointer could not be resolved during write we need to log an error
|
|
779
|
-
if (step === "write")
|
|
780
|
-
console.log(
|
|
797
|
+
if (step === "write") {
|
|
798
|
+
console.log(`ERR: ๐งจ FAILED to resolve pointer \"${this.oldPointer}\" used by ${this._debugInfo.join(", ")}`);
|
|
799
|
+
if (this.debug)
|
|
800
|
+
debugger;
|
|
801
|
+
}
|
|
781
802
|
}
|
|
782
803
|
return false;
|
|
783
804
|
}
|
|
@@ -790,7 +811,7 @@ class JsonPointer {
|
|
|
790
811
|
const obj = this._objects[i];
|
|
791
812
|
const prop = this._props[i];
|
|
792
813
|
if (this.debug) {
|
|
793
|
-
console.log(`< Resolved pointer ${this._debugInfo[i]}: ${this.newPointer}`);
|
|
814
|
+
console.log(`< Resolved pointer ${this._debugInfo[i]}: ${this.oldPointer} โ ${this.newPointer}`);
|
|
794
815
|
}
|
|
795
816
|
obj[prop] = this.newPointer;
|
|
796
817
|
}
|
|
@@ -802,10 +823,13 @@ class PointerResolver {
|
|
|
802
823
|
inputObject;
|
|
803
824
|
/* the original json definition */
|
|
804
825
|
inputDefinition;
|
|
805
|
-
|
|
826
|
+
/** The graph property this pointer belongs to */
|
|
827
|
+
property;
|
|
828
|
+
constructor(doc, obj, def, prop) {
|
|
806
829
|
this.document = doc;
|
|
807
830
|
this.inputObject = obj;
|
|
808
831
|
this.inputDefinition = def;
|
|
832
|
+
this.property = prop;
|
|
809
833
|
}
|
|
810
834
|
debug = false;
|
|
811
835
|
}
|
|
@@ -913,3 +937,35 @@ class NodeIndexResolver extends PointerResolver {
|
|
|
913
937
|
return context.nodeIndexMap.get(this.inputObject);
|
|
914
938
|
}
|
|
915
939
|
}
|
|
940
|
+
class MeshIndexResolver extends PointerResolver {
|
|
941
|
+
init(context) {
|
|
942
|
+
}
|
|
943
|
+
resolve(context) {
|
|
944
|
+
let index = context.meshIndexMap.get(this.inputObject);
|
|
945
|
+
if (index >= 0) {
|
|
946
|
+
return index;
|
|
947
|
+
}
|
|
948
|
+
let obj = null;
|
|
949
|
+
if (this.inputObject.isDisposed()) {
|
|
950
|
+
// If the mesh was deduplicated we try to get the mesh from a parent node
|
|
951
|
+
// This is a HACK, we need to get the reference to the mesh this one was deduplicated with since the parent node potentially has many children
|
|
952
|
+
const parents = this.property?.listParents();
|
|
953
|
+
for (const parent of parents) {
|
|
954
|
+
if (parent instanceof Node) {
|
|
955
|
+
obj = parent.getMesh();
|
|
956
|
+
if (obj) {
|
|
957
|
+
console.log(`INFO: resolved mesh \"${this.inputObject.getName()}\" from parent node \"${parent.getName()}\"`);
|
|
958
|
+
break;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
if (obj.isDisposed()) {
|
|
963
|
+
console.warn("WARN: mesh is disposed", this.inputObject.getName());
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
if (!obj) {
|
|
967
|
+
return undefined;
|
|
968
|
+
}
|
|
969
|
+
return context.meshIndexMap.get(obj);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
@@ -81,10 +81,11 @@ export async function packGLTF(inputFile, outputFile, options) {
|
|
|
81
81
|
},
|
|
82
82
|
config,
|
|
83
83
|
}),
|
|
84
|
-
needle_mesh_transform({ file: inputFile, config }
|
|
84
|
+
needle_mesh_transform({ file: inputFile, config }),
|
|
85
85
|
needle_animation_transform({}),
|
|
86
86
|
resample(),
|
|
87
87
|
// Can not dedup "materials" because progressive textures will then assigned to the wrong material instance
|
|
88
|
+
// HACK: keepUniqueNames is enabled to fix https://linear.app/needle/issue/NE-6288
|
|
88
89
|
dedup({ propertyTypes: [PropertyType.MESH, PropertyType.MATERIAL] }),
|
|
89
90
|
// prune(),
|
|
90
91
|
];
|
|
@@ -102,7 +103,7 @@ export async function packGLTF(inputFile, outputFile, options) {
|
|
|
102
103
|
transforms.push(join({ keepNamed: false }));
|
|
103
104
|
}
|
|
104
105
|
await document.transform(...transforms);
|
|
105
|
-
logger.info(
|
|
106
|
+
logger.info(`โ Writing to ${outputFile}`);
|
|
106
107
|
await writeNodeIO(io, outputFile, document);
|
|
107
108
|
if (useCache && cacheKey != undefined && existsSync(outputFile))
|
|
108
109
|
addToCache(cacheKey, readFileSync(outputFile));
|
|
@@ -10,6 +10,6 @@ declare type MeshTransformContext = {
|
|
|
10
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
11
|
*/
|
|
12
12
|
export declare function determineMeshCompression(document: Document, filePath: string): NEEDLE_mesh_compression_model_root;
|
|
13
|
-
export declare const needle_mesh_transform: (context: MeshTransformContext
|
|
13
|
+
export declare const needle_mesh_transform: (context: MeshTransformContext) => Transform;
|
|
14
14
|
export declare function compressPrimitive(document: Document, mesh: Mesh, prim: Primitive, index: number, weldOptions: Required<WeldOptions>, meshOptions: Required<SimplifyOptions>): Promise<Primitive>;
|
|
15
15
|
export {};
|
|
@@ -3,6 +3,7 @@ import { meshopt, simplifyPrimitive, SIMPLIFY_DEFAULTS, WELD_DEFAULTS, draco } f
|
|
|
3
3
|
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
|
+
import { calculateMeshDensity } from "../utils/mesh.js";
|
|
6
7
|
const NAME = 'needle_mesh_transform';
|
|
7
8
|
/**
|
|
8
9
|
* 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
|
|
@@ -18,7 +19,7 @@ export function determineMeshCompression(document, filePath) {
|
|
|
18
19
|
return rootModel;
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
|
-
if (filePath.endsWith(".vrm")) {
|
|
22
|
+
if (filePath?.toLowerCase().endsWith(".vrm")) {
|
|
22
23
|
const hasBlendshapes = document.getRoot().listMeshes().some((mesh) => mesh.listPrimitives().some((prim) => prim.listTargets().length > 0));
|
|
23
24
|
if (hasBlendshapes) {
|
|
24
25
|
return new NEEDLE_mesh_compression_model_root("meshopt");
|
|
@@ -57,10 +58,10 @@ export function determineMeshCompression(document, filePath) {
|
|
|
57
58
|
// }
|
|
58
59
|
return new NEEDLE_mesh_compression_model_root("draco");
|
|
59
60
|
}
|
|
60
|
-
export const needle_mesh_transform = function (context
|
|
61
|
+
export const needle_mesh_transform = function (context) {
|
|
61
62
|
return async (document, _context) => {
|
|
62
63
|
// TODO: needs to be moved into NEEDLE_progressive
|
|
63
|
-
await simplifierTransform(document, _context,
|
|
64
|
+
await simplifierTransform(document, _context, context);
|
|
64
65
|
await applyMeshCompression(document, _context, context);
|
|
65
66
|
};
|
|
66
67
|
};
|
|
@@ -88,21 +89,28 @@ async function applyMeshCompression(document, transform_context, context) {
|
|
|
88
89
|
document.getLogger().info(`[${NAME}]: โ will apply draco mesh compression (default)`);
|
|
89
90
|
return draco()(document, transform_context);
|
|
90
91
|
}
|
|
91
|
-
async function simplifierTransform(document, _context,
|
|
92
|
+
async function simplifierTransform(document, _context, meshContext) {
|
|
92
93
|
const simplifier = MeshoptSimplifier;
|
|
93
94
|
if (!simplifier) {
|
|
94
95
|
console.log(`WARN: [${NAME}] simplifier dependency required โ install "meshoptimizer".`);
|
|
95
96
|
return false;
|
|
96
97
|
}
|
|
97
98
|
const logger = document.getLogger();
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
const isVRM = meshContext.file?.toLowerCase().endsWith(".vrm");
|
|
100
|
+
const options = {
|
|
101
|
+
...SIMPLIFY_DEFAULTS,
|
|
102
|
+
simplifier: simplifier,
|
|
103
|
+
ratio: 0.75,
|
|
104
|
+
error: 0.0005,
|
|
105
|
+
};
|
|
101
106
|
const weldOptions = {
|
|
102
107
|
...WELD_DEFAULTS,
|
|
103
|
-
tolerance: 0.00000000001,
|
|
104
108
|
overwrite: true,
|
|
105
109
|
};
|
|
110
|
+
// let meshInformation: MeshInformation | null = null;
|
|
111
|
+
// if (isVRM) {
|
|
112
|
+
// meshInformation = getMeshInformation(document);
|
|
113
|
+
// }
|
|
106
114
|
for (const mesh of document.getRoot().listMeshes()) {
|
|
107
115
|
let i = 0;
|
|
108
116
|
for (const prim of mesh.listPrimitives()) {
|
|
@@ -119,9 +127,25 @@ async function simplifierTransform(document, _context, _options) {
|
|
|
119
127
|
const ext = prim.getExtension(NEEDLE_mesh_compression.EXTENSION_NAME);
|
|
120
128
|
const mesh_compression = ext?.model;
|
|
121
129
|
const simplifyOpts = { ...options };
|
|
122
|
-
if (
|
|
130
|
+
if (isVRM) {
|
|
131
|
+
const density = calculateMeshDensity(prim);
|
|
132
|
+
if (density > 0) {
|
|
133
|
+
const targetDensity = 2_000;
|
|
134
|
+
const t = (1 / density) * targetDensity;
|
|
135
|
+
simplifyOpts.ratio = Math.min(Math.max(.1, t), .95);
|
|
136
|
+
simplifyOpts.error = 15 / density;
|
|
137
|
+
simplifyOpts.lockBorder = true;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
simplifyOpts.ratio = 0.8;
|
|
141
|
+
simplifyOpts.error = 0.001;
|
|
142
|
+
simplifyOpts.lockBorder = true;
|
|
143
|
+
}
|
|
144
|
+
logger.info(`${mesh.getName()}: Density: ${density.toFixed(2)}, Ratio: ${simplifyOpts.ratio.toFixed(2)}, Error: ${simplifyOpts.error}`);
|
|
145
|
+
}
|
|
146
|
+
if (!ext && !isVRM) {
|
|
123
147
|
// If no extension exists on the primitive dont apply any simplification
|
|
124
|
-
//
|
|
148
|
+
// AND if the file is not a VRM file
|
|
125
149
|
continue;
|
|
126
150
|
}
|
|
127
151
|
if (mesh_compression) {
|
|
@@ -9,7 +9,7 @@ import { addMetaToCache, addToCache, getHash, getKey, tryGetFromCache, tryGetMet
|
|
|
9
9
|
import sharp from "sharp";
|
|
10
10
|
import { detectTextureCompressionMode, ensureIsDirectory, foreachGLTF, generateGuid, getOutputPath, isLightmap, setLightmapName, ioTryReadWithMissingResources, validateImageMimeType, writeNodeIO } from '../utils/index.js';
|
|
11
11
|
import { needle_asset, writeNeedleAsset } from './needle_asset.js';
|
|
12
|
-
import { MeshoptDecoder, MeshoptSimplifier } from 'meshoptimizer';
|
|
12
|
+
import { MeshoptDecoder, MeshoptEncoder, MeshoptSimplifier } from 'meshoptimizer';
|
|
13
13
|
import { compressPrimitive, determineMeshCompression } from './needle_mesh_transform.js';
|
|
14
14
|
import { calculateMeshDensity, getMeshInformation } from "../utils/mesh.js";
|
|
15
15
|
export async function make_progressive(opts) {
|
|
@@ -58,6 +58,7 @@ async function onProcess(file, opts) {
|
|
|
58
58
|
registerExtensions(io);
|
|
59
59
|
io.registerDependencies({
|
|
60
60
|
'meshopt.decoder': MeshoptDecoder,
|
|
61
|
+
'meshopt.encoder': MeshoptEncoder,
|
|
61
62
|
'draco3d.decoder': await draco3d.createDecoderModule(),
|
|
62
63
|
'draco3d.encoder': await draco3d.createEncoderModule(),
|
|
63
64
|
});
|
|
@@ -533,7 +534,7 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
533
534
|
// const t = l / MAX_LODS;
|
|
534
535
|
const latest = LODS[LODS.length - 1];
|
|
535
536
|
const ratio = latest.ratio * .5;
|
|
536
|
-
let error = Math.min(.013 * (l + 1), 0.
|
|
537
|
+
let error = Math.min(.013 * (l + 1), 0.003);
|
|
537
538
|
if (opts.config.usecase === "product") {
|
|
538
539
|
error *= .5;
|
|
539
540
|
}
|
|
@@ -544,6 +545,7 @@ export function make_progressive_meshes(filePath, _options, opts) {
|
|
|
544
545
|
// toleranceNormal: 0.1 + t * 0.1,
|
|
545
546
|
});
|
|
546
547
|
}
|
|
548
|
+
logger.debug(`[${NAME}]: Generating mesh LODs:\n${JSON.stringify(LODS, undefined, 2)}`);
|
|
547
549
|
/** Map of visited GUID -> extension */
|
|
548
550
|
const visited = new Map();
|
|
549
551
|
const root = doc.getRoot();
|
|
@@ -842,6 +844,8 @@ async function createMeshLod(inputFileName, doc, basePath, level, mesh, meshInfo
|
|
|
842
844
|
const newAttr = newDoc.createAccessor();
|
|
843
845
|
newAttr.setArray(attr.getArray());
|
|
844
846
|
newAttr.setType(attr.getType());
|
|
847
|
+
newAttr.setSparse(attr.getSparse());
|
|
848
|
+
newAttr.setNormalized(attr.getNormalized());
|
|
845
849
|
newPrim.setAttribute(semantics[i], newAttr);
|
|
846
850
|
}
|
|
847
851
|
else {
|
|
@@ -853,20 +857,29 @@ async function createMeshLod(inputFileName, doc, basePath, level, mesh, meshInfo
|
|
|
853
857
|
const newIndices = newDoc.createAccessor();
|
|
854
858
|
newIndices.setArray(indices.getArray());
|
|
855
859
|
newIndices.setType(indices.getType());
|
|
860
|
+
newIndices.setSparse(indices.getSparse());
|
|
861
|
+
newIndices.setNormalized(indices.getNormalized());
|
|
856
862
|
newPrim.setIndices(newIndices);
|
|
857
863
|
}
|
|
858
864
|
const targets = prim.listTargets();
|
|
859
865
|
for (const target of targets) {
|
|
860
866
|
const newTarget = newDoc.createPrimitiveTarget(target.getName());
|
|
861
867
|
newPrim.addTarget(newTarget);
|
|
862
|
-
|
|
868
|
+
// const attributes = target.listAttributes();
|
|
869
|
+
const semantics = target.listSemantics();
|
|
870
|
+
for (const attr of semantics) {
|
|
863
871
|
const accessor = target.getAttribute(attr);
|
|
864
872
|
if (accessor) {
|
|
865
873
|
const newAccessor = newDoc.createAccessor();
|
|
866
874
|
newAccessor.setArray(accessor.getArray());
|
|
867
875
|
newAccessor.setType(accessor.getType());
|
|
876
|
+
newAccessor.setSparse(accessor.getSparse());
|
|
877
|
+
newAccessor.setNormalized(accessor.getNormalized());
|
|
868
878
|
newTarget.setAttribute(attr, newAccessor);
|
|
869
879
|
}
|
|
880
|
+
else {
|
|
881
|
+
console.error(`[${NAME}] Missing target attribute ${attr} in mesh LOD ${level}`);
|
|
882
|
+
}
|
|
870
883
|
}
|
|
871
884
|
}
|
|
872
885
|
;
|
package/dist/utils/mesh.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Mesh, Primitive } from "@gltf-transform/core";
|
|
1
|
+
import { Document, Mesh, Primitive } from "@gltf-transform/core";
|
|
2
2
|
export declare type MeshInformation = {
|
|
3
3
|
readonly maxVertexCount: number;
|
|
4
4
|
readonly totalVertexCount: number;
|
|
5
5
|
readonly totalIndexCount: number;
|
|
6
6
|
};
|
|
7
|
-
export declare function getMeshInformation(mesh: Mesh): MeshInformation;
|
|
7
|
+
export declare function getMeshInformation(mesh: Mesh | Document): MeshInformation;
|
|
8
8
|
/**
|
|
9
9
|
* Calculate the density of a mesh.
|
|
10
10
|
* @param prim - The primitive to calculate the density for.
|
package/dist/utils/mesh.js
CHANGED
|
@@ -1,18 +1,35 @@
|
|
|
1
|
+
import { Mesh } from "@gltf-transform/core";
|
|
1
2
|
export function getMeshInformation(mesh) {
|
|
2
3
|
let maxVertexCount = 0;
|
|
3
4
|
let totalIndexCount = 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
let totalVertexCount = 0;
|
|
6
|
+
if (mesh instanceof Mesh) {
|
|
7
|
+
totalVertexCount = mesh.listPrimitives().reduce((acc, prim) => {
|
|
8
|
+
const indices = prim.getIndices()?.getCount() || 0;
|
|
9
|
+
totalIndexCount += indices;
|
|
10
|
+
const vertices = prim.getAttribute('POSITION')?.getCount() || 0;
|
|
11
|
+
maxVertexCount = Math.max(maxVertexCount, vertices);
|
|
12
|
+
return acc + vertices;
|
|
13
|
+
}, 0);
|
|
14
|
+
return {
|
|
15
|
+
maxVertexCount,
|
|
16
|
+
totalVertexCount,
|
|
17
|
+
totalIndexCount,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
mesh.getRoot().listMeshes().forEach(mesh => {
|
|
22
|
+
const info = getMeshInformation(mesh);
|
|
23
|
+
maxVertexCount = Math.max(maxVertexCount, info.maxVertexCount);
|
|
24
|
+
totalVertexCount += info.totalVertexCount;
|
|
25
|
+
totalIndexCount += info.totalIndexCount;
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
maxVertexCount,
|
|
29
|
+
totalVertexCount,
|
|
30
|
+
totalIndexCount,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
16
33
|
}
|
|
17
34
|
/**
|
|
18
35
|
* Calculate the density of a mesh.
|
package/dist/utils/nodeio.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from "fs";
|
|
1
2
|
import { writeFile } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
2
4
|
import sharp from "sharp";
|
|
3
5
|
/**
|
|
4
6
|
* Handle writing glTf, GLB and VRM files
|
|
5
7
|
*/
|
|
6
8
|
export async function writeNodeIO(nodeio, filename, document) {
|
|
9
|
+
const directoryPath = path.dirname(filename);
|
|
10
|
+
if (!existsSync(directoryPath)) {
|
|
11
|
+
mkdirSync(directoryPath);
|
|
12
|
+
}
|
|
7
13
|
// see https://github.com/donmccurdy/glTF-Transform/issues/1390
|
|
8
14
|
if (filename.endsWith(".vrm")) {
|
|
9
15
|
const bin = await nodeio.writeBinary(document);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const version = "2.7.0-alpha";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const version = "2.7.0-alpha";
|
package/dist/utils/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-build-pipeline",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0-alpha",
|
|
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",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@donmccurdy/caporal": "~0.0.10",
|
|
50
|
-
"@gltf-transform/core": "^4.
|
|
51
|
-
"@gltf-transform/extensions": "^4.
|
|
52
|
-
"@gltf-transform/functions": "^4.
|
|
50
|
+
"@gltf-transform/core": "^4.1.1",
|
|
51
|
+
"@gltf-transform/extensions": "^4.1.1",
|
|
52
|
+
"@gltf-transform/functions": "^4.1.1",
|
|
53
53
|
"command-exists": "^1.2.9",
|
|
54
54
|
"command-line-args": "^5.2.1",
|
|
55
55
|
"draco3dgltf": "^1.5.3",
|