Nodejs threejs GLTFExporter (server side) Blob issue

Hi.

First to thanks in advance to who will waste his time under this question.

: )

I am trying to make a DAE2GLB convertor with very specific optimizations.
# node myConvertor.js input.dae output.glb

I am currently able to open a dae file or gltf/glb file and modify using three modules.

But my problem comes when I try yo use the GLTFExporter in the same way I used ColladataLoader or GLTFLoader because it depends on Blob objects.

UI have look into the other available exporters and ColladaExporter doesn’t use Blobs for anything, the same for STLExporter or ObjectExporter.

Unfortunately as far as I know Blobs are only available under browser context. I tried to search for a valid solution for nodejs on server side and Blob support but I didn’tBest regardsI really need to concluded anything.

I am able to open a DAE,GLTF orI really need to GLB file and modify reIits content (as for example custom buffergeometry vertex attributes) andsave everything into a JSON file.

I would prefer to use GLTF binary format (GLB) because it’s faster.

I am also aware that thre are browser emulation contexts for nodejs as Electron, but I would prefer not using it.

The question is if it would be possible to make GLTFExporter.js usable in a nodejs server runtime context

Best regards

I’ve used the following hack to run GLTFExporter in Node.js —

const THREE = require('three');
const Canvas = require('canvas');
const { Blob, FileReader } = require('vblob');

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

// https://github.com/mrdoob/three.js/issues/9562
require('three/examples/js/exporters/GLTFExporter');

At the time I didn’t find a fix for canvas.toBlob(), so you cannot go directly to .glb, but export to .gltf should work. There are various tools (e.g. https://glb-packer.glitch.me/) for packing glTF to GLB if needed.

2 Likes

Hi.

Many many thanks for the information.

Could you please attach a full example?

I would prefer to obtain a glb instead a gltf but it is ok.

Best regards

I can’t write a complete DAE Converter for you, but here’s a (more) complete example of the code above: https://gist.github.com/donmccurdy/9f094575c1f1a48a2ddda513898f6496

For a more general purpose converter, you may want to try https://github.com/KhronosGroup/COLLADA2GLTF and then apply the specific optimizations afterward.

Hi.

First of all thanks a lot for the information.

I tried to install canvas on nodejs, but it has dependencies with external programs I cannot install at work (I am not admin in my PC). I am using a portable nodejs+npm installation.

Anyway using electron I could make GLTFExporter working without any problem in some minutes.

The problem of Electron is that runs in an emulated webbrowser context and this is exactly what I am trying to avoid. I want to make make my convertor (not only a convertor) to run in a headless mode.

Anyway many thanks for you time.

Finally I did it.

Now I can import all supported THREE Formats, apply my own object modifications and finally at the end export one or many models to one or multiple GLB files.

It’s super fast.

: )

Many thanks again for the clue.

1 Like

Hey, can you share a working snippet of your code? I got to successfully exporting GLTF but not GLB.
Thank you : )

I got the same problem,I am appreciate if you can help me how to write the threrejs scene to glb under Nodejs? my mail: 748503128@qq.com thank you

Hello Don,

I tried code snippet shared by you using Three js version 143 and I get error as:
code:
‘ERR_PACKAGE_PATH_NOT_EXPORTED’
message:
‘Package subpath ‘./examples/js/exporters/GLTFExporter.js’ is not defined by “exports” in d:\three-js-legachy\node_modules\three\package.json’
stack:
'Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath ‘./examples/js/exporters/GLTFExporter.js’ is not defined by “exports” in d:\three-js-legachy\node

Can someone please guide. Thank you

Both three.js and Node.js have changed since this was written. I don’t know the details of your Node.js version or what your code looks like, but an import like this might work better now:

import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
1 Like

Has anyone gotten this to work in TypeScript? I have tried the recommended code above, with some adjustments, and the exporter still cannot find Blob. I think I’m missing something. Thanks!

So, I have my tsconfig set to use DOM apis (eventhough this is a node application)

{
  "compilerOptions": {
    "strict": true,
    "lib": ["ESNext", "DOM"],
    "target": "ES2018",
    "moduleResolution": "node",
    "outDir": "./dist",
    "resolveJsonModule": true,
    "declaration": true,
    "sourceMap": true,
    "removeComments": false,
    "module": "ESNext",
    "noEmitOnError": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

This makes intellisense work when inspecting the globalThis object. The following does not given any errors. I placed this code in a file named polyfills.ts for clarity.

// polyfills.ts
impoty * as THREE from 'three';

// createElement, Blob, and FileReader already exist?
globalThis.Blob = Blob;
globalThis.FileReader = FileReader;
globalThis.THREE = THREE;

The actuall application looks something similar to the code below. The problem is that I consitently get the ReferenceError: Blob is not defined error.

// index.ts

import "polyfills.ts";
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';

// Create a mesh
// const mesh: THREE.Mesh = ...

const gltfExporter = new GLTFExporter();

gltfExporter.parse(
        mesh,
        (gltf) => {
          // Do something
        },
        (error) => {
          // Handle error
        }
);

UPDATE

I figured out what I was doing wrong (of course after asking a question). The DOM entry in tsconfig made me think that the entries were present, but they werent. I needed to remove that first before attempting to declare global. Once I got rid of it, I surprisingly had no problems overwriting the global object. What I ended with is shown below. For anyone referencing this later, make sure to import the polyfills file before performing the export.

/* eslint-disable no-var */

// polyfills.ts
import * as THREE from "three";
import { Canvas } from "canvas";
import { Blob as mockBlob, FileReader as mockFR } from "vblob";

declare global {
  var Blob: typeof mockBlob;
  var FileReader: typeof mockFR;
  var document: {
    createElement(nodeName: string): Canvas;
  };
}

// Patch global scope to imitate browser environment.
globalThis.Blob = mockBlob;
globalThis.FileReader = mockFR;
globalThis.THREE = THREE;
globalThis.document = {
  createElement: (nodeName: string): Canvas => {
    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;
  },
};

export {};