GLTF Exporter in Node Backend

Hello
I have a create a mesh with textures in Node backend using three.js.
And While exporting it using GLTFExporter but I faced this issues.
error: TypeError: Cannot read properties of undefined (reading 'width') at processImage (\controllers\/GLTFExporter.js:732:2
Without texture, I can export it to gltf model.
How can we solve this issue?
Any solutions are welcome! Thank you.

GLTFExporter does not fully work in a node environment out-of-the-box since the module depends on web APIs. For texture support, you have to implement fallbacks for stuff like canvas and Blob to make things work.

Thanks @Mugen87
Actually, I used three.js on backend and I could export a cube as gltf with gltf exporter.
This is my current code.

import * as THREE from 'three';
import { GLTFExporter } from './GLTFExporter';
import fs from 'fs';
import { JSDOM } from 'jsdom';
import { Blob, FileReader } from 'vblob';
import { async } from 'regenerator-runtime';

const gl = require('gl')(1200, 800, { preserveDrawingBuffer: true }); //headless-gl
// Create a DOM
const { window } = new JSDOM();
global.document = window.document;

let request = require('request');

export async function GenerateGLB(props) {
  let frontMaterial, backMaterial;
  const card = {
    width: 4,
    height: 6,
    thick: 0.06
  };
  var glbPath;
  // init scene
  let scene = new THREE.Scene();
  scene.background = new THREE.Color(0x123456);

  // init camera
  let camera = new THREE.PerspectiveCamera(45, 1200 / 800, 0.1, 10000);
  camera.position.set(-1, 1, 10);
  camera.lookAt(scene.position);

  scene.add(camera);

  // init renderer
  let renderer = new THREE.WebGLRenderer({ context: gl });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(window.devicePixelRatio);
  document.body.appendChild(renderer.domElement);


  const textureLoader = new THREE.TextureLoader();

  var base64str = base64_encode("download/4.jpg");
  const frontImage = textureLoader.load(base64str);

  frontMaterial = new THREE.MeshBasicMaterial({
    map: frontImage,
    side: THREE.DoubleSide
  });
  let frontPlane = new THREE.Mesh(new THREE.PlaneBufferGeometry(card.width, card.height), frontMaterial);
  frontPlane.overdraw = true;
  frontPlane.position.set(0, 0, card.thick / 2.0 + 0.001);

  scene.add(frontPlane);

  const backImage = textureLoader.load(props.backImageUrl);

  backMaterial = new THREE.MeshBasicMaterial({
    map: backImage,
    side: THREE.DoubleSide
  });
  backMaterial.needsUpdate = true;

  let backPlane = new THREE.Mesh(new THREE.PlaneGeometry(card.width, card.height), backMaterial);
  backPlane.overdraw = true;
  backPlane.position.set(0, 0, -card.thick / 2.0 - 0.001);
  scene.add(backPlane);

  const geometry = new THREE.BoxBufferGeometry(card.width, card.height, card.thick);
  const material = new THREE.MeshBasicMaterial({ color: 0xddff00 });
  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);
  glbPath = await exportGLTF(scene);
  return glbPath;
}

function download(uri, filename, callback) {
  request.head(uri, function (err, res, body) {
    console.log('content-type:', res.headers['content-type']);
    console.log('content-length:', res.headers['content-length']);
    request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
  });
}

function base64_encode(file) {
  var bitmap = fs.readFileSync(file);
  return new Buffer(bitmap).toString('base64');
}

const exportGLTF = async (input) => {
  const gltfExporter = new GLTFExporter();
  const options = {
    trs: false,
    onlyVisible: false,
    trunteDrawRange: true,
    binary: false,
    forcePowerOfTwoTextures: false,
    maxTextureSize: 1024 || Infinity
  };
  return new Promise((resolve, reject) => {
    gltfExporter.parse(
      input,
      function (result) {
        if (result instanceof ArrayBuffer) {
            console.log('ArrayBuffer')
        } else {
          const output = JSON.stringify(result, null, 2);
          const nowDate = new Date().getTime();
          var path = 'download/' + nowDate + '.gltf';
         fs.writeFile(path, output, (error) => {
            if (error) {
              console.log('An error has occurred ', error);
              reject(error)
            }
              console.log('Data written successfully to disk', path);
              resolve(path)
              // return path;
          });
        }
      },
      function (error) {
        console.log('An error happened during parsing', error);
      },
      options
    );
  })
};


Without frontPlan and backPlan, I could export the model as gltf successfully.
But when I exporty frontplan and backplan, it occurred undefined width of the texture.
Actually, even though, I added texture, but the image properites is undefined now.
That’s weird…

1 Like