javafx 3D Box,显示的翻译不是预期的

javafx 3D Box, translation displayed is not what is expected

我目前正在处理一个项目,我应该在窗格上显示 3D 框,为此我正在使用 javafx 3D。首先我画了一个大盒子,在我的项目中叫做 container 并创建了一个相机。然后将按钮添加到场景中,我在大容器中绘制其他较小的盒子。然而,由于某种原因,小盒子会相互影响并改变它们的坐标。更多图片说明:

这是box1,我用键"X"画的:

这是box2,我用键"C"画的:

如果我只添加 box1 并重新启动我的应用程序,然后绘制 box2,则相应地输出图像 1 和图像 2。但是,如果我不是每次都重新启动应用程序并且我想绘制另一个框,我会得到以下输出。

box2 的输出,如果在它之前绘制了 box1:

box1 的输出,如果在它之前绘制了 box2:

如果我先画box1,box1看起来没问题,但是图3是box2的输出。如果我先绘制 box2,box1 输出将变为图像 4。

import javafx.scene.Camera;   
import javafx.scene.Group;     
import javafx.scene.Parent; 
import javafx.scene.PerspectiveCamera;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;

public class ContainerPane extends Parent {
//size of big container
private final double CONTAINER_DEPTH = 16.5;
private final double CONTAINER_WIDTH = 2.5;
private final double CONTAINER_HEIGHT = 4.0;
//group in which the box, container and camera are added
private Group root;
//coordiantes of box 1, row one is the actual coordinates and row two is the width, height, depth
private double[][] box1 = {{0, 4, 30},
                            {1, 2, 1.5}};
//coordiantes of box 2, row one is the actual coordinates and row two is the width, height, depth                           
private double[][] box2 = {{0, 6, 28},
                           {1.5, 1, 2}};

public ContainerPane(int Scene_Width, int Scene_Length){
    //create the group
    root = new Group();
    root.setAutoSizeChildren(false);

    //creating container
    Box container = new Box(CONTAINER_WIDTH , CONTAINER_HEIGHT, CONTAINER_DEPTH);
    container.setCullFace(CullFace.NONE);
    //drawing the container with only lines
    container.setDrawMode(DrawMode.LINE);
    //setting the color of the container
    PhongMaterial material = new PhongMaterial(Color.ORANGE);
    container.setMaterial(material);        
    root.getChildren().add(container);

    //create a camera
    PerspectiveCamera camera = new PerspectiveCamera(true);
    //add possible rotations and position of camera
    camera.getTransforms().addAll(new Translate(0, 0, -35));
    root.getChildren().add(camera);

    //create a Scene from the group
    SubScene subScene = new SubScene(root, Scene_Width, Scene_Length, true, SceneAntialiasing.BALANCED);
    //set a camera for the scene
    subScene.setCamera(camera);
    getChildren().add(subScene);
}
public void drawBox1(){
    //clear everything from root except container and camera
    try{
        root.getChildren().remove(2);
    }
    catch(Exception exception){

    }
    //create box1
    Box box = new Box(box1[1][0], box1[1][1], box1[1][2]);
    box.setDrawMode(DrawMode.FILL);
    box.setMaterial(new PhongMaterial(Color.BLUE));
    box.setTranslateX(-CONTAINER_WIDTH/2 + box.getWidth()/2 + 0.5*box1[0][0]);
    box.setTranslateY(-CONTAINER_HEIGHT/2 + box.getHeight()/2 + 0.5*box1[0][1]);
    box.setTranslateZ(CONTAINER_DEPTH/2 - box.getDepth()/2 - 0.5*box1[0][2]);
    //add it to the group
    root.getChildren().add(box);
}

public void drawBox2(){
    try{
        root.getChildren().remove(2);
    }
    catch(Exception exception){

    }
    Box box = new Box(box2[1][0], box2[1][1], box2[1][2]);
    box.setDrawMode(DrawMode.FILL);
    box.setMaterial(new PhongMaterial(Color.BLUE));
    box.setTranslateX(-CONTAINER_WIDTH/2 + box.getWidth()/2 + 0.5*box2[0][0]);
    box.setTranslateY(-CONTAINER_HEIGHT/2 + box.getHeight()/2 + 0.5*box2[0][1]);
    box.setTranslateZ(CONTAINER_DEPTH/2 - box.getDepth()/2 - 0.5*box2[0][2]);
    root.getChildren().add(box);
}
}

主文件,我在其中创建 class.

的实例
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;

public class Error extends Application {

@Override
public void start(Stage primaryStage) {
    ContainerPane container = new ContainerPane(750, 750);
    Scene scene = new Scene(container);
    scene.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>(){
        @Override
        public void handle(KeyEvent event){
           if(event.getCode() == KeyCode.X){
               container.drawBox1();
           }
           if(event.getCode() == KeyCode.C){
               container.drawBox2();
           }
    }});
    primaryStage.setScene(scene);
    primaryStage.show();
}

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    launch(args);
}

}

总结提出的问题:无论将它们添加到场景的顺序如何,绘制两个相似的 3D 框都应该有效,但事实是顺序 很重要 和结果是错误.

(为方便起见,我将框 2 设置为红色)

当先绘制较高的蓝色框 1 时,较短的红色框与较高的框尺寸完全相同。

当先绘制较短的红色框 2 时,会绘制较高的蓝色框,其尺寸与较短的红色框完全相同。

说明

对此行为有一个解释:当您将长方体、圆柱体或球体绘制到 scene/subscene 时,网格管理器中有一个内部缓存 javafx.scene.shape.PredefinedMeshManager class:

private HashMap<Integer, TriangleMesh> boxCache = null;

第一次添加网格时:

if (key == 0) {
    key = generateKey(w, h, d);
}
mesh = manager.getBoxMesh(w, h, d, key);

密钥为空,管理员为您创建盒子:

if (mesh == null) {
    mesh = Box.createMesh(w, h, d);
    boxCache.put(key, mesh);
}

并存储此网格的密钥。在 Box 的情况下,这是生成此密钥的方式:

private static int generateKey(float w, float h, float d) {
    int hash = 3;
    hash = 97 * hash + Float.floatToIntBits(w);
    hash = 97 * hash + Float.floatToIntBits(h);
    hash = 97 * hash + Float.floatToIntBits(d);
    return hash;
}

现在,对于第二个网格,您生成密钥,然后去向经理询问现有网格:

TriangleMesh mesh = boxCache.get(key); 

鉴于 box1box2 不同:

 Box1: {W: 1, H: 2, D: 1.5}
 Box2: {W: 1.5, H: 1, D: 2}

任何人都认为缓存会 return 为空并生成一个新的网格...

...但这不是我们得到的。这里我们遇到了错误:生成密钥的方法只考虑了高度、宽度和深度的值,但不考虑顺序,并且 whd 的任何排列都将具有相同的键 :

hash = 97 * hash + Float.floatToIntBits(w);
hash = 97 * hash + Float.floatToIntBits(h);
hash = 97 * hash + Float.floatToIntBits(d);

虽然此错误与此 无关,但它是由于网格管理器的问题而发生的,并且已提交错误。

解决方法

现在我只会使用稍微不同的尺寸,例如:

 Box1: {W: 1, H: 2, D: 1.5}
 Box2: {W: 1.500001, H: 1, D: 2}

或者,如果您无法更改这些维度,则可以提供自己的 TriangleMesh 框实现,该框不会被缓存。例如,您可以在 FXyz3D library 中找到一个。

更新

我刚刚注意到这个错误已经提交 here

This bug is the result of mistakenly assuming that two boxes with an equal hash key have equal dimensions.