Created
March 5, 2024 13:26
-
-
Save michaeldll/ea976e723967df998ca346c6ff9e053b to your computer and use it in GitHub Desktop.
Compress KTX2 with toktx using Node.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { exec } from 'child_process'; | |
import fs from 'fs'; | |
import path from 'path'; | |
// By Michael de Laborde | |
// This script compresses PNG and JPG textures to KTX2 using the Khronos toktx tool. | |
// Uses low quality but highly compressed ETC1S compression by default. | |
// REQUIREMENTS: | |
// Install toktx 4.3.1 from https://github.com/KhronosGroup/KTX-Software/releases/tag/v4.3.1 | |
// USAGE: | |
// node compress-textures.js [options] input.jpg | |
// OPTIONS: | |
// --high - Use high quality UASTC compression, e.g. for normal maps | |
// --medium - Use UASTC compression | |
// --flip - Flip the texture vertically | |
// --noMipmaps - Do not generate mipmaps | |
// --linear - Use linear color space, e.g. for normal maps and other non-color textures such as ORM maps | |
// EXAMPLE USAGE FOR TEXTURES: | |
// Albedo: --low (=etc1s) | |
// Emissive: --low (=etc1s) | |
// Normal: --high --linear | |
// ORM: --high --linear OR --medium --linear | |
// Transmission: --medium --linear | |
// Clearcoat: --medium --linear | |
// Additional information: https://github.com/KhronosGroup/3D-Formats-Guidelines/blob/main/KTXArtistGuide.md | |
const args = process.argv.slice(2); | |
// Find the input file as the last argument | |
let input = args[args.length - 1]; | |
// Check if input file exists | |
if (!fs.existsSync(input)) { | |
throw new Error('Input file does not exist'); | |
} | |
// Find compression, use ETC1S by default | |
let compression = 'etc1s'; | |
for (let i = 0; i < args.length; i++) { | |
if (args[i] === '--medium') { | |
compression = 'uastc'; | |
} else if (args[i] === '--high') { | |
compression = 'uastcHighQuality'; | |
} | |
} | |
// Check if --flip flag is present | |
let flip = ''; | |
for (let i = 0; i < args.length; i++) { | |
if (args[i] === '--flip') { | |
flip = '--lower_left_maps_to_s0t0'; | |
} | |
} | |
// Build mipmaps by default unless --noMipmaps flag is present | |
let mipmaps = '--genmipmap'; | |
for (let i = 0; i < args.length; i++) { | |
if (args[i] === '--noMipmaps') { | |
mipmaps = ''; | |
} | |
} | |
// Linear color space, e.g. for normal maps | |
let linear = ''; | |
for (let i = 0; i < args.length; i++) { | |
if (args[i] === '--linear') { | |
linear = '--assign_oetf linear --assign_primaries none'; | |
} | |
} | |
// Set compression command | |
let command = ''; | |
if (compression === 'etc1s') { | |
command = '--t2 --encode etc1s --clevel 4 --qlevel 255'; | |
} else if (compression === 'uastc') { | |
command = '--t2 --encode uastc --uastc_quality 4 --uastc_rdo_l .5 --uastc_rdo_d 65536 --zcmp 22'; | |
} else if (compression === 'uastcHighQuality') { | |
command = '--t2 --encode uastc --uastc_quality 4 --uastc_rdo_l .2 --uastc_rdo_d 65536 --zcmp 22'; | |
} | |
// Add new target directory | |
const parent = path.dirname(input); | |
const targetDirectory = parent + '/ktx2'; | |
if (!fs.existsSync(targetDirectory)) { | |
fs.mkdirSync(targetDirectory); | |
} | |
// Set output extensions and path to new targetDirectory | |
const output = targetDirectory + '/' + path.basename(input.replace(/\.[^/.]+$/, '.ktx2')); | |
exec(`toktx ${command} ${flip} ${mipmaps} ${output} ${input}`, (error, stdout, stderr) => { | |
if (error) { | |
console.log(`error when building KTX2: ${error.message}`); | |
return; | |
} | |
if (stderr) { | |
console.log(`stderr when building KTX2: ${stderr}`); | |
return; | |
} | |
console.log('Successfully built KTX2 texture'); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment