[Solved] GLTFLoader DRACOLoader in nodejs

Hi.

I am trying to load a GLTF binary file (glb) with DRACO compression in a nodejs script.
Is there any way to achieve this?

Best regards

1 Like

GLTFLoader has a dependency to XMLHttpRequest so it’s not possible to use GLTFLoader.load() without a XHR emulation. Instead, can you try to use fs to load the glb and then pass it directly to the .parse() method. So something like:

const loader = new GLTFLoader();
loader.parse( data, path, onLoad, onError );

There will be problems if your model has textures since HTMLImageElement is not available on node. Same for URL.createObjectURL() which is needed for processing binary image data.

TBH I don’t know if it works at all, so I guess you have to find it out. I would start with a simple glb first and move on with Draco if the everything works.

Hi.

I am aware of these dependecies.

I was able to load a GLB file without DRACO compression.

My problem is on making Draco decoder working.

Best regards

I don’t think THREE.DRACOLoader is set up to work in node.js. You’ll probably need to some changes to it, like loading the decoder from the NPM package instead of a script tag. https://www.npmjs.com/package/draco3dgltf

Hi.

Many thanks for the info.

I installed this module in my npm installation but I didn’t see any example about how to parse a gltf file compressed with DRACO compression. The example is related to load a native draco file.

Could you assist me providing me an example?

Many thanks in advanced.

I don’t have an example of this, but look at the THREE.DRACOLoader source code. It loads a plain Draco decoder using a script tag, which won’t work in nodejs. You need a modified version of DRACOLoader that uses the npm package instead.

Msny thanks.

I wilk try.

:slight_smile:

Best regards

Hi.

After many tries, I have not been able to make GLTFLoader with DRACO compression uder nodejs context.

I have tried to use draco3dgltf npm module but there is any valid example loading a gltf file compressed with DRACO compression.

Could anybody help me?

Many thanks in advanced

Here’s an example:

Note that, as mentioned in a comment in the gist, GLTFLoader cannot load embedded textures in a Node.js context, because of reliance on browser APIs.

Many, many thanks.

But the link you have sent to me is no longer valid.

COuld you publish it again?

And very sorry for the delay in aswering you.

Best regards

Hi.

DOn’t worry.

The change was very easy to implement.

it works like a charm.

: )

Many thanks.

Hi again.

Very sorry.

I was loading glbs without DRACO compression, this was the reason that it worked:

I have modified the DRACOLoader.js just including the line:
const decoder = require('draco3dgltf').createDecoderModule();
after the line use strict at the beginning.

Let me attach my example: example.js

/* Instructions:
* Have node-v10.13.0-win-x64 installed
* set PATH=%PATH%;C:\Users\ua4192\Documents\Viewer3d\node-v10.13.0-win-x64
* node --max-old-space-size=32000  example.js  <input_file> <input_folder>
*                           <input_file> --> is a plain text with 1 filename (<filename>.glb) in each line
*                           <input_folder> --> is the folder where glb files are allocated.
* 
 * Example:
*            node --max-old-space-size=32000  Generate_CMS_Scene_toDeliver.js lista.txt c:\temp\

*/
// ---------------------------------------------------------------------------------------------------------
// - REQUIREMENTS:
// ---------------------------------------------------------------------------------------------------------
               var  start = Date.now();
               var  before = Date.now();
               var  fs = require("fs")
               global.THREE = require('three')
               //require('./node_modules/three/examples/js/libs/draco/draco_decoder.js');
               require('./node_modules/three/examples/js/loaders/DRACOLoader.js')
               require('./node_modules/three/examples/js/loaders/GLTFLoader.js')

// ---------------------------------------------------------------------------------------------------------
// - ARGUMENTS::
// ---------------------------------------------------------------------------------------------------------
               var listFile = process.argv[2]; // Plain text with filenames separated by lines (each line one filename)
               var inFolder = process.argv[3]; // Input folder where glb files are allocated
               before = Date.now();

               // ---------------------------------------------------------------------------------------------------------
               // - FUNCTIONS:
               // ---------------------------------------------------------------------------------------------------------
               function toArrayBuffer(buf) {
                              var ab = new ArrayBuffer(buf.length);
                              var view = new Uint8Array(ab);
                              for (var i = 0; i < buf.length; ++i) {
                                             view[i] = buf[i];
                              }
                              return ab;
               }

// ---------------------------------------------------------------------------------------------------------
// - GLOBAL VARIABLES::
// ---------------------------------------------------------------------------------------------------------
               var fileArray = fs.readFileSync(listFile, 'utf8').toString().split("\n");
               console.log(fileArray);
               var glbLoader = new THREE.GLTFLoader();
               //THREE.DRACOLoader.setDecoderPath( './node_modules/three/examples/js/libs/draco/gltf/' );
               //THREE.DRACOLoader.setDecoderConfig({type: 'js'});
               
               glbLoader.setDRACOLoader( new THREE.DRACOLoader() );
               
               for (var i=0; i<fileArray.length-1;i++) {
                              var inFilename = inFolder + fileArray[i];
                              console.log("opening file " + inFilename);
                              if (fs.existsSync(inFilename)) {
                                             console.log("loading glb " + inFilename);
                                             var data = fs.readFileSync(inFilename);
                                             var arrayBuffer = toArrayBuffer(data);
                                             console.log(arrayBuffer);
                                             glbLoader.parse( arrayBuffer, '', function ( glb ) {
                                                            console.log(glb.scene);
                                             }, function (event){
                                                            console.log(event);
                                                            console.log("loader failed");
                                             } );
                              } else {
                                             console.log("File doesn't exist " + inFilename);
                              }
               }

COuld you tell me where is my mistake?

Many thanks in advanced.

1 Like

Hi.

Afer adding JSDOM requirements in my example Here the code:

// ---------------------------------------------------------------------------------------------------------
// - REQUIREMENTS:
// ---------------------------------------------------------------------------------------------------------
var start = Date.now();
var before = Date.now();
var fs = require(“fs”)
require(‘jsdom-global’)()
global.DOMParser = window.DOMParser
var DOMParser = window.DOMParser
global.THREE = require(‘three’)
require(’./node_modules/three/examples/js/loaders/DRACOLoader.js’)
require(’./node_modules/three/examples/js/loaders/GLTFLoader.js’)

// ---------------------------------------------------------------------------------------------------------
// - ARGUMENTS::
// ---------------------------------------------------------------------------------------------------------
var listFile = process.argv[2]; // Plain text with filenames separated by lines (each line one filename)
var inFolder = process.argv[3]; // Input folder where glb files are allocated
before = Date.now();

// ---------------------------------------------------------------------------------------------------------
// - FUNCTIONS:
// ---------------------------------------------------------------------------------------------------------
function toArrayBuffer(buf) {
var ab = new ArrayBuffer(buf.length);
var view = new Uint8Array(ab);
for (var i = 0; i < buf.length; ++i) {
view[i] = buf[i];
}
return ab;
}

// ---------------------------------------------------------------------------------------------------------
// - GLOBAL VARIABLES::
// ---------------------------------------------------------------------------------------------------------
var fileArray = fs.readFileSync(listFile, ‘utf8’).toString().split("\n");
console.log(fileArray);
var glbLoader = new THREE.GLTFLoader();
//THREE.DRACOLoader.setDecoderPath( ‘./node_modules/three/examples/js/libs/draco/gltf/’ );
//THREE.DRACOLoader.setDecoderConfig({type: ‘js’});

glbLoader.setDRACOLoader( new THREE.DRACOLoader() );

for (var i=0; i<fileArray.length-1;i++) {
var inFilename = inFolder + fileArray[i];
console.log("opening file " + inFilename);
if (fs.existsSync(inFilename)) {
console.log("loading glb " + inFilename);
var data = fs.readFileSync(inFilename);
var arrayBuffer = toArrayBuffer(data);
console.log(arrayBuffer);
glbLoader.parse( arrayBuffer, ‘’, function ( glb ) {
console.log(glb.scene);
}, function (event){
console.log(event);
console.log(“loader failed”);
} );
} else {
console.log("File doesn’t exist " + inFilename);
}
}

Here are my results:

C:\Users\ua4192\DOCUME~1\Viewer3d\app>node --max-old-space-size=32000 dracoLoaderExample.js C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\listaGLB
2.txt C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2
[ ‘F0221-FH386-STD00002.glb’,
‘M532A375D00200-FDT01.glb’,
‘M879A011D000F3-BS001.glb’,
‘M994A018D20000.glb’,
‘TP201114-------P0001.glb’,
‘’ ]
opening file C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\F0221-FH386-STD00002.glb
loading glb C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\F0221-FH386-STD00002.glb
ArrayBuffer { byteLength: 16824 }
opening file C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\M532A375D00200-FDT01.glb
loading glb C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\M532A375D00200-FDT01.glb
ArrayBuffer { byteLength: 36140 }
opening file C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\M879A011D000F3-BS001.glb
loading glb C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\M879A011D000F3-BS001.glb
ArrayBuffer { byteLength: 39268 }
opening file C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\M994A018D20000.glb
loading glb C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\M994A018D20000.glb
ArrayBuffer { byteLength: 5344 }
opening file C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\TP201114-------P0001.glb
loading glb C:\Users\ua4192\DOCUME~1\Viewer3d\LAST\ASDESIGN\glb2\TP201114-------P0001.glb
ArrayBuffer { byteLength: 17704 }

As you can see, the scene object is never shown in line
console.log(glb.scene); .

And the glbs are Ok, I can open them via THREE.JS editor.
Best regards

Forget it

I finally found my error.

Best regards and many thanks for your support.

1 Like

Sorry, how did you eventually resolve this?

I used your custom loader but I had to do this (I was getting a preload not defined error):

// Custom-built version of DRACOLoader, for Node.js.
const NodeDRACOLoader = require('./NodeDRACOLoader.js');

const DRACOLoader = new NodeDRACOLoader()

// GLTFLoader prefetches the decoder module, when Draco is needed, to speed up
// parsing later. This isn't necessary for our custom decoder, so set the
// method to a no-op.
DRACOLoader.getDecoderModule = () => {};
DRACOLoader.preload = () => {};

loader.setDRACOLoader( DRACOLoader );
loader.load('draco.drc', (gltf)=> ...

and change the imports + exports in NodeDRACOLoader.js because the globals weren’t working right

const THREE = require('three');

const decoder = require('draco3dgltf').createDecoderModule();

function DRACOLoader(manager)

module.exports = DRACOLoader

require('draco3dgltf').createDecoderModule() returns a promise for me, so later on in the NodeDRACOLoader it gives me the following error:
TypeError: dracoDecoder.DecoderBuffer is not a constructor

I resolved this error by replacing the decodeDracoFile function in NodeDRACOLoader.js with

decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap, attributeTypeMap) {
    decoderPromise.then(decoder => {
      this.decodeDracoFileInternal( rawBuffer, decoder, callback, attributeUniqueIdMap, attributeTypeMap);
    })
  },
1 Like

do you need gltf under node with full binary unpack, or is this just for structural testing, for instance jest checking gtlf integrity or something? in the latter case i’ve made a node compatible gltfloader that only creates structure (GLTFStructureLoader): GitHub - pmndrs/gltfjsx: 🎮 Turns GLTFs into JSX components

I think the async method here is just a change in the API for recent versions of the Draco package. You can use await similar to the previous setup:

const draco3dgltf = require('draco3dgltf');
const decoder = await draco3dgltf.createDecoderModule();
1 Like

It doesn’t work for me:

const decoder = await draco3dgltf.createDecoderModule();
                ^^^^^

SyntaxError: await is only valid in async function