JavaFX 3D Tutorial #3 – 3D Object Transform (Rotation) with Keyboard Input

In this chapter, we will talk about applying transformation on the 3D objects. JavaFX provides following transforms

  1. Affine
  2. Rotate
  3. Scale
  4. Shear
  5. Translate

For now, we make use of Rotate transformation. All the transformations can be applied in a similar manner.

Applying Transforms

All JavaFX nodes have the method getTransforms() that will return an observable list of all the transforms applied on that. You can simple add a new transform by

 Transform whatever = new Transform();
 yourObject.getTransforms().add(whatever);

When applying transform, it is important that you take care of existing transformation. Let’s say, your object is currently rotated 30 degrees, when you rotate it again by 10, you have to add with 30.

This can be achieved using createConcatenation() method that combines two transformations.

Transformable Container

When we work on 3D programming, we will have to do the transformation regularly. The following class helps to do this in the easy way.

class SmartGroup extends Group {
    Rotate r;
    Transform t = new Rotate();

    void rotateByX(int ang) {
      r = new Rotate(ang, Rotate.X_AXIS);
      t = t.createConcatenation(r);
      this.getTransforms().clear();
      this.getTransforms().addAll(t);
    }

    void rotateByY(int ang) {
      r = new Rotate(ang, Rotate.Y_AXIS);
      t = t.createConcatenation(r);
      this.getTransforms().clear();
      this.getTransforms().addAll(t);
    }
  }

With this SmartGroup, you can separate the rotation logic. It also remembers previous rotation values.

With this, you can rotate an object or a set of objects (the whole container) as follows.

    SmartGroup group = new SmartGroup();
    //Add all the components
    group.getChildren().addAll(box, sphere);

    //Rotate by X - 10 degrees
    group.rotateByX(10);
    //Make it 20
   group.rotateByX(10); 

Listen for Keyboard Inputs

You can register for keyboard press events using KeyEvent.KEY_PRESSED. We have to attach a keyboard listener to the stage itself so that all the key events can be tracked.

primaryStage.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
   //Process according to the key pressed.
   switch (event.getCode()) {
   }
});

Working Example

import javafx.application.Application;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Box;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import javafx.stage.Stage;


/**
 * @author afsal villan
 * @version 1.0
 *
 * https://genuinecoder.com
 */
public class Rotation3D extends Application {

  private static final int WIDTH = 1400;
  private static final int HEIGHT = 800;

  @Override
  public void start(Stage primaryStage) {
    //Create box
    Box box = new Box(100, 20, 50);

    //Prepare transformable Group container
    SmartGroup group = new SmartGroup();
    group.getChildren().add(box);

    Camera camera = new PerspectiveCamera();
    Scene scene = new Scene(group, WIDTH, HEIGHT);
    scene.setFill(Color.SILVER);
    scene.setCamera(camera);

    //Move to center of the screen
    group.translateXProperty().set(WIDTH / 2);
    group.translateYProperty().set(HEIGHT / 2);
    group.translateZProperty().set(-1200);

    //Add keyboard control.
    primaryStage.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
      switch (event.getCode()) {
        case W:
          group.translateZProperty().set(group.getTranslateZ() + 100);
          break;
        case S:
          group.translateZProperty().set(group.getTranslateZ() - 100);
          break;
        case Q:
          group.rotateByX(10);
          break;
        case E:
          group.rotateByX(-10);
          break;
        case NUMPAD6:
          group.rotateByY(10);
          break;
        case NUMPAD4:
          group.rotateByY(-10);
          break;
      }
    });

    primaryStage.setTitle("Genuine Coder");
    primaryStage.setScene(scene);
    primaryStage.show();
  }


  public static void main(String[] args) {
    launch(args);
  }

  class SmartGroup extends Group {

    Rotate r;
    Transform t = new Rotate();

    void rotateByX(int ang) {
      r = new Rotate(ang, Rotate.X_AXIS);
      t = t.createConcatenation(r);
      this.getTransforms().clear();
      this.getTransforms().addAll(t);
    }

    void rotateByY(int ang) {
      r = new Rotate(ang, Rotate.Y_AXIS);
      t = t.createConcatenation(r);
      this.getTransforms().clear();
      this.getTransforms().addAll(t);
    }
  }
}

 

Visit JavaFX 3D Course Index Page

Muhammed Afsal Villan
Muhammed Afsal Villan is an experienced full-stack developer, specialized in desktop and mobile application development. He also regularly publishes quality tutorials on his YouTube channel named 'Genuine Coder'. He likes to contribute to open-source projects and is always enthusiastic about new technologies.

35 COMMENTS