42

I have a great problem about the rotation in three.js I want to rotate my 3D cube in one of my game.

//init
geometry = new THREE.CubeGeometry grid, grid, grid
material = new THREE.MeshLambertMaterial {color:0xFFFFFF * Math.random(), shading:THREE.FlatShading, overdraw:true, transparent: true, opacity:0.8}
for i in [1...@shape.length]
    othergeo = new THREE.Mesh new THREE.CubeGeometry(grid, grid, grid)
    othergeo.position.x = grid * @shape[i][0]
    othergeo.position.y = grid * @shape[i][1]
    THREE.GeometryUtils.merge geometry, othergeo
@mesh = new THREE.Mesh geometry, material

//rotate
@mesh.rotation.y += y * Math.PI / 180
@mesh.rotation.x += x * Math.PI / 180
@mesh.rotation.z += z * Math.PI / 180

and (x, y, z) may be (1, 0, 0)

then the cube can rotate, but the problem is the cube rotate on its own axis,so after it has rotated, it can't rotate as expected.

I find the page How to rotate a Three.js Vector3 around an axis?, but it just let a Vector3 point rotate around the world axis?

and I have tried to use matrixRotationWorld as

@mesh.matrixRotationWorld.x += x * Math.PI / 180
@mesh.matrixRotationWorld.y += y * Math.PI / 180
@mesh.matrixRotationWorld.z += z * Math.PI / 180

but it doesn't work, I don't whether I used it in a wrong way or there are other ways..

so, how to let the 3D cube rotate around the world's axis???

Community
  • 1
  • 1
Tony Han
  • 2,130
  • 3
  • 24
  • 37

9 Answers9

71

Since release r59, three.js provides those three functions to rotate a object around object axis.

object.rotateX(angle);
object.rotateY(angle);
object.rotateZ(angle);
cdosborn
  • 3,111
  • 29
  • 30
Hetong
  • 823
  • 6
  • 11
  • 1
    Thanks. This should be the top answer, mos of the other answers are either deprecated or more inefficient. Great job! – Hugo Nava Kopp Dec 04 '15 at 17:14
  • 4
    This should not be used for animation! Taken from the [Three.js documentation](http://threejs.org/docs/index.html#Reference/Core/Geometry): `This is typically done as a one time operation, and not during a loop Use Object3D.rotation for typical real-time mesh rotation.` – Matt C Aug 30 '16 at 20:18
  • @MatthewCliatt You are right. Could you give a better or efficient solution for animation? – Hetong Oct 11 '16 at 04:15
  • @Hetong I'm still looking for the correct solution myself. – Matt C Oct 12 '16 at 01:40
55

Here are the two functions I use. They are based on matrix rotations. and can rotate around arbitrary axes. To rotate using the world's axes you would want to use the second function rotateAroundWorldAxis().

// Rotate an object around an arbitrary axis in object space
var rotObjectMatrix;
function rotateAroundObjectAxis(object, axis, radians) {
    rotObjectMatrix = new THREE.Matrix4();
    rotObjectMatrix.makeRotationAxis(axis.normalize(), radians);

    // old code for Three.JS pre r54:
    // object.matrix.multiplySelf(rotObjectMatrix);      // post-multiply
    // new code for Three.JS r55+:
    object.matrix.multiply(rotObjectMatrix);

    // old code for Three.js pre r49:
    // object.rotation.getRotationFromMatrix(object.matrix, object.scale);
    // old code for Three.js r50-r58:
    // object.rotation.setEulerFromRotationMatrix(object.matrix);
    // new code for Three.js r59+:
    object.rotation.setFromRotationMatrix(object.matrix);
}

var rotWorldMatrix;
// Rotate an object around an arbitrary axis in world space       
function rotateAroundWorldAxis(object, axis, radians) {
    rotWorldMatrix = new THREE.Matrix4();
    rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);

    // old code for Three.JS pre r54:
    //  rotWorldMatrix.multiply(object.matrix);
    // new code for Three.JS r55+:
    rotWorldMatrix.multiply(object.matrix);                // pre-multiply

    object.matrix = rotWorldMatrix;

    // old code for Three.js pre r49:
    // object.rotation.getRotationFromMatrix(object.matrix, object.scale);
    // old code for Three.js pre r59:
    // object.rotation.setEulerFromRotationMatrix(object.matrix);
    // code for r59+:
    object.rotation.setFromRotationMatrix(object.matrix);
}

So you should call these functions within your anim function (requestAnimFrame callback), resulting in a rotation of 90 degrees on the x-axis:

var xAxis = new THREE.Vector3(1,0,0);
rotateAroundWorldAxis(mesh, xAxis, Math.PI / 180);
Peter
  • 9,643
  • 6
  • 61
  • 108
Cory Gross
  • 36,833
  • 17
  • 68
  • 80
  • I think it would be costly to keep creating a new THREE.Matrix4 for every call though, would you not reuse it if its for every frame animation? – Neil Jun 18 '12 at 21:50
  • 3
    I got these two functions that someone had posted on github. I had to edit them to use new function names and such for the newest version dev version of threejs. I have wondered this myself. The garbage collector will get rid of the objects every so often. I have viewed the memory usage graph in Chrome and it goes up and down where objects are created and destroyed, but the animation looks fine for everything I've used so far. – Cory Gross Jun 18 '12 at 23:33
  • When I try to change the function to reuse the same matrix I cannot get any of my demos to work. If anyone manages to make this happen without creating new matrices like this, please post up. – Cory Gross Jun 18 '12 at 23:34
  • How can I use this to rotate a plane around its edge? – Tibor Szasz Dec 22 '12 at 00:53
  • 2
    Caution with this approach! In Three.js one should do either direct matrix manipulation on an Object3D (with object.matrixAutoUpdate = false) or manipulation of properties like rotation, position etc. [three.js docs](http://threejs.org/docs/#Manual/Introduction/Matrix_transformations) Further `rotateAroundWorldAxis` does NOT work for non-uniform scaling applied to the object.You have to extract rotation from `object.matrix`, multiply with `rotWorldMatrix` and set the components of `object.rotation` from the result. – Michbeckable Feb 27 '15 at 10:43
  • @Michbeckable can you post an example of what you describe here? – Tschallacka Feb 10 '16 at 14:27
  • @MichaelDibbets It is some time ago but the problem is direct multiplication with the `object.matrix` as this matrix is a composite of rotation, scale and transform. To be safe with non-uniform scaling you can use `object.matrix.extractRotation(m)` and multiply matrix `m` with `rotWorldMatrix`. Finally you can do `object.rotation.setFromRotationMatrix(m)`. If you have `matrixAutoUpdate = true` the change to the rotation property will finally be composited into the `object.matrix` by ThreeJS automatically. – Michbeckable Feb 11 '16 at 20:39
13

I needed the rotateAroundWorldAxis function but the above code doesn't work with the newest release (r52). It looks like getRotationFromMatrix() was replaced by setEulerFromRotationMatrix()

function rotateAroundWorldAxis( object, axis, radians ) {

    var rotationMatrix = new THREE.Matrix4();

    rotationMatrix.makeRotationAxis( axis.normalize(), radians );
    rotationMatrix.multiplySelf( object.matrix );                       // pre-multiply
    object.matrix = rotationMatrix;
    object.rotation.setEulerFromRotationMatrix( object.matrix );
}
jonsca
  • 10,218
  • 26
  • 54
  • 62
guntrumm
  • 161
  • 2
  • 7
9

with r55 you have to change
rotationMatrix.multiplySelf( object.matrix );
to
rotationMatrix.multiply( object.matrix );

block23
  • 91
  • 1
  • 2
8

In Three.js R59, object.rotation.setEulerFromRotationMatrix(object.matrix); has been changed to object.rotation.setFromRotationMatrix(object.matrix);

3js is changing so rapidly :D

mind1n
  • 1,196
  • 3
  • 15
  • 34
6

Just in case...in r52 the method is called setEulerFromRotationMatrix instead of getRotationFromMatrix

Leprosy
  • 1,085
  • 4
  • 14
  • 36
4

Somewhere around r59 this gets easier (rotate around x):

bb.GraphicsEngine.prototype.calcRotation = function ( obj, rotationX)
{
    var euler = new THREE.Euler( rotationX, 0, 0, 'XYZ' );
    obj.position.applyEuler(euler);
}
acarlon
  • 16,764
  • 7
  • 75
  • 94
2

In Three.js R66, this is what I use (CoffeeScript version):

THREE.Object3D.prototype.rotateAroundWorldAxis = (axis, radians) ->
  rotWorldMatrix = new THREE.Matrix4()
  rotWorldMatrix.makeRotationAxis axis.normalize(), radians
  rotWorldMatrix.multiply this.matrix
  this.matrix = rotWorldMatrix
  this.rotation.setFromRotationMatrix this.matrix
aymericbeaumet
  • 6,853
  • 2
  • 37
  • 50
2

I solved in this way:

I created the 'ObjectControls' module for ThreeJS that allows you to rotate a single OBJECT (or a Group), and not the SCENE.

Include the libary:

<script src="ObjectControls.js"></script>

Usage:

var controls = new ObjectControls(camera, renderer.domElement, yourMesh);

You can find here a live demo here: https://albertopiras.github.io/threeJS-object-controls/

Here is the repo: https://github.com/albertopiras/threeJS-object-controls.

Alberto Piras
  • 517
  • 6
  • 8
  • 3
    Links die astonishingly fast, which will make this answer less useful. Can you distill the relevant parts of the solution into text or code in the body of the Answer? –  Sep 20 '17 at 16:26