JavaFX围绕场景固定轴的三维旋转

JavaFX围绕场景固定轴的三维旋转,javafx,3d,rotational-matrices,trackball,Javafx,3d,Rotational Matrices,Trackball,创建虚拟轨迹球 我想使用JavaFX创建一个虚拟轨迹球设备,其中X和Y鼠标拖动事件以直观的方式旋转我的虚拟轨迹球 直观(至少对我来说)意味着,我的场景轴是: X从左向右递增 Y自上而下递增 Z向垂直于屏幕向观众方向增加 我希望垂直鼠标拖动事件使轨迹球在屏幕上滚动 场景X轴和鼠标水平拖动事件以使轨迹球移动 围绕场景Y轴旋转 从OracleJavaFXSmampleApp3D开始,我对场景进行了修改 包括固定轴x:红色、y:绿色、z:蓝色、摄像头和透视摄像头 在轴原点上训练,我的轨迹球(现在是一

创建虚拟轨迹球

我想使用JavaFX创建一个虚拟轨迹球设备,其中X和Y鼠标拖动事件以直观的方式旋转我的虚拟轨迹球

直观(至少对我来说)意味着,我的场景轴是:

  • X从左向右递增
  • Y自上而下递增
  • Z向垂直于屏幕向观众方向增加
我希望垂直鼠标拖动事件使轨迹球在屏幕上滚动 场景X轴和鼠标水平拖动事件以使轨迹球移动 围绕场景Y轴旋转

从OracleJavaFXSmampleApp3D开始,我对场景进行了修改 包括固定轴x:红色、y:绿色、z:蓝色、摄像头和透视摄像头 在轴原点上训练,我的轨迹球(现在是一个立方体,所以我们 可以观察旋转时的行为)

  • 鼠标沿X方向拖动移动,旋转 围绕轨迹球的y轴的轨迹球
  • 鼠标拖动运动 在Y方向上,围绕轨迹旋转轨迹球 轨迹球的x轴
首先,我将轨迹球绕Y轴旋转45度(通过拖动 水平移动鼠标)。然后,如果我垂直拖动鼠标,轨迹球 绕其X轴旋转。但是,轨迹球的X轴现在已被删除 通过上一次旋转旋转45度,我没有得到我想要的行为,即围绕固定的X轴旋转轨迹球(即,在我的场景中出现的固定的红色轴)

此代码基于以下来源的原始代码:

XForm的代码位于

我需要如何更改代码以实现我的目标

package moleculesampleapp;

import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Shape3D;

public class MoleculeSampleApp1 extends Application {

    Group root = new Group();
    Xform axisXForm = new Xform();
    Xform boxXForm = new Xform();
    Xform worldXForm = new Xform();
    Xform cameraXform = new Xform();
    PhongMaterial redMaterial,greenMaterial,blueMaterial;

    PerspectiveCamera camera = new PerspectiveCamera(true);

    private static double CAMERA_INITIAL_DISTANCE = -450;
    private static double CAMERA_INITIAL_X_ANGLE = -10.0;
    private static double CAMERA_INITIAL_Y_ANGLE = 0.0;
    private static double CAMERA_NEAR_CLIP = 0.1;
    private static double CAMERA_FAR_CLIP = 10000.0;
    private static double AXIS_LENGTH = 250.0;
    private static double MOUSE_SPEED = 0.1;
    private static double ROTATION_SPEED = 2.0;

    double mousePosX, mousePosY;
    double mouseOldX, mouseOldY;
    double mouseDeltaX, mouseDeltaY;

    private void handleMouse(Scene scene) {

        scene.setOnMousePressed(me -> {
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            mouseOldX = me.getSceneX();
            mouseOldY = me.getSceneY();
        });

        scene.setOnMouseDragged(me -> {
            mouseOldX = mousePosX;
            mouseOldY = mousePosY;
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            mouseDeltaX = (mousePosX - mouseOldX);
            mouseDeltaY = (mousePosY - mouseOldY);

            if (me.isPrimaryButtonDown()) {
                boxXForm.ry.setAngle(boxXForm.ry.getAngle() - mouseDeltaX * MOUSE_SPEED * ROTATION_SPEED); // left right
                boxXForm.rx.setAngle(boxXForm.rx.getAngle() + mouseDeltaY * MOUSE_SPEED * ROTATION_SPEED); // up down
            }
        });
    }

    private void handleKeyboard(Scene scene) {
        scene.setOnKeyPressed(event -> {
            switch (event.getCode()) {
            case Z:
                camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
                cameraXform.ry.setAngle(CAMERA_INITIAL_Y_ANGLE);
                cameraXform.rx.setAngle(CAMERA_INITIAL_X_ANGLE);
                boxXForm.reset();
                break;
            }
        });
    }

    PhongMaterial createMaterial(Color diffuseColor, Color specularColor) {
        PhongMaterial material =  new PhongMaterial(diffuseColor);
        material.setSpecularColor(specularColor);
        return material;
    }

    @Override
    public void start(Stage primaryStage) {
        root.getChildren().add(worldXForm);
        root.setDepthTest(DepthTest.ENABLE);

        // Create materials
        redMaterial = createMaterial(Color.DARKRED,Color.RED);
        greenMaterial = createMaterial(Color.DARKGREEN,Color.GREEN);
        blueMaterial = createMaterial(Color.DARKBLUE,Color.BLUE);

        // Build Camera
        root.getChildren().add(camera);
        cameraXform.getChildren().add(camera);
        camera.setNearClip(CAMERA_NEAR_CLIP);
        camera.setFarClip(CAMERA_FAR_CLIP);
        camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
        cameraXform.ry.setAngle(CAMERA_INITIAL_Y_ANGLE);
        cameraXform.rx.setAngle(CAMERA_INITIAL_X_ANGLE);

        // Build Axes
        Box xAxis = new Box(AXIS_LENGTH, 1, 1);
        Box yAxis = new Box(1, AXIS_LENGTH, 1);
        Box zAxis = new Box(1, 1, AXIS_LENGTH);
        xAxis.setMaterial(redMaterial);
        yAxis.setMaterial(greenMaterial);
        zAxis.setMaterial(blueMaterial);
        axisXForm.getChildren().addAll(xAxis, yAxis, zAxis);
        worldXForm.getChildren().addAll(axisXForm);

        // Build shiney red box
        Shape3D box = new Box(80, 80, 80);
        box.setMaterial(redMaterial);
        boxXForm.getChildren().add(box);
        worldXForm.getChildren().addAll(boxXForm);

        Scene scene = new Scene(root, 1024, 768, true);
        scene.setFill(Color.GREY);
        handleKeyboard(scene);
        handleMouse(scene);

        primaryStage.setTitle("Molecule Sample Application");
        primaryStage.setScene(scene);
        primaryStage.show();

        scene.setCamera(camera);
    }

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

}

如果我正确理解你的问题,你唯一要做的就是替换这一行

Xform cameraXform = new Xform(RotateOrder.ZYX);

这改变了单次旋转的旋转顺序,应该可以满足您的需要。

感谢bronkowitz在这篇文章中:为我提供了这个解决方案

package moleculesampleapp;

import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.Shape3D;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Rotate;

public class MoleculeSampleApp1 extends Application {

    Group root = new Group();
    XformBox cameraXform = new XformBox();
    XformBox ballXForm = new XformBox();
    Shape3D ball;
    PhongMaterial redMaterial, greenMaterial, blueMaterial;

    PerspectiveCamera camera = new PerspectiveCamera(true);

    private static double CAMERA_INITIAL_DISTANCE = -450;
    private static double CAMERA_INITIAL_X_ANGLE = -10.0;
    private static double CAMERA_INITIAL_Y_ANGLE = 0.0;
    private static double CAMERA_NEAR_CLIP = 0.1;
    private static double CAMERA_FAR_CLIP = 10000.0;
    private static double AXIS_LENGTH = 250.0;
    private static double MOUSE_SPEED = 0.1;
    private static double ROTATION_SPEED = 2.0;

    double mouseStartPosX, mouseStartPosY;
    double mousePosX, mousePosY;
    double mouseOldX, mouseOldY;
    double mouseDeltaX, mouseDeltaY;

    private void handleMouse(Scene scene) {
        System.out.printf("handleMouse%n");

        scene.setOnMousePressed(me -> {
            mouseStartPosX = me.getSceneX();
            mouseStartPosY = me.getSceneY();
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            mouseOldX = me.getSceneX();
            mouseOldY = me.getSceneY();
        });

        scene.setOnMouseDragged(me -> {
            mouseOldX = mousePosX;
            mouseOldY = mousePosY;
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            mouseDeltaX = (mousePosX - mouseOldX);
            mouseDeltaY = (mousePosY - mouseOldY);

            if (me.isPrimaryButtonDown()) {
                ballXForm.addRotation(-mouseDeltaX * MOUSE_SPEED * ROTATION_SPEED, Rotate.Y_AXIS);
                ballXForm.addRotation(mouseDeltaY * MOUSE_SPEED * ROTATION_SPEED, Rotate.X_AXIS);
            }
        });
    }

    private void handleKeyboard(Scene scene) {
        scene.setOnKeyPressed(event -> ballXForm.reset());
    }

    PhongMaterial createMaterial(Color diffuseColor, Color specularColor) {
        PhongMaterial material = new PhongMaterial(diffuseColor);
        material.setSpecularColor(specularColor);
        return material;
    }

    @Override
    public void start(Stage primaryStage) {
        System.out.printf("start%n");
        root.setDepthTest(DepthTest.ENABLE);

        // Create materials
        redMaterial = createMaterial(Color.DARKRED, Color.RED);
        greenMaterial = createMaterial(Color.DARKGREEN, Color.GREEN);
        blueMaterial = createMaterial(Color.DARKBLUE, Color.BLUE);

        // Build Camera
        root.getChildren().add(camera);
        cameraXform.getChildren().add(camera);
        camera.setNearClip(CAMERA_NEAR_CLIP);
        camera.setFarClip(CAMERA_FAR_CLIP);
        camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
        camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
        cameraXform.addRotation(CAMERA_INITIAL_X_ANGLE, Rotate.X_AXIS);
        cameraXform.addRotation(CAMERA_INITIAL_Y_ANGLE, Rotate.Y_AXIS);

        // Build Axes
        Box xAxis = new Box(AXIS_LENGTH, 1, 1);
        Box yAxis = new Box(1, AXIS_LENGTH, 1);
        Box zAxis = new Box(1, 1, AXIS_LENGTH);
        xAxis.setMaterial(redMaterial);
        yAxis.setMaterial(greenMaterial);
        zAxis.setMaterial(blueMaterial);
        root.getChildren().addAll(xAxis, yAxis, zAxis);

        // Build shiney red ball
        ball = new Sphere(50);
        ball.setDrawMode(DrawMode.LINE); // draw mesh so we can watch how it rotates
        ballXForm.getChildren().add(ball);
        root.getChildren().addAll(ballXForm);

        Scene scene = new Scene(root, 1024, 768, true);
        scene.setFill(Color.GREY);
        handleKeyboard(scene);
        handleMouse(scene);

        primaryStage.setTitle("TrackBall");
        primaryStage.setScene(scene);
        primaryStage.show();

        scene.setCamera(camera);
    }

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

}

class XformBox extends Group {

    XformBox() {
        super();
        getTransforms().add(new Affine());
    }

    /**
     * Accumulate rotation about specified axis
     *
     * @param angle
     * @param axis
     */
    public void addRotation(double angle, Point3D axis) {
        Rotate r = new Rotate(angle, axis);
        /**
         * This is the important bit and thanks to bronkowitz in this post
         * https://stackoverflow.com/questions/31382634/javafx-3d-rotations for
         * getting me to the solution that the rotations need accumulated in
         * this way
         */
        getTransforms().set(0, r.createConcatenation(getTransforms().get(0)));
    }

    /**
     * Reset transform to identity transform
     */
    public void reset() {
        getTransforms().set(0, new Affine());
    }
}

为什么你想让你的生活如此艰难,而不使用右手坐标系?请看,我使用的是JavaFX默认坐标系。但无论如何,这并不重要。问题是如何相对于固定的场景坐标系旋转对象,而不是相对于对象自己的坐标系旋转对象。这是不正确的。在JavaFX中,默认的Z轴离开查看器(进入屏幕)。@mipa抱歉,是的,在JavaFX中,Z轴离开屏幕。然而,这与所提出的问题没有区别。从代码中可以看到,我使用默认的轴方向。我想学习的是如何操纵球体,使其始终以这样的方式运行:鼠标在屏幕垂直轴上拖动,使球体绕场景X轴旋转,鼠标在屏幕水平轴上拖动,使球体绕场景垂直轴旋转。你能帮我解决这个问题吗?唉,不行。我希望相机能保持固定。我的鼠标处理代码只需要旋转球体(我希望场景中还有其他3D对象保持固定)。