javafx - 如何相对于节点旋转轴而不是场景旋转轴将偏航,俯仰和滚动增量(不是欧拉)应用于节点? [英] javafx - How to apply yaw, pitch and roll deltas (not euler) to a node in respect to the nodes rotation axes instead of the scene rotation axes?

查看:29
本文介绍了javafx - 如何相对于节点旋转轴而不是场景旋转轴将偏航,俯仰和滚动增量(不是欧拉)应用于节点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请耐心等待我的冗长问题,我正在努力使其尽可能清楚.(在另一个问题中发现.)

在下面的示例中,所有旋转按钮都是对来自陀螺仪传感器的陀螺仪值的测试替换.传感器固定在现实世界的躯干上,因此按钮旨在表示要应用到虚拟躯干相对于躯干坐标系而非场景坐标系的旋转增量.

如果从零"旋转开始,所有按钮都可以自行正常工作.但是当我按 3 次偏航,然后滚动时,我看到滚动旋转在场景轴上起作用.但我想将其应用于当前的躯干旋转.

我已经从这里尝试了一些相关问题的建议,但没有找到解决方案.

附注:我不确定偏航、俯仰和滚动这些术语是否通常与欧拉角绑定,所以我想强调一下,据我了解,陀螺传感器的值不是欧拉角,因为它们代表旋转相对于当前躯干旋转的增量,而不是与躯干起点绝对"的累积角度.因此,如果我不恰当地使用了这些术语,请尽量理解我的意思.

(背景信息:我有一个机器人项目 roboshock.de,其陀螺仪传感器连接到机器人躯干,我想在屏幕上可视化机器人的旋转.下面示例中的旋转按钮只是一个测试替换来自传感器的陀螺仪值.)

非常感谢任何帮助.

import javafx.application.Application;导入 javafx.event.ActionEvent;导入 javafx.event.EventHandler;导入javafx.scene.Group;导入 javafx.scene.Parent;导入javafx.scene.Scene;导入 javafx.scene.control.Button;导入 javafx.scene.layout.HBox;导入 javafx.scene.layout.StackPane;导入 javafx.scene.paint.Color;导入 javafx.scene.paint.PhongMaterial;导入javafx.scene.shape.Box;导入javafx.scene.transform.Rotate;导入javafx.stage.Stage;公共类 PuppetTestApp 扩展应用程序 {整数宽度 = 800;整数高度 = 500;XGroup 躯干组;双躯干X = 50;双躯干 = 80;公共父 createRobot() {盒子躯干 = 新盒子(torsoX,躯干,20);torso.setMaterial(新的 PhongMaterial(Color.RED));盒子头 = 新盒子(20, 20, 20);head.setMaterial(new PhongMaterial(Color.YELLOW.darker()));head.setTranslateY(-torsoY/2 -10);躯干组 = 新 XGroup();torsoGroup.getChildren().addAll(躯干,头);返回躯干组;}公共父 createUI() {HBox buttonBox = new HBox();按钮 b;buttonBox.getChildren().add(b = new Button("Exit"));b.setOnAction((ActionEvent arg0) -> { System.exit(0); } );buttonBox.getChildren().add(b = new Button("pitch up"));b.setOnAction(new TurnAction(torsoGroup.rx, -15) );buttonBox.getChildren().add(b = new Button("pitch down"));b.setOnAction(new TurnAction(torsoGroup.rx, 15) );buttonBox.getChildren().add(b = new Button("Yaw left"));b.setOnAction(new TurnAction(torsoGroup.ry, -15) );buttonBox.getChildren().add(b = new Button("Yaw right"));b.setOnAction(new TurnAction(torsoGroup.ry, 15) );buttonBox.getChildren().add(b = new Button("向右滚动"));b.setOnAction(new TurnAction(torsoGroup.rz, -15) );buttonBox.getChildren().add(b = new Button("向左滚动"));b.setOnAction(new TurnAction(torsoGroup.rz, 15) );返回按钮框;}类 TurnAction 实现 EventHandler<ActionEvent>{最终旋转旋转;双三角角;公共TurnAction(旋转旋转,双targetAngle){this.rotate = 旋转;this.deltaAngle = targetAngle;}@覆盖公共无效句柄(ActionEvent arg0){addRotate(torsoGroup, 旋转, deltaAngle);}}私人无效addRotate(XGroup节点,旋转旋转,双角){//这里我做错了//不工作 1://变换 newRotate = new Rotate(angle, rotate.getAxis());//node.getTransforms().add(newRotate);//不工作 2:双 x = 旋转.getAngle();旋转.setAngle(x + 角度);}公共类 XGroup 扩展组 {公共旋转 rx = new Rotate(0, Rotate.X_AXIS);公共旋转 ry = new Rotate(0, Rotate.Y_AXIS);公共旋转 rz = new Rotate(0, Rotate.Z_AXIS);公共 XGroup() {极好的();getTransforms().addAll(rz, ry, rx);}公共无效setRotate(双x,双y,双z){rx.setAngle(x);ry.setAngle(y);rz.setAngle(z);}public void setRotateX(double x) { rx.setAngle(x);}公共无效 setRotateY(双 y) { ry.setAngle(y);}public void setRotateZ(double z) { rz.setAngle(z);}}@覆盖公共无效开始(阶段阶段)抛出异常{父机器人 = createRobot();父 ui = createUI();StackPane 组合 = 新 StackPane();combine.getChildren().addAll(ui, 机器人);combine.setStyle("-fx-background-color: 线性渐变(到底部,玉米丝,午夜蓝);");场景场景=新场景(组合,宽度,高度);stage.setScene(场景);舞台.show();}公共静态无效主要(字符串[]参数){启动(参数);}}

解决方案

对于初学者来说,JavaFX 3D 应用程序需要考虑以下几点:

  • 深度缓冲和抗锯齿
  • 子场景
  • 相机

而你没有这些.

您需要启用深度缓冲区,因为您可以看到小黄色框似乎在大框的顶部(浅黄色的脸根本不应该可见):

根据Scene的JavaDoc:

<块引用>

包含 3D 形状或具有 3D 变换的 2D 形状的场景可以使用深度缓冲区支持进行正确的深度排序渲染

变化:

Scene 场景 = new Scene(combined, width, height);

到:

Scene scene = new Scene(combined, width, height, true, SceneAntialiasing.BALANCED);

一旦你这样做了,你就会意识到当框转到 z > 0 时不再可见,并且顶部的按钮也不再可点击.

在同一场景中混合 2D 和 3D 并不是一个好主意.为此,您需要一个 SubScene,您可以在其中布置 3D 内容,并将 2D 留在场景本身中.此外,您可以将深度缓冲区和抗锯齿选项移至子场景:

父机器人 = createRobot();//添加子场景SubScene subScene = new SubScene(robot, width, height, true, SceneAntialiasing.BALANCED);父 ui = createUI();StackPane 组合 = 新 StackPane();combine.getChildren().addAll(ui, subScene);场景场景=新场景(组合,宽度,高度);

现在问题出在布局上,方框会显示在左上角,而不是中心.

您可以将翻译添加到您的组:

public XGroup() {极好的();getTransforms().addAll(new Translate(width/2, height/2, 0), rz, ry, rx);}

现在您可以看到正确的深度排序渲染,并且可以访问 ui 按钮.

另一种选择是添加相机.您可以删除翻译转换,也可以缩放"以查看更大的框:

父机器人 = createRobot();SubScene subScene = new SubScene(robot, width, height, true, SceneAntialiasing.BALANCED);PerspectiveCamera 相机 = 新 PerspectiveCamera(true);camera.setNearClip(0.01);camera.setFarClip(100000);camera.setTranslateZ(-400);subScene.setCamera(相机);

轮换

现在,就旋转而言,如果您想在框的当前状态上应用给定的旋转,并且与场景"轴无关(我认为您的意思是三个正交的非旋转轴),您有在应用新的轮换之前考虑之前的状态.

在此

仿射[0.70710678, 0.50000000, 0.50000000, 0.00.00000000, 0.70710678, -0.70710678, 0.0-0.70710678, 0.50000000, 0.50000000, 0.0]

即 x' 轴(蓝色)是 {0.7071, 0.0, -0.7071}.

所以最后你可以将局部轴上的旋转定义为:

private void addRotate(XGroup node, Rotate rotate, double angle) {仿射仿射 = node.getTransforms().isEmpty() ?新仿射():新仿射(node.getTransforms().get(0));双 A11 = 仿射.getMxx(), A12 = 仿射.getMxy(), A13 = 仿射.getMxz();双 A21 = affine.getMyx(), A22 = affine.getMyy(), A23 = affine.getMyz();双 A31 = 仿射.getMzx(), A32 = 仿射.getMzy(), A33 = 仿射.getMzz();//在局部轴上旋转旋转 newRotateX = new Rotate(angle, new Point3D(A11, A21, A31));旋转 newRotateY = new Rotate(angle, new Point3D(A12, A22, A32));旋转 newRotateZ = new Rotate(angle, new Point3D(A13, A23, A33));//应用旋转affine.prepend(rotate.getAxis() == Rotate.X_AXIS ? newRotateX :rotate.getAxis() == Rotate.Y_AXIS ?newRotateY : newRotateZ);node.getTransforms().setAll(仿射);}

我相信这会给你想要的东西.

这是整个修改后的代码:

private final int width = 800;私人最终 int 高度 = 500;私人 XGroup 躯干组;私人最终双躯干X = 50;私人最终双躯干= 80;公共父 createRobot() {盒子躯干 = 新盒子(torsoX,躯干,20);torso.setMaterial(新的 PhongMaterial(Color.RED));盒子头 = 新盒子(20, 20, 20);head.setMaterial(new PhongMaterial(Color.YELLOW.darker()));head.setTranslateY(-torsoY/2 -10);盒子 x = 新盒子 (200, 2, 2);x.setMaterial(new PhongMaterial(Color.BLUE));盒子 y = 新盒子(2, 200, 2);y.setMaterial(new PhongMaterial(Color.BLUEVIOLET));框 z = 新框 (2, 2, 200);z.setMaterial(new PhongMaterial(Color.BURLYWOOD));躯干组 = 新 XGroup();torsoGroup.getChildren().addAll(躯干, 头, x, y, z);返回躯干组;}公共父 createUI() {HBox buttonBox = new HBox();按钮 b;buttonBox.getChildren().add(b = new Button("Exit"));b.setOnAction( (ActionEvent arg0) -> { Platform.exit(); } );buttonBox.getChildren().add(b = new Button("pitch up"));b.setOnAction(new TurnAction(torsoGroup.rx, -15) );buttonBox.getChildren().add(b = new Button("pitch down"));b.setOnAction(new TurnAction(torsoGroup.rx, 15) );buttonBox.getChildren().add(b = new Button("Yaw left"));b.setOnAction(new TurnAction(torsoGroup.ry, -15) );buttonBox.getChildren().add(b = new Button("Yaw right"));b.setOnAction(new TurnAction(torsoGroup.ry, 15) );buttonBox.getChildren().add(b = new Button("向右滚动"));b.setOnAction(new TurnAction(torsoGroup.rz, -15) );buttonBox.getChildren().add(b = new Button("向左滚动"));b.setOnAction(new TurnAction(torsoGroup.rz, 15) );返回按钮框;}类 TurnAction 实现 EventHandler<ActionEvent>{最终旋转旋转;双三角角;公共TurnAction(旋转旋转,双targetAngle){this.rotate = 旋转;this.deltaAngle = targetAngle;}@覆盖公共无效句柄(ActionEvent arg0){addRotate(torsoGroup, 旋转, deltaAngle);}}私人无效addRotate(XGroup节点,旋转旋转,双角){仿射仿射 = node.getTransforms().isEmpty() ?新仿射():新仿射(node.getTransforms().get(0));双 A11 = 仿射.getMxx(), A12 = 仿射.getMxy(), A13 = 仿射.getMxz();双 A21 = affine.getMyx(), A22 = affine.getMyy(), A23 = affine.getMyz();双 A31 = 仿射.getMzx(), A32 = 仿射.getMzy(), A33 = 仿射.getMzz();旋转 newRotateX = new Rotate(angle, new Point3D(A11, A21, A31));旋转 newRotateY = new Rotate(angle, new Point3D(A12, A22, A32));旋转 newRotateZ = new Rotate(angle, new Point3D(A13, A23, A33));affine.prepend(rotate.getAxis() == Rotate.X_AXIS ? newRotateX :rotate.getAxis() == Rotate.Y_AXIS ?newRotateY : newRotateZ);node.getTransforms().setAll(仿射);}公共类 XGroup 扩展组 {公共旋转 rx = new Rotate(0, Rotate.X_AXIS);公共旋转 ry = new Rotate(0, Rotate.Y_AXIS);公共旋转 rz = new Rotate(0, Rotate.Z_AXIS);}@覆盖公共无效开始(阶段阶段)抛出异常{父机器人 = createRobot();SubScene subScene = new SubScene(robot, width, height, true, SceneAntialiasing.BALANCED);PerspectiveCamera 相机 = 新 PerspectiveCamera(true);camera.setNearClip(0.01);camera.setFarClip(100000);camera.setTranslateZ(-400);subScene.setCamera(相机);父 ui = createUI();StackPane 组合 = new StackPane(ui, subScene);combine.setStyle("-fx-background-color: 线性渐变(到底部,玉米丝,午夜蓝);");场景场景=新场景(组合,宽度,高度);stage.setScene(场景);舞台.show();}

Please bear with my long question, I am trying to make it as clear as possible. (As found in another question.)

In the example below, all rotate buttons are a test replacement for the gyro values coming in from a gyroscope sensor. The sensor is fixed to the real world torso, so the buttons are meant to represent rotation deltas to be applied to the virtual torso in respect to the torsos coordinate system, not the scene coordinate system.

All buttons work fine by themselfs if starting from a "zero" rotation. But when I press 3 times yaw, and then roll, then I see that the roll rotation works on the scene axes. But I would like to apply it to the current torso rotation instead.

I have already tried several suggestions to related problems from here, but did not come to a solution.

A side note: I am not sure if the terms yaw, pitch and roll are usually bound to euler angles, so I want to underline that to my understanding the values from the gyro sensor are not euler angles, as they represent rotation deltas relative to the current torso rotation, and not accumulated angles "absolute" to the torso starting point. So if I have used these terms inappropriately please try to understand what I have meant anyway.

(Background info: I have a robot project roboshock.de with a gyroscope sensor connected to the robots torso, and I want to visualize the rotation of the robot on the screen. The rotate buttons in the below example are only a test replacement for the gyro values coming in from the sensor.)

Any help is much appreciated.

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;

public class PuppetTestApp extends Application {

    int width = 800;
    int height = 500;
    XGroup torsoGroup;
    double torsoX = 50;
    double torsoY = 80;

    public Parent createRobot() {
        Box torso = new Box(torsoX, torsoY, 20);
        torso.setMaterial(new PhongMaterial(Color.RED));
        Box head = new Box(20, 20, 20);
        head.setMaterial(new PhongMaterial(Color.YELLOW.darker()));
        head.setTranslateY(-torsoY / 2 -10);

        torsoGroup = new XGroup();
        torsoGroup.getChildren().addAll(torso, head);
        return torsoGroup;
    }

    public Parent createUI() {
        HBox buttonBox = new HBox();

        Button b;
        buttonBox.getChildren().add(b = new Button("Exit"));
        b.setOnAction( (ActionEvent arg0) -> { System.exit(0); } );

        buttonBox.getChildren().add(b = new Button("pitch up"));
        b.setOnAction(new TurnAction(torsoGroup.rx, -15) );

        buttonBox.getChildren().add(b = new Button("pitch down"));
        b.setOnAction(new TurnAction(torsoGroup.rx, 15) );

        buttonBox.getChildren().add(b = new Button("Yaw left"));
        b.setOnAction(new TurnAction(torsoGroup.ry, -15) );

        buttonBox.getChildren().add(b = new Button("Yaw right"));
        b.setOnAction(new TurnAction(torsoGroup.ry, 15) );

        buttonBox.getChildren().add(b = new Button("Roll right"));
        b.setOnAction(new TurnAction(torsoGroup.rz, -15) );

        buttonBox.getChildren().add(b = new Button("Roll left"));
        b.setOnAction(new TurnAction(torsoGroup.rz, 15) );

        return buttonBox;
    }

    class TurnAction implements EventHandler<ActionEvent> {
        final Rotate rotate;
        double deltaAngle;

        public TurnAction(Rotate rotate, double targetAngle) {
            this.rotate = rotate;
            this.deltaAngle = targetAngle;
        }

        @Override
        public void handle(ActionEvent arg0) {
            addRotate(torsoGroup, rotate, deltaAngle);
        } 
    }

    private void addRotate(XGroup node, Rotate rotate, double angle) {

        // HERE I DO SOMETHING WRONG

        // not working 1:
        //Transform newRotate = new Rotate(angle, rotate.getAxis());
        //node.getTransforms().add(newRotate);

        // not working 2:
        double x = rotate.getAngle();
        rotate.setAngle(x + angle);
    }

    public class XGroup extends Group {

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

        public XGroup() { 
            super(); 
            getTransforms().addAll(rz, ry, rx); 
        }

        public void setRotate(double x, double y, double z) {
            rx.setAngle(x);
            ry.setAngle(y);
            rz.setAngle(z);
        }

        public void setRotateX(double x) { rx.setAngle(x); }
        public void setRotateY(double y) { ry.setAngle(y); }
        public void setRotateZ(double z) { rz.setAngle(z); }
    }

    @Override 
    public void start(Stage stage) throws Exception {
        Parent robot = createRobot();
        Parent ui = createUI();
        StackPane combined = new StackPane();
        combined.getChildren().addAll(ui, robot);
        combined.setStyle("-fx-background-color: linear-gradient(to bottom, cornsilk, midnightblue);");

        Scene scene = new Scene(combined, width, height);
        stage.setScene(scene);
        stage.show();
     }

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

解决方案

For starters, there are a few things that you should consider for a JavaFX 3D application:

  • depth buffer, and antialiasing
  • subScene
  • camera

and you don't have any of those.

You need depth buffer enabled, as you can see that the small yellow box seems to be on top of the big box (the light yellow face shouldn't be visible at all):

According to JavaDoc for Scene:

A scene containing 3D shapes or 2D shapes with 3D transforms may use depth buffer support for proper depth sorted rendering

Change:

Scene scene = new Scene(combined, width, height);

to:

Scene scene = new Scene(combined, width, height, true, SceneAntialiasing.BALANCED);

Once you do it, you'll realize that when the box goes to z > 0 is no longer visible, and the buttons on top are not clickable anymore.

It is not good idea to mix 2D and 3D in the same scene. For that you need a SubScene, where you can lay out your 3D content, and leave the 2D in the Scene itself. Also, you can move the depth buffer and antialiasing options to the subScene:

Parent robot = createRobot();
// add subScene
SubScene subScene = new SubScene(robot, width, height, true, SceneAntialiasing.BALANCED);
Parent ui = createUI();
StackPane combined = new StackPane();
combined.getChildren().addAll(ui, subScene);
Scene scene = new Scene(combined, width, height);

Now the problem is the layout, the boxes will show up in the top left corner, not in the center.

You can add a translation to your group:

public XGroup() { 
    super(); 
    getTransforms().addAll(new Translate(width/2, height/2, 0), rz, ry, rx); 
}    

Now you can see the correct depth sorted rendering, and the ui buttons are accessible.

Another option is adding a camera. You can remove the translate transform, and you can also "zoom" to see your boxes bigger:

Parent robot = createRobot();
SubScene subScene = new SubScene(robot, width, height, true, SceneAntialiasing.BALANCED);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setNearClip(0.01);
camera.setFarClip(100000);
camera.setTranslateZ(-400);
subScene.setCamera(camera);

Rotations

Now, in terms of rotations, if you want to apply a given rotation over the current state of the boxes, and not related to the "scene" axes (I take you mean the three orthogonal non rotated axis), you have to take into account the previous state before applying a new rotation.

In this blog post about the Rubik's cube, each face (composed of 9 small "cubies") can be rotated again and again and the affected cubies carry on a number of previous rotations. The project can be found here.

In this case, using transforms and Affine prepend was the key to have always updated local orthogonal axes on the 3D body.

I'd suggest this:

private void addRotate(XGroup node, Rotate rotate, double angle) {
    Transform newRotate = new Rotate(angle, rotate.getAxis());
    Affine affine = node.getTransforms().isEmpty() ? new Affine() : new Affine(node.getTransforms().get(0));
    affine.prepend(newRotate);
    node.getTransforms().setAll(affine);
}

Nonetheless, your new rotations are all defined over the scene orthogonal axes.

If you want your local axes instead, you can get them from the affine matrix. If you print the affine at any time, you can get x', y', z' axes from the columns 1, 2 and 3:

Affine [
     0.70710678, 0.50000000,  0.50000000, 0.0
     0.00000000, 0.70710678, -0.70710678, 0.0
    -0.70710678, 0.50000000,  0.50000000, 0.0]

I.e, the x' axis (blue) is {0.7071, 0.0, -0.7071}.

So finally you can define the rotations over the local axis as:

private void addRotate(XGroup node, Rotate rotate, double angle) {

    Affine affine = node.getTransforms().isEmpty() ? new Affine() : new Affine(node.getTransforms().get(0));
    double A11 = affine.getMxx(), A12 = affine.getMxy(), A13 = affine.getMxz(); 
    double A21 = affine.getMyx(), A22 = affine.getMyy(), A23 = affine.getMyz(); 
    double A31 = affine.getMzx(), A32 = affine.getMzy(), A33 = affine.getMzz(); 

    // rotations over local axis  
    Rotate newRotateX = new Rotate(angle, new Point3D(A11, A21, A31));
    Rotate newRotateY = new Rotate(angle, new Point3D(A12, A22, A32));
    Rotate newRotateZ = new Rotate(angle, new Point3D(A13, A23, A33));

    // apply rotation
    affine.prepend(rotate.getAxis() == Rotate.X_AXIS ? newRotateX : 
            rotate.getAxis() == Rotate.Y_AXIS ? newRotateY : newRotateZ);
    node.getTransforms().setAll(affine);
}

I believe this will give you what you were looking for.

This is the whole modified code:

private final int width = 800;
private final int height = 500;
private XGroup torsoGroup;
private final double torsoX = 50;
private final double torsoY = 80;

public Parent createRobot() {
    Box torso = new Box(torsoX, torsoY, 20);
    torso.setMaterial(new PhongMaterial(Color.RED));
    Box head = new Box(20, 20, 20);
    head.setMaterial(new PhongMaterial(Color.YELLOW.darker()));
    head.setTranslateY(-torsoY / 2 -10);

    Box x = new Box(200, 2, 2);
    x.setMaterial(new PhongMaterial(Color.BLUE));
    Box y = new Box(2, 200, 2);
    y.setMaterial(new PhongMaterial(Color.BLUEVIOLET));
    Box z = new Box(2, 2, 200);
    z.setMaterial(new PhongMaterial(Color.BURLYWOOD));

    torsoGroup = new XGroup();
    torsoGroup.getChildren().addAll(torso, head, x, y, z);
    return torsoGroup;
}

public Parent createUI() {
    HBox buttonBox = new HBox();

    Button b;
    buttonBox.getChildren().add(b = new Button("Exit"));
    b.setOnAction( (ActionEvent arg0) -> { Platform.exit(); } );

    buttonBox.getChildren().add(b = new Button("pitch up"));
    b.setOnAction(new TurnAction(torsoGroup.rx, -15) );

    buttonBox.getChildren().add(b = new Button("pitch down"));
    b.setOnAction(new TurnAction(torsoGroup.rx, 15) );

    buttonBox.getChildren().add(b = new Button("Yaw left"));
    b.setOnAction(new TurnAction(torsoGroup.ry, -15) );

    buttonBox.getChildren().add(b = new Button("Yaw right"));
    b.setOnAction(new TurnAction(torsoGroup.ry, 15) );

    buttonBox.getChildren().add(b = new Button("Roll right"));
    b.setOnAction(new TurnAction(torsoGroup.rz, -15) );

    buttonBox.getChildren().add(b = new Button("Roll left"));
    b.setOnAction(new TurnAction(torsoGroup.rz, 15) );

    return buttonBox;
}

class TurnAction implements EventHandler<ActionEvent> {
    final Rotate rotate;
    double deltaAngle;

    public TurnAction(Rotate rotate, double targetAngle) {
        this.rotate = rotate;
        this.deltaAngle = targetAngle;
    }

    @Override
    public void handle(ActionEvent arg0) {
        addRotate(torsoGroup, rotate, deltaAngle);
    } 
}

private void addRotate(XGroup node, Rotate rotate, double angle) {
    Affine affine = node.getTransforms().isEmpty() ? new Affine() : new Affine(node.getTransforms().get(0));
    double A11 = affine.getMxx(), A12 = affine.getMxy(), A13 = affine.getMxz(); 
    double A21 = affine.getMyx(), A22 = affine.getMyy(), A23 = affine.getMyz(); 
    double A31 = affine.getMzx(), A32 = affine.getMzy(), A33 = affine.getMzz(); 

    Rotate newRotateX = new Rotate(angle, new Point3D(A11, A21, A31));
    Rotate newRotateY = new Rotate(angle, new Point3D(A12, A22, A32));
    Rotate newRotateZ = new Rotate(angle, new Point3D(A13, A23, A33));

    affine.prepend(rotate.getAxis() == Rotate.X_AXIS ? newRotateX : 
            rotate.getAxis() == Rotate.Y_AXIS ? newRotateY : newRotateZ);

    node.getTransforms().setAll(affine);
}

public class XGroup extends Group {
    public Rotate rx = new Rotate(0, Rotate.X_AXIS);
    public Rotate ry = new Rotate(0, Rotate.Y_AXIS);
    public Rotate rz = new Rotate(0, Rotate.Z_AXIS);
}

@Override 
public void start(Stage stage) throws Exception {
    Parent robot = createRobot();
    SubScene subScene = new SubScene(robot, width, height, true, SceneAntialiasing.BALANCED);
    PerspectiveCamera camera = new PerspectiveCamera(true);
    camera.setNearClip(0.01);
    camera.setFarClip(100000);
    camera.setTranslateZ(-400);
    subScene.setCamera(camera);

    Parent ui = createUI();
    StackPane combined = new StackPane(ui, subScene);
    combined.setStyle("-fx-background-color: linear-gradient(to bottom, cornsilk, midnightblue);");

    Scene scene = new Scene(combined, width, height);
    stage.setScene(scene);
    stage.show();
}

这篇关于javafx - 如何相对于节点旋转轴而不是场景旋转轴将偏航,俯仰和滚动增量(不是欧拉)应用于节点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆