Skip to content

Instantly share code, notes, and snippets.

@bengsfort
Last active April 17, 2020 19:56
Show Gist options
  • Save bengsfort/08f8af2958a34f593139dc443a7a6d0f to your computer and use it in GitHub Desktop.
Save bengsfort/08f8af2958a34f593139dc443a7a6d0f to your computer and use it in GitHub Desktop.
node.js script that converts all `.fbx` assets within a directory to the `.gltf` file format. Requires FBX2glTF (https://github.com/facebookincubator/FBX2glTF) and fs-extra (https://github.com/jprichardson/node-fs-extra). Assumes usage of a module bundler that allows for importing files for use in JS (such as Webpack or Rollup).
/**
* Converts all .fbx files in the provided folder to .gltf files.
* @param {String} src
* @param {String} dest
*/
const spawn = require('child_process').spawn;
const fs = require('fs-extra');
const path = require('path');
const ART_SRC = process.argv[2] || './art';
const ART_DEST = process.argv[3] || './src/assets/art';
/**
* Writes a JS module importing the gltf model and all of its dependencies.
* @param {string} target The path to the gltf output.
* @return {Promise} A promise to write the module out.
*/
function writeModelModule(target) {
const output = `${target}_out`;
const filename = path.basename(target);
const importDir = `${filename}_out`;
return fs.readdir(output)
// Filter out the build files
.then(files => files.filter(file => path.extname(file).toLowerCase() !== '.gltf'))
// Create imports for each asset
.then(assets => assets.map(asset => `import './${importDir}/${asset}';`))
// Create the module content
.then(imports =>
`import ${filename} from './${importDir}/${filename}.gltf';\n` +
imports.join('\n') +
`\nexport default ${filename};\n`
)
.then(moduleContent => fs.outputFile(`${target}.js`, moduleContent));
}
/**
* Converts an fbx file at the given path to a gltf file.
* @param {string} file The path to the fbx file.
* @return {Promise<boolean>} A boolean representing the result.
*/
function convertFile(file) {
return new Promise((resolve, reject) => {
const target = path.join(ART_DEST, path.basename(file, '.fbx'));
try {
const convert = spawn('./node_modules/fbx2gltf/bin/Darwin/FBX2glTF', [
'-i',
file,
'-o',
target,
'-d', // Enable draco compression
'-e', // Inline buffers within non-binary gltf
// '-v', // Verbose output
'--khr-materials-unlit', // Unlit shader
]);
convert.stdout.on('data', data => process.stdout.write(data));
convert.stderr.on('data', data => process.stderr.write(data));
convert.on('close', code => {
process.stdout.write('Writing helper model JS module\n\n');
writeModelModule(target)
.then(() => resolve({ target, status: true }));
});
} catch (e) {
process.stderr.write(`There was an error: ${e}`);
resolve({ target, status: false });
}
});
}
/**
* Recurse through a directory to try to find fbx files.
* @param {string} dir The directory.
* @return {Promise<string[]>} A list of fbx paths.
*/
async function recurseDirectory(dir) {
let fbx = [];
try {
const subdirs = [];
const files = await fs.readdir(dir);
files.map(file => {
const ext = path.extname(file);
if (ext.toLowerCase() === '.fbx') {
fbx.push(path.join(dir, file));
} else if (ext.toLowerCase() === '' && file.indexOf('.') < 0) {
subdirs.push(path.join(dir, file));
}
});
for (let i = 0; i < subdirs.length; i++) {
const subfiles = await recurseDirectory(subdirs[i]);
fbx = fbx.concat(subfiles);
}
} catch (e) {
console.error(e);
}
return fbx;
}
// Open up the source directory
fs.pathExists(ART_SRC, async function(err, exists) {
if (err || !exists) {
return;
}
// Find all of the files in the directory & its children
process.stdout.write(`Reading directory ${ART_SRC}...\n`);
const models = await recurseDirectory(ART_SRC);
const numModels = models.length;
process.stdout.write(`Found ${numModels} fbx files.\n`);
// Clean the target directory
process.stdout.write(`Cleaning target directory: ${ART_DEST}\n`);
await fs.emptyDir(ART_DEST);
// Iterate through all models and convert them to glTF
Promise.all(
models.map((model, i) => {
process.stdout.write(`Converting model ${(i + 1)} of ${numModels}\n`);
return convertFile(model);
})
).then(results => {
// Write the results of all of the files
process.stdout.write('Finished converting files:\n\n');
results.map((val, index) => {
process.stdout.write(`${models[index]}\n`);
if (val.status) {
process.stdout.write(`Success ->\t${val.target}\n`);
} else {
process.stdout.write('ERROR!');
}
});
}).catch(e => {
process.stderr.write(`There was an error converting the files :(`);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment