Sunday, February 10, 2013

Belajar animasi 3D dengan javaFX

Awalnya mau buat simulasi gerak planet, tapi kebetulan kemarin mata kuliah fisika galaksi belum lulus, akhirnya g jadi deh. Yang ingin saya tunjukkan dalam program ini adalah bagaimana teknologi 3D diserap ke dalam javaFX. Sebenarnya program ini sudah ada dalam sample bawaan dari JavaFX, hanya saya modifikasi dikit. Tapi itupun saya mesti repot-repot buka buku sana-sini untuk pahami konsep semisal affine transformation, spline curve (cubic dan quad) atau hal-hal non-teknis lainnya. tapi g masalah, sekali mendayung 2,3 pulau terlampaui. Pada program ini saya gunakan kubus (seperti contohnya). Sebenarnya bisa juga digunakan bola. Anda tinggal buat circle (lingkaran) yang di-fill dengan LinearGradient. Cuma buat menampakkan efek 3D, makanya saya gunakan kubus. Hikmahnya adalah saya tahu beberapa hal yang sebelumnya saya salah pahami dalam konsep 3D. Jangan lupa, sebelum menjalankan program ini, anda mesti pastikan kartu grafis anda up-to-date. Kebetulan saya kemarin, programnya g jalan karena drivernya sudah eskpire, padahal sudah di download dari versi terbaru di situs resminya. Ternyata yang dipasang di situ sudah ekspire, dan saya mesti mendownload dari update ala microsoft windows.
package fjr.physics.animate;

import javafx.animation.KeyValue;
import javafx.animation.PathTransition;
import javafx.animation.PathTransition.OrientationType;
import javafx.animation.KeyFrame;
import javafx.animation.PathTransitionBuilder;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.AnchorPaneBuilder;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcToBuilder;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathBuilder;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.RectangleBuilder;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;

public class GerakPlanet extends Application {

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

 @Override
 public void start(Stage primaryStage) throws Exception {
  Group root = new Group();
  Planet planet1 = new Planet();
  planet1.setTranslateX(60);
  planet1.setTranslateY(320);

  PathTransition pathTransition = PathTransitionBuilder.create()
    .duration(Duration.seconds(4))
    .orientation(OrientationType.ORTHOGONAL_TO_TANGENT)
    .cycleCount(Timeline.INDEFINITE).autoReverse(false).path(planet1.getPath())
    .node(planet1.getCube()).build();
  pathTransition.play();
  
  final Timeline animasi = new Timeline();
  animasi.getKeyFrames().addAll(
    new KeyFrame(Duration.ZERO,
      new KeyValue(planet1.rx.angleProperty(), 0d)),
    new KeyFrame(Duration.seconds(5), new KeyValue(planet1.rx
      .angleProperty(), 720d)));

  animasi.setCycleCount(Timeline.INDEFINITE);
  animasi.setAutoReverse(false);
  animasi.play();
  
  //add tombol control
  AnchorPane pane = AnchorPaneBuilder.create()
    .children(ButtonBuilder.create()
      .translateX(10)
      .translateY(10)
      .minWidth(60)
      .text("play")
      .onAction(new EventHandler() {
       @Override
       public void handle(ActionEvent arg0) {
        animasi.play();
       }
      })
      .build(), 
      ButtonBuilder.create()
      .text("pause")
      .translateX(10)
      .minWidth(60)
      .translateY(40)
      .onAction(new EventHandler() {
       @Override
       public void handle(ActionEvent arg0) {
        animasi.pause();
       }
      })
      .build()
      )
    .build();

  root.getChildren().addAll(planet1 ,pane);
  primaryStage.setScene(new Scene(root, 500, 700));
  primaryStage.show();
 }

 class Planet extends Group {

  Path path;
  Rectangle rect;
  Group cube; 
  private int size = 40;
  private Color color = Color.ORANGE;
  private double shade = 1; 

  final Rotate rx = new Rotate(0, Rotate.X_AXIS);
  final Rotate ry = new Rotate(0, Rotate.Y_AXIS);
  final Rotate rz = new Rotate(0, Rotate.Z_AXIS);

  Planet() {
   getTransforms().addAll(rx,ry,rz);
   path = PathBuilder
     .create()
     .elements(
       new MoveTo(50, 200),
       ArcToBuilder.create().radiusX(50).radiusY(40)
         .x(400).y(200).build(),
       ArcToBuilder.create().radiusX(50).radiusY(40).x(50)
         .y(200).build()).build();
   path.setStroke(Color.DODGERBLUE);
   rect = new Rectangle(0, 0, 40, 40);
   rect.setArcHeight(10);
   rect.setArcWidth(10);
   rect.setFill(Color.ORANGE);
   
   //sebenarnya tadi mau pasang rectangle, hanya g keliatan efek 3D 
   this.getChildren().addAll(path, createCube());
   
  }
  
  private  Group createCube(){
   cube = new Group();
   cube.getChildren().addAll(
       RectangleBuilder.create() // back face
             .width(size).height(size)
             .fill(color.deriveColor(0.0, 1.0, (1 - 0.5*shade), 1.0))
             .translateX(-0.5*size)
             .translateY(-0.5*size)
             .translateZ(0.5*size)
             .build(),
         RectangleBuilder.create() // bottom face
             .width(size).height(size)
             .fill(color.deriveColor(0.0, 1.0, (1 - 0.4*shade), 1.0))
             .translateX(-0.5*size)
             .translateY(0)
             .rotationAxis(Rotate.X_AXIS)
             .rotate(90)
             .build(),
         RectangleBuilder.create() // right face
             .width(size).height(size)
             .fill(color.deriveColor(0.0, 1.0, (1 - 0.3*shade), 1.0))
             .translateX(-1*size)
             .translateY(-0.5*size)
             .rotationAxis(Rotate.Y_AXIS)
             .rotate(90)
             .build(),
         RectangleBuilder.create() // left face
             .width(size).height(size)
             .fill(color.deriveColor(0.0, 1.0, (1 - 0.2*shade), 1.0))
             .translateX(0)
             .translateY(-0.5*size)
             .rotationAxis(Rotate.Y_AXIS)
             .rotate(90)
             .build(),
         RectangleBuilder.create() // top face
             .width(size).height(size)
             .fill(color.deriveColor(0.0, 1.0, (1 - 0.1*shade), 1.0))
             .translateX(-0.5*size)
             .translateY(-1*size)
             .rotationAxis(Rotate.X_AXIS)
             .rotate(90)
             .build()
             ,
         RectangleBuilder.create() // top face
             .width(size).height(size)
             .fill(color)
             .translateX(-0.5*size)
             .translateY(-0.5*size)
             .translateZ(-0.5*size)
             .build()
             );
   return cube;
  }
  
  public Group getCube(){return cube;}
 
  public Path getPath(){return path;}
  
  public Rectangle getRectangle(){return rect;}
 }
}
Hasil eksekusinya dapat di liat di video berikut (kebetulan saya g tau meng-embed applet di blog):