JavaFX 8 Z-buffer issue - java

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:)

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:
Behind the blue box:
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.

Related

Camera doesnt rotate x axis using javafx

ive been using javafx recently and while i was doing so ive caught a problem in my code but spending a lot of time on it (mostly googling it) i cannot find the solution to my problem.
The problem happens when i try to rotate the camera using the x axis but instead of doing what i was wanting to happen instead it rotates around (0,0,0). Im not sure if it does rotate around (0,0,0) but that was the solution i could figure out. My camera starts staring at a cube but when tun left (adds 2 to the x axis) and the box goes a big circle. When turning right the box moves the other way around the circle. When applying the axis to the box, the box goes around fine.
My code is a bit messy but what i tried is to get the movement of turning and the box should go left if i turn right and right if i turn left as if in real life how it works.
public class javafx extends Application {
int ze = 0;
int ye = 0;
int xe = 0;
PerspectiveCamera cam = new PerspectiveCamera();
//the rotation angles//
Rotate rx = new Rotate();
{ rx.setAxis(Rotate.X_AXIS); }
Rotate ry = new Rotate();
{ ry.setAxis(Rotate.Y_AXIS); }
Rotate rz = new Rotate();
{ rz.setAxis(Rotate.Z_AXIS); }
int xt = 0;
int yt = 0;
int one;
int two;
boolean flip = false;
public static void addRotate(Node node, Point3D rotationAxis,
double angle) {
ObservableList<Transform> transforms = node.getTransforms();
try {
for (Transform t : transforms) {
rotationAxis = t.inverseDeltaTransform(rotationAxis);
}
} catch (NonInvertibleTransformException ex) {
throw new IllegalStateException(ex);
}
transforms.add(new Rotate(angle, rotationAxis));
}
public void start(Stage stage) {
Box cube = new Box();
cam.getTransforms().addAll(rx, rz, ry);
cube.setDepth(100.0);
cube.setHeight(100.0);
cube.setWidth(200.0);
cube.setCullFace(CullFace.BACK);
cube.setDrawMode(DrawMode.FILL);
PhongMaterial material = new PhongMaterial();
material.setDiffuseColor(Color.BROWN);
cube.setMaterial(material);
cube.setTranslateX(1500.0);
cube.setTranslateY(500.0);
cube.setTranslateZ(0.0);
cam.setTranslateX(0);
cam.setTranslateY(0);
cam.setTranslateZ(0);
cam.setScaleX(2);
cam.setScaleY(2);
cam.setScaleZ(2);
Group root = new Group(cube, cam);
Dimension dimension =
Toolkit.getDefaultToolkit().getScreenSize();
double screenHeight = dimension.getHeight();
double screenWidth = dimension.getWidth();
Scene scene = new Scene(root, 0,0);
stage.setFullScreen(true);
scene.setCamera(cam);
stage.setTitle("3d space");
stage.setScene(scene);
stage.show();
stage.setOnHiding( event -> { System.exit(0);} );
//to get the input from a mouse vvvv //
ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
Runnable toRun;
toRun = new Runnable() {
public void run() {
if(!flip){
one = MouseInfo.getPointerInfo().getLocation().x;
two = MouseInfo.getPointerInfo().getLocation().x;
flip = true;
}else{
flip = false;
if(one > MouseInfo.getPointerInfo().getLocation().x){
xt = xt +2;
ry.setAngle(xt);
System.out.println("left");
}else{
if(one < MouseInfo.getPointerInfo().getLocation().x){
System.out.println("right");
xt = xt -2;
ry.setAngle(xt);
}
}
}
}
};
ScheduledFuture<?> handle = scheduler.scheduleAtFixedRate(toRun, 1, 1, TimeUnit.MILLISECONDS);
}
public static void main(String args[]){
launch(args);
}
}
the answer is cam.set.pivot(222) to the area it circles around, also you can rotate everything around the camera

Custom titlebar drag behaviour does not support window snap

While this stackoverflow post helps solve the drag issue
How to drag an undecorated window (stage) of JavaFX
It definitly doesn't let window snap work, is there some way i'm supposed to use this to get window snap working?
This is a single Class javafx App you can try . You can drag the stage with the green circle and close App with the red one . when stage.getX==0 is at the left of the screen and the app resize at maxHeight of the screen wich is in Screen.getPrimary().getBounds().getMaxY() and Screen.getPrimary().getBounds().getMaxX() divided by two to get half width of the screen . for the right side is a litte bit tricky when stage.getX() pluss the current width of the window matchs Screen.getPrimary().getBounds().getMaxY() means the right side of the window reaches the right side of the screen , but when the app is rezised in the right its x coordinate change , that will trigger the listener again , to avoid that I put a boolean boolean isInRightSide . You can comment and uncomment that boolean to see that behavior . Any other values of x position of the stage will reset to the original sizes .
public class App extends Application {
boolean isInRightSide = false;
double offsetX;
double offsetY;
int defaultWidth = 640;
int defaultHeight = 480;
#Override
public void start(Stage stage) {
double screenMaxX = Screen.getPrimary().getBounds().getWidth();
System.out.println(Screen.getPrimary().getBounds());
Circle dragCircle = new Circle(30, new Color(0, 1, 0, 1));
Circle closeCircle = new Circle(30, new Color(1, 0, 0, 1));
HBox hBox = new HBox(dragCircle, closeCircle);
closeCircle.setOnMouseClicked(e -> stage.close());
hBox.setAlignment(Pos.TOP_RIGHT);
dragCircle.setOnMousePressed(e -> {
offsetX = e.getSceneX();
offsetY = e.getSceneY();
}
);
dragCircle.setOnMouseDragged(e -> {
stage.setX(e.getScreenX() - offsetX);
stage.setY(e.getScreenY() - offsetY);
e.consume();
});
// sanap to left and right
stage.xProperty().addListener(e -> {
if (stage.getX() + defaultWidth > screenMaxX) {
stage.setX(screenMaxX - defaultWidth);
}
if (stage.getX() <= 0 && !stage.isFullScreen()) {
stage.setHeight(Screen.getPrimary().getBounds().getMaxY());
stage.setWidth(Screen.getPrimary().getBounds().getMaxX() / 2);
} else if (!isInRightSide) {
stage.setHeight(defaultHeight);
stage.setWidth(defaultWidth);
}
if (stage.getX() + defaultWidth == screenMaxX) {
isInRightSide = true;
stage.setHeight(Screen.getPrimary().getBounds().getMaxY());
stage.setWidth(Screen.getPrimary().getBounds().getMaxX() / 2);
} else {
isInRightSide = false;
}
});
stage.initStyle(StageStyle.UNDECORATED);
Scene scene = new Scene(new AnchorPane(hBox), defaultWidth, defaultHeight);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
app start
app snap when reaches right side

JavaFX: Coordinates of nodes are wrong for no obvious reason

I'm trying to visualize a graph in JavaFX. The nodes shall be interactive. At the moment I have a VBox and place as many HBoxes into it as I need levels in my graph. All the HBoxes are set to position their childrens centered. The childrens represent the individual nodes and are Buttons.
The VBox itself is placed in a StackPane with a Canvas as another children of the StackPane.
This StackPane is then placed into the Scene.
I want to use the Canvas to draw the joining Edges between the nodes. To get The coordinates of the nodes i use:
System.out.println(button.localToScene(button.getBoundsInLocal().getMinX(),
button.getBoundsInLocal().getMinY()));
But no matter where the button is positioned, the resulting coordinate remains:
Point2D [x = 0.0, y = 108.0]
for all the buttons. Every single Button has apparently the same coordinate though it's displayed correctly!
Please help me, im quite desperate right now...
Edit:
Here is the minimal example that works:
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
VBox root = new VBox();
HBox buttons = new HBox();
TabPane tabs = new TabPane();
buttons.getChildren().addAll(
new Button("1"),
new Button("2"),
new Button("3"));
tabs.getTabs().addAll(
new TabBoxes("____A____"),
new TabBoxes("____B____"),
new TabBoxes("____C____")
);
root.getChildren().addAll(buttons, tabs);
primaryStage.setScene(new Scene(root, 500, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
TabBoxes.java
public class TabBoxes extends Tab {
private VBox vBox = new VBox();
private ArrayList<HBox> hBoxes;
private Canvas canvas = new Canvas();
private StackPane stack = new StackPane();
private static final int V_INIT_SPACING = 60;
private static final int V_SPACING = 60;
private static final int H_SPACING = 60;
TabBoxes(String s){
super(s);
this.setContent(stack);
setClosable(false);
stack.getChildren().add(canvas);
stack.getChildren().add(vBox);
canvas.setWidth(2000);
canvas.setHeight(1080);
hBoxes = new ArrayList<>();
vBox.setSpacing(V_SPACING);
vBox.translateYProperty().set(V_INIT_SPACING);
for (int i = 0; i < 5; i++) {
hBoxes.add(new HBox() {
{
setSpacing(H_SPACING);
setAlignment(Pos.CENTER);
}
});
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j <= i; j++) {
hBoxes.get(i).getChildren().add(new Button(i + "||" + j){
{
setPrefSize(100,60);
setOnAction(e-> System.out.println("My coordinates are: " + localToScene(getBoundsInLocal().getMinX(),getBoundsInLocal().getMinY())));
}
});
}
}
vBox.getChildren().addAll(hBoxes);
}
}
boundsInLocal are coordinates in the Button's own coordinate system. It's not surprising, that these values are the same.
If you want to calculate the coordinates in the Canvas coordinate system, transform the coordinates to the StackPane coordinates and then transform the coordinates to the Canvas coordinates:
Node n = button;
Point2D p = new Point2D(button.getBoundsInLocal().getMinX(), button.getBoundsInLocal().getMinY());
while (n != stackPane) {
p = n.localToParent(p);
n = n.getParent();
}
p = canvas.parentToLocal(p);

JavaFx path transition in gridPane

I can not understand how moveTo method work in javafx. here is my example
I have a GridPane that consist of 4 columns and 1 row. All the columns h has a StackPane and the last Stackpane also has an empty label.
public class PathTransitionTExample extends Application {
StackPane pane;
GridPane grid;
Label label;
Scene scene;
#Override
public void start(Stage primaryStage) {
label = new Label();
label.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
grid = new GridPane();
grid.getStyleClass().add("gridPane");
for (int x = 0; x < 4; x++) {
grid.add(pane = new StackPane(), x, 0);
pane.setPrefSize(50, 50);
pane.getStyleClass().add("stackPane");
}
pane = (StackPane) grid.getChildren().get(3);
pane.getChildren().add(label);
scene = new Scene(grid, 260, 50);
scene.getStylesheets().add(PathTransitionTest.class.getResource("pathCSS.css").toExternalForm());
scene.setOnMouseClicked(me -> pathTransition());
primaryStage.setTitle("Path Transition");
primaryStage.setScene(scene);
primaryStage.show();
}
//pathCSS.css
.gridPane{
-fx-background-color: red;
-fx-hGap: 20;
}
.stackPane{
-fx-background-color: orange;
}
.label {
-fx-background-color: blue;
}
I want to move the label in the grid pane from 0.3 to 0.0 however when I am trying to do that the whole transition slips.
private void pathTransition() {
//the Coordinates of the label
double oldMinX = grid.getChildren().get(3).getLayoutX();
double oldMinY = grid.getChildren().get(3).getLayoutY();
//the coordinates of the stack pane where i want the label move
double newMinX = grid.getChildren().get(1).getLayoutX();
double newMinY = grid.getChildren().get(1).getLayoutY();
Path path = new Path();
path.getElements().add(new MoveTo(oldMinX, oldMinY ));
path.getElements().add(new LineTo(newMinX,newMinY ));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(new Duration(500));
pathTransition.setPath(path);
pathTransition.setNode(label);
pathTransition.play();
}
If I change the arguments of MoveTo and LineTo by the following I can achieve the animation I want but I cant understand why. :\
double oldMinX = grid.getChildren().get(3).getLayoutX() -185;
double oldMinY = grid.getChildren().get(3).getLayoutY()+ grid.getChildren().get(3).getBoundsInLocal().getHeight()/2 ;
double newMinX = grid.getChildren().get(1).getLayoutX()-255 ;
double newMinY = grid.getChildren().get(1).getLayoutY() + grid.getChildren().get(0).getBoundsInLocal().getHeight()/2 ;
I guess It is because transitions use different coordinate systems than scenes but I cant really find anything that explains well :( Could someone give me some hints how It is working?
Thank you so much in advance.
I realized that i shouldn't use GridPane. If i do it without using containers the transition is working fine.

StackedAreaChart's default coloring policy after 8 colors on subsequent data repopulations

I am trying to understand how the StackedAreaChart colors its series, in order to color series consistently in my application when data is replaced with entirely new data. Initially, I thought StackedAreaChart cycles through 8 default colors. In other words, data series are colored according to their index in getData(), mod 8. But I am encountering unexpected behavior:
The output above comes from the application below, which clears the StackedAreaChart's data and repopulates it with 10 new series every time the window is clicked. As you can see, only the first 8 colors are consistent across clicks/repopulations.
public class TestChartColors extends Application {
private int clickCount = 0;
#Override
public void start(Stage primaryStage) {
NumberAxis xAxis = new NumberAxis(0, 10, 1);
NumberAxis yAxis = new NumberAxis(0, 10, 1);
final StackedAreaChart<Number,Number> chart = new StackedAreaChart<>(xAxis,yAxis);
chart.setLegendVisible(false);
chart.setCreateSymbols(false);
chart.setAnimated(false);
chart.setOnMouseClicked((MouseEvent event) -> {
clickCount++;
chart.getData().clear();
for(int i=0; i<10; i++){
chart.getData().add(flatSeries());
}
primaryStage.setTitle("After " + clickCount + " clicks.");
});
StackPane root = new StackPane();
root.getChildren().add(chart);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.setTitle("Click the window.");
primaryStage.show();
}
private Series<Number,Number> flatSeries(){
Series<Number,Number> s = new Series<>();
ObservableList<XYChart.Data<Number, Number>> d = s.getData();
d.add(new XYChart.Data<>(0, 1));
d.add(new XYChart.Data<>(10, 1));
return s;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Workaround found:
Insert
while(chart.getData().size() % 8 != 0){
final XYChart.Series<Number,Number> series = new XYChart.Series<>();
series.getData().add(new XYChart.Data<>(0,0));
chart.getData().add(series);
}
before chart.getData().clear();.
StackedAreaChart seems to continue the numbering where it left off, except, strangely, for the first 8 colors.

Categories

Resources