GLTFLoader for .glb mesh with textures on node

This issue has come up a couple of times earlier here and here. Following these discussions, I have been able to load a .glb file without textures on node. However, on trying to load a .glb mesh with textures, I am getting the following error:

TypeError: URL.createObjectURL is not a function
at /home/three-software-renderer/node_modules/three/examples/js/loaders/GLTFLoader.js:1991:21
at process._tickCallback (internal/process/next_tick.js:68:7)
at Function.Module.runMain (internal/modules/cjs/loader.js:745:11)
at startup (internal/bootstrap/node.js:266:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)
loader failed

I cloned this repo: GitHub - AVGP/three-software-renderer: Universal, in-memory Three.js renderer based on the original THREE.SoftwareRenderer and then modified the file example/render-to-png.js as below. The code works for sample.glb(2CylinderEngine.glb) but not for chair.glb (both files attached)2CylinderEngine.glb (1.8 MB) chair.glb (3.3 MB)

The code is:
(1) render-to-png.js

const THREE = require(“three”);
global.THREE = require(‘three’);

require(‘./…/node_modules/three/examples/js/loaders/GLTFLoader.js’);
require(‘jsdom-global’)();

global.DOMParser = window.DOMParser
var DOMParser = window.DOMParser

const SoftwareRenderer = require(“…/”).SoftwareRenderer;

const PNG = require(“pngjs”).PNG;
const fs = require(“fs”);
const getPixels = require(“get-pixels”);
const loader = new THREE.GLTFLoader();

const width = 1024;
const height = 768;
const camera = new THREE.PerspectiveCamera(75, width / height, 1, 10000);
camera.position.z = 500;
const scene = new THREE.Scene();

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

function dumpObject(obj, lines = , isLast = true, prefix = ‘’) {
const localPrefix = isLast ? ‘└─’ : ‘├─’;
lines.push(${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]);
const newPrefix = prefix + (isLast ? ’ ’ : '│ ');
const lastNdx = obj.children.length - 1;
obj.children.forEach((child, ndx) => {
const isLast = ndx === lastNdx;
dumpObject(child, lines, isLast, newPrefix);
});
return lines;
}

var inFileName = __dirname + ‘/sample.glb’;
if(fs.existsSync(inFileName)) {

var data = fs.readFileSync(inFileName);
var arrayBuffer = toArrayBuffer(data);
loader.parse( arrayBuffer, ‘’, function ( glb ) {

  var root = glb.scene;
  scene.add(glb.scene);
  renderAndExport();

}, function (event){
console.log(event);
console.log(“loader failed”);
} );

}

const renderAndExport = function() {

// Render into pixels-array (RGBA)
const renderer = new SoftwareRenderer();
renderer.setSize(width, height);
renderer.setClearColor(0xffffff, 1);
var imagedata = renderer.render(scene, camera);

// Create a PNG from the pixels array (RGBA)
const png = new PNG({
width: width,
height: height,
filterType: -1
});

for(var i=0;i<imagedata.data.length;i++) {
png.data[i] = imagedata.data[i];
}
console.log(png.data);
if (!fs.existsSync(“temp”)) {
fs.mkdirSync(“temp”);
}
png.pack().pipe(fs.createWriteStream(“temp/sample.png”));

}

The only other change in the repo is the package.json file:

{
“name”: “three-software-renderer”,
“version”: “1.2.0”,
“description”: “Universal Three.js in-memory renderer”,
“main”: “index.js”,
“scripts”: {
“example”: “node example/render-to-png.js”,
“test”: “mocha”
},
“repository”: {
“type”: “git”,
“url”: “git+https://github.com/avgp/three-software-renderer.git”
},
“keywords”: [
“three.js”,
“webgl”,
“node”,
“browser”
],
“author”: “Martin Naumann”,
“license”: “ISC”,
“bugs”: {
“url”: “Issues · AVGP/three-software-renderer · GitHub
},
“homepage”: “GitHub - AVGP/three-software-renderer: Universal, in-memory Three.js renderer based on the original THREE.SoftwareRenderer”,
“devDependencies”: {
“mocha”: “^3.1.2”,
“pngjs”: “^3.0.0”
},
“dependencies”: {
“blob-util”: “^2.0.2”,
“get-pixels”: “^3.3.2”,
“jsdom”: “^15.1.1”,
“jsdom-global”: “^3.0.2”,
“three”: “^0.106.2”,
“three-gltf-loader”: “^1.106.0”,
“xmlhttprequest”: “^1.8.0”
}
}

To run the example, you need to cd into the example directory and run :

$ node render-to-png.js

The output will be generated in temp/sample.png

From what I had understood, createObjectURL is a window property and I had expected jsdom-global package to take care of it. But I am not sure as to how I should proceed now.

To my understanding, SoftwareRenderer requires a THREE.DataTexture object, rather than the normal THREE.Texture objects, which are dependent on HTML image elements in the browser. I think you’ll likely have to directly modify GLTFLoader to use node.js APIs to create these textures, there is no node.js logic in it now.

@donmccurdy SoftwareRenderer does require a THREE.DataTexture object. I tried modifying GLTFLoader but could not make much headway there. I did find a hacky solution though, removing the textures from the .glb file and then adding them manually in Node. That overrides the dependecy.