GLTFExporter and TextureData

Hi.

I want to export my glb files with a TextureData saved in an MeshStandardMaterial in the map attribute but when I try to export the files I get an error:

I create the texture in this way:

var size = 32;
var len = size * size
var textureSize = len * 3;
var textureData = new Uint8Array(textureSize);
var color = new THREE.Color();
for (var i=0; i<len; i++){
	color.setHex(i);
	textureData[i*3] = Math.floor(color.r * 255);
	textureData[i*3+1] = Math.floor(color.g * 255);
	textureData[i*3+2] = Math.floor(color.b * 255);
}
var texture = new THREE.DataTexture( textureData , size, size, THREE.RGBFormat );

Then I assign the texture to material.map

When I use the GLTFExporter I get this error message:

Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
    at processImage (GLTFExporter.js:803)
    at processTexture (GLTFExporter.js:894)
    at processMaterial (GLTFExporter.js:1027)
    at processMesh (GLTFExporter.js:1445)
    at processNode (GLTFExporter.js:1812)
    at processNode (GLTFExporter.js:1860)
    at processNode (GLTFExporter.js:1860)
    at processScene (GLTFExporter.js:1921)
    at processInput (GLTFExporter.js:1974)
    at GLTFExporter.parse (GLTFExporter.js:2004)

Many thanks in advanced.

It seems GLTFExporter does not support DataTexture yet. Can you please open an issue for this at GitHub?

For reproduction: https://jsfiddle.net/p14ckLba/

I’ve quickly hacked in support via ImageData however this requires that the data texture is always in RGBA format. Guess this feature request requires a separate code path in processImage() with no dependencies to canvas.

Hi.

I implemented a function in order to do the job:

function createImage(uint8array, w, h){
		// we asume that uint8array is always a Uint8Array with size w*h*4;
		console.log(uint8array);
		var len = uint8array.length / 4;
		console.log("len: ", len);
		//var image = document.createElement("img");
		var header_size = 54;
		var width = w;
		var height = h;  
		var image_size = width * height * 4;
		var arr = new Uint8Array(header_size + image_size);
		var view = new DataView(arr.buffer);
		view.setUint16(0, 0x424D, false);
		view.setUint32(2, arr.length, true);
		view.setUint32(10, header_size, true);
		view.setUint32(14, 40, true);
		view.setInt32(18, width, true);
		view.setInt32(22, height, true);
		view.setUint16(26, 1, true);
		view.setUint16(28, 32, true);
		view.setUint32(30, 0, true);
		view.setUint32(34, image_size, true);
		view.setInt32(38, 10000, true);
		view.setInt32(42, 10000, true);
		view.setUint32(46, 0, true);
		view.setUint32(50, 0, true);
		
		for (var i=0; i<len; i++) {
			var index = header_size + (i*4);
			arr[index] = uint8array[i*4];
			arr[index+1] = uint8array[i*4+1];
			arr[index+2] = uint8array[i*4+2];
		}
		var buf1 = new Buffer.from(arr.buffer, 'base64');
		
            var image = new Image(width, height);
            var blob = new Blob([buf1], { type: "image/bmp" });
            var url = window.URL.createObjectURL(blob);
		image.src = url;
		return image;
	}

The problem is that from browser it works but from my nodejs conversion process it complains about createObjectURL method. The error is on GLTFExporter.js library.

Do you think it will work using your approach?

Best regards

I’m afraid your code is not acceptable. The exporter is primarily intended for the web. Code like Buffer.from() does not work in browsers.

Hi.

In fact my code is working on nodejs but only without using textures.
About Buffer.from() method, it works if you use the appropiate requires in nodejs script.

require(‘jsdom-global’)();
global.DOMParser = window.DOMParser;
var DOMParser = window.DOMParser;
global.THREE = require(“three”);

My problem is on createObjectURL method.

Best regards

Could you please explain your aproach using ImageData?

Instead of always doing this in the exporter:

ctx.drawImage( image, 0, 0, canvas.width, canvas.height );

I’ve changed it to:

if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
	( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
	( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {

	ctx.drawImage( image, 0, 0, canvas.width, canvas.height );

} else {

	var imageData = new ImageData( new Uint8ClampedArray( image.data.buffer ), image.width, image.height );
	ctx.putImageData( imageData, 0, 0 );

}

However, this approach is too restrictive since it only supports RGBA textures. It does not honor the maxTextureSize and forcePowerOfTwoTextures options, too.

I’m running into the same issue.

If nothing has been done yet, I think I’m going to go ahead and implement the feature, then make a pull request for it.

@donmccurdy Would the above code would be sufficient for exporting a model using DataTextures to glTF? I don’t think it makes sense to support all possible configurations of data textures. Besides, a data texture has to be generate by the user so maybe missing support for maxTextureSize and forcePowerOfTwoTextures is tolerable?

Finished implementing the feature, and made a pull request for it:

1 Like

@Mugen87 I think so, yes. The option for forcePowerOfTwoTextures goes back to Facebook 3D posts requiring it. Since that product has been discontinued, I’m not sure we even need the option now.

1 Like