5

My problem is with Z-Buffer in JavaFX 3D, it does not seem to work as intended on my machine.

I'am aware of questions: Overlapping shapes and ...Z Order...

However I do have Z-Buffer enabled, and nodes are still being rendered in the order they are added to the scenegraph.

Maybe I'm missing some dependencies or whatsoever?

I'm posting the code, I hope someone can help me. I'm creating a transition that moves the node around another on an elliptic path.

Thank you in advance!

public class OrbitExp extends Application {
Group root = new Group();
Scene scene = new Scene(root, 800, 600, true, SceneAntialiasing.BALANCED);
PerspectiveCamera camera = new PerspectiveCamera();
@Override
public void start(Stage primaryStage) {
    root.setDepthTest(DepthTest.ENABLE); 
//Tried to set Depthtest explicitly. Assumed maybe it did not inherit:S
    System.out.println(
        "3D supported? " +
        Platform.isSupported(ConditionalFeature.SCENE3D)
    );    // returns true

    System.out.println("root z-buffer: " + root.getDepthTest());
    initCamera();
    Box 
            box1 = new Box(50,50,50),
            box2 = new Box(10,10,10);
    root.setTranslateX(scene.getWidth()/2);
    root.setTranslateY(scene.getHeight()/2);
    PhongMaterial 
            pmat = new PhongMaterial(Color.BLUE),
            pmat2 = new PhongMaterial(Color.RED);
    box1.setMaterial(pmat);
    box2.setMaterial(pmat2);
    scene.setFill(Color.LIGHTGREEN);
    root.getChildren().addAll(box1,box2);
    SequentialTransition sqt = orbit(box1, box2, 40, 40, Duration.seconds(3), 360);
    sqt.play();
    scene.setOnMouseClicked(click->{
        Node node = (Node)(click.getPickResult().getIntersectedNode());
        System.out.println("Tx: "+node.getTranslateX());
        System.out.println("Ty: "+node.getTranslateY());
        System.out.println("Tz: "+node.getTranslateZ());
    }); 
// just for debugging, but coords does seem to be alright
    primaryStage.setScene(scene);
    primaryStage.show();
}
public static void main(String[] args) {
    launch(args);
}
private void initCamera() {
    camera.setTranslateZ(-50);
    camera.setTranslateY(20);
    camera.setFarClip(5000);
    camera.setNearClip(0);
    scene.setCamera(camera);
}
SequentialTransition orbit(Node node1, Node node2,double a, double b, Duration totalDuration, int N) {
    SequentialTransition sqt = new SequentialTransition();
    Duration dur = new Duration(totalDuration.toMillis()*(1.0d/N));
    node2.setTranslateX(a+node1.getTranslateX());
    node2.setTranslateZ(node1.getTranslateZ());
    for (int i = 1; i < N; i++) {
        TranslateTransition tt = new TranslateTransition(dur, node2);
        double 
                angle = i*(360.0d/N),
                toX = (Math.cos(Math.toRadians(angle))*a)+node1.getTranslateX(),
                toZ = (Math.sin(Math.toRadians(angle))*b)+node1.getTranslateZ();
        tt.setToX(toX);
        tt.setToZ(toZ);
        tt.setInterpolator(Interpolator.LINEAR);
        sqt.getChildren().add(tt);
        System.out.println("angle = " + angle + "\nangle in rads: " + Math.toRadians(angle) + "\ntoX = " + toX + "\ntoZ = " + toZ);
    }
    sqt.setCycleCount(Timeline.INDEFINITE);
    return sqt;
}

}

This was my first post by the way:)

Community
  • 1
  • 1
el_chupacabra
  • 169
  • 1
  • 2
  • 9

1 Answers1

5

If you check the code on the link you provided using rectangles, depth buffer works fine.

Changing the rectangles to use 3D boxes works as well.

The issue is how you define the rotation of one of the boxes related to the other, so instead of using a RotateTransition or a SequentialTransition of TranslateTransition like you do, I've applied a Rotate transform to the red box setting a pivot in the center of the blue one, and used an AnimationTimer to modify the angle of that rotation to create the 'orbit' effect.

You can even use transparency on the big box (since 8u60) to see the small one beneath it.

private final Group shapes = new Group();
private long lastTimerCall;
private AnimationTimer timeline;

@Override
public void start(Stage stage) throws Exception {
    Scene scene = new Scene(createRotatingShapes(), 400, 300,
            true, SceneAntialiasing.BALANCED);
    scene.setFill(Color.LIGHTGREEN);
    final PerspectiveCamera camera = new PerspectiveCamera();
    camera.setRotationAxis(Rotate.X_AXIS);
    camera.setRotate(10);
    camera.setTranslateZ(200);
    scene.setCamera(camera);

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

private Group createRotatingShapes() {
    final Box box1 = new Box(50, 50, 50);
    // Transparency in box1: last node of the group
    box1.setMaterial(new PhongMaterial(Color.web("#0000FF80")));

    box1.setTranslateZ(50);

    final Box box2 = new Box(10, 10, 10);
    box2.setMaterial(new PhongMaterial(Color.RED));

    box2.setTranslateZ(-50);

    shapes.getChildren().addAll(box2, box1);

    shapes.setTranslateX(200);
    shapes.setTranslateY(150);

    rotateAroundYAxis(box2);

    return shapes;
}

private int count = 0;
private void rotateAroundYAxis(Node node) {
    Rotate r = new Rotate(0, 0, 0, 100, Rotate.Y_AXIS);
    node.getTransforms().add(r);
    lastTimerCall = System.nanoTime();
    timeline = new AnimationTimer() {
        @Override public void handle(long now) {
            if (now > lastTimerCall + 100_000_000l) {
                r.setAngle((count++)%360);
            }
        }
    };
    timeline.start();
}


@Override
public void stop() {
    timeline.stop();
}

In front of the box:

In front of

Behind the blue box:

Behind

EDIT

If you have a look to the Camera JavaDoc for nearClip:

Specifies the distance from the eye of the near clipping plane of this Camera in the eye coordinate space. Objects closer to the eye than nearClip are not drawn. nearClip is specified as a value greater than zero. A value less than or equal to zero is treated as a very small positive number.

(bold is mine).

So the problem with your code was this line:

camera.setNearClip(0);

Just change it to:

camera.setNearClip(0.01);

and it will work as you expected.

Community
  • 1
  • 1
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • Thank you for your quick answer, I do see that your solution is working fine, and I'm readily accepting it as an answer. However I do fail to see why is cube1 being behind cube2 is still being rendered before cube2 in my program, could you please clarify on "how you define the rotation of one of the boxes related to the other". Heaps of thanks! – el_chupacabra Feb 06 '16 at 12:04
  • 1
    The `Rotate r` uses `pivotZ = 100`, the distance between the two boxes, so you only need to rotate the red box. With depth buffer, the order of the boxes in the group is no relevant, other than to set the transparency in the blue one. If you switch the order it will work, but the blue box won't be transparent. – José Pereda Feb 06 '16 at 13:00
  • 1
    Check your camera settings and change this: `camera.setNearClip(0.01);`. Your code will work now. I've edited my answer with this. – José Pereda Feb 06 '16 at 13:30
  • 2
    Wrong clipping settings are subtle. I stumbled upon this as well in [another answer](http://stackoverflow.com/questions/34001900/how-to-render-3d-graphics-properly), where I debugged for a while and proposed a solution, but in the end, noticed that the asker had simply set wrong clipping planes. (In future questions about ~"odd rendering issues in JavaFX", I'll *first* check the clipping planes...). +1 by the way – Marco13 Feb 06 '16 at 13:46
  • Thank you, all is fine now. Everything is being rendered as it was meant to be. I wasn't aware that setting camera nearClip to 0 could cause a problem. Documentation says: _"A value less than or equal to zero is treated as a very small positive number."_, so I thought it is safe :S – el_chupacabra Feb 08 '16 at 09:18