Rotation around an arbitrary axis

As I cannot find anything I'm asking here:

I would like to animate a door swinging and that means I have to rotate a Spatial around an axis which is parallel to the world up vector but it's not at the Spatial's center.

The rotation axis in my example is along the edge of a box. In 3d math I think this manipulation would consist of a translation so that the the rotation axis is the y-axis then a rotation and another translation back near to the origin so that the rotation axis is where it was.

How can I tell jME to do that?

Someone wants to do the opposite in this thread:

But the solution could work just as well for you… attach your door to a Node, and give the door a local translation. Put the Node at where the "hinges" of the door are, and rotate the Node.

I found another way: I use a reference point which marks the hinge and rotate it using the same matrix as the node. Then the node is translated using the difference of the old location of the reference point to the new one.

This is the controller:

package de.worldofmystery.client.controller;

import java.util.logging.Logger;

import com.jme.math.FastMath;
import com.jme.math.Matrix3f;
import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.util.LoggingSystem;

import de.worldofmystery.common.WomWorld;

 * a controller for door animation
 * @author galun
 * @version $Id:,v 1.2 2006/08/27 15:44:38 galun Exp $
public class DoorController extends Controller {

   private static final long serialVersionUID = 872371512005036263L;
   private Logger log = LoggingSystem.getLogger();
   public static final int CLOCKWISE = -1;
   public static final int COUNTERCLOCKWISE = 1;
   private Spatial spatial;
   private Vector3f originalTranslation;
   private Vector3f hinge;
   private Vector3f ref;
   private Matrix3f rot;
   private Vector3f lockAxis;
   private float start;
   private float stop;
   private float curTime;
   private int direction;
   private float radPerSec = 360 * FastMath.DEG_TO_RAD;

   public DoorController(Spatial spatial) {
      this.spatial = spatial;
      // save a deep copy of the original translation
      originalTranslation = new Vector3f(spatial.getLocalTranslation());
      rot = new Matrix3f();
      curTime = 0;

   public void update(float time) {
      if (! isActive())
      curTime += time * getSpeed();
      float rad = start + curTime * radPerSec * direction;"door curTime=" + curTime + ", direction=" + direction + ", start=" + start + ", stop=" + stop + ", rad=" + rad);
      if (((stop - rad) * direction) <= 0) {
         rad = stop;
      rot.fromAngleNormalAxis(rad, lockAxis);
      rot.mult(ref, ref);

    * set the animation (rotation) this controller should do
    * @param hinge the pivot point for the rotation in the model space
    * @param axis the x, y or z axis to rotate around (not the hinge axis!)
    * @param start the starting angle in degrees
    * @param stop the stop angle in degrees
   public void setRotation(Vector3f hinge, Vector3f axis, float start, float stop, int direction) {
      if (axis != null)
         this.lockAxis = axis;
         this.lockAxis = WomWorld.getInstance().skyVector;
      this.hinge = new Vector3f(hinge); // better use a deep copy
      this.start = start * FastMath.DEG_TO_RAD;
      this.stop = stop * FastMath.DEG_TO_RAD;
      this.direction = direction;
      ref = new Vector3f(hinge);
      if (spatial instanceof Node)
         ((Node)spatial).attachChild(new VisibleAxis("hinge", hinge));

   public Spatial getSpatial() {
      return spatial;

if you find that easier, do it! :slight_smile:

why are you calling unlock on the spatial every update?

The call to unlock() was a relict of a trial and error session and is removed.

An additional node would have worked as well, but then I would have to do that on all existing scenes and all future ones.

I think I would have gone that way with more complex rotation/translation/scaling but for doors with only one rotation axis which is the sky vector I believe this is the easier method.

Meanwhile this controller was extended to handle open and close animation and there is another one which opens and closes a gate with translation so I'm happy :slight_smile: