1
votes

I want to rotate a texture appearing on one face of a cube, with Three.js.

As this cube is created multiple times with different textures, I use a function which create the material and the mesh, with a texture parameter.

However I need this texture to be rotated by 90°, and I found that the best way would be to create a canvas, put the texture in, rotate it and use this canvas as the mesh texture. I tried to achieve this with this code : Three.js Rotate Texture, but unfortunately I always get a black texture.

Here is my function :

function createCube(pos, texture) {
     var img = new Image();
     img.src = texture;

     var imgWidth = imgHeight = img.width;
     var mapCanvas = document.createElement( 'canvas' );
     mapCanvas.width = mapCanvas.height = img.width;

     // document.body.appendChild( mapCanvas );
     var ctx = mapCanvas.getContext( '2d' );
     ctx.translate( imgWidth / 2, imgHeight / 2 );
     ctx.rotate( Math.PI / 2 );
     ctx.translate( -imgWidth / 2, -imgHeight / 2 );
     ctx.drawImage( img, 0, 0, imgWidth, imgHeight );

     var texture = new THREE.Texture( mapCanvas );
     texture.needsUpdate = true;
     var materials = [
                //Left side (posx)
                new THREE.MeshLambertMaterial({
                    color: 0x1a0e05
                }),
                //Right side (negx)
                new THREE.MeshLambertMaterial({
                    color: 0x1a0e05
                }),
                //Top side (posy)
                new THREE.MeshLambertMaterial({
                    color: 0x1a0e05
                }),
                //Bottom side (negy)
                new THREE.MeshLambertMaterial({
                    color: 0xffffff,
                    map: texture
                }),
                //Front side (posz)
                new THREE.MeshLambertMaterial({
                    color: 0x1a0e05
                }),
                //Back side (negz)
                new THREE.MeshLambertMaterial({
                    color: 0x1a0e05
                })
    ];

    cube = new THREE.Mesh(new THREE.CubeGeometry(100, 2, 100, 1, 1, 1), new THREE.MeshFaceMaterial(materials));

    ...

    return cube;

}

Maybe that could be due to the texture not yet loaded when appended to the canvas ? I removed the img.onLoad (from Three.js Rotate Texture) because I didn't know how to use this callback inside a multiple times called function... Moreover this function as to return the mesh, can I do that using an extern .onLoad function ?

Thanks !

1

1 Answers

1
votes

Your guess of "due to the texture not yet loaded when appended to the canvas" is most definitely right.

You just need to reintroduce the callback, which needs some reordering of your code. Should work somewhat like this (untested, but should make the idea clear if not work right away):

function createCube(pos, texture) {
     var img = new Image();

     var imgWidth = imgHeight = img.width;
     var mapCanvas = document.createElement( 'canvas' );
     mapCanvas.width = mapCanvas.height = img.width;

     var texture = new THREE.Texture( mapCanvas );


     img.onload = function() {


         // document.body.appendChild( mapCanvas );
         var ctx = mapCanvas.getContext( '2d' );
         ctx.translate( imgWidth / 2, imgHeight / 2 );
         ctx.rotate( Math.PI / 2 );
         ctx.translate( -imgWidth / 2, -imgHeight / 2 );
         ctx.drawImage( img, 0, 0, imgWidth, imgHeight );

         texture.needsUpdate = true;


    }

    var materials = [
              //Left side (posx)
              new THREE.MeshLambertMaterial({
                  color: 0x1a0e05
              }),
              //Right side (negx)
              new THREE.MeshLambertMaterial({
                  color: 0x1a0e05
              }),
              //Top side (posy)
              new THREE.MeshLambertMaterial({
                  color: 0x1a0e05
              }),
              //Bottom side (negy)
              new THREE.MeshLambertMaterial({
                  color: 0xffffff,
                  map: texture
              }),
              //Front side (posz)
              new THREE.MeshLambertMaterial({
                  color: 0x1a0e05
              }),
              //Back side (negz)
              new THREE.MeshLambertMaterial({
                  color: 0x1a0e05
              })
    ];

    img.src = texture; 

    cube = new THREE.Mesh(new THREE.CubeGeometry(100, 2, 100, 1, 1, 1), new THREE.MeshFaceMaterial(materials));

    ...

    return cube;

}

You need to have a render loop, or alternatively, re-render the scene in the onload callback too.