babylonjs-editor-cli 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/bin/babylonjs-editor-cli.js +3 -0
  2. package/build/index.node.js +21 -0
  3. package/build/package.json +34 -0
  4. package/build/src/export.mjs +2 -0
  5. package/build/src/export.mjs.map +1 -0
  6. package/build/src/index.mjs +22 -0
  7. package/build/src/index.mjs.map +1 -0
  8. package/build/src/pack/assets/assets.mjs +26 -0
  9. package/build/src/pack/assets/assets.mjs.map +1 -0
  10. package/build/src/pack/assets/ktx.mjs +121 -0
  11. package/build/src/pack/assets/ktx.mjs.map +1 -0
  12. package/build/src/pack/assets/material.mjs +53 -0
  13. package/build/src/pack/assets/material.mjs.map +1 -0
  14. package/build/src/pack/assets/particle-system.mjs +75 -0
  15. package/build/src/pack/assets/particle-system.mjs.map +1 -0
  16. package/build/src/pack/assets/process.mjs +78 -0
  17. package/build/src/pack/assets/process.mjs.map +1 -0
  18. package/build/src/pack/assets/texture.mjs +61 -0
  19. package/build/src/pack/assets/texture.mjs.map +1 -0
  20. package/build/src/pack/geometry.mjs +19 -0
  21. package/build/src/pack/geometry.mjs.map +1 -0
  22. package/build/src/pack/pack.mjs +127 -0
  23. package/build/src/pack/pack.mjs.map +1 -0
  24. package/build/src/pack/scene.mjs +241 -0
  25. package/build/src/pack/scene.mjs.map +1 -0
  26. package/build/src/tools/extract.mjs +51 -0
  27. package/build/src/tools/extract.mjs.map +1 -0
  28. package/build/src/tools/fs.mjs +9 -0
  29. package/build/src/tools/fs.mjs.map +1 -0
  30. package/build/src/tools/ktx.mjs +36 -0
  31. package/build/src/tools/ktx.mjs.map +1 -0
  32. package/build/src/tools/process.mjs +21 -0
  33. package/build/src/tools/process.mjs.map +1 -0
  34. package/build/src/tools/scalar.mjs +23 -0
  35. package/build/src/tools/scalar.mjs.map +1 -0
  36. package/build/src/tools/scene.mjs +68 -0
  37. package/build/src/tools/scene.mjs.map +1 -0
  38. package/build/src/tools/worker.mjs +35 -0
  39. package/build/src/tools/worker.mjs.map +1 -0
  40. package/build/src/tools/workers/md5.mjs +10 -0
  41. package/build/src/tools/workers/md5.mjs.map +1 -0
  42. package/declaration/src/export.d.mts +1 -0
  43. package/declaration/src/index.d.mts +1 -0
  44. package/declaration/src/pack/assets/assets.d.mts +11 -0
  45. package/declaration/src/pack/assets/ktx.d.mts +28 -0
  46. package/declaration/src/pack/assets/material.d.mts +11 -0
  47. package/declaration/src/pack/assets/particle-system.d.mts +12 -0
  48. package/declaration/src/pack/assets/process.d.mts +9 -0
  49. package/declaration/src/pack/assets/texture.d.mts +7 -0
  50. package/declaration/src/pack/geometry.d.mts +10 -0
  51. package/declaration/src/pack/pack.d.mts +5 -0
  52. package/declaration/src/pack/scene.d.mts +13 -0
  53. package/declaration/src/tools/extract.d.mts +8 -0
  54. package/declaration/src/tools/fs.d.mts +2 -0
  55. package/declaration/src/tools/ktx.d.mts +3 -0
  56. package/declaration/src/tools/process.d.mts +5 -0
  57. package/declaration/src/tools/scalar.d.mts +8 -0
  58. package/declaration/src/tools/scene.d.mts +21 -0
  59. package/declaration/src/tools/worker.d.mts +18 -0
  60. package/declaration/src/tools/workers/md5.d.mts +1 -0
  61. package/esbuild.mjs +57 -0
  62. package/package.json +34 -0
  63. package/src/export.mts +1 -0
  64. package/src/index.mts +28 -0
  65. package/src/pack/assets/assets.mts +46 -0
  66. package/src/pack/assets/ktx.mts +158 -0
  67. package/src/pack/assets/material.mts +86 -0
  68. package/src/pack/assets/particle-system.mts +109 -0
  69. package/src/pack/assets/process.mts +102 -0
  70. package/src/pack/assets/texture.mts +91 -0
  71. package/src/pack/geometry.mts +38 -0
  72. package/src/pack/pack.mts +159 -0
  73. package/src/pack/scene.mts +346 -0
  74. package/src/tools/extract.mts +74 -0
  75. package/src/tools/fs.mts +11 -0
  76. package/src/tools/ktx.mts +44 -0
  77. package/src/tools/process.mts +21 -0
  78. package/src/tools/scalar.mts +28 -0
  79. package/src/tools/scene.mts +90 -0
  80. package/src/tools/worker.mts +39 -0
  81. package/src/tools/workers/md5.mts +13 -0
  82. package/tsconfig.json +21 -0
@@ -0,0 +1,35 @@
1
+ import { join } from "node:path/posix";
2
+ import { Worker } from "node:worker_threads";
3
+ /**
4
+ * Creates a new worker and returns its reference.
5
+ * @param path defines the relative path to the worker entry point relative to THIS file.
6
+ */
7
+ export function loadWorker(path, workerData) {
8
+ path = join(import.meta.dirname.replace(/\\/g, "/"), path);
9
+ return new Worker(path, {
10
+ workerData,
11
+ });
12
+ }
13
+ /**
14
+ * Creates a new worker and returns the result computed by the worker.
15
+ * Posts the given message data to the worker and waits until the worker posts a message back.
16
+ * @param path defines the relative path to the worker according to THIS file. Typically "workers/myWorker.js".
17
+ * @param data defines the data object posted to the worker once it has been initialized.
18
+ * @param transfer defines the optional transferable objects to pass to the worker.
19
+ * @returns a promise that resolves with the data posted by the worker.
20
+ */
21
+ export async function executeSimpleWorker(pathOrWorker, data) {
22
+ const worker = loadWorker(pathOrWorker, data);
23
+ return new Promise((resolve) => {
24
+ worker.on("message", (result) => {
25
+ if (data.id && result.id !== data.id) {
26
+ return;
27
+ }
28
+ else if (!data.id) {
29
+ worker.terminate();
30
+ }
31
+ resolve(result);
32
+ });
33
+ });
34
+ }
35
+ //# sourceMappingURL=worker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.mjs","sourceRoot":"","sources":["../../../src/tools/worker.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,UAA6B;IACrE,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE;QACvB,UAAU;KACV,CAAC,CAAC;AACJ,CAAC;AAID;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAI,YAAoB,EAAE,IAAuB;IACzF,MAAM,MAAM,GAAG,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAE9C,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,EAAE;QACjC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,IAAI,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;gBACtC,OAAO;YACR,CAAC;iBAAM,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACrB,MAAM,CAAC,SAAS,EAAE,CAAC;YACpB,CAAC;YAED,OAAO,CAAC,MAAM,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { workerData, parentPort } from "node:worker_threads";
2
+ import md5 from "md5";
3
+ import fs from "fs-extra";
4
+ let content = workerData;
5
+ if (typeof workerData === "string") {
6
+ content = await fs.readFile(workerData);
7
+ }
8
+ const hash = md5(content);
9
+ parentPort?.postMessage(hash);
10
+ //# sourceMappingURL=md5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"md5.mjs","sourceRoot":"","sources":["../../../../src/tools/workers/md5.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,MAAM,UAAU,CAAC;AAE1B,IAAI,OAAO,GAAG,UAAU,CAAC;AAEzB,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;IACpC,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;AAC1B,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export { pack, IPackOptions } from "./pack/pack.mjs";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ export interface ICreateAssetsOptions {
2
+ projectDir: string;
3
+ publicDir: string;
4
+ baseAssetsDir: string;
5
+ outputAssetsDir: string;
6
+ optimize: boolean;
7
+ exportedAssets: string[];
8
+ cache: Record<string, string>;
9
+ compressedTexturesEnabled: boolean;
10
+ }
11
+ export declare function createAssets(options: ICreateAssetsOptions): Promise<void>;
@@ -0,0 +1,28 @@
1
+ export type KTXToolsType = "-astc.ktx" | "-dxt.ktx" | "-pvrtc.ktx" | "-etc1.ktx" | "-etc2.ktx";
2
+ export declare const allKtxFormats: KTXToolsType[];
3
+ export declare const ktxSupportedextensions: string[];
4
+ /**
5
+ * Returns the absolute path to the compressed textures CLI path (PVRTexTool).
6
+ * The value is retrieved from the local storage so it's per computer and not per project.
7
+ */
8
+ export declare function getCompressedTexturesCliPath(): string | null;
9
+ /**
10
+ * Sets the absolute path to the compressed textures CLI path (PVRTexTool).
11
+ * The value is stored in the local storage so it's per computer and not per project.
12
+ */
13
+ export declare function setCompressedTexturesCliPath(absolutePath: string): void;
14
+ /**
15
+ * Returns the filename of the compressed texture according to the given path and the destination format.
16
+ * @param path defines the path of the texture to get its final name.
17
+ * @param format defines the destination format of the texture.
18
+ * @example image.png -> image-asct.ktx
19
+ */
20
+ export declare function getCompressedTextureFilename(path: string, format: KTXToolsType): string;
21
+ export type CompressFileToKtxOptions = {
22
+ format: KTXToolsType;
23
+ force?: boolean;
24
+ exportedAssets?: string[];
25
+ destinationFolder?: string;
26
+ };
27
+ export declare function compressFileToKtx(absolutePath: string, options: Partial<CompressFileToKtxOptions>): Promise<void>;
28
+ export declare function compressFileToKtxFormat(absolutePath: string, options: CompressFileToKtxOptions): Promise<string | null>;
@@ -0,0 +1,11 @@
1
+ export interface IProcessExportedMaterialOptions {
2
+ force: boolean;
3
+ publicDir: string;
4
+ exportedAssets: string[];
5
+ optimize: boolean;
6
+ }
7
+ export declare function processExportedMaterial(absolutePath: string, options: IProcessExportedMaterialOptions): Promise<void>;
8
+ export interface IExtractNodeMaterialTexturesOptions {
9
+ extractedTexturesOutputPath: string;
10
+ }
11
+ export declare function extractNodeMaterialTextures(materialData: any, options: IExtractNodeMaterialTexturesOptions): Promise<string[]>;
@@ -0,0 +1,12 @@
1
+ export interface IProcessExportedMaterialOptions {
2
+ force: boolean;
3
+ publicDir: string;
4
+ exportedAssets: string[];
5
+ optimize: boolean;
6
+ }
7
+ export declare function processExportedNodeParticleSystemSet(absolutePath: string, options: IProcessExportedMaterialOptions): Promise<void>;
8
+ export interface IExtractParticleSystemTexturesOptions {
9
+ extractedTexturesOutputPath: string;
10
+ }
11
+ export declare function extractParticleSystemTextures(particleSystemData: any, options: IExtractParticleSystemTexturesOptions): Promise<string | null>;
12
+ export declare function extractNodeParticleSystemSetTextures(particleSystemData: any, options: IExtractParticleSystemTexturesOptions): Promise<string[]>;
@@ -0,0 +1,9 @@
1
+ import { ICreateAssetsOptions } from "./assets.mjs";
2
+ export interface IProcessAssetFileOptions extends ICreateAssetsOptions {
3
+ outputAssetsDir: string;
4
+ exportedAssets: string[];
5
+ optimize: boolean;
6
+ cache: Record<string, string>;
7
+ compressedTexturesEnabled: boolean;
8
+ }
9
+ export declare function processAssetFile(file: string, options: IProcessAssetFileOptions): Promise<void>;
@@ -0,0 +1,7 @@
1
+ export declare function getExtractedTextureOutputPath(publicDir: string): string;
2
+ export interface IComputeExportedTextureOptions {
3
+ force: boolean;
4
+ exportedAssets: string[];
5
+ compressedTexturesEnabled: boolean;
6
+ }
7
+ export declare function processExportedTexture(absolutePath: string, options: IComputeExportedTextureOptions): Promise<void>;
@@ -0,0 +1,10 @@
1
+ import { readSceneDirectories } from "../tools/scene.mjs";
2
+ export interface ICreateGeometryFilesOptions {
3
+ sceneFile: string;
4
+ sceneName: string;
5
+ publicDir: string;
6
+ exportedAssets: string[];
7
+ babylonjsEditorToolsVersion: string;
8
+ directories: Awaited<ReturnType<typeof readSceneDirectories>>;
9
+ }
10
+ export declare function createGeometryFiles(options: ICreateGeometryFilesOptions): Promise<void>;
@@ -0,0 +1,5 @@
1
+ export interface IPackOptions {
2
+ optimize: boolean;
3
+ pvrTexToolAbsolutePath?: string;
4
+ }
5
+ export declare function pack(projectDir: string, options: IPackOptions): Promise<void>;
@@ -0,0 +1,13 @@
1
+ import { readSceneDirectories } from "../tools/scene.mjs";
2
+ export interface ICreateBabylonSceneOptions {
3
+ sceneFile: string;
4
+ sceneName: string;
5
+ publicDir: string;
6
+ babylonjsEditorToolsVersion: string;
7
+ exportedAssets: string[];
8
+ optimize: boolean;
9
+ compressedTexturesEnabled: boolean;
10
+ config: any;
11
+ directories: Awaited<ReturnType<typeof readSceneDirectories>>;
12
+ }
13
+ export declare function createBabylonScene(options: ICreateBabylonSceneOptions): Promise<void>;
@@ -0,0 +1,8 @@
1
+ export interface IExtractTextureAssetFromDataStringOptions {
2
+ extractedTexturesOutputPath: string;
3
+ }
4
+ export declare function extractTextureAssetFromDataString(dataString: string, options: IExtractTextureAssetFromDataStringOptions): Promise<string | null>;
5
+ export interface IExtractTextureAssetFromUrlOptions {
6
+ extractedTexturesOutputPath: string;
7
+ }
8
+ export declare function extractTextureAssetFromUrl(url: string, options: IExtractTextureAssetFromUrlOptions): Promise<string | null>;
@@ -0,0 +1,2 @@
1
+ import { glob } from "glob";
2
+ export declare function normalizedGlob(...args: Parameters<typeof glob>): Promise<string[]>;
@@ -0,0 +1,3 @@
1
+ export declare let pvrTexToolAbsolutePath: string | undefined;
2
+ export declare function setPVRTexToolAbsolutePath(absolutePath: string): void;
3
+ export declare function locatePVRTexTool(): Promise<string | undefined>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Executes the given command asynchronously using `child_process`
3
+ * @param command defines the command to execute.
4
+ */
5
+ export declare function executeAsync(command: string): Promise<void>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Returns the power of two sizes until the given limit.
3
+ * @param limit The limit size.
4
+ * @param from The starting size. Default is 1.
5
+ * @returns An array of power of two sizes.
6
+ */
7
+ export declare function getPowerOfTwoSizesUntil(limit?: number, from?: number): number[];
8
+ export declare function getPowerOfTwoUntil(limit: number): number;
@@ -0,0 +1,21 @@
1
+ export declare function ensureSceneDirectories(scenePath: string): Promise<void>;
2
+ export declare function readSceneDirectories(scenePath: string): Promise<{
3
+ nodesFiles: string[];
4
+ meshesFiles: string[];
5
+ lodsFiles: string[];
6
+ lightsFiles: string[];
7
+ cameraFiles: string[];
8
+ skeletonFiles: string[];
9
+ shadowGeneratorFiles: string[];
10
+ sceneLinkFiles: string[];
11
+ guiFiles: string[];
12
+ soundFiles: string[];
13
+ particleSystemFiles: string[];
14
+ morphTargetManagerFiles: string[];
15
+ morphTargetFiles: string[];
16
+ animationGroupFiles: string[];
17
+ spriteMapFiles: string[];
18
+ spriteManagerFiles: string[];
19
+ geometryFiles: string[];
20
+ nodeParticleSystemSetFiles: string[];
21
+ }>;
@@ -0,0 +1,18 @@
1
+ import { Worker } from "node:worker_threads";
2
+ /**
3
+ * Creates a new worker and returns its reference.
4
+ * @param path defines the relative path to the worker entry point relative to THIS file.
5
+ */
6
+ export declare function loadWorker(path: string, workerData: WorkerMessageData): Worker;
7
+ export type WorkerMessageData = {
8
+ id?: string;
9
+ } & Record<string, any>;
10
+ /**
11
+ * Creates a new worker and returns the result computed by the worker.
12
+ * Posts the given message data to the worker and waits until the worker posts a message back.
13
+ * @param path defines the relative path to the worker according to THIS file. Typically "workers/myWorker.js".
14
+ * @param data defines the data object posted to the worker once it has been initialized.
15
+ * @param transfer defines the optional transferable objects to pass to the worker.
16
+ * @returns a promise that resolves with the data posted by the worker.
17
+ */
18
+ export declare function executeSimpleWorker<T>(pathOrWorker: string, data: WorkerMessageData): Promise<T>;
@@ -0,0 +1 @@
1
+ export {};
package/esbuild.mjs ADDED
@@ -0,0 +1,57 @@
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/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "babylonjs-editor-cli",
3
+ "version": "5.0.0",
4
+ "description": "Babylon.js Editor CLI is a command line interface to help you package your scenes made using the Babylon.js Editor",
5
+ "productName": "Babylon.js Editor CLI",
6
+ "scripts": {
7
+ "build": "tsc -p . && node esbuild.mjs",
8
+ "watch": "concurrently \"tsc -p . --watch\" \"node esbuild.mjs --watch\" -c bgYellow.bold,bgBlue.bold --names cli-ts,editor-dependencies"
9
+ },
10
+ "typings": "declaration/src/export.d.mts",
11
+ "license": "(Apache-2.0)",
12
+ "bin": {
13
+ "babylonjs-editor-cli": "./bin/babylonjs-editor-cli.js"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "types": "./declaration/src/export.d.ts",
18
+ "import": "./build/src/export.mjs",
19
+ "require": "./build/index.node.js"
20
+ }
21
+ },
22
+ "devDependencies": {},
23
+ "dependencies": {
24
+ "chalk": "5.6.2",
25
+ "cli-spinners": "3.4.0",
26
+ "commander": "14.0.2",
27
+ "dotenv": "17.2.3",
28
+ "fs-extra": "11.2.0",
29
+ "glob": "11.1.0",
30
+ "pngjs": "7.0.0",
31
+ "ora": "9.1.0",
32
+ "sharp": "0.34.3"
33
+ }
34
+ }
package/src/export.mts ADDED
@@ -0,0 +1 @@
1
+ export { pack, IPackOptions } from "./pack/pack.mjs";
package/src/index.mts ADDED
@@ -0,0 +1,28 @@
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();
@@ -0,0 +1,46 @@
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
+ }
@@ -0,0 +1,158 @@
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
+ }