Using GLB with textures on node

I need to load .glb or .gltf files on node. I’ve read all related topics here, but seems that there is no good answer. I have a glb file that should be processed by node, then attached to another code generated 3d-object and then send to client for rendering. Anybody has a working solution?

I thought that this will work:

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));
}

But it doesn’t work with glb files at all. And it fails with gltf files as well.

For those who are looking for the answer to the question about load GLB models on node.js with three.js
Here’s a working solution for models WITHOUT textures:

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}`);
  });
}
1 Like

For using textures from GLB files we can add several browser things to the node.js environment. And modify three.js library little bit.

const jsdom = require('jsdom');
const {JSDOM} = jsdom;
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
global.document = dom.window.document;
global.window = dom.window;
global.THREE = THREE;
global.self = global;
global.URL = require('url');
global.Blob = require('cross-blob');

URL.createObjectURL = (blob) => {
  return new Promise(resolve => {
    blob.arrayBuffer().then(buffer => {
      const base64 = new Buffer.from(buffer).toString('base64');
      const completedURI = `data:image/jpeg;base64,` + base64;
      resolve(completedURI);
    });
  })
};
3 Likes

Hi @muhmundr

I tried your code, but it seems the program is stuck and can not finish model loading.

Can you point out to a model that can be loaded with this code? I am wondering what is wrong with my model.

Maybe this can help?

Actually any model from Sketchfab can be processed on node.js. https://sketchfab.com/feed

Or you can try with models without textures first. They can be generated here:

for me this terminates early

      console.log(0); // <--- this is logged
      loader.parse(arrayBuffer, '',
        (object3D) => {
          console.log(1); // <--- this is not
          resolve(object3D);
        },
        (error) => {
          console.log(error); // <--- neither is this
          reject('Loader failed')
        });

what was your three version ? since r119 was released on Jul 30, 2020 it must have been r118 or earlier - let me try that… nope, same behavior in r118

ok, it seems to be because of this bit.

1 Like

In the end, I threw away jsdom and left this instead:

const Image = require('canvas').Image;
global.document = {
	createElementNS: (ns, type) => {
		if(type === 'img') {
			const img = new Image();
			img.addEventListener = (type, handler) => {
				img['on' + type] = handler.bind(img);
			};
			img.removeEventListener = (type) => {
				img['on' + type] = null;
			};
			return img;
		}
	}
};

global.URL = require('url');
global.self = { URL };
global.Blob = require('cross-blob');
1 Like