How to create a GLTF Sequence from Command Line/Script?

I am hoping to output some animations from Blender to 3D PDF. The pathway I have found that is very promising is based on this post:

I am using some less standard animation techniques (Morph targets & Modifier keyframes) which do not render out easily from Blender to a usable format. Thanks to the above post I have proven that I can use all the animation techniques I need and port them to my end software (Simlab Composer) just fine.

The main problem is that my actual production projects have far more data than the web editor can handle, which is fine, but I’d really like to continue using that sort of workflow (Obj to GLTF to Single GLTF/GLB). I have found a few tools to make the first step of batch conversion to GLTF work, but I am having trouble finding what tool or workflow I can use to join them together into one file to be sequenced by GLTF-Transform.

Ideally i’d like to take any tool and write a shell script or similar automation script to set and forget as I would imagine I will have a few hundred frames at the very least I need to crank out.

Let me know if you require more info, cheers!

If these OBJ files are collectively too big for the three.js editor, I think you’d be on the right path using obj2gltf to convert each one to a glTF offline. GLTF-Transform’s merge command isn’t quite what you need to combine those, at least in its current form, because it creates a separate scene for each input file. The sequence command to animate between the meshes would need them all to be in a single scene.

A short Node.js script to merge multiple glTF files into a single glTF file, with a single scene, would be something like this:

^Not mentioned there, you may want to give the root node for each “frame” a meaningful numbered name, like frame_025. Once you’ve got a glTF file in that format, the sequence command should do what you expect.

1 Like

This is an excellent resource, but I am having some trouble using the io.read function with my files.

I am trying to mimic what you wrote on the StackExchange but I am getting this error with all of my .gltf and .glb files that are both local to the project hierarchy, and ones that I have in other places on my disk. However, I am getting something more expected from the sample files from the KhronosGroup github.

I tried simply logging the output of the io.read call to understand what it wanted, but I am getting this error when I supply (what I think is) a valid path. For example:

console.log(io.read("models/GLTF Out/Cone.gltf"))

Gives me this error: "Error: ENOENT: No such file or directory, open “c:\users…\models\GLTF Out\data:application\octet-stream;base64, AAAAAAL8AAIC…” and so on.

I looked at the documentation and the source for the Read command and I can see that your read function checks with a factory pattern to see if its binary or not, and part of the string matching uses a RegEx to check against the keywords I am seeing in my log.

I’m not totally sure what I am doing wrong, it seems that when I use a file path that is in my project it freaks out. I have even tried copying an embedded glTF from the Sample Models that had worked in its own directory into this local path, where it breaks.

Hm that sounds like an issue that was (or should be) fixed in v0.12 of glTF Transform, are you using an older version?

Alternatively, writing to GLB with obj2gltf should work, and reduce file size a bit.

Are you using an older version?

Just checking with the command line, I seem to be on version 0.12.1
edit: I can see from the Github and NPM pages that you are up to 0.12.4, I am playing around with getting this new version into my file. Having some trouble, but I don’t want to clog this thread with NPM issues.

I will give a try with GLB files from obj2gltf, thanks!

I will link the Github page for my script, but i’ll also add what is relevant here in the forum. Mostly because the actual script is a mess, lol.

// Define in and out directories
const __dirname = path.basename('./models');
const outDir = path.join(__dirname, 'GLTF Out');

// get .obj files by filtering all files in the directory
// by extension type
const files = fs.readdirSync(__dirname);
const targetFiles = files.filter(file => {
    return path.extname(file).toLowerCase() === ".obj"
});

// Convert OBJ to GLTF using obj2gltf

for (const file in targetFiles) {
    const fn = path.join(__dirname, targetFiles[file]); // get .obj path
// changeExtension is a tiny function I wrote so that I can pass
// the correct namesake to the writeFileSync call later on. I will write out below
    const fn_out = path.join(outDir, changeExtension(targetFiles[file], ".gltf"));

    obj2gltf(fn)
         .then(function (gltf){
             const data = Buffer.from(JSON.stringify(gltf));
             fs.writeFileSync(fn_out, data);
         });

Following that, I have my NodeIO instance and a document instance. For debugging, I simply use console.log(io.read("file/path.gltf") using a path to different directories to test different issues. Mostly the relative ./models/GLTF Out.

I have a feeling that I am not using the obj2gltf library correctly, but their documentation on Github isn’t extremely clear when using it as a library instead of a CLI.

For example, in the above code after the promise is returned, I pass ‘gltf’ as an argument, but its never really explained what that is. I would assume if they had a switch or some other factory pattern inside they would take that parameter in as a string, but I’m not sure what the idea is… Their example for glTFToGLB is also confusing, I feel like I am missing something.

Further update:

I added in a buffer merger as I switched to .GLB output. I seem to be generating the correct output, but I am unable to load the files in anything (Windows 3D Viewer, Simlab, or your GLTF-Viewer).

Code as it stands here

I’m not sure if putting my terminal output of the Document object, or the source of the models would be helpful, but if so i can do that.

Is the repository private? I’m getting a 404 … I can see the MrMagicPenguin/GLTFMerger · GitHub repository but it shows up as empty.

My mistake it was private. Try again, sorry!

Okay, I think I have it fixed now. I think the main problem is that I was calling scene.SetName instead of Child.setName. or, its quite likely that __dirname is a “magic” name that i shouldn’t be throwing around lightly like I had been. Not sure why I did, now that I think about it…

I will update this post when I have a polished implementation, but I will accept @donmccurdy’s answer as your linked thread and just in general your help has been invaluable to me.

1 Like