babylonjs-editor-cli 5.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esbuild.mjs DELETED
@@ -1,57 +0,0 @@
1
- import esbuild from "esbuild";
2
-
3
- import { argv, exit } from "node:process";
4
- import { readFile } from "node:fs/promises";
5
-
6
- const args = argv.slice(2);
7
- const isWatch = args.includes("--watch");
8
-
9
- const replaceImportMetaDirname = {
10
- name: "replaceImportMetaDirname",
11
- setup(build) {
12
- build.onLoad({ filter: /.*/ }, async (args) => {
13
- const source = await readFile(args.path, "utf8");
14
-
15
- const transformedSource = source.replace(/import.meta.dirname/g, '"__dirname"');
16
-
17
- return {
18
- loader: "default",
19
- contents: transformedSource,
20
- };
21
- });
22
- },
23
- };
24
-
25
- const mainBuildOptions = {
26
- entryPoints: ["./src/export.mts"],
27
- bundle: true,
28
- platform: "node",
29
- target: "node20", // target version of Node.js
30
- format: "cjs", // output format as CommonJS
31
- outfile: "./build/index.node.js",
32
- treeShaking: false,
33
- loader: {
34
- ".mts": "ts",
35
- },
36
- keepNames: true,
37
- minify: !isWatch,
38
- plugins: [replaceImportMetaDirname],
39
- };
40
-
41
- if (args.includes("--watch")) {
42
- esbuild
43
- .context(mainBuildOptions)
44
- .then(async (buildcontext) => {
45
- await buildcontext.watch();
46
- console.log("Watching...");
47
- })
48
- .catch((error) => {
49
- console.error(error);
50
- exit(1);
51
- });
52
- } else {
53
- esbuild.build(mainBuildOptions).catch((error) => {
54
- console.error(error);
55
- exit(1);
56
- });
57
- }
package/src/export.mts DELETED
@@ -1 +0,0 @@
1
- export { pack, IPackOptions } from "./pack/pack.mjs";
package/src/index.mts DELETED
@@ -1,28 +0,0 @@
1
- import dotEnv from "dotenv";
2
- import { Command } from "commander";
3
-
4
- import packageJson from "../package.json" with { type: "json" };
5
-
6
- import { pack } from "./pack/pack.mjs";
7
-
8
- dotEnv.config();
9
-
10
- const program = new Command();
11
-
12
- program
13
- .version(packageJson.version)
14
- .name("Babylon.js Editor CLI")
15
- .description("Babylon.js Editor CLI is a command line interface to help you package your scenes made using the Babylon.js Editor")
16
- .option("-h, --help", "display help for command");
17
-
18
- program
19
- .command("pack")
20
- .description("Packs the project located in the specified directory. Current directory is used by default.")
21
- .argument("[projectDir]", "The root directory of the project to package", process.cwd())
22
- .action((projectDir: string) => {
23
- pack(projectDir, {
24
- optimize: true,
25
- });
26
- });
27
-
28
- program.parse();
@@ -1,46 +0,0 @@
1
- import { cpus } from "node:os";
2
- import { extname, join } from "node:path/posix";
3
-
4
- import { normalizedGlob } from "../../tools/fs.mjs";
5
-
6
- import { processAssetFile } from "./process.mjs";
7
-
8
- export interface ICreateAssetsOptions {
9
- projectDir: string;
10
- publicDir: string;
11
- baseAssetsDir: string;
12
- outputAssetsDir: string;
13
- optimize: boolean;
14
- exportedAssets: string[];
15
- cache: Record<string, string>;
16
- compressedTexturesEnabled: boolean;
17
- }
18
-
19
- export async function createAssets(options: ICreateAssetsOptions) {
20
- const files = await normalizedGlob(join(options.baseAssetsDir, "**/*"), {
21
- nodir: true,
22
- ignore: {
23
- childrenIgnored: (p) => extname(p.name) === ".scene",
24
- },
25
- });
26
-
27
- const promises: Promise<void>[] = [];
28
-
29
- const cpusCount = cpus().length;
30
- console.log(`Using ${cpusCount} cpus to process assets...`);
31
-
32
- for (const file of files) {
33
- if (promises.length >= cpusCount) {
34
- await Promise.all(promises);
35
- promises.length = 0;
36
- }
37
-
38
- promises.push(
39
- processAssetFile(file, {
40
- ...options,
41
- })
42
- );
43
- }
44
-
45
- await Promise.all(promises);
46
- }
@@ -1,158 +0,0 @@
1
- import { basename, extname, join, dirname } from "node:path/posix";
2
-
3
- import fs from "fs-extra";
4
- import { PNG } from "pngjs";
5
- import { createReadStream } from "node:fs";
6
-
7
- import { executeAsync } from "../../tools/process.mjs";
8
- import { pvrTexToolAbsolutePath } from "../../tools/ktx.mjs";
9
-
10
- export type KTXToolsType = "-astc.ktx" | "-dxt.ktx" | "-pvrtc.ktx" | "-etc1.ktx" | "-etc2.ktx";
11
-
12
- export const allKtxFormats: KTXToolsType[] = ["-astc.ktx", "-dxt.ktx", "-pvrtc.ktx", "-etc1.ktx", "-etc2.ktx"];
13
-
14
- export const ktxSupportedextensions: string[] = [".png", ".jpg", ".jpeg", ".bmp"];
15
-
16
- /**
17
- * Returns the absolute path to the compressed textures CLI path (PVRTexTool).
18
- * The value is retrieved from the local storage so it's per computer and not per project.
19
- */
20
- export function getCompressedTexturesCliPath() {
21
- let value = "";
22
-
23
- try {
24
- value = localStorage.getItem("editor-compressed-textures-cli-path") ?? "";
25
- } catch (e) {
26
- // Catch silently.
27
- }
28
-
29
- return value || null;
30
- }
31
-
32
- /**
33
- * Sets the absolute path to the compressed textures CLI path (PVRTexTool).
34
- * The value is stored in the local storage so it's per computer and not per project.
35
- */
36
- export function setCompressedTexturesCliPath(absolutePath: string) {
37
- try {
38
- localStorage.setItem("editor-compressed-textures-cli-path", absolutePath);
39
- } catch (e) {
40
- // Catch silently.
41
- }
42
- }
43
-
44
- /**
45
- * Returns the filename of the compressed texture according to the given path and the destination format.
46
- * @param path defines the path of the texture to get its final name.
47
- * @param format defines the destination format of the texture.
48
- * @example image.png -> image-asct.ktx
49
- */
50
- export function getCompressedTextureFilename(path: string, format: KTXToolsType) {
51
- return `${path.substring(0, path.lastIndexOf("."))}${format}`;
52
- }
53
-
54
- export type CompressFileToKtxOptions = {
55
- format: KTXToolsType;
56
- force?: boolean;
57
- exportedAssets?: string[];
58
- destinationFolder?: string;
59
- };
60
-
61
- export async function compressFileToKtx(absolutePath: string, options: Partial<CompressFileToKtxOptions>): Promise<void> {
62
- if (options.format) {
63
- await compressFileToKtxFormat(absolutePath, options as CompressFileToKtxOptions);
64
- } else {
65
- await Promise.all(
66
- allKtxFormats.map((f) =>
67
- compressFileToKtxFormat(absolutePath, {
68
- ...options,
69
- format: f,
70
- })
71
- )
72
- );
73
- }
74
- }
75
-
76
- export async function compressFileToKtxFormat(absolutePath: string, options: CompressFileToKtxOptions): Promise<string | null> {
77
- const name = basename(absolutePath);
78
- const extension = extname(name).toLocaleLowerCase();
79
-
80
- if (!ktxSupportedextensions.includes(extension)) {
81
- return null;
82
- }
83
-
84
- const filename = getCompressedTextureFilename(name, options.format);
85
-
86
- options.destinationFolder ??= dirname(absolutePath);
87
- options.destinationFolder = join(options.destinationFolder, filename);
88
-
89
- // if (!editor.state.compressedTexturesEnabled) {
90
- // options.exportedAssets?.push(options.destinationFolder);
91
- // return null;
92
- // }
93
-
94
- if ((await fs.pathExists(options.destinationFolder)) && !options.force) {
95
- options.exportedAssets?.push(options.destinationFolder);
96
- return options.destinationFolder;
97
- }
98
-
99
- const hasAlpha = await new Promise<boolean>((resolve) => {
100
- const stream = createReadStream(absolutePath);
101
-
102
- stream
103
- .pipe(new PNG())
104
- .on("metadata", (p) => {
105
- resolve(p.alpha);
106
- stream.close();
107
- })
108
- .on("error", () => {
109
- resolve(false);
110
- stream.close();
111
- });
112
- });
113
-
114
- if (!pvrTexToolAbsolutePath) {
115
- return null;
116
- }
117
-
118
- let command: string | null = null;
119
- switch (options.format) {
120
- case "-astc.ktx":
121
- command = `"${pvrTexToolAbsolutePath}" -i "${absolutePath}" -flip y -pot + -m -dither -ics lRGB -f ASTC_8x8,UBN,lRGB -q astcveryfast -o "${options.destinationFolder}"`;
122
- break;
123
-
124
- case "-dxt.ktx":
125
- command = `"${pvrTexToolAbsolutePath}" -i "${absolutePath}" -flip y -pot + -m -ics lRGB ${hasAlpha ? "-l" : ""} -f ${hasAlpha ? "BC2" : "BC1"},UBN,lRGB -o "${options.destinationFolder}"`;
126
- break;
127
-
128
- case "-pvrtc.ktx":
129
- command = `"${pvrTexToolAbsolutePath}" -i "${absolutePath}" -flip y -pot + -square + -m -dither -ics lRGB ${hasAlpha ? "-l" : ""} -f ${hasAlpha ? "PVRTCI_2BPP_RGBA" : "PVRTCI_2BPP_RGB"},UBN,lRGB -q pvrtcfastest -o "${options.destinationFolder}"`;
130
- break;
131
-
132
- case "-etc1.ktx":
133
- command = `"${pvrTexToolAbsolutePath}" -i "${absolutePath}" -flip y -pot + -m -dither -ics lRGB -f ETC1,UBN,lRGB -q etcfast -o "${options.destinationFolder}"`;
134
- break;
135
-
136
- case "-etc2.ktx":
137
- command = `"${pvrTexToolAbsolutePath}" -i "${absolutePath}" -flip y -pot + -m -dither -ics lRGB -f ${hasAlpha ? "ETC2_RGBA" : "ETC2_RGB"},UBN,lRGB -q etcfast -o "${options.destinationFolder}"`;
138
- break;
139
- }
140
-
141
- if (!command) {
142
- return null;
143
- }
144
-
145
- console.log(`Compressing image "${filename}"`);
146
-
147
- try {
148
- await executeAsync(command);
149
- console.log(`Compressed image "${filename}"`);
150
- options.exportedAssets?.push(options.destinationFolder);
151
-
152
- return options.destinationFolder;
153
- } catch (e) {
154
- console.error(`Failed to compress image "${filename}"`);
155
- }
156
-
157
- return null;
158
- }
@@ -1,86 +0,0 @@
1
- import { join } from "node:path/posix";
2
-
3
- import fs from "fs-extra";
4
-
5
- import { extractTextureAssetFromDataString, extractTextureAssetFromUrl } from "../../tools/extract.mjs";
6
-
7
- import { compressFileToKtx } from "./ktx.mjs";
8
- import { getExtractedTextureOutputPath } from "./texture.mjs";
9
-
10
- export interface IProcessExportedMaterialOptions {
11
- force: boolean;
12
- publicDir: string;
13
- exportedAssets: string[];
14
- optimize: boolean;
15
- }
16
-
17
- export async function processExportedMaterial(absolutePath: string, options: IProcessExportedMaterialOptions) {
18
- const materialData = await fs.readJSON(absolutePath);
19
- if (materialData.customType !== "BABYLON.NodeMaterial") {
20
- return;
21
- }
22
-
23
- const extractedTexturesOutputPath = getExtractedTextureOutputPath(options.publicDir);
24
- await fs.ensureDir(extractedTexturesOutputPath);
25
-
26
- const relativePaths = await extractNodeMaterialTextures(materialData, {
27
- extractedTexturesOutputPath,
28
- });
29
-
30
- await fs.writeJSON(absolutePath, materialData, {
31
- encoding: "utf-8",
32
- });
33
-
34
- await Promise.all(
35
- relativePaths.map(async (relativePath) => {
36
- const finalPath = join(options.publicDir, relativePath);
37
-
38
- options.exportedAssets.push(finalPath);
39
-
40
- await compressFileToKtx(finalPath, {
41
- force: options.force,
42
- exportedAssets: options.exportedAssets,
43
- });
44
- })
45
- );
46
- }
47
-
48
- export interface IExtractNodeMaterialTexturesOptions {
49
- extractedTexturesOutputPath: string;
50
- }
51
-
52
- export async function extractNodeMaterialTextures(materialData: any, options: IExtractNodeMaterialTexturesOptions) {
53
- const blocks = materialData.blocks.filter(
54
- (block: any) => (block.customType === "BABYLON.TextureBlock" || block.customType === "BABYLON.ImageSourceBlock") && block.texture?.name
55
- );
56
-
57
- const relativePaths: string[] = [];
58
-
59
- await Promise.all(
60
- blocks.map(async (block: any) => {
61
- if (block.texture?.name?.startsWith("http://") || block.texture.name.startsWith("https://")) {
62
- const relativePath = await extractTextureAssetFromUrl(block.texture.name, {
63
- ...options,
64
- });
65
-
66
- if (relativePath) {
67
- relativePaths.push(relativePath);
68
- block.texture.name = block.texture.url = relativePath;
69
- }
70
- }
71
-
72
- if (block.texture.name?.startsWith("data:")) {
73
- const relativePath = await extractTextureAssetFromDataString(block.texture.name, {
74
- ...options,
75
- });
76
-
77
- if (relativePath) {
78
- relativePaths.push(relativePath);
79
- block.texture.name = block.texture.url = relativePath;
80
- }
81
- }
82
- })
83
- );
84
-
85
- return relativePaths;
86
- }
@@ -1,109 +0,0 @@
1
- import { join } from "node:path/posix";
2
-
3
- import fs from "fs-extra";
4
-
5
- import { extractTextureAssetFromDataString, extractTextureAssetFromUrl } from "../../tools/extract.mjs";
6
-
7
- import { compressFileToKtx } from "./ktx.mjs";
8
- import { getExtractedTextureOutputPath } from "./texture.mjs";
9
-
10
- export interface IProcessExportedMaterialOptions {
11
- force: boolean;
12
- publicDir: string;
13
- exportedAssets: string[];
14
- optimize: boolean;
15
- }
16
-
17
- export async function processExportedNodeParticleSystemSet(absolutePath: string, options: IProcessExportedMaterialOptions) {
18
- const particleSystemData = await fs.readJSON(absolutePath);
19
- if (particleSystemData.customType !== "BABYLON.NodeParticleSystemSet") {
20
- return;
21
- }
22
-
23
- const extractedTexturesOutputPath = getExtractedTextureOutputPath(options.publicDir);
24
- await fs.ensureDir(extractedTexturesOutputPath);
25
-
26
- const relativePaths = await extractNodeParticleSystemSetTextures(particleSystemData, {
27
- extractedTexturesOutputPath,
28
- });
29
-
30
- await fs.writeJSON(absolutePath, particleSystemData, {
31
- encoding: "utf-8",
32
- });
33
-
34
- await Promise.all(
35
- relativePaths.map(async (relativePath) => {
36
- const finalPath = join(options.publicDir, relativePath);
37
-
38
- options.exportedAssets.push(finalPath);
39
-
40
- if (options.optimize) {
41
- await compressFileToKtx(finalPath, {
42
- force: options.force,
43
- exportedAssets: options.exportedAssets,
44
- });
45
- }
46
- })
47
- );
48
- }
49
-
50
- export interface IExtractParticleSystemTexturesOptions {
51
- extractedTexturesOutputPath: string;
52
- }
53
-
54
- export async function extractParticleSystemTextures(particleSystemData: any, options: IExtractParticleSystemTexturesOptions) {
55
- let relativePath: string | null = null;
56
-
57
- if (particleSystemData.texture?.name.startsWith("http://") || particleSystemData.texture?.name.startsWith("https://")) {
58
- relativePath = await extractTextureAssetFromUrl(particleSystemData.texture.name, {
59
- ...options,
60
- });
61
- } else if (particleSystemData.texture?.name.startsWith("data:")) {
62
- relativePath = await extractTextureAssetFromDataString(particleSystemData.texture.name, {
63
- ...options,
64
- });
65
- }
66
-
67
- if (relativePath) {
68
- particleSystemData.texture.name = relativePath;
69
- particleSystemData.texture.url = particleSystemData.texture.name;
70
- }
71
-
72
- return relativePath;
73
- }
74
-
75
- export async function extractNodeParticleSystemSetTextures(particleSystemData: any, options: IExtractParticleSystemTexturesOptions) {
76
- const blocks = particleSystemData.blocks.filter((block) => block.customType === "BABYLON.ParticleTextureSourceBlock");
77
-
78
- const relativePaths: string[] = [];
79
-
80
- await Promise.all(
81
- blocks.map(async (block: any) => {
82
- if (block.url?.startsWith("http://") || block.url?.startsWith("https://")) {
83
- const relativePath = await extractTextureAssetFromUrl(block.url, {
84
- ...options,
85
- });
86
-
87
- if (relativePath) {
88
- relativePaths.push(relativePath);
89
- block.url = `/scene/${relativePath}`;
90
- }
91
- }
92
-
93
- if (block.textureDataUrl?.startsWith("data:")) {
94
- const relativePath = await extractTextureAssetFromDataString(block.textureDataUrl, {
95
- ...options,
96
- });
97
-
98
- if (relativePath) {
99
- relativePaths.push(relativePath);
100
- delete block.textureDataUrl;
101
- block.url = `/scene/${relativePath}`;
102
- block.serializedCachedData = false;
103
- }
104
- }
105
- })
106
- );
107
-
108
- return relativePaths;
109
- }
@@ -1,102 +0,0 @@
1
- import { basename, extname, join } from "node:path/posix";
2
-
3
- import fs from "fs-extra";
4
-
5
- import { compressFileToKtx } from "./ktx.mjs";
6
- import { ICreateAssetsOptions } from "./assets.mjs";
7
- import { processExportedTexture } from "./texture.mjs";
8
- import { processExportedMaterial } from "./material.mjs";
9
- import { processExportedNodeParticleSystemSet } from "./particle-system.mjs";
10
-
11
- const supportedImagesExtensions: string[] = [".jpg", ".jpeg", ".webp", ".png", ".bmp"];
12
- const supportedCubeTexturesExtensions: string[] = [".env", ".dds"];
13
- const supportedAudioExtensions: string[] = [".mp3", ".wav", ".wave", ".ogg"];
14
- const supportedJsonExtensions: string[] = [".material", ".gui", ".cinematic", ".npss", ".json"];
15
- const supportedMiscExtensions: string[] = [".3dl", ".exr", ".hdr"];
16
-
17
- const supportedExtensions: string[] = [
18
- ...supportedImagesExtensions,
19
- ...supportedCubeTexturesExtensions,
20
- ...supportedAudioExtensions,
21
- ...supportedJsonExtensions,
22
- ...supportedMiscExtensions,
23
- ];
24
-
25
- export interface IProcessAssetFileOptions extends ICreateAssetsOptions {
26
- outputAssetsDir: string;
27
- exportedAssets: string[];
28
- optimize: boolean;
29
- cache: Record<string, string>;
30
- compressedTexturesEnabled: boolean;
31
- }
32
-
33
- export async function processAssetFile(file: string, options: IProcessAssetFileOptions) {
34
- const isNavMesh = file.includes(".navmesh");
35
- const extension = extname(file).toLocaleLowerCase();
36
-
37
- if (!isNavMesh && !supportedExtensions.includes(extension)) {
38
- return;
39
- }
40
-
41
- if (basename(file).startsWith("editor_preview")) {
42
- return;
43
- }
44
-
45
- const relativePath = file.replace(join(options.projectDir, "/"), "");
46
- const split = relativePath.split("/");
47
-
48
- let path = "";
49
- for (let i = 0; i < split.length - 1; ++i) {
50
- try {
51
- await fs.ensureDir(join(options.publicDir, path, split[i]));
52
- } catch (e) {
53
- // Catch silently.
54
- }
55
-
56
- path = join(path, split[i]);
57
- }
58
-
59
- let isNewFile = false;
60
-
61
- const fileStat = await fs.stat(file);
62
- const hash = fileStat.mtimeMs.toString();
63
-
64
- isNewFile = !options.cache[relativePath] || options.cache[relativePath] !== hash;
65
-
66
- options.cache[relativePath] = hash;
67
-
68
- const finalPath = join(options.publicDir, relativePath);
69
- const finalPathExists = await fs.pathExists(finalPath);
70
-
71
- if (isNewFile || !finalPathExists) {
72
- await fs.copyFile(file, finalPath);
73
- }
74
-
75
- options.exportedAssets.push(finalPath);
76
-
77
- if (options.optimize && options.compressedTexturesEnabled) {
78
- await compressFileToKtx(finalPath, {
79
- force: isNewFile,
80
- exportedAssets: options.exportedAssets,
81
- });
82
- }
83
-
84
- if (options.optimize) {
85
- if (supportedImagesExtensions.includes(extension)) {
86
- await processExportedTexture(finalPath, {
87
- ...options,
88
- force: isNewFile,
89
- });
90
- } else if (extension === ".material") {
91
- await processExportedMaterial(finalPath, {
92
- ...options,
93
- force: isNewFile,
94
- });
95
- } else if (extension === ".npss") {
96
- await processExportedNodeParticleSystemSet(finalPath, {
97
- ...options,
98
- force: isNewFile,
99
- });
100
- }
101
- }
102
- }
@@ -1,91 +0,0 @@
1
- import { basename, dirname, extname, join } from "node:path/posix";
2
-
3
- import sharp from "sharp";
4
- import fs from "fs-extra";
5
-
6
- import { getPowerOfTwoUntil } from "../../tools/scalar.mjs";
7
-
8
- import { compressFileToKtx } from "./ktx.mjs";
9
-
10
- export function getExtractedTextureOutputPath(publicDir: string) {
11
- return join(publicDir, "assets", "editor-generated_extracted-textures");
12
- }
13
-
14
- export interface IComputeExportedTextureOptions {
15
- force: boolean;
16
- exportedAssets: string[];
17
- compressedTexturesEnabled: boolean;
18
- }
19
-
20
- export async function processExportedTexture(absolutePath: string, options: IComputeExportedTextureOptions): Promise<void> {
21
- const extension = extname(absolutePath).toLocaleLowerCase();
22
-
23
- const metadata = await sharp(absolutePath).metadata();
24
- if (!metadata.width || !metadata.height) {
25
- return console.error(`Failed to compute exported image "${absolutePath}". Image metadata is invalid.`);
26
- }
27
-
28
- const width = metadata.width;
29
- const height = metadata.height;
30
-
31
- const isPowerOfTwo = width === getPowerOfTwoUntil(width) || height === getPowerOfTwoUntil(height);
32
-
33
- type _DownscaledTextureSize = {
34
- width: number;
35
- height: number;
36
- };
37
-
38
- const availableSizes: _DownscaledTextureSize[] = [];
39
-
40
- let midWidth = (width * 0.66) >> 0;
41
- let midHeight = (height * 0.66) >> 0;
42
-
43
- if (isPowerOfTwo) {
44
- midWidth = getPowerOfTwoUntil(midWidth);
45
- midHeight = getPowerOfTwoUntil(midHeight);
46
- }
47
-
48
- availableSizes.push({
49
- width: midWidth,
50
- height: midHeight,
51
- });
52
-
53
- let lowWidth = (width * 0.33) >> 0;
54
- let lowHeight = (height * 0.33) >> 0;
55
-
56
- if (isPowerOfTwo) {
57
- lowWidth = getPowerOfTwoUntil(lowWidth);
58
- lowHeight = getPowerOfTwoUntil(lowHeight);
59
- }
60
-
61
- availableSizes.push({
62
- width: lowWidth,
63
- height: lowHeight,
64
- });
65
-
66
- for (const size of availableSizes) {
67
- const nameWithoutExtension = basename(absolutePath).replace(extension, "");
68
- const finalName = `${nameWithoutExtension}_${size.width}_${size.height}${extension}`;
69
- const finalPath = join(dirname(absolutePath), finalName);
70
-
71
- options.exportedAssets.push(finalPath);
72
-
73
- if (options.force || !(await fs.pathExists(finalPath))) {
74
- try {
75
- const buffer = await sharp(absolutePath).resize(size.width, size.height).toBuffer();
76
-
77
- await fs.writeFile(finalPath, buffer);
78
-
79
- console.log(`Exported scaled texture "${finalName}"`);
80
- } catch (e) {
81
- console.error(`Failed to export image scaled image "${finalName}"`);
82
- }
83
- }
84
-
85
- if (options.compressedTexturesEnabled) {
86
- await compressFileToKtx(finalPath, {
87
- ...options,
88
- });
89
- }
90
- }
91
- }