@needle-tools/gltf-build-pipeline 1.5.0-alpha.2 → 1.5.0-alpha.4
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 -1
- package/dist/cache/cache.d.ts +2 -2
- package/dist/cache/cache.js +36 -20
- package/dist/extensions/NEEDLE_opaque/NEEDLE_opaque.js +19 -4
- package/dist/transforms/needle_mesh_transform.d.ts +5 -1
- package/dist/transforms/needle_mesh_transform.js +13 -7
- package/dist/transforms/needle_progressive.js +13 -2
- package/dist/utils/nodeio.js +5 -8
- package/limit-caches.mjs +4 -0
- package/pack-gltf.mjs +3 -3
- package/package.json +2 -1
- package/dist/functions/gzip.d.ts +0 -1
- package/dist/functions/gzip.js +0 -16
- package/dist/functions/index.d.ts +0 -1
- package/dist/functions/index.js +0 -1
- package/dist/functions/list-texture-slots.d.ts +0 -1
- package/dist/functions/list-texture-slots.js +0 -35
- package/dist/transforms/needle_simplify.d.ts +0 -3
- package/dist/transforms/needle_simplify.js +0 -76
- package/dist/transforms/types/squoosh.d.ts +0 -31
- package/dist/transforms/types/squoosh.js +0 -33
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +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
|
-
## [1.5.0-alpha.
|
|
7
|
+
## [1.5.0-alpha.4] - 2023-05-03
|
|
8
|
+
- fix: issue in NEEDLE_opaque resolving of VRM textures, also resolve `meta.texture`
|
|
9
|
+
- fix: VRMs now compress meshes with mesh-opt if nothing is specified
|
|
10
|
+
- change: include version in cache directory
|
|
11
|
+
|
|
12
|
+
## [1.5.0-alpha.3] - 2023-05-03
|
|
13
|
+
- add: VRM progressive textures support
|
|
8
14
|
- fix: VRM format should be saved as GLB. See https://github.com/donmccurdy/glTF-Transform/issues/1390
|
|
9
15
|
- fix: VRM texture index handling
|
|
10
16
|
|
package/dist/cache/cache.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare function clearCache(): void;
|
|
|
8
8
|
* Ensure that the cache size does not exceed the given maximum size in MB.
|
|
9
9
|
* @param maxSizeMB The maximum size in MB.
|
|
10
10
|
*/
|
|
11
|
-
export declare function
|
|
11
|
+
export declare function limitCacheSize(maxSizeMB?: number): void;
|
|
12
12
|
/**
|
|
13
13
|
* Add data to the cache.
|
|
14
14
|
* @param key The key to store the data under.
|
|
@@ -48,4 +48,4 @@ export declare function getKeyWithHash(property: ExtensibleProperty): {
|
|
|
48
48
|
* @param property The property to get the hash for.
|
|
49
49
|
* @returns The hash as a number.
|
|
50
50
|
*/
|
|
51
|
-
export declare function getHash(property: ExtensibleProperty | object): number;
|
|
51
|
+
export declare function getHash(property: ExtensibleProperty | object, level?: number): number;
|
package/dist/cache/cache.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ExtensibleProperty, Texture, Primitive, Accessor, Mesh } from "@gltf-transform/core";
|
|
2
2
|
import tmp from 'tmp';
|
|
3
|
-
import fs from 'fs';
|
|
3
|
+
import fs, { statSync } from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { getVersion } from "../utils/version.js";
|
|
6
6
|
// import * as CHECKDISCSPACE from 'check-disk-space'
|
|
@@ -24,7 +24,7 @@ const buildPipelineVersion = getVersion(false);
|
|
|
24
24
|
* This will remove all files in the cache directory.
|
|
25
25
|
*/
|
|
26
26
|
export function clearCache() {
|
|
27
|
-
const dir = getCacheDirectory();
|
|
27
|
+
const dir = getCacheDirectory(true);
|
|
28
28
|
fs.rmSync(dir, { recursive: true });
|
|
29
29
|
console.log(`Cleared cache at ${dir}`);
|
|
30
30
|
}
|
|
@@ -32,16 +32,27 @@ export function clearCache() {
|
|
|
32
32
|
* Ensure that the cache size does not exceed the given maximum size in MB.
|
|
33
33
|
* @param maxSizeMB The maximum size in MB.
|
|
34
34
|
*/
|
|
35
|
-
export function
|
|
36
|
-
const dir = getCacheDirectory();
|
|
35
|
+
export function limitCacheSize(maxSizeMB = 1024) {
|
|
36
|
+
const dir = getCacheDirectory(true);
|
|
37
|
+
console.log(`Limit cache size to ${maxSizeMB.toFixed(2)} MB at ${dir}...`);
|
|
37
38
|
let totalSize = 0;
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
function recurseDirectories(dir) {
|
|
40
|
+
const files = fs.readdirSync(dir).map(file => {
|
|
41
|
+
const path = `${dir}/${file}`;
|
|
42
|
+
const stats = statSync(path);
|
|
43
|
+
if (stats.isDirectory()) {
|
|
44
|
+
recurseDirectories(path);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
totalSize += stats.size;
|
|
48
|
+
}
|
|
49
|
+
return { path, size: stats.size };
|
|
50
|
+
});
|
|
51
|
+
return files;
|
|
52
|
+
}
|
|
53
|
+
const files = recurseDirectories(dir);
|
|
44
54
|
const maxSizeInBytes = maxSizeMB * 1024 * 1024;
|
|
55
|
+
console.log(`Current cache size is ${(totalSize / (1024 * 1024)).toFixed(1)} MB`);
|
|
45
56
|
if (totalSize < maxSizeInBytes)
|
|
46
57
|
return;
|
|
47
58
|
files.sort((a, b) => {
|
|
@@ -55,6 +66,7 @@ export function ensureCacheSize(maxSizeMB) {
|
|
|
55
66
|
const file = files.shift();
|
|
56
67
|
if (!file)
|
|
57
68
|
break;
|
|
69
|
+
console.log(`Removing cache file ${file.path}...`);
|
|
58
70
|
fs.rmSync(file.path);
|
|
59
71
|
totalSize -= file.size;
|
|
60
72
|
}
|
|
@@ -153,7 +165,7 @@ export function getKeyWithHash(property) {
|
|
|
153
165
|
* @param property The property to get the hash for.
|
|
154
166
|
* @returns The hash as a number.
|
|
155
167
|
*/
|
|
156
|
-
export function getHash(property) {
|
|
168
|
+
export function getHash(property, level = 0) {
|
|
157
169
|
let hash = 0;
|
|
158
170
|
if (property instanceof ExtensibleProperty) {
|
|
159
171
|
const extensions = property.listExtensions();
|
|
@@ -175,7 +187,7 @@ export function getHash(property) {
|
|
|
175
187
|
hash += hashObject(weights);
|
|
176
188
|
const primitives = property.listPrimitives();
|
|
177
189
|
for (const prim of primitives)
|
|
178
|
-
hash += getHash(prim);
|
|
190
|
+
hash += getHash(prim, level + 1);
|
|
179
191
|
}
|
|
180
192
|
else if (property instanceof Primitive) {
|
|
181
193
|
const indices = property.getIndices();
|
|
@@ -198,8 +210,9 @@ export function getHash(property) {
|
|
|
198
210
|
}
|
|
199
211
|
return hash;
|
|
200
212
|
}
|
|
201
|
-
|
|
202
|
-
|
|
213
|
+
const cleanBuildPipelineVersion = buildPipelineVersion.replace(/[^a-zA-Z0-9]/g, "_");
|
|
214
|
+
function getCacheDirectory(root) {
|
|
215
|
+
const tmpDir = path.join(tmp.tmpdir, "Needle", "build-pipeline-cache", root ? "" : cleanBuildPipelineVersion);
|
|
203
216
|
if (!fs.existsSync(tmpDir)) {
|
|
204
217
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
205
218
|
}
|
|
@@ -211,12 +224,7 @@ function hashObject(obj) {
|
|
|
211
224
|
return hashBuffer(buffer);
|
|
212
225
|
}
|
|
213
226
|
const str = JSON.stringify(obj);
|
|
214
|
-
|
|
215
|
-
for (let i = 0; i < str.length; i++) {
|
|
216
|
-
hash = ((hash << 5) - hash) + str.charCodeAt(i);
|
|
217
|
-
hash |= 0;
|
|
218
|
-
}
|
|
219
|
-
return hash;
|
|
227
|
+
return hashString(str);
|
|
220
228
|
}
|
|
221
229
|
function hashBuffer(buffer, offset, length) {
|
|
222
230
|
let hash = 0;
|
|
@@ -227,3 +235,11 @@ function hashBuffer(buffer, offset, length) {
|
|
|
227
235
|
}
|
|
228
236
|
return hash;
|
|
229
237
|
}
|
|
238
|
+
function hashString(str) {
|
|
239
|
+
let hash = 0;
|
|
240
|
+
for (let i = 0; i < str.length; i++) {
|
|
241
|
+
hash = ((hash << 5) - hash) + str.charCodeAt(i);
|
|
242
|
+
hash |= 0;
|
|
243
|
+
}
|
|
244
|
+
return hash;
|
|
245
|
+
}
|
|
@@ -557,6 +557,7 @@ class JsonPointerHandler {
|
|
|
557
557
|
const object = tryGetInArray(context.textures, imageIndex);
|
|
558
558
|
if (object) {
|
|
559
559
|
const res = new TextureIndexResolver(this.document, object, definition);
|
|
560
|
+
res.debug = this.debug;
|
|
560
561
|
res.init(context); // < init here otherwise the fields defined in the TexturePointer will be undefined (this caused e.g. finding the previous texture filter to fail)
|
|
561
562
|
pointer = new JsonPointer(path, res, value);
|
|
562
563
|
pointer.debug = this.debug;
|
|
@@ -590,6 +591,7 @@ class JsonPointerHandler {
|
|
|
590
591
|
if (!pointer) {
|
|
591
592
|
const definition = context.jsonDoc.json.materials[index];
|
|
592
593
|
const res = new MaterialIndexResolver(this.document, mat, definition);
|
|
594
|
+
res.debug = this.debug;
|
|
593
595
|
res.init(context); // < init here otherwise the fields defined in the TexturePointer will be undefined (this caused e.g. finding the previous texture filter to fail)
|
|
594
596
|
pointer = new JsonPointer("/materials/", res, value);
|
|
595
597
|
pointer.debug = this.debug;
|
|
@@ -605,6 +607,7 @@ class JsonPointerHandler {
|
|
|
605
607
|
const object = context.nodes[value];
|
|
606
608
|
if (object) {
|
|
607
609
|
const res = new NodeIndexResolver(this.document, object, context.jsonDoc.json.nodes[value]);
|
|
610
|
+
res.debug = this.debug;
|
|
608
611
|
res.init(context);
|
|
609
612
|
const dictKey = `/${key}/${value}`;
|
|
610
613
|
let pointer = this.map.get(dictKey);
|
|
@@ -616,10 +619,19 @@ class JsonPointerHandler {
|
|
|
616
619
|
this.map.set(dictKey, pointer);
|
|
617
620
|
}
|
|
618
621
|
}
|
|
619
|
-
|
|
620
|
-
|
|
622
|
+
// key texture is matching for example for VRM meta/texture (AvatarSample_A) and the regex is also for handling VRM material properties
|
|
623
|
+
else if (key === "texture" || fullPath.match(/materialProperties\/\d+?\/textureProperties/g)) {
|
|
624
|
+
const index = value;
|
|
625
|
+
const definition = context.jsonDoc.json.textures[index];
|
|
626
|
+
const imageIndex = findSource(definition);
|
|
627
|
+
if (this.debug)
|
|
628
|
+
console.log("Texture index", index, imageIndex, context.jsonDoc.json.textures.length, "Textures:" + context.textures.length);
|
|
629
|
+
const object = tryGetInArray(context.textures, imageIndex);
|
|
621
630
|
if (object) {
|
|
622
|
-
|
|
631
|
+
if (this.debug)
|
|
632
|
+
console.log("Found texture index at " + fullPath + ": " + value);
|
|
633
|
+
const res = new TextureIndexResolver(this.document, object, definition);
|
|
634
|
+
res.debug = this.debug;
|
|
623
635
|
res.init(context);
|
|
624
636
|
const dictKey = `/textures/${value}`;
|
|
625
637
|
let pointer = this.map.get(dictKey);
|
|
@@ -629,6 +641,9 @@ class JsonPointerHandler {
|
|
|
629
641
|
pointer.debug = this.debug;
|
|
630
642
|
pointer.register(obj, key);
|
|
631
643
|
this.map.set(dictKey, pointer);
|
|
644
|
+
if (ext) {
|
|
645
|
+
ext.setReference(dictKey, object);
|
|
646
|
+
}
|
|
632
647
|
}
|
|
633
648
|
}
|
|
634
649
|
break;
|
|
@@ -734,7 +749,7 @@ class TextureIndexResolver extends PointerResolver {
|
|
|
734
749
|
// the texture was not found - add it
|
|
735
750
|
// create a new texture def entry
|
|
736
751
|
if (this.debug)
|
|
737
|
-
console.log("
|
|
752
|
+
console.log("Could not find texture, creating a new one: " + this._debugName);
|
|
738
753
|
const hasExtensionWithSource = this.inputDefinition.extensions && Object.keys(this.inputDefinition.extensions).some(key => this.inputDefinition.extensions[key]["source"] !== undefined);
|
|
739
754
|
const textureDef = {
|
|
740
755
|
source: hasExtensionWithSource ? undefined : imgIndex,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { Transform, Document, Primitive, Mesh } from "@gltf-transform/core";
|
|
2
2
|
import { SimplifyOptions, WeldOptions } from "@gltf-transform/functions";
|
|
3
|
-
|
|
3
|
+
declare type MeshTransformContext = {
|
|
4
|
+
file: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const needle_mesh_transform: (context: MeshTransformContext, simplify: SimplifyOptions) => Transform;
|
|
4
7
|
export declare function compressPrimitive(document: Document, mesh: Mesh, prim: Primitive, index: number, weldOptions: Required<WeldOptions>, meshOptions: Required<SimplifyOptions>): Promise<Primitive>;
|
|
8
|
+
export {};
|
|
@@ -3,14 +3,14 @@ import { meshopt, simplifyPrimitive, SIMPLIFY_DEFAULTS, weldPrimitive, WELD_DEFA
|
|
|
3
3
|
import { MeshoptEncoder, MeshoptSimplifier } from "meshoptimizer";
|
|
4
4
|
import { NEEDLE_mesh_compression } from "../extensions/index.js";
|
|
5
5
|
const NAME = 'needle_mesh_transform';
|
|
6
|
-
export const needle_mesh_transform = function (
|
|
6
|
+
export const needle_mesh_transform = function (context, simplify) {
|
|
7
7
|
return async (document, _context) => {
|
|
8
8
|
// TODO: needs to be moved into NEEDLE_progressive
|
|
9
|
-
await simplifierTransform(document, _context,
|
|
10
|
-
await applyMeshCompression(document, _context);
|
|
9
|
+
await simplifierTransform(document, _context, simplify);
|
|
10
|
+
await applyMeshCompression(document, _context, context);
|
|
11
11
|
};
|
|
12
12
|
};
|
|
13
|
-
async function applyMeshCompression(document,
|
|
13
|
+
async function applyMeshCompression(document, transform_context, context) {
|
|
14
14
|
const rootExtension = document.getRoot().getExtension(NEEDLE_mesh_compression.EXTENSION_NAME);
|
|
15
15
|
if (rootExtension) {
|
|
16
16
|
const rootModel = rootExtension.model;
|
|
@@ -22,17 +22,23 @@ async function applyMeshCompression(document, _context) {
|
|
|
22
22
|
console.log(`[${NAME}]: ${rootModel.compression.toUpperCase()} → will apply ${rootModel.compression} mesh compression`);
|
|
23
23
|
if (rootModel.compression === "draco") {
|
|
24
24
|
const transform = draco();
|
|
25
|
-
return transform(document,
|
|
25
|
+
return transform(document, transform_context);
|
|
26
26
|
}
|
|
27
27
|
else if (rootModel.compression === "meshopt") {
|
|
28
28
|
const transform = meshopt({ encoder: MeshoptEncoder });
|
|
29
|
-
return transform(document,
|
|
29
|
+
return transform(document, transform_context);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
// If nothing is configured apply meshopt compression as long as we're running gltf-transform 3.x
|
|
34
|
+
if (context.file.endsWith(".vrm")) {
|
|
35
|
+
console.log(`[${NAME}]: VRM → will apply meshopt mesh compression`);
|
|
36
|
+
const transform = meshopt({ encoder: MeshoptEncoder });
|
|
37
|
+
return transform(document, transform_context);
|
|
38
|
+
}
|
|
33
39
|
// by default we still want to run draco if the root doesnt have a compression model configured
|
|
34
40
|
console.log(`[${NAME}]: No compression model configured → will apply draco mesh compression`);
|
|
35
|
-
return draco()(document,
|
|
41
|
+
return draco()(document, transform_context);
|
|
36
42
|
}
|
|
37
43
|
async function simplifierTransform(document, _context, _options) {
|
|
38
44
|
const simplifier = MeshoptSimplifier;
|
|
@@ -38,7 +38,7 @@ function registerExtensions(io) {
|
|
|
38
38
|
]);
|
|
39
39
|
}
|
|
40
40
|
async function onProcess(file, opts) {
|
|
41
|
-
if (!file.endsWith(".glb") && !file.endsWith(".gltf")) {
|
|
41
|
+
if (!file.endsWith(".glb") && !file.endsWith(".gltf") && !file.endsWith(".vrm")) {
|
|
42
42
|
console.log("ERR: file is not gltf/glb");
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
@@ -71,7 +71,18 @@ async function onProcess(file, opts) {
|
|
|
71
71
|
}
|
|
72
72
|
//@ts-ignore
|
|
73
73
|
document.getLogger().verbosity = 0;
|
|
74
|
-
|
|
74
|
+
const transforms = [
|
|
75
|
+
needle_asset(),
|
|
76
|
+
make_progressive_textures(file, { size: [256, 256] }, opts),
|
|
77
|
+
sparse(),
|
|
78
|
+
];
|
|
79
|
+
// TODO: progressive meshes aren't supported yet for VRMs
|
|
80
|
+
if (!file.endsWith(".vrm")) {
|
|
81
|
+
transforms.push(make_progressive_meshes(file, {}, opts));
|
|
82
|
+
}
|
|
83
|
+
else
|
|
84
|
+
console.warn("WARN: Progressive meshes are not supported for VRM files yet");
|
|
85
|
+
await document.transform(...transforms);
|
|
75
86
|
let filename = file;
|
|
76
87
|
if (opts.debug === true) {
|
|
77
88
|
// get extension
|
package/dist/utils/nodeio.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { writeFile } from "fs/promises";
|
|
2
2
|
/**
|
|
3
3
|
* Handle writing glTf, GLB and VRM files
|
|
4
4
|
*/
|
|
5
5
|
export async function writeNodeIO(nodeio, filename, document) {
|
|
6
|
-
let filename_write = filename;
|
|
7
6
|
// see https://github.com/donmccurdy/glTF-Transform/issues/1390
|
|
8
7
|
if (filename.endsWith(".vrm")) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
await nodeio.write(filename_write, document);
|
|
12
|
-
if (filename.endsWith(".vrm")) {
|
|
13
|
-
// rename the file to .vrm
|
|
14
|
-
renameSync(filename_write, filename);
|
|
8
|
+
const bin = await nodeio.writeBinary(document);
|
|
9
|
+
await writeFile(filename, bin);
|
|
15
10
|
}
|
|
11
|
+
else
|
|
12
|
+
await nodeio.write(filename, document);
|
|
16
13
|
return filename;
|
|
17
14
|
}
|
package/limit-caches.mjs
ADDED
package/pack-gltf.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import { needle_animation_transform } from "./dist/transforms/needle_animation_t
|
|
|
12
12
|
import { foreachGLTF } from './fileutils.mjs';
|
|
13
13
|
import { needle_asset } from './dist/transforms/needle_asset.js';
|
|
14
14
|
import { MeshoptDecoder, MeshoptEncoder } from 'meshoptimizer';
|
|
15
|
-
import { getVersion, getHash, addToCache, tryGetFromCache, isLOD,
|
|
15
|
+
import { getVersion, getHash, addToCache, tryGetFromCache, isLOD, limitCacheSize, isMeshLOD } from './dist/index.js';
|
|
16
16
|
import { writeNodeIO } from './dist/utils/nodeio.js';
|
|
17
17
|
|
|
18
18
|
console.log(`[Needle Build Pipeline] pack glTF — version ${getVersion()}`)
|
|
@@ -135,7 +135,7 @@ async function packFile(file) {
|
|
|
135
135
|
debug: debug,
|
|
136
136
|
reportGPUMemory: val => totalGPUTextureMemmory += val
|
|
137
137
|
}),
|
|
138
|
-
needle_mesh_transform({ ratio: 0.75, error: 0.0005 }),
|
|
138
|
+
needle_mesh_transform({ file }, { ratio: 0.75, error: 0.0005 }),
|
|
139
139
|
needle_animation_transform(),
|
|
140
140
|
// draco(),
|
|
141
141
|
);
|
|
@@ -178,7 +178,7 @@ foreachGLTF(inputArgs, async (file) => {
|
|
|
178
178
|
// if the user's cache directory has more than 25GB free space, we allow a cache of 2 GB - otherwise 1 GB
|
|
179
179
|
// const freeSpaceInMB = (await getCacheAvailableSpaceOnDisc().catch(() => 0)) / 1024 / 1024;
|
|
180
180
|
// const maxCacheSize = freeSpaceInMB > 25000 ? 2000 : 1000;
|
|
181
|
-
|
|
181
|
+
limitCacheSize();
|
|
182
182
|
}
|
|
183
183
|
console.log(`[Needle Build Pipeline] v${getVersion()} — Finished packing ${totalFilesProcessed} files in ${durationInMinutes.toFixed(1)} minutes; total size ${totalFileSizeInMBBefore.toFixed(1)} MB → ${totalFileSizeInMB.toFixed(1)} MB, total GPU memory: ${totalGPUMemoryInMB.toFixed(1)} MB`);
|
|
184
184
|
}).catch(err => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-build-pipeline",
|
|
3
|
-
"version": "1.5.0-alpha.
|
|
3
|
+
"version": "1.5.0-alpha.4",
|
|
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",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"pack-gltf": "node --no-experimental-fetch pack-gltf.mjs",
|
|
14
14
|
"dev:pack-gltf": "npm run compile && node pack-gltf.mjs",
|
|
15
15
|
"clear-caches": "node clear-caches.mjs",
|
|
16
|
+
"limit-caches": "node limit-caches.mjs",
|
|
16
17
|
"compile": "tsc || exit 1"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
package/dist/functions/gzip.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function gzipFile(filepath: string): Promise<string>;
|
package/dist/functions/gzip.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { promises as fs, existsSync } from 'fs';
|
|
2
|
-
import { gzip } from 'node-gzip';
|
|
3
|
-
export async function gzipFile(filepath) {
|
|
4
|
-
if (filepath.endsWith('.gz')) {
|
|
5
|
-
return filepath;
|
|
6
|
-
}
|
|
7
|
-
if (!existsSync(filepath)) {
|
|
8
|
-
console.log("File not found: " + filepath);
|
|
9
|
-
return filepath;
|
|
10
|
-
}
|
|
11
|
-
const inBuffer = await fs.readFile(filepath);
|
|
12
|
-
const outBuffer = await gzip(inBuffer);
|
|
13
|
-
const outPath = filepath;
|
|
14
|
-
await fs.writeFile(outPath, outBuffer);
|
|
15
|
-
return outPath;
|
|
16
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./gzip.js";
|
package/dist/functions/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./gzip.js";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export {};
|
|
2
|
-
// TODO: remove once this is resolved: https://github.com/donmccurdy/glTF-Transform/issues/855
|
|
3
|
-
// modified to take a document as a parameter
|
|
4
|
-
// export function listTextureSlots(texture: Texture, document: Document): string[] {
|
|
5
|
-
// const root = document.getRoot();
|
|
6
|
-
// const slots = texture
|
|
7
|
-
// .getGraph()
|
|
8
|
-
// .listParentEdges(texture)
|
|
9
|
-
// .filter((edge) => edge.getParent() !== root)
|
|
10
|
-
// .map((edge) => edge.getName());
|
|
11
|
-
// return Array.from(new Set(slots));
|
|
12
|
-
// }
|
|
13
|
-
// export function getTextureChannelMask(texture: Texture, document: Document): number {
|
|
14
|
-
// let mask = 0x0000;
|
|
15
|
-
// for (const edge of document.getGraph().listParentEdges(texture)) {
|
|
16
|
-
// const parent = edge.getParent();
|
|
17
|
-
// let { channels } = edge.getAttributes() as { channels: number | undefined };
|
|
18
|
-
// if (
|
|
19
|
-
// channels &&
|
|
20
|
-
// edge.getName() === 'baseColorTexture' &&
|
|
21
|
-
// parent instanceof Material &&
|
|
22
|
-
// parent.getAlphaMode() === Material.AlphaMode.OPAQUE
|
|
23
|
-
// ) {
|
|
24
|
-
// channels &= ~TextureChannel.A;
|
|
25
|
-
// }
|
|
26
|
-
// if (channels) {
|
|
27
|
-
// mask |= channels;
|
|
28
|
-
// continue;
|
|
29
|
-
// }
|
|
30
|
-
// if (parent.propertyType !== PropertyType.ROOT) {
|
|
31
|
-
// document.getLogger().warn(`Missing attribute ".channels" on edge, "${edge.getName()}".`);
|
|
32
|
-
// }
|
|
33
|
-
// }
|
|
34
|
-
// return mask;
|
|
35
|
-
// }
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { Primitive } from "@gltf-transform/core";
|
|
2
|
-
import { simplifyPrimitive, weldPrimitive } from "@gltf-transform/functions";
|
|
3
|
-
import { MeshoptSimplifier } from "meshoptimizer";
|
|
4
|
-
import { NEEDLE_mesh_compression } from "../extensions/index.js";
|
|
5
|
-
const NAME = 'needle_simplify';
|
|
6
|
-
export const needle_mesh_transform = function (options) {
|
|
7
|
-
const simplifier = MeshoptSimplifier;
|
|
8
|
-
if (!simplifier) {
|
|
9
|
-
throw new Error(`${NAME}: simplifier dependency required — install "meshoptimizer".`);
|
|
10
|
-
}
|
|
11
|
-
return (document, _context) => {
|
|
12
|
-
const logger = document.getLogger();
|
|
13
|
-
options = { ...options, simplifier: simplifier };
|
|
14
|
-
const weldOptions = {
|
|
15
|
-
tolerance: 0.00000000001,
|
|
16
|
-
overwrite: true
|
|
17
|
-
};
|
|
18
|
-
for (const mesh of document.getRoot().listMeshes()) {
|
|
19
|
-
for (const prim of mesh.listPrimitives()) {
|
|
20
|
-
// const key = getKey(prim);
|
|
21
|
-
// const cached = tryGetFromCache(key);
|
|
22
|
-
// if (cached) {
|
|
23
|
-
// logger.debug(`${NAME}: Skipping primitive of mesh "${mesh.getName()}": Already cached.`);
|
|
24
|
-
// continue;
|
|
25
|
-
// }
|
|
26
|
-
// updatePrimitive(prim, simplifier, null, prim.getIndices());
|
|
27
|
-
// continue;
|
|
28
|
-
const ext = prim.getExtension(NEEDLE_mesh_compression.EXTENSION_NAME);
|
|
29
|
-
if (!ext) {
|
|
30
|
-
// logger.warn("Skipping primitive of mesh " + mesh.getName() + ": No NEEDLE_compression_mesh extension.");
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
let meshOptions = { ...options };
|
|
34
|
-
if (ext.model) {
|
|
35
|
-
const model = ext.model;
|
|
36
|
-
if (!model.useSimplifier) {
|
|
37
|
-
logger.debug(`${NAME}: Skipping primitive of mesh "${mesh.getName()}": useSimplifier is false.`);
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (model.error >= 0)
|
|
41
|
-
meshOptions.error = model.error;
|
|
42
|
-
if (model.ratio >= 0)
|
|
43
|
-
meshOptions.ratio = model.ratio;
|
|
44
|
-
meshOptions.lockBorder = model.lockBorder;
|
|
45
|
-
}
|
|
46
|
-
if (prim.getMode() !== Primitive.Mode.TRIANGLES) {
|
|
47
|
-
logger.warn(`${NAME}: Skipping primitive of mesh "${mesh.getName()}": Requires TRIANGLES draw mode.`);
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
logger.debug(`${NAME}: Welding primitive of mesh "${mesh.getName()}" → tolerance: ${weldOptions.tolerance}`);
|
|
51
|
-
weldPrimitive(document, prim, weldOptions);
|
|
52
|
-
logger.debug(`${NAME}: Simplifying primitive of mesh "${mesh.getName()}" → ratio: ${meshOptions.ratio}, error: ${meshOptions.error}, lockBorder: ${meshOptions.lockBorder}`);
|
|
53
|
-
simplifyPrimitive(document, prim, meshOptions);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
// function updatePrimitive(prim: Primitive, simplifier: any, positionArray, dstIndicesArray) {
|
|
59
|
-
// const position = prim.getAttribute('POSITION')!;
|
|
60
|
-
// const srcIndices = prim.getIndices()!;
|
|
61
|
-
// const srcVertexCount = position.getCount();
|
|
62
|
-
// const [remap, unique] = simplifier.compactMesh(dstIndicesArray);
|
|
63
|
-
// // logger.debug(`${NAME}: ${formatDeltaOp(position.getCount(), unique)} vertices, error: ${error.toFixed(4)}.`);
|
|
64
|
-
// // (3) Write vertex attributes.
|
|
65
|
-
// for (const srcAttribute of deepListAttributes(prim)) {
|
|
66
|
-
// const dstAttribute = srcAttribute.clone();
|
|
67
|
-
// remapAttribute(dstAttribute, remap, unique);
|
|
68
|
-
// deepSwapAttribute(prim, srcAttribute, dstAttribute);
|
|
69
|
-
// if (srcAttribute.listParents().length === 1) srcAttribute.dispose();
|
|
70
|
-
// }
|
|
71
|
-
// // (4) Write indices.
|
|
72
|
-
// const dstIndices = srcIndices.clone();
|
|
73
|
-
// dstIndices.setArray(srcVertexCount <= 65534 ? new Uint16Array(dstIndicesArray) : dstIndicesArray);
|
|
74
|
-
// prim.setIndices(dstIndices);
|
|
75
|
-
// if (srcIndices.listParents().length === 1) srcIndices.dispose();
|
|
76
|
-
// }
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export declare class WebPEncoderOptions {
|
|
2
|
-
quality: number;
|
|
3
|
-
target_size: number;
|
|
4
|
-
target_PSNR: number;
|
|
5
|
-
method: number;
|
|
6
|
-
sns_strength: number;
|
|
7
|
-
filter_strength: number;
|
|
8
|
-
filter_sharpness: number;
|
|
9
|
-
/** 1 = strong filter */
|
|
10
|
-
filter_type: number;
|
|
11
|
-
partitions: number;
|
|
12
|
-
segments: number;
|
|
13
|
-
pass: number;
|
|
14
|
-
show_compressed: number;
|
|
15
|
-
preprocessing: number;
|
|
16
|
-
autofilter: number;
|
|
17
|
-
partition_limit: number;
|
|
18
|
-
alpha_compression: number;
|
|
19
|
-
alpha_filtering: number;
|
|
20
|
-
alpha_quality: number;
|
|
21
|
-
lossless: number;
|
|
22
|
-
exact: number;
|
|
23
|
-
image_hint: number;
|
|
24
|
-
emulate_jpeg_size: number;
|
|
25
|
-
thread_level: number;
|
|
26
|
-
low_memory: number;
|
|
27
|
-
near_lossless: number;
|
|
28
|
-
use_delta_palette: number;
|
|
29
|
-
/** probably sharp rgb -> YUV conversion */
|
|
30
|
-
use_sharp_yuv: number;
|
|
31
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// client: https://github.com/GoogleChromeLabs/squoosh/blob/97d13de273b67ee01a80cb32161f1bb2103b73cb/src/features/encoders/webP/client/index.tsx#L292
|
|
2
|
-
// emscripten bindings:
|
|
3
|
-
export class WebPEncoderOptions {
|
|
4
|
-
quality = 75;
|
|
5
|
-
target_size = 0;
|
|
6
|
-
target_PSNR = 0;
|
|
7
|
-
method = 4;
|
|
8
|
-
sns_strength = 50;
|
|
9
|
-
filter_strength = 60;
|
|
10
|
-
filter_sharpness = 0;
|
|
11
|
-
/** 1 = strong filter */
|
|
12
|
-
filter_type = 1;
|
|
13
|
-
partitions = 0;
|
|
14
|
-
segments = 4;
|
|
15
|
-
pass = 1;
|
|
16
|
-
show_compressed = 0;
|
|
17
|
-
preprocessing = 0;
|
|
18
|
-
autofilter = 0;
|
|
19
|
-
partition_limit = 0;
|
|
20
|
-
alpha_compression = 1;
|
|
21
|
-
alpha_filtering = 1;
|
|
22
|
-
alpha_quality = 100;
|
|
23
|
-
lossless = 0;
|
|
24
|
-
exact = 0;
|
|
25
|
-
image_hint = 0;
|
|
26
|
-
emulate_jpeg_size = 0;
|
|
27
|
-
thread_level = 0;
|
|
28
|
-
low_memory = 0;
|
|
29
|
-
near_lossless = 100;
|
|
30
|
-
use_delta_palette = 0;
|
|
31
|
-
/** probably sharp rgb -> YUV conversion */
|
|
32
|
-
use_sharp_yuv = 0;
|
|
33
|
-
}
|