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
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
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.
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.
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.
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);
})
},
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();
It doesnât work for me:
const decoder = await draco3dgltf.createDecoderModule();
^^^^^
SyntaxError: await is only valid in async function