@needle-tools/gltf-build-pipeline 2.2.1-alpha.7 → 2.3.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 +9 -0
- package/dist/cli/index.js +33 -15
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/extensions/NEEDLE_opaque/NEEDLE_opaque.js +19 -1
- package/dist/scripts/pack-gltf.js +11 -3
- package/dist/transforms/needle_mesh_transform.js +6 -3
- package/dist/transforms/needle_progressive.d.ts +2 -0
- package/dist/transforms/needle_progressive.js +21 -8
- package/dist/transforms/needle_toktx.js +2 -2
- package/dist/transforms/needle_webp.d.ts +1 -0
- package/dist/transforms/needle_webp.js +8 -0
- package/dist/utils/fileutils.d.ts +13 -0
- package/dist/utils/fileutils.js +35 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ 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.3.0-alpha] - 2023-06-22
|
|
8
|
+
- add: flatten and join for `product` usecase
|
|
9
|
+
|
|
10
|
+
## [2.2.1-alpha.8] - 2023-06-22
|
|
11
|
+
- add: CLI <output> support
|
|
12
|
+
- fix: register webp extension for valid glTF files. [issue 5122](https://linear.app/needle/issue/NE-5122)
|
|
13
|
+
- fix: issue where VRM material would be pruned
|
|
14
|
+
- change: VRM use meshopt compression where any blendshapes are found
|
|
15
|
+
|
|
7
16
|
## [2.2.1-alpha.7] - 2023-06-19
|
|
8
17
|
- fix: WARN if image validation can not run
|
|
9
18
|
- change: compress lightmap textures with webp
|
package/dist/cli/index.js
CHANGED
|
@@ -6,9 +6,8 @@ import { getVersion } from '../utils/version.js';
|
|
|
6
6
|
import { UsecaseOptions, createConfig, getConfig } from '../config/index.js';
|
|
7
7
|
import { make_progressive } from '../transforms/needle_progressive.js';
|
|
8
8
|
import { cacheSizeLimit, clearCache, limitCacheSize } from '../cache/cache.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import path, { dirname } from 'path';
|
|
9
|
+
import { ensureIsDirectory, foreachGLTF, isDirectory } from '../utils/fileutils.js';
|
|
10
|
+
import path, { resolve } from 'path';
|
|
12
11
|
import { ERROR_CODES } from '../constants.js';
|
|
13
12
|
// For testing / dev you can run `npm link` in the package directory
|
|
14
13
|
// For removing the link run `npm rm --global @needle-tools/gltf-build-pipeline`
|
|
@@ -57,7 +56,7 @@ This will produce multiple versions of the input file, each with a different lev
|
|
|
57
56
|
Each version will be compressed and written to the output directory.
|
|
58
57
|
`)
|
|
59
58
|
.argument('<input>', "The input glTF, GLB or VRM file that should be compressed OR a directory that contains files to be compressed")
|
|
60
|
-
|
|
59
|
+
.argument('<output>', "The output file or directory. It can be an absolute path or a relative path to the input file. If not provided, the input file will be overwritten.", { default: null })
|
|
61
60
|
.option("--compress", `When enabled files will be compressed`, { validator: program.BOOLEAN, default: true })
|
|
62
61
|
.option("--progressive", `When enabled files will processed to be progressively loaded`, { validator: program.BOOLEAN, default: true })
|
|
63
62
|
.option("--usecase <usecase>", `The usecase for the compression. This will set the compression settings. Possible options: [${UsecaseOptions.join(", ")}]`, { validator: program.STRING })
|
|
@@ -78,8 +77,8 @@ Each version will be compressed and written to the output directory.
|
|
|
78
77
|
totalFileSizeInMB: 0,
|
|
79
78
|
totalFileSizeInMBBefore: 0,
|
|
80
79
|
};
|
|
81
|
-
const
|
|
82
|
-
let config = getConfig(options.config?.toString(),
|
|
80
|
+
const input = args.input.toString();
|
|
81
|
+
let config = getConfig(options.config?.toString(), input, {
|
|
83
82
|
logger
|
|
84
83
|
});
|
|
85
84
|
if (options.usecase) {
|
|
@@ -92,18 +91,29 @@ Each version will be compressed and written to the output directory.
|
|
|
92
91
|
config.usecase = options.usecase.toString();
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
const
|
|
94
|
+
let output = args.output?.toString() || input;
|
|
95
|
+
const inputIsDirectory = isDirectory(input);
|
|
96
|
+
const outputIsDirectory = isDirectory(output);
|
|
97
|
+
if (inputIsDirectory && !outputIsDirectory) {
|
|
98
|
+
logger.error("When <input> is a directory then <output> must also be a directory");
|
|
99
|
+
process.exit(ERROR_CODES.INVALID_ARGS);
|
|
100
|
+
}
|
|
101
|
+
// Handle the output bath being relative to the input path
|
|
102
|
+
if (path.isAbsolute(output) === false) {
|
|
103
|
+
const base = ensureIsDirectory(input);
|
|
104
|
+
output = resolve(base, output);
|
|
105
|
+
}
|
|
98
106
|
const set = new Set();
|
|
99
107
|
const useCache = options.cache !== false;
|
|
100
108
|
const debug = options.debug === true;
|
|
101
|
-
|
|
109
|
+
const verbose = options.verbose === true;
|
|
110
|
+
await foreachGLTF(input, async (file) => {
|
|
102
111
|
logger.info("→ Transform " + file);
|
|
103
112
|
const progressive_results = new Array();
|
|
104
113
|
if (options.progressive === true) {
|
|
105
114
|
if (await make_progressive({
|
|
106
115
|
path: file,
|
|
116
|
+
outpath: output,
|
|
107
117
|
results: progressive_results,
|
|
108
118
|
config,
|
|
109
119
|
debug,
|
|
@@ -121,16 +131,24 @@ Each version will be compressed and written to the output directory.
|
|
|
121
131
|
stats,
|
|
122
132
|
packedFiles: set,
|
|
123
133
|
debug,
|
|
134
|
+
verbose,
|
|
124
135
|
useCache,
|
|
125
136
|
logger: logger,
|
|
126
137
|
};
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
// If we have produced progressive assets then the array already contains the input
|
|
139
|
+
if (progressive_results.length > 0) {
|
|
140
|
+
for (const res of progressive_results) {
|
|
141
|
+
if (await packGLTF(res, res, opts) === false) {
|
|
142
|
+
process.exit(ERROR_CODES.PACKING_FAILED);
|
|
143
|
+
}
|
|
130
144
|
}
|
|
131
145
|
}
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
// If we have not produced progressive assets then we just need to compress the input
|
|
147
|
+
// We can not just check if the file is in the list of results because the output path might have changed the name
|
|
148
|
+
else {
|
|
149
|
+
if (await packGLTF(file, output, opts) === false) {
|
|
150
|
+
process.exit(ERROR_CODES.PACKING_FAILED);
|
|
151
|
+
}
|
|
134
152
|
}
|
|
135
153
|
}
|
|
136
154
|
});
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
|
@@ -524,6 +524,7 @@ class JsonPointerHandler {
|
|
|
524
524
|
map = new Map();
|
|
525
525
|
debug = false;
|
|
526
526
|
breakPoint = false;
|
|
527
|
+
VRM_0_MaterialIndexRegex = new RegExp(/materialProperties\/(?<material_index>\d+)\//g);
|
|
527
528
|
constructor(document, id) {
|
|
528
529
|
this.document = document;
|
|
529
530
|
this.id = id;
|
|
@@ -671,7 +672,7 @@ class JsonPointerHandler {
|
|
|
671
672
|
// key texture is matching for example for VRM meta/texture (AvatarSample_A)
|
|
672
673
|
key === "texture"
|
|
673
674
|
// VRM 0.0 material properties
|
|
674
|
-
|| fullPath.match(/materialProperties
|
|
675
|
+
|| fullPath.match(/materialProperties\/(?<material_index>\d+)\/textureProperties/g)
|
|
675
676
|
// VRM 1.0 meta
|
|
676
677
|
|| fullPath.match(/meta\/thumbnailImage/g)) {
|
|
677
678
|
const index = value;
|
|
@@ -705,6 +706,23 @@ class JsonPointerHandler {
|
|
|
705
706
|
this.map.set(dictKey, pointer);
|
|
706
707
|
prop.setReference(dictKey, object, { channels: TextureChannel.R | TextureChannel.G | TextureChannel.B | TextureChannel.A });
|
|
707
708
|
}
|
|
709
|
+
const materialMatch = this.VRM_0_MaterialIndexRegex.exec(fullPath);
|
|
710
|
+
if (materialMatch) {
|
|
711
|
+
const materialIndex = parseInt(materialMatch.groups.material_index);
|
|
712
|
+
const material = context.materials[materialIndex];
|
|
713
|
+
prop.setReference(`/materials/${materialIndex}`, material);
|
|
714
|
+
// const res = new MaterialIndexResolver(this.document, material, context.jsonDoc.json.materials![materialIndex]);
|
|
715
|
+
// res.debug = this.debug;
|
|
716
|
+
// res.init(context);
|
|
717
|
+
// const dictKey = `/materials/${materialIndex}`
|
|
718
|
+
// let pointer = this.map.get(dictKey);
|
|
719
|
+
// if (!pointer) {
|
|
720
|
+
// pointer = new JsonPointer(null, res, value);
|
|
721
|
+
// }
|
|
722
|
+
// pointer.debug = this.debug;
|
|
723
|
+
// pointer.register(obj, key, fullPath);
|
|
724
|
+
// this.map.set(dictKey, pointer);
|
|
725
|
+
}
|
|
708
726
|
}
|
|
709
727
|
break;
|
|
710
728
|
default:
|
|
@@ -4,10 +4,10 @@ import { ALL_EXTENSIONS } from '@gltf-transform/extensions';
|
|
|
4
4
|
import draco3d from 'draco3dgltf';
|
|
5
5
|
import { existsSync, readFileSync, statSync, writeFileSync } from 'fs';
|
|
6
6
|
import { MeshoptDecoder, MeshoptEncoder } from 'meshoptimizer';
|
|
7
|
-
import { dedup, prune, resample } from '@gltf-transform/functions';
|
|
7
|
+
import { dedup, flatten, join, prune, resample } from '@gltf-transform/functions';
|
|
8
8
|
import { ALL_EXTENSIONS as NEEDLE_EXTENSIONS, NEEDLE_compression_texture, NEEDLE_mesh_compression, } from '../extensions/index.js';
|
|
9
9
|
import { isLOD, isMeshLOD, needle_animation_transform, needle_asset, needle_mesh_transform, needle_texture_transform, } from '../transforms/index.js';
|
|
10
|
-
import { writeNodeIO } from "../utils/index.js";
|
|
10
|
+
import { getOutputPath, writeNodeIO } from "../utils/index.js";
|
|
11
11
|
import { addToCache, getHash, tryGetFromCache, } from "../cache/index.js";
|
|
12
12
|
/** Compress a glTF file
|
|
13
13
|
*/
|
|
@@ -60,7 +60,7 @@ export async function packGLTF(inputFile, outputFile, options) {
|
|
|
60
60
|
});
|
|
61
61
|
const document = await io.read(inputFile);
|
|
62
62
|
/** @ts-ignore */
|
|
63
|
-
document.getLogger().verbosity = options.
|
|
63
|
+
document.getLogger().verbosity = options.verbose ? Verbosity.DEBUG : options.debug ? Verbosity.INFO : Verbosity.WARN;
|
|
64
64
|
let totalGPUTextureMemory = 0;
|
|
65
65
|
/** @type {Array<import('@gltf-transform/core').Transform>} */
|
|
66
66
|
const transforms = [
|
|
@@ -91,8 +91,16 @@ export async function packGLTF(inputFile, outputFile, options) {
|
|
|
91
91
|
}));
|
|
92
92
|
}
|
|
93
93
|
await document.transform(...transforms);
|
|
94
|
+
const isVRM = inputFile.toLowerCase().endsWith(".vrm");
|
|
95
|
+
if (!isVRM && config?.usecase === "product") {
|
|
96
|
+
await document.transform(flatten(), join({ keepNamed: false }));
|
|
97
|
+
}
|
|
94
98
|
if (!outputFile)
|
|
95
99
|
outputFile = inputFile;
|
|
100
|
+
else {
|
|
101
|
+
outputFile = getOutputPath(inputFile, outputFile);
|
|
102
|
+
logger.info(`⇄ Writing to ${outputFile}`);
|
|
103
|
+
}
|
|
96
104
|
await writeNodeIO(io, outputFile, document);
|
|
97
105
|
if (useCache && cacheKey != undefined && existsSync(outputFile))
|
|
98
106
|
addToCache(cacheKey, readFileSync(outputFile));
|
|
@@ -33,9 +33,12 @@ async function applyMeshCompression(document, transform_context, context) {
|
|
|
33
33
|
}
|
|
34
34
|
// Meshop is better at re-using vertex attributes
|
|
35
35
|
if (context.file.endsWith(".vrm")) {
|
|
36
|
-
document.
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
const hasBlendshapes = document.getRoot().listMeshes().some((mesh) => mesh.listPrimitives().some((prim) => prim.listTargets().length > 0));
|
|
37
|
+
if (hasBlendshapes) {
|
|
38
|
+
document.getLogger().info(`[${NAME}]: VRM → will apply meshopt mesh compression`);
|
|
39
|
+
const transform = meshopt({ encoder: MeshoptEncoder });
|
|
40
|
+
return transform(document, transform_context);
|
|
41
|
+
}
|
|
39
42
|
}
|
|
40
43
|
// by default we still want to run draco if the root doesnt have a compression model configured
|
|
41
44
|
document.getLogger().info(`[${NAME}]: → will apply draco mesh compression (default)`);
|
|
@@ -2,6 +2,8 @@ import { ILogger, Texture, Transform, vec2 } from '@gltf-transform/core';
|
|
|
2
2
|
import { Config } from '../config/index.js';
|
|
3
3
|
declare interface ProgressiveOptions {
|
|
4
4
|
path: string | Array<string>;
|
|
5
|
+
/** Directory to output progressive assets to */
|
|
6
|
+
outpath?: string;
|
|
5
7
|
results: Array<string>;
|
|
6
8
|
config: Config;
|
|
7
9
|
debug?: boolean;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { BufferUtils, Document, NodeIO, PropertyType, TextureChannel, Verbosity } from '@gltf-transform/core';
|
|
2
|
-
import { SIMPLIFY_DEFAULTS, WELD_DEFAULTS, listTextureChannels, listTextureSlots, sparse } from '@gltf-transform/functions';
|
|
2
|
+
import { SIMPLIFY_DEFAULTS, WELD_DEFAULTS, flatten, join, listTextureChannels, listTextureSlots, sparse } from '@gltf-transform/functions';
|
|
3
3
|
import draco3d from 'draco3dgltf';
|
|
4
4
|
import { NEEDLE_PROGRESSIVE_EXTENSION_NAME, NEEDLE_progressive } from "../extensions/NEEDLE_progressive/index.js";
|
|
5
5
|
import { createOpaqueExtension, currentGenerator, NEEDLE_compression_texture, NEEDLE_mesh_compression, NEEDLE_progressive_mesh_settings, NEEDLE_progressive_texture_settings, registerExtensions as registerDefaultExtensions } from '../extensions/index.js';
|
|
6
6
|
import path, { basename, resolve } from 'path';
|
|
7
|
-
import { existsSync, readFileSync, rmSync, statSync, writeFileSync } from 'fs';
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'fs';
|
|
8
8
|
import { addMetaToCache, addToCache, getHash, getKey, tryGetFromCache, tryGetMetaFromCache } from '../cache/index.js';
|
|
9
9
|
import sharp from "sharp";
|
|
10
|
-
import { detectTextureCompressionMode, foreachGLTF, generateGuid, isLightmap, setLightmapName, validateImageMimeType, writeNodeIO } from '../utils/index.js';
|
|
10
|
+
import { detectTextureCompressionMode, ensureIsDirectory, foreachGLTF, generateGuid, getOutputPath, isLightmap, setLightmapName, validateImageMimeType, writeNodeIO } from '../utils/index.js';
|
|
11
11
|
import { needle_asset, writeNeedleAsset } from './needle_asset.js';
|
|
12
12
|
import { MeshoptDecoder, MeshoptSimplifier } from 'meshoptimizer';
|
|
13
13
|
import { compressPrimitive } from './needle_mesh_transform.js';
|
|
@@ -77,7 +77,7 @@ async function onProcess(file, opts) {
|
|
|
77
77
|
transforms.push(make_progressive_textures(file, { size: [smallesTextureSize, smallesTextureSize] }, opts));
|
|
78
78
|
}
|
|
79
79
|
// TODO: progressive meshes aren't supported yet for VRMs
|
|
80
|
-
const isVRM = file.endsWith(".vrm");
|
|
80
|
+
const isVRM = file.toLowerCase().endsWith(".vrm");
|
|
81
81
|
if (!isVRM && opts.config?.meshes?.lods !== false) {
|
|
82
82
|
transforms.push(make_progressive_meshes(file, {}, opts));
|
|
83
83
|
}
|
|
@@ -85,8 +85,12 @@ async function onProcess(file, opts) {
|
|
|
85
85
|
logger.warn("WARN: Progressive meshes are not supported for VRM files yet");
|
|
86
86
|
transforms.push(sparse());
|
|
87
87
|
logger.info("→ Transforming: " + file);
|
|
88
|
+
if (!isVRM && opts.config?.usecase === "product") {
|
|
89
|
+
await document.transform(flatten(), join({ keepNamed: false }));
|
|
90
|
+
}
|
|
88
91
|
await document.transform(...transforms);
|
|
89
|
-
const filename = file;
|
|
92
|
+
const filename = getOutputPath(file, opts.outpath);
|
|
93
|
+
;
|
|
90
94
|
await writeNodeIO(io, filename, document);
|
|
91
95
|
opts.results.push(filename);
|
|
92
96
|
logger.info("← Transforming done: " + filename);
|
|
@@ -111,7 +115,10 @@ export function make_progressive_textures(filePath, _options = TEXTURE_RESIZE_DE
|
|
|
111
115
|
const startTime = Date.now();
|
|
112
116
|
const filename = basename(filePath);
|
|
113
117
|
const options = { ...TEXTURE_RESIZE_DEFAULTS, ..._options };
|
|
114
|
-
|
|
118
|
+
let directory = path.dirname(filePath);
|
|
119
|
+
if (opts.outpath) {
|
|
120
|
+
directory = ensureIsDirectory(opts.outpath);
|
|
121
|
+
}
|
|
115
122
|
// const fileNameWithoutExtension = path.basename(filePath, path.extname(filePath));
|
|
116
123
|
const textureLodBasePath = directory + "/";
|
|
117
124
|
return createTransform(NAME, async (doc) => {
|
|
@@ -367,7 +374,10 @@ async function createTextureLod(args, opts) {
|
|
|
367
374
|
const ext = ".glb";
|
|
368
375
|
const imageName = "image_" + level + "_" + guid;
|
|
369
376
|
const filename = imageName + ext;
|
|
370
|
-
const outName = basePath
|
|
377
|
+
const outName = path.join(basePath, filename);
|
|
378
|
+
if (!existsSync(basePath)) {
|
|
379
|
+
mkdirSync(basePath, { recursive: true });
|
|
380
|
+
}
|
|
371
381
|
logger.info(`Write texture LOD ${level} to \"${filename}\"`);
|
|
372
382
|
newIo.write(outName, newDoc);
|
|
373
383
|
return {
|
|
@@ -480,7 +490,10 @@ async function resizeTexture(options, texture, logger, uri, slots) {
|
|
|
480
490
|
};
|
|
481
491
|
}
|
|
482
492
|
export function make_progressive_meshes(filePath, _options, opts) {
|
|
483
|
-
|
|
493
|
+
let directory = path.dirname(filePath);
|
|
494
|
+
if (opts.outpath) {
|
|
495
|
+
directory = ensureIsDirectory(opts.outpath);
|
|
496
|
+
}
|
|
484
497
|
// const fileNameWithoutExtension = path.basename(filePath, path.extname(filePath));
|
|
485
498
|
const outputDirectory = directory + "/";
|
|
486
499
|
return createTransform("PROGRESSIVE_MESHES", async (doc, _context) => {
|
|
@@ -2,7 +2,7 @@ import { BufferUtils, FileUtils, ImageUtils, uuid } from "@gltf-transform/core";
|
|
|
2
2
|
import { listTextureSlots, getTextureChannelMask, fitPowerOfTwo, TextureResizeFilter } from "@gltf-transform/functions";
|
|
3
3
|
import { KHRTextureBasisu } from "@gltf-transform/extensions";
|
|
4
4
|
import { checkKTXSoftware, createParams, KTX_SOFTWARE_VERSION_MIN, Mode } from "./toktx.js";
|
|
5
|
-
import { existsSync, mkdirSync,
|
|
5
|
+
import { existsSync, mkdirSync, rmSync } from "fs";
|
|
6
6
|
import { formatBytes, spawn, waitExit } from './util.js';
|
|
7
7
|
import { ETC1S_DEFAULTS, UASTC_DEFAULTS } from "./toktx_types.js";
|
|
8
8
|
import fs from 'fs/promises';
|
|
@@ -233,7 +233,7 @@ export class NeedleToktxContext {
|
|
|
233
233
|
this.basisuExtension?.dispose();
|
|
234
234
|
}
|
|
235
235
|
if (this.cacheDirectory && existsSync(this.cacheDirectory)) {
|
|
236
|
-
|
|
236
|
+
rmSync(this.cacheDirectory, { recursive: true });
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
}
|
|
@@ -6,6 +6,7 @@ export interface NeedleWebPOptions {
|
|
|
6
6
|
export declare class NeedleWebPContext implements INeedleTextureTransformContext {
|
|
7
7
|
options: NeedleWebPOptions;
|
|
8
8
|
logger: ILogger;
|
|
9
|
+
private compressed;
|
|
9
10
|
constructor(options: NeedleWebPOptions);
|
|
10
11
|
prepare(document: Document): Promise<void>;
|
|
11
12
|
process(index: number, texture: Texture, settings: Pick<NEEDLE_compression_texture_schema, "quality">, context: NeedleTransformContext): Promise<boolean>;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import sharp from 'sharp';
|
|
2
2
|
import { BufferUtils, HTTPUtils } from '@gltf-transform/core';
|
|
3
|
+
import { EXTTextureWebP } from '@gltf-transform/extensions';
|
|
3
4
|
export class NeedleWebPContext {
|
|
4
5
|
options;
|
|
5
6
|
logger;
|
|
7
|
+
compressed = 0;
|
|
6
8
|
constructor(options) {
|
|
7
9
|
this.options = options;
|
|
8
10
|
}
|
|
9
11
|
async prepare(document) {
|
|
12
|
+
this.compressed = 0;
|
|
10
13
|
this.logger = document.getLogger();
|
|
11
14
|
}
|
|
12
15
|
async process(index, texture, settings, context) {
|
|
@@ -30,8 +33,13 @@ export class NeedleWebPContext {
|
|
|
30
33
|
.setMimeType('image/webp')
|
|
31
34
|
.setImage(BufferUtils.toView(buffer))
|
|
32
35
|
.setURI(HTTPUtils.basename(texture.getURI()) + '.webp');
|
|
36
|
+
this.compressed += 1;
|
|
33
37
|
return true;
|
|
34
38
|
}
|
|
35
39
|
async finalize(document) {
|
|
40
|
+
if (this.compressed > 0) {
|
|
41
|
+
const webpExtension = document.createExtension(EXTTextureWebP);
|
|
42
|
+
webpExtension.setRequired(true);
|
|
43
|
+
}
|
|
36
44
|
}
|
|
37
45
|
}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the output path for a file based on the output directory or file.
|
|
3
|
+
* If outDirOrFile is a filepath, it will be returned as is.
|
|
4
|
+
* If outDirOrFile is a directory, the file will be placed in that directory.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getOutputPath(filepath: string, outDirOrFile: string | undefined | null, opts?: {
|
|
7
|
+
create?: boolean;
|
|
8
|
+
}): string;
|
|
1
9
|
export declare function getFileName(filepath: string): string;
|
|
2
10
|
export declare function getDirName(filepath: string): string;
|
|
3
11
|
export declare type GLTFFileCallback = (file: string) => void | Promise<void>;
|
|
4
12
|
export declare function foreachGLTF(args: string | string[], callback: GLTFFileCallback): Promise<void>;
|
|
5
13
|
export declare function replaceExtension(filepath: string, newExtension: string): string;
|
|
6
14
|
export declare function isDirectory(path: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Ensure that a path is a directory.
|
|
17
|
+
* If the directory does not exist, it will be created.
|
|
18
|
+
*/
|
|
19
|
+
export declare function ensureIsDirectory(path: string): string;
|
package/dist/utils/fileutils.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
-
import { existsSync, statSync, readdirSync, readFileSync } from 'fs';
|
|
2
|
-
import { isAbsolute } from 'path';
|
|
1
|
+
import { existsSync, statSync, readdirSync, readFileSync, mkdirSync } from 'fs';
|
|
2
|
+
import { dirname, isAbsolute, join } from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Get the output path for a file based on the output directory or file.
|
|
5
|
+
* If outDirOrFile is a filepath, it will be returned as is.
|
|
6
|
+
* If outDirOrFile is a directory, the file will be placed in that directory.
|
|
7
|
+
*/
|
|
8
|
+
export function getOutputPath(filepath, outDirOrFile, opts) {
|
|
9
|
+
if (!outDirOrFile) {
|
|
10
|
+
return filepath;
|
|
11
|
+
}
|
|
12
|
+
if (!isDirectory(outDirOrFile)) {
|
|
13
|
+
return outDirOrFile;
|
|
14
|
+
}
|
|
15
|
+
const filename = getFileName(filepath);
|
|
16
|
+
// create the directory if it doesn't exist (default behavior)
|
|
17
|
+
if (opts?.create != false && !existsSync(outDirOrFile)) {
|
|
18
|
+
mkdirSync(outDirOrFile, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
const outpath = join(outDirOrFile, filename);
|
|
21
|
+
return outpath;
|
|
22
|
+
}
|
|
3
23
|
export function getFileName(filepath) {
|
|
4
24
|
const parts = filepath.replaceAll("\\", "/").split("/");
|
|
5
25
|
return parts[parts.length - 1];
|
|
@@ -139,3 +159,16 @@ export function isDirectory(path) {
|
|
|
139
159
|
const hasExt = path.replaceAll("\\", "/").split("/").pop()?.includes(".");
|
|
140
160
|
return !hasExt;
|
|
141
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Ensure that a path is a directory.
|
|
164
|
+
* If the directory does not exist, it will be created.
|
|
165
|
+
*/
|
|
166
|
+
export function ensureIsDirectory(path) {
|
|
167
|
+
if (!isDirectory(path)) {
|
|
168
|
+
path = dirname(path);
|
|
169
|
+
}
|
|
170
|
+
if (!existsSync(path)) {
|
|
171
|
+
mkdirSync(path, { recursive: true });
|
|
172
|
+
}
|
|
173
|
+
return path;
|
|
174
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-build-pipeline",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.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",
|