|
#!/usr/bin/env node |
|
|
|
const fs = require( 'fs' ); |
|
const path = require( 'path' ); |
|
const Canvas = require( 'canvas' ); |
|
const { Blob, FileReader } = require( 'vblob' ); |
|
THREE = global.THREE = require( 'three' ); |
|
require( 'three/examples/js/loaders/deprecated/LegacyJSONLoader.js' ); |
|
require( 'three/examples/js/exporters/GLTFExporter.js' ); |
|
require( 'three/examples/js/utils/BufferGeometryUtils.js' ); |
|
|
|
if ( process.argv.length <= 2 ) { |
|
|
|
console.log( `Usage: ${path.basename( __filename )} model.json [ --optimize ]` ); |
|
process.exit( - 1 ); |
|
|
|
} |
|
|
|
var file = process.argv[ 2 ]; |
|
var optimize = process.argv.indexOf( '--optimize' ) > 0; |
|
var resourceDirectory = THREE.LoaderUtils.extractUrlBase( file ); |
|
|
|
// |
|
|
|
// Patch global scope to imitate browser environment. |
|
global.window = global; |
|
global.Blob = Blob; |
|
global.FileReader = FileReader; |
|
global.THREE = THREE; |
|
global.document = { |
|
createElement: ( nodeName ) => { |
|
|
|
if ( nodeName !== 'canvas' ) throw new Error( `Cannot create node ${nodeName}` ); |
|
|
|
const canvas = new Canvas( 256, 256 ); |
|
// This isn't working — currently need to avoid toBlob(), so export to embedded .gltf not .glb. |
|
// canvas.toBlob = function () { |
|
// return new Blob([this.toBuffer()]); |
|
// }; |
|
return canvas; |
|
|
|
} |
|
}; |
|
|
|
// |
|
|
|
// Load legacy JSON file and construct a mesh. |
|
|
|
var jsonContent = fs.readFileSync( file, 'utf8' ); |
|
var loader = new THREE.LegacyJSONLoader(); |
|
var { geometry, materials } = loader.parse( JSON.parse( jsonContent ), resourceDirectory ); |
|
|
|
var mesh; |
|
var boneDefs = geometry.bones || []; |
|
var animations = geometry.animations || []; |
|
var hasVertexColors = geometry.colors.length > 0; |
|
|
|
geometry = new THREE.BufferGeometry().fromGeometry( geometry ); |
|
|
|
// Remove unnecessary vertex colors and groups added during BufferGeometry conversion. |
|
if ( ! hasVertexColors ) geometry.removeAttribute( 'color' ); |
|
if ( geometry.groups.length === 1 ) geometry.clearGroups(); |
|
|
|
if ( ! materials ) { |
|
|
|
materials = new THREE.MeshStandardMaterial( { color: 0x888888, roughness: 1, metalness: 0 } ); |
|
|
|
} |
|
|
|
if ( optimize ) { |
|
|
|
geometry = THREE.BufferGeometryUtils.mergeVertices( geometry ); |
|
|
|
} |
|
|
|
if ( boneDefs.length ) { |
|
|
|
var { roots, bones } = initBones( boneDefs ); |
|
mesh = new THREE.SkinnedMesh( geometry, materials ); |
|
roots.forEach( ( bone ) => mesh.add( bone ) ); |
|
mesh.updateMatrixWorld( true ); |
|
mesh.bind( new THREE.Skeleton( bones ), mesh.matrixWorld ); |
|
mesh.normalizeSkinWeights(); |
|
|
|
} else { |
|
|
|
mesh = new THREE.Mesh( geometry, materials ); |
|
|
|
} |
|
|
|
// |
|
|
|
// Export to glTF. |
|
var exporter = new THREE.GLTFExporter(); |
|
exporter.parse( mesh, ( json ) => { |
|
|
|
var content = JSON.stringify( json ); |
|
fs.writeFileSync( path.basename( file, '.json' ) + '.gltf', content, 'utf8' ); |
|
|
|
}, { binary: false, animations } ); |
|
|
|
// |
|
|
|
/** Previously SkinnedMesh.initBones(). */ |
|
function initBones( boneDefs ) { |
|
|
|
var bones = [], bone, gbone; |
|
var i, il; |
|
|
|
// first, create array of 'Bone' objects from geometry data |
|
|
|
for ( i = 0, il = boneDefs.length; i < il; i ++ ) { |
|
|
|
gbone = boneDefs[ i ]; |
|
|
|
// create new 'Bone' object |
|
|
|
bone = new THREE.Bone(); |
|
bones.push( bone ); |
|
|
|
// apply values |
|
|
|
bone.name = gbone.name; |
|
bone.position.fromArray( gbone.pos ); |
|
bone.quaternion.fromArray( gbone.rotq ); |
|
if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); |
|
|
|
} |
|
|
|
// second, create bone hierarchy |
|
var roots = []; |
|
|
|
for ( i = 0, il = boneDefs.length; i < il; i ++ ) { |
|
|
|
gbone = boneDefs[ i ]; |
|
|
|
if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) { |
|
|
|
// subsequent bones in the hierarchy |
|
|
|
bones[ gbone.parent ].add( bones[ i ] ); |
|
|
|
} else { |
|
|
|
// topmost bone, immediate child of the skinned mesh |
|
|
|
roots.push( bones[ i ] ); |
|
|
|
} |
|
|
|
} |
|
|
|
return { roots, bones }; |
|
|
|
} |
Hi Don,
Thanks for this useful script, which gives me the opportunity to try to convert the previous Geometry based JSON model file into a GLTF file. But when I run this script I meet some issues, could you help me?
I used your script to try to convert a Geometry format JSON model to GLTF, but I failed to convert it whenever I use the mac or Windows system. Below is my operations:
Is this a correct workflow?
I also test npm install canvas vblob three@0.110.0 -g, it also doesn’t work.
Then I also use the JSON file that I export from blender to test whether it be converted successfully or not.
However, it always failed. When I run the script there is no response, e.g. no warning, no error, no new file created in the folder.
Could you give me an example or some advice on this problem?
Thank you