对于初学者,对于JavaFX 3D应用程序,您应该考虑以下几点:
而你没有这些。
您需要启用深度缓冲区,因为您可以看到黄色小框似乎位于大框的顶部(浅黄色的面应该根本看不见):
根据JavaDoc
Scene
:
包含3D形状或具有3D变换的2D形状的场景可以使用深度缓冲区支持进行适当的深度排序渲染
更改:
Scene scene = new Scene(combined, width, height);
收件人:
Scene scene = new Scene(combined, width, height, true, SceneAntialiasing.BALANCED);
一旦你这样做了,你就会意识到当盒子转到z时>0不再可见,顶部的按钮也不再可单击。
在同一场景中混合2D和3D不是一个好主意。为此,你需要
SubScene
,您可以在其中布局3D内容,并将2D保留在场景本身中。此外,还可以将深度缓冲区和抗锯齿选项移动到子场景:
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);
现在的问题是布局,方框将显示在左上角,而不是中间。
您可以向组中添加翻译:
public XGroup() {
super();
getTransforms().addAll(new Translate(width/2, height/2, 0), rz, ry, rx);
}
现在,您可以看到正确的深度排序渲染,并且可以访问ui按钮。
另一个选项是添加相机。您可以删除“平移”变换,也可以“缩放”以查看更大的长方体:
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);
轮换
现在,就旋转而言,如果要在长方体的当前状态上应用给定的旋转,并且与“场景”轴无关(我认为您是指三个正交的非旋转轴),则必须在应用新旋转之前考虑之前的状态。
在这个
blog post
关于魔方,每个面(由9个小“立方体”组成)可以反复旋转,并且受影响的立方体可以进行之前的多次旋转。可以找到该项目
here
.
在这种情况下,使用变换和仿射
prepend
是始终更新3D实体上的局部正交轴的关键。
我建议:
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);
}
尽管如此,新旋转都是在场景正交轴上定义的。
如果需要局部轴,可以从仿射矩阵中获取它们。如果你打印
affine
在任何时候,您都可以从列1、2和3中获取x、y、z轴:
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]
一、 e,x轴(蓝色)为
{0.7071, 0.0, -0.7071}
.
因此,最后可以将局部轴上的旋转定义为:
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);
}
我相信这会给你你想要的。
这是整个修改后的代码:
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();
}