portfolio/scripts/model-pipeline.js
2024-07-16 11:46:01 -05:00

150 lines
4.7 KiB
JavaScript

import { execSync } from 'node:child_process'
import { readdirSync, copyFileSync, unlinkSync, mkdirSync, existsSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { exit } from 'node:process'
/**
* This script is used to transform gltf and glb files into Threlte components.
* It uses the `@threlte/gltf` package to do so.
* It works in two steps:
* 1. Transform the gltf/glb files located in the sourceDir directory
* 2. Move the Threlte components to the targetDir directory
*/
const configuration = {
sourceDir: resolve(join('static', 'models')),
targetDir: resolve(join('src', 'lib', 'components', 'models')),
overwrite: false,
root: '/models/',
types: true,
keepnames: false,
meta: false,
shadows: false,
printwidth: 120,
precision: 2,
draco: null,
preload: false,
suspense: false,
isolated: false,
transform: {
enabled: false,
resolution: 1024,
simplify: {
enabled: false,
weld: 0.0001,
ratio: 0.75,
error: 0.001
}
}
}
// if the target directory doesn't exist, create it
mkdirSync(configuration.targetDir, { recursive: true })
// throw error if source directory doesn't exist
if (!existsSync(configuration.sourceDir)) {
throw new Error(`Source directory ${configuration.sourceDir} doesn't exist.`)
}
// read the directory, filter for .glb and .gltf files and files *not* ending
// with -transformed.gltf or -transformed.glb as these should not be transformed
// again.
const gltfFiles = readdirSync(configuration.sourceDir).filter((file) => {
return (
(file.endsWith('.glb') || file.endsWith('.gltf')) &&
!file.endsWith('-transformed.gltf') &&
!file.endsWith('-transformed.glb')
)
})
if (gltfFiles.length === 0) {
console.log('No gltf or glb files found.')
exit()
}
const filteredGltfFiles = gltfFiles.filter((file) => {
if (!configuration.overwrite) {
const componentFilename = file.split('.').slice(0, -1).join('.') + '.svelte'
const componentPath = join(configuration.targetDir, componentFilename)
if (existsSync(componentPath)) {
console.error(`File ${componentPath} already exists, skipping.`)
return false
}
}
return true
})
if (filteredGltfFiles.length === 0) {
console.log('No gltf or glb files to process.')
exit()
}
filteredGltfFiles.forEach((file) => {
// run the gltf transform command on every file
const path = join(configuration.sourceDir, file)
// parse the configuration
const args = []
if (configuration.root) args.push(`--root ${configuration.root}`)
if (configuration.types) args.push('--types')
if (configuration.keepnames) args.push('--keepnames')
if (configuration.meta) args.push('--meta')
if (configuration.shadows) args.push('--shadows')
args.push(`--printwidth ${configuration.printwidth}`)
args.push(`--precision ${configuration.precision}`)
if (configuration.draco) args.push(`--draco ${configuration.draco}`)
if (configuration.preload) args.push('--preload')
if (configuration.suspense) args.push('--suspense')
if (configuration.isolated) args.push('--isolated')
if (configuration.transform.enabled) {
args.push(`--transform`)
args.push(`--resolution ${configuration.transform.resolution}`)
if (configuration.transform.simplify.enabled) {
args.push(`--simplify`)
args.push(`--weld ${configuration.transform.simplify.weld}`)
args.push(`--ratio ${configuration.transform.simplify.ratio}`)
args.push(`--error ${configuration.transform.simplify.error}`)
}
}
const formattedArgs = args.join(' ')
// run the command
const cmd = `npx @threlte/gltf@latest ${path} ${formattedArgs}`
try {
execSync(cmd, {
cwd: configuration.sourceDir
})
} catch (error) {
console.error(`Error transforming model: ${error}`)
}
})
// read dir again, but search for .svelte files only.
const svelteFiles = readdirSync(configuration.sourceDir).filter((file) => file.endsWith('.svelte'))
svelteFiles.forEach((file) => {
// now move every file to /src/components/models
const path = join(configuration.sourceDir, file)
const newPath = join(configuration.targetDir, file)
copyFile: try {
// Sanity check, we checked earlier if the file exists. Still, the CLI takes
// a while, so who knows what happens in the meantime.
if (!configuration.overwrite) {
// check if file already exists
if (existsSync(newPath)) {
console.error(`File ${newPath} already exists, skipping.`)
break copyFile
}
}
copyFileSync(path, newPath)
} catch (error) {
console.error(`Error copying file: ${error}`)
}
// remove the file from /static/models
try {
unlinkSync(path)
} catch (error) {
console.error(`Error removing file: ${error}`)
}
})