I’m trying to build what should be a simple piece of code to slice up a stereo 360 cube map (cross) into a nice CubeTexture without having to manually cut it up and load six different textures. So. I use ImageLoader and then draw the whole thing to a canvas, and then pull six different ImageData objects from that. So far, so good.
I can successfully apply any of those ImageData objects to a new THREE.Texture and it works fine. But when I try to pass an array of them to CubeTexture, I get this error once I set .needsUpdate true:
three.js:19863 THREE.WebGLState: TypeError: Failed to execute 'texImage2D' on 'WebGLRenderingContext': No function was found that matched the signature provided.
I get the same error if I call ImageUtils.getDataURL( imageData ) individually. They are successfully converted to PNGs (I can read out the URL) but the same type error happens when I try to apply them. Yet I can upload them individually as Texture. So if I wanted to make my cube out of six planes, that would work. It’s just that I don’t want to do that, I want to figure out what’s wrong with CubeTexture…
Anyone else run into this?
*** EDIT: I am reasonably sure this is a bug, because if I create a plain Texture out of each ImageData face and then a new MeshStandardMaterial from each of those textures, and push all those materials into the array for the cube, there are no errors. Code is below.
export class PanoramaCube extends THREE.Group {
/* Take a stereoscopic equirectangular cross of arbitrary size, and slice it onto two three.js cubes,
* one cube for each eye in WEBVR. */
private _img:HTMLImageElement;
private _geom:THREE.BoxBufferGeometry = new THREE.BoxBufferGeometry(20,20,20);
private _oL:THREE.Mesh;
private _oR:THREE.Mesh;
constructor(public src:string) {
super();
this._img = new THREE.ImageLoader().load(src);
$(this._img).on('load',this._createTextures.bind(this));
}
private async _createTextures(evt:Event):Promise<void> {
THREE.ImageUtils.crossOrigin = '*';
this._img.crossOrigin = '*';
let canvas:HTMLCanvasElement = document.createElement('canvas');
canvas.width = this._img.naturalWidth;
canvas.height = this._img.naturalHeight;
let ctx:CanvasRenderingContext2D = canvas.getContext('2d');
ctx.drawImage(this._img, 0, 0, this._img.naturalWidth, this._img.naturalHeight); //
let panels:any[] = [];
let ord:number[] = [6,4,1,9,5,7]; //px, nx, py, ny, pz, nz... one side cross of the stereo image
for (let i:number = 1; i < 3; i++) { //left and right
for (let k of ord) {
let row:number = Math.floor(k/4);
let imgData:ImageData = ctx.getImageData((k % 4) * .125 * i * this._img.naturalWidth, row * (this._img.naturalHeight/3),
this._img.naturalWidth * .125, this._img.naturalHeight/3);
panels.push(imgData);
}
//THIS WORKS:
let materials:THREE.MeshStandardMaterial[] = [];
for (let p of panels) {
let t:THREE.Texture = new THREE.Texture(p);
t.needsUpdate = true;
materials.push(new THREE.MeshStandardMaterial({map:t, side:THREE.BackSide}));
}
//it's not clear why applying an array of six materials to a cube causes each to be mapped onto its own side, but sure...
if (i==1) this._oL = new THREE.Mesh(this._geom,materials);
else this._oR = new THREE.Mesh(this._geom,materials);
//THIS THROWS A WEBGL ERROR:
let t:THREE.CubeTexture = new THREE.CubeTexture(panels); //give CubeTexture an array of ImageData
t.needsUpdate = true;
let testMat:THREE.MeshStandardMaterial = new THREE.MeshStandardMaterial({map:t});
let someNewMesh:THREE.Mesh = new THREE.Mesh(this._geom,testMat); //crashland
//
}
this._oL.layers.enable(1);
this._oR.layers.set(2);
this.add(this._oL);
this.add(this._oR);
$(canvas).remove();
}
}