2017-12-14 143 views
6

Đó là Hanukkah và tôi đang cố gắng để animate một con quay (dreidel):Làm thế nào để tạo hoạt ảnh cho một đầu kéo sợi?

spinning top

tôi có thể lấy nó để quay trên trục của nó. Đây là mã của tôi:

import static javafx.scene.paint.Color.*; 

import javafx.animation.KeyFrame; 
import javafx.animation.KeyValue; 
import javafx.animation.Timeline; 
import javafx.application.Application; 
import javafx.beans.property.DoubleProperty; 
import javafx.beans.property.SimpleDoubleProperty; 
import javafx.geometry.Point3D; 
import javafx.scene.Camera; 
import javafx.scene.Group; 
import javafx.scene.PerspectiveCamera; 
import javafx.scene.Scene; 
import javafx.scene.SceneAntialiasing; 
import javafx.scene.paint.PhongMaterial; 
import javafx.scene.shape.Box; 
import javafx.scene.shape.Cylinder; 
import javafx.scene.shape.Sphere; 
import javafx.scene.transform.Rotate; 
import javafx.scene.transform.Translate; 
import javafx.stage.Stage; 
import javafx.util.Duration; 

public class DreidelAnim extends Application { 

    private double bodyBase = 30; 
    private double bodyHeight = bodyBase * 3/2; 
    private double baseRadius = bodyBase/2; 

    @Override 
    public void start(Stage stage) throws Exception { 
     DoubleProperty spinAngle = new SimpleDoubleProperty(); 
     Rotate spin = new Rotate(0, Rotate.Z_AXIS); 
     spin.angleProperty().bind(spinAngle); 

     Timeline spinAnim = new Timeline(new KeyFrame(Duration.seconds(2), new KeyValue(spinAngle, 360))); 
     spinAnim.setCycleCount(Timeline.INDEFINITE); 
     spinAnim.play(); 

     Group dreidel = createDreidel(); 
     Translate zTrans = new Translate(0, 0, -(bodyHeight/2 + baseRadius)); 
     dreidel.getTransforms().addAll(spin, zTrans); 

     Scene scene = new Scene(dreidel, 200, 200, true, SceneAntialiasing.BALANCED); 
     scene.setFill(SKYBLUE); 
     scene.setCamera(createCamera()); 

     stage.setScene(scene); 
     stage.show(); 
    } 

    private Group createDreidel() { 
     double handleHeight = bodyBase * 3/4; 
     Cylinder handle = new Cylinder(bodyBase/6, handleHeight); 
     handle.setTranslateZ(-(bodyHeight + handleHeight)/2); 
     handle.setRotationAxis(Rotate.X_AXIS); 
     handle.setRotate(90); 
     handle.setMaterial(new PhongMaterial(RED)); 

     Box body = new Box(bodyBase, bodyBase, bodyHeight); 
     body.setMaterial(new PhongMaterial(BLUE)); 

     Sphere base = new Sphere(baseRadius); 
     base.setTranslateZ(bodyHeight/2); 
     base.setMaterial(new PhongMaterial(GREEN)); 

     return new Group(handle, body, base); 
    } 

    private Camera createCamera() { 
     PerspectiveCamera camera = new PerspectiveCamera(true); 
     camera.setFarClip(1000); 

     int xy = 150; 
     Translate trans = new Translate(-xy, xy, -120); 
     Rotate rotXY = new Rotate(70, new Point3D(1, 1, 0)); 
     Rotate rotZ = new Rotate(45, new Point3D(0, 0, 1)); 
     camera.getTransforms().addAll(trans, rotXY, rotZ); 

     return camera; 
    } 

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

Tôi tạo ra một mô hình đơn giản, quay nó xung quanh trục của nó, và dịch nó để đỉnh của nó là trên (0, 0, 0). Dưới đây là kết quả:

enter image description here

Làm thế nào tôi có thể đạt được một cái gì đó tương tự như hình ảnh trên đỉnh nơi nó cũng quay quanh một trục xoay?

Trả lời

8

Xoay trục xung quanh đối tượng quay được gọi là Precession. Chuyển động quay tròn yêu cầu 2 phép quay:

  1. Xoay đối tượng quanh trục bên trong của nó (song song với tay cầm màu đỏ).
  2. Xoay một trong các trục bên trong xung quanh trục tĩnh (z trong trường hợp này).

Trên khuôn mặt của nó, bạn cần 2 Animation trường hợp. Tuy nhiên, cả hai phép quay đều giống nhau. Điểm pivot cho cả hai là (0, 0, 0) (sau zTrans) và cả hai đều nằm xung quanh trục z, chỉ một trong số chúng được nghiêng ở một góc.

Đây là mã chỉnh sửa:

import static javafx.scene.paint.Color.*; 

import javafx.animation.KeyFrame; 
import javafx.animation.KeyValue; 
import javafx.animation.Timeline; 
import javafx.application.Application; 
import javafx.beans.property.DoubleProperty; 
import javafx.beans.property.SimpleDoubleProperty; 
import javafx.geometry.Point3D; 
import javafx.scene.Camera; 
import javafx.scene.Group; 
import javafx.scene.PerspectiveCamera; 
import javafx.scene.Scene; 
import javafx.scene.SceneAntialiasing; 
import javafx.scene.paint.PhongMaterial; 
import javafx.scene.shape.Box; 
import javafx.scene.shape.Cylinder; 
import javafx.scene.shape.Sphere; 
import javafx.scene.transform.Rotate; 
import javafx.scene.transform.Translate; 
import javafx.stage.Stage; 
import javafx.util.Duration; 

public class FinalDreidelSpin extends Application { 

    private double bodyBase = 30; 
    private double bodyHeight = bodyBase * 3/2; 
    private double baseRadius = bodyBase/2; 

    @Override 
    public void start(Stage stage) throws Exception { 
     double tiltAngle = 40; 
     DoubleProperty spinAngle = new SimpleDoubleProperty(); 

     Rotate spin = new Rotate(0, Rotate.Z_AXIS); 
     Rotate tilt = new Rotate(tiltAngle, Rotate.X_AXIS); 

     spin.angleProperty().bind(spinAngle); 

     Timeline spinAnim = new Timeline(); 
     spinAnim.getKeyFrames().add(new KeyFrame(Duration.seconds(2), new KeyValue(spinAngle, 360))); 
     spinAnim.setCycleCount(Timeline.INDEFINITE); 
     spinAnim.play(); 

     Group dreidel = createDreidel(); 
     Translate zTrans = new Translate(0, 0, -(bodyHeight/2 + baseRadius)); 
     dreidel.getTransforms().addAll(spin, tilt, spin, zTrans); 

     Scene scene = new Scene(new Group(dreidel, createAxes()), 200, 200, true, SceneAntialiasing.BALANCED); 
     scene.setFill(SKYBLUE); 
     scene.setCamera(createCamera()); 

     stage.setScene(scene); 
     stage.show(); 
    } 

    private Group createDreidel() { 
     double handleHeight = bodyBase * 3/4; 
     Cylinder handle = new Cylinder(bodyBase/6, handleHeight); 
     handle.setTranslateZ(-(bodyHeight + handleHeight)/2); 
     handle.setRotationAxis(Rotate.X_AXIS); 
     handle.setRotate(90); 
     handle.setMaterial(new PhongMaterial(RED)); 

     Box body = new Box(bodyBase, bodyBase, bodyHeight); 
     body.setMaterial(new PhongMaterial(BLUE)); 

     Sphere base = new Sphere(baseRadius); 
     base.setTranslateZ(bodyHeight/2); 
     base.setMaterial(new PhongMaterial(GREEN)); 

     return new Group(handle, body, base); 
    } 

    private Camera createCamera() { 
     PerspectiveCamera camera = new PerspectiveCamera(true); 
     camera.setFarClip(1000); 

     int xy = 150; 
     Translate trans = new Translate(-xy, xy, -100); 
     Rotate rotXY = new Rotate(70, new Point3D(1, 1, 0)); 
     Rotate rotZ = new Rotate(45, new Point3D(0, 0, 1)); 
     camera.getTransforms().addAll(trans, rotXY, rotZ); 

     return camera; 
    } 

    private Group createAxes() { 
     int axisWidth = 1; 
     int axisLength = 400; 

     Cylinder xAxis = new Cylinder(axisWidth, axisLength); 
     xAxis.setMaterial(new PhongMaterial(CYAN)); 

     Cylinder yAxis = new Cylinder(axisWidth, axisLength); 
     yAxis.setRotationAxis(Rotate.Z_AXIS); 
     yAxis.setRotate(90); 
     yAxis.setMaterial(new PhongMaterial(MAGENTA)); 

     Cylinder zAxis = new Cylinder(axisWidth, axisLength); 
     zAxis.setRotationAxis(Rotate.X_AXIS); 
     zAxis.setRotate(90); 
     zAxis.setMaterial(new PhongMaterial(YELLOW)); 

     return new Group(xAxis, yAxis, zAxis); 
    } 

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

Nơi tôi thêm trục cơ quan đại diện để xem tiện lợi. Lưu ý rằng danh sách getTransforms() không yêu cầu đối tượng của nó là duy nhất (không giống như getChildren()), cho phép chúng tôi sử dụng lại cùng một hoạt ảnh. Thứ tự của các hình động cũng quan trọng như được ghi chú bên dưới.

Độ nghiêng là vòng xoay đơn giản xung quanh trục x hoặc y.
Nếu chúng ta tilt và sau đó spin, getTransforms().addAll(tilt, spin, zTrans), chúng tôi sẽ nhận được luân chuyển nội bộ (liệt kê 1 ở trên), chỉ nghiêng:

spin

Nếu chúng ta spin và sau đó tilt, getTransforms().addAll(spin, tilt, zTrans), chúng tôi sẽ nhận được tuế sai (liệt kê 2 ở trên):

precession

kết hợp 2 như trong mã hoàn chỉnh sẽ cho kết quả mong muốn:

full

3

Đây là một câu trả lời có thể, rất nhiều trụ sở tại tiếp cận @ user1803551, nhưng sử dụng một lưới 3D có thể sử dụng một hình ảnh kết cấu, và một thời gian tuế sai khác nhau.

Đây là cách nó trông giống như:

dreidel

Để áp dụng một kết cấu, tôi sẽ sử dụng khái niệm net cho cơ thể của dreidel, và hình ảnh này:

dựa trên số image này.

Cuối cùng tôi sẽ thêm một hình trụ thông thường cho tay cầm.

Tôi sẽ không đi sâu vào chi tiết cách tạo TriangleMesh cho phần thân, nhưng chúng tôi xác định 9 đỉnh (tọa độ 3D), 16 tọa độ kết cấu (2D) và 14 mặt tam giác bao gồm các chỉ số đỉnh và chỉ mục kết cấu . Khối lập phương được xác định bởi cạnh của nó là width và kim tự tháp theo số height. Kích thước thực là L = 4 * width, H = 2 * width + height. Ví dụ, mặt 0 có đỉnh 0 - 2 - 1, và các chỉ số kết cấu 8 - 3 - 7, trong đó đỉnh 0 có tọa độ {width/2, width/2, width/2} và chỉ số kết cấu 8 có tọa độ {width, 2 * width}, được chuẩn hóa trong khoảng từ [0, 1] : {width/L, 2 * width/H}.

Trong trường hợp này, và vì lợi ích của các mẫu, các giá trị được mã hóa:

float width = 375f; 
float height = 351f; 

Đây là lớp hình dạng 3D:

class DreidelMesh extends Group { 

    float width = 375f; 
    float height = 351f; 

    public DreidelMesh(){ 
     MeshView bodyMesh = new MeshView(createBodyMesh()); 
     PhongMaterial material = new PhongMaterial(); 
     material.setDiffuseMap(new Image(getClass().getResourceAsStream("3dreidel3d.png"))); 
     bodyMesh.setMaterial(material); 

     Cylinder handle = new Cylinder(45, 260); 
     handle.setTranslateY(-(handle.getHeight() + width)/2); 
     material = new PhongMaterial(Color.web("#daaf6d")); 
     handle.setMaterial(material); 

     getTransforms().add(new Rotate(90, Rotate.X_AXIS)); 
     getChildren().addAll(bodyMesh, handle); 
    } 

    private TriangleMesh createBodyMesh() { 
     TriangleMesh m = new TriangleMesh(); 

     float L = 4f * width; 
     float H = 2f * width + height; 
     float w2 = width/2f; 

     // POINTS 
     m.getPoints().addAll(
      w2, w2, w2, 
      w2, w2, -w2, 
      w2, -w2, w2, 
      w2, -w2, -w2, 
      -w2, w2, w2, 
      -w2, w2, -w2, 
      -w2, -w2, w2, 
      -w2, -w2, -w2, 
      0f, w2 + height, 0f 
     ); 

     // TEXTURES 
     m.getTexCoords().addAll(
      width/L, 0f, 
      2f * width/ L, 0f, 
      0f, width/H, 
      width/L, width/H, 
      2f * width/ L, width/H, 
      3f * width/ L, width/H, 
      1f, width/H, 
      0f, 2f * width/H, 
      width/L, 2f * width/H, 
      2f * width/ L, 2f * width/H, 
      3f * width/ L, 2f * width/H, 
      1f, 2f * width/H, 
      width/2f/L, 1f, 
      3f * width/2f/L, 1f, 
      5f * width/2f/L, 1f, 
      7f * width/2f/L, 1f 
     ); 

     // FACES 
     m.getFaces().addAll(
      0, 8, 2, 3, 1, 7,   
      2, 3, 3, 2, 1, 7,   
      4, 9, 5, 10, 6, 4,   
      6, 4, 5, 10, 7, 5,   
      0, 8, 1, 7, 8, 12,   
      4, 9, 0, 8, 8, 13,   
      5, 10, 4, 9, 8, 14,   
      1, 11, 5, 10, 8, 15,    
      2, 3, 6, 4, 3, 0,    
      3, 0, 6, 4, 7, 1,    
      0, 8, 4, 9, 2, 3,   
      2, 3, 4, 9, 6, 4,   
      1, 11, 3, 6, 5, 10,   
      5, 10, 3, 6, 7, 5 
     ); 
     return m; 
    } 
} 

dreidel

Cuối cùng, hình dạng này sẽ được thêm vào hiện trường, và tôi sẽ cung cấp hai hình ảnh động (thay vì một), một cho việc quay tròn, và một chậm cho tuế sai:

@Override 
public void start(Stage stage) { 
    double tiltAngle = 15; 
    DoubleProperty spinAngle = new SimpleDoubleProperty(); 
    DoubleProperty precessionAngle = new SimpleDoubleProperty(); 

    Rotate spin = new Rotate(0, Rotate.Z_AXIS); 
    Rotate precession = new Rotate(0, Rotate.Z_AXIS); 
    Rotate tilt = new Rotate(tiltAngle, Rotate.X_AXIS); 

    spin.angleProperty().bind(spinAngle); 
    precession.angleProperty().bind(precessionAngle); 

    Timeline spinAnim = new Timeline(); 
    spinAnim.getKeyFrames().add(new KeyFrame(Duration.seconds(1.5), new KeyValue(spinAngle, 360))); 
    spinAnim.setCycleCount(Timeline.INDEFINITE); 
    spinAnim.play(); 

    Timeline precessionAnim = new Timeline(); 
    precessionAnim.getKeyFrames().add(new KeyFrame(Duration.seconds(4), new KeyValue(precessionAngle, 360))); 
    precessionAnim.setCycleCount(Timeline.INDEFINITE); 
    precessionAnim.play(); 

    Group dreidel = new Group(new DreidelMesh()); 
    Translate zTrans = new Translate(0, 0, - dreidel.getBoundsInLocal().getMaxZ()); 
    dreidel.getTransforms().addAll(precession, tilt, spin, zTrans); 

    Scene scene = new Scene(new Group(dreidel), 300, 300, true, SceneAntialiasing.BALANCED); 
    scene.setFill(SKYBLUE); 
    scene.setCamera(createCamera()); 

    stage.setScene(scene); 
    stage.setTitle("JavaFX 3D - Dreidel"); 
    stage.show(); 
} 

Chạy ứng dụng sẽ hiển thị các hình ảnh động hiển thị ở trên.

+0

Vâng, tôi nghĩ bạn đã trả lời câu hỏi mở rộng "Làm thế nào để sinh động một đầu * quay đẹp *?" :) Mục đích của tôi là tập trung vào toán/hoạt ảnh. Kết quả cuối cùng rất đẹp với DIY! – user1803551

+0

Nhân tiện, gif động của bạn dừng lại sau 1 chu kỳ và hình ảnh biến mất. Có thể là một vấn đề về phía tôi. – user1803551

+0

Nó hoạt động tốt ở đây (Mac/Safari và Windows/Firefox). –

Các vấn đề liên quan