Using GLTFLoader on node

I would like to use the GLTFLoader to do some server side loading of models for the purpose of making thumbnail images. Towards this end, I have created a small node script which downloads zip files containing gltf models from S3, unzips them to a tmp directory, then attempts to load the models locally.

My first attempt was to use loader.parse(myGLTF,...). This correctly reads the gltf manifest as json, but as soon as it attempts loads the first .bin file, I get an exception TypeError: Cannot read property 'slice' of undefined. This exception occurs in GLTFParser.prototype.loadBufferView. It seems that it can’t load the local binary files.

For my second attempt, having followed several other threads that suggest to server the files statically, I stood up a server which serves the gltf files (.gltf (JSON) and, .bin (binary)) at http://localhost:8080/… When calling loader.load("http://127.0.0.1:8080/model0.gltf,..."), I can see that the XMLHttpRequest is being made, but I get an error, Unexpected end of JSON input. It can’t even read the gltf json.

The gltf models I am using are sound. I have successfully loaded the same in the client. And I can use the Threejs gltf preview in VS Code to see the models.

Has anyone successfully used GLTFLoader on the server?

Also, I have attempted to do both of the above with and without a LoadingManager, attempting to use the LoadingManager’s capability to modify urls to use file://. Using the LoadingManager callbacks, I can see that my files are being “succesfully loaded”, but the errors remain.

I haven’t seen this done yet… if you have some control over the input models, using .glb may be simpler as there are no external resources. You should be able to pre-preload the buffer in that case, and call

loader.parse( buffer, './', (gltf) => { ... })

Also see https://github.com/mrdoob/three.js/pull/13541

I merged the .gltf and .bin files into one .glb and it works as you suggested. Thanks!

Im trying to do the same thing here and I encoutered the same problems: load() of a local or distant file fails on Cannot read property 'slice' of undefined while trying to read .bin

Now I’m trying to use a glb file but I end up with other errors and I suppose this comes from how I load the glb file:

loader.parse(Buffer.from(fs.readFileSync(path, 'binary')), ...

Any idea how to read it properly?

Well… it says “SyntaxError: Unexpected token g in JSON at position 0”

const loadGLTFModel = (fileName) => {
  const loader = new THREE.GLTFLoader();
  const data = fs.readFileSync(fileName);
  const buffer = new Buffer.from( data ).toString();
  loader.parse(buffer, './', (gltf) => console.log(gltf));
}

It works with “gltf” files, but only to the point of loading .bin files. And then it fails with message “THREE.GLTFLoader: Failed to load buffer” because it requires XMLHttpRequest to load it.

This is a complex topic — it takes a lot of ugly hacks to get code requiring browser APIs to work in node.js. Please review [Solved] GLTFLoader DRACOLoader in nodejs for some of the steps you’ll need to take, although (depending on what features you need) this may not be completely solved.

Yes, the topic is complex but seems that this is pretty popular topic. I found solution for work with GLB on node.js without textures. It works pretty well. I need only process of the 3d objects without rendering.
I think that the only thing that should be solved is the Texture loading. I guess that if redefine the load textures function to the node.js function which will read data and convert it to base64 - it will work like a charm.

const toArrayBuffer = (buf) => {
  const arrayBuffer = new ArrayBuffer(buf.length);
  const view = new Uint8Array(arrayBuffer);
  for (let i = 0; i < buf.length; ++i) {
    view[i] = buf[i];
  }
  return arrayBuffer;
}

const loadGLTFModel = (fileName) => {
  const loader = new THREE.GLTFLoader();
  return new Promise((resolve, reject) => {
    if (fs.existsSync(fileName)) {
      const data = fs.readFileSync(fileName);
      const arrayBuffer = toArrayBuffer(data);
      loader.parse(arrayBuffer, '',
        (object3D) => {
          resolve(object3D);
        },
        (error) => {
          console.log(error);
          reject('Loader failed')
        });
    } else reject(`Cannot find ${fileName}`);
  });
}

I think this is a kind of “ugly hack” that you had in mind, right? :slight_smile: