Last active
April 17, 2020 19:56
-
-
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).
This file contains hidden or 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
/** | |
* 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