I wonder if there is a possibility to perform a perspective rotation on a square in JavaFX. When I normally rotate it on the x-axis I just get a compressed square, however I wanted to simulate a real-life rotation with perspective. The result should look something like this
What it should look like
Of course depending on the degree. Is there any way to achieve this, best case where it is possible to specify a certain degree.
Here is what the code for the compression looks like:
public class HelloWorld extends Application {
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Rectangle rectangle = new Rectangle();
rectangle.setWidth(500);
rectangle.setHeight(500);
rectangle.setRotationAxis(Rotate.X_AXIS);
rectangle.setRotate(50);
Scene scene = new Scene(root, 1000, 1000);
root.getChildren().addAll(rectangle);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
By default, a Scene uses a ParallelCamera, which does not define any perspective. Hence the coordinate mapping for x and y coordinates are effectively independent of the z coordinate.
In order to see a three-dimensional effect in the scene, specify a PerspectiveCamera:
public class HelloWorld extends Application {
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Rectangle rectangle = new Rectangle();
rectangle.setWidth(500);
rectangle.setHeight(500);
rectangle.setRotationAxis(Rotate.X_AXIS);
rectangle.setRotate(50);
Scene scene = new Scene(root, 1000, 1000);
scene.setCamera(new PerspectiveCamera());
root.getChildren().addAll(rectangle);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
(As you can see, your angle is computed in the opposite direction to the angle you want.)
You can further configure properties of the PerspectiveCamera, such as the field of view, if needed. Refer to the documentation.
JavaFX supports real 3D so there is no need to mess around with this PerspectiveTransform which is just an Effect. You just have to set up a proper 3D Scene with a PerspectiveCamera and you can get exactly what you want.
Related
I have a code written using javafx and java 11. As I showed in the below code snippet, when I move my ImageView to right of the scene, The ImageView disappears.
At first:
and then after moving it using arrow keys with the help of scene event listener:
then:
and finally:
I don't use fxml. Here is my demo which has the problem too.
public class HelloApplication extends Application {
public String fetchResource(String path) {
return Objects.requireNonNull(getClass().getResource(path)).toString();
}
#Override
public void start(Stage stage) throws IOException {
Rectangle r = new Rectangle(100, 100);
ImageView spaceShip = new ImageView(fetchResource("spaceShip.png"));
spaceShip.setFitHeight(100);
spaceShip.setFitWidth(100);
spaceShip.setX(0);
spaceShip.setY(0);
EventHandler<KeyEvent> keyListener = event -> {
if (event.getCode() == KeyCode.RIGHT) {
spaceShip.setX(spaceShip.getX() + 20);
}
};
Group game = new Group(spaceShip);
Scene scene = new Scene(game, 1840, 1080);
scene.setOnKeyPressed(keyListener);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I should mention that I create a rectangle and check the scenario with that, And there was not any problem. I think there is some problem with ImageView.
I couldn't reproduce the issue with your code, but I could with SceneBuilder. You may get the desired result if you wrap the Group in a Pane (at least it works in SceneBuilder).
Group game = new Group(spaceShip);
Pane pane = new Pane(game);
Scene scene = new Scene(pane, 1840, 1080);
stage.setScene(scene);
stage.show();
I'm experiencing poor performance of JavaFX when the size of a Stage increases, even if only a small portion of the area changes.
The following example updates only a little square of 25x25, changing its background.
public class WindowSizeTest extends Application {
private int speed = 1500000000;
#Override
public void start(Stage primaryStage) {
StackPane content = new StackPane();
content.setPrefSize(25, 25);
content.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
new AnimationTimer() {
#Override
public void handle(long now) {
double val = ((double) now % speed) / speed;
BackgroundFill backgroundFill = new BackgroundFill(Color.color(val, val, val), CornerRadii.EMPTY, Insets.EMPTY);
Background background = new Background(backgroundFill);
content.setBackground(background);
}
}.start();
StackPane root = new StackPane(content);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Everything goes smoothly when the window is small:
..but..
if maximized on my 4k external monitor, it uses a lot my GPU and the performance of the entire system goes down:
It seems that JavaFX redraws the entire window content and not only the modified part (same behaviour on Windows).
Is this the expected behaviour? Can someone tell me why this is happening?
I am building an application that has 2 SubScenes, one for GUI and one for displaying 3D models (in my case OBJ).
For the import of the OBJ file I use the ObjModelImporterJFX library by InteractiveMesh.
Like the title says and as seen in this picture the models start to glitch when they are handled by a SubScene. It seems like some covered parts that should not be visible are rendered like they were. Just for comparison this is how it looks normally, when the models are handled direcly by the scene.
Here is my code:
public class Main extends Application{
#Override
public void start(Stage stage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
PerspectiveCamera camera = new PerspectiveCamera(true);
Group model = loadOBJ("course.obj");
Group course = new Group(model);
BorderPane borderPane = new BorderPane();
Pane pane1 = new Pane(course);
Pane pane2 = new Pane(root);
SubScene subScene1 = new SubScene(pane1, 1000, 720);
subScene1.setCamera(camera);
SubScene subScene2 = new SubScene(pane2,500,500);
borderPane.setLeft(subScene1);
borderPane.setRight(subScene2);
Scene scene = new Scene(borderPane, 1280,720, true);
stage.setScene(scene);
stage.show();
}
private Group loadOBJ(String fileName){
URL url = getClass().getResource(fileName);
Group modelRoot = new Group();
ObjModelImporter importer = new ObjModelImporter();
importer.read(url);
for (MeshView view : importer.getImport()){
modelRoot.getChildren().add(view);
}
return modelRoot;
}
public static void main(String[] args){
launch(args);
}
}
I hope that someone has an idea why this is. Thanks in advance :)
UPDATE: To fix this enable depthBuffer by changing the constructor of the SubScene from
SubScene subScene1 = new SubScene(pane1, 1000, 720);
to
SubScene subScene1 = new SubScene(pane1, 1000, 720, true, null);
I'm currently working on an app that will play a video using JavaFX. I want to place the volume slider on the right side of the border pane. Currently, the app will resize to the size of the window, which will overlap with my volume slider.
I have a controller that takes care of all the actions performed by the app and implements the Initializable interface. Here's how my initialize() method looks like.
#Override
public void initialize(URL locate, ResourceBundle resources){
String path = new File("src/media/dance.mp4").getAbsolutePath();
media = new Media (new File (path).toURI().toString());
player = new MediaPlayer(media);
mediaView.setMediaPlayer(player);
DoubleProperty width = mediaView.fitWidthProperty();
DoubleProperty height = mediaView.fitHeightProperty();
width.bind(Bindings.selectDouble(mediaView.sceneProperty(), "width"));
height.bind(Bindings.selectDouble(mediaView.sceneProperty(), "height"));
volumeSlider.setValue(player.getVolume() * 100);
volumeSlider.valueProperty().addListener(new InvalidationListener() {
#Override
public void invalidated(Observable observable) {
player.setVolume(volumeSlider.getValue() / 100);
}
});
}
I tried to subtract 64px directly after mediaView.fitWidthProperty() and mediaView.sceneProperty(), but both gave me an error because they are not of type double. I tried mediaView.fitWidthProperty().subtract(64), but it said I would need to change the data type from DoubleProperty to DoubleBind, which would not be compatible with Bindings.selectDouble() method that requires a DoubleProperty.
How can I get this to work?
EDITED
There is an easier way! Just bind to the property directly:
mediaView.fitWidthProperty().bind(
mediaView.getScene().widthProperty().subtract(64));
Here is a full app to demonstrate:
public class FXWidthBind extends Application {
#Override
public void start(Stage stage) {
Media media = new Media("http://i.imgur.com/OJTwZuc.mp4");
MediaPlayer player = new MediaPlayer(media);
MediaView mediaView = new MediaView(player);
Pane root = new Pane();
Scene scene = new Scene(root);
root.getChildren().add(mediaView);
stage.setWidth(1000);
stage.setHeight(800);
stage.setScene(scene);
stage.show();
mediaView.fitWidthProperty().bind(mediaView.getScene().widthProperty().subtract(64));
mediaView.fitHeightProperty().bind(mediaView.getScene().heightProperty());
}
public static void main(String[] args) {
launch(args);
}
}
The solution is to subtract the width property of your SceneProperty (video screen).
width.bind(Bindings.selectDouble(mediaView.sceneProperty(), "width").subtract(64.0));
Here, I'm binding the width of my MediaView with the width of my SceneProperty minus 64 pixels. Since DoubleProperty is a wrapper around the primitive type double, you can use the subtract() method to modify the actual value underneath.
Before
After
I am using Javafx (without using FXML), and I am passing the stage into the a controller to change the scene on the stage when a button is clicked. The scene changes correctly but the size of the stage and the scene increases.It increases in size by about 0.1 (in the width) and the height sometimes also increases (not every time).
Here is the controller being used.
public class Controller {
public Controller(){
}
public static void homeButtonhandler(Stage stage){
stage.close();
}
public static void adminButtonhandler(Stage stage){
adminPane adminPane1 = new adminPane(stage);
Scene adminScene = new Scene (adminPane1);
stage.setScene(adminScene);
}}
The adminPane extends another class I created called mainPane which both extend the Pane class. Which have other panes within them to create the GUI structure. The sizing for the main pane is set up like so:
top = createTop(stage);
this.getChildren().addAll(top);
this.setWidth(stage.getWidth());
this.setPrefWidth(stage.getWidth());
this.setMaxWidth(stage.getWidth());
this.setMinWidth(stage.getWidth());
this.setHeight(stage.getHeight());
this.setMaxHeight(stage.getHeight());
this.setMinHeight(stage.getHeight());
this.setPrefHeight(stage.getHeight());
I am testing the classes using:
public class test extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
//primaryStage.setResizable(true);
mainPane myPane = new mainPane(primaryStage);
Scene homeScene = new Scene (myPane);
primaryStage.setScene(homeScene);
primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("/resources/icons/joulesIcon.png")));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I believe it has something to do with my passing of the stage, any pointers would be much appreciated.
I also found that stuff was being drawn below the bottom of the window when I used primaryStage.setScene(new Scene(root, primaryStage.getWidth(), primaryStage.getHeight())). My case was resolved by using the dimensions of the old scene rather than the dimensions of the primary stage.
public void setScene(Parent root) {
Scene oldScene = primaryStage.getScene();
primaryStage.setScene(oldScene == null
? new Scene(root, primaryStage.getMinWidth(), primaryStage.getMinHeight())
: new Scene(root, oldScene.getWidth(), oldScene.getHeight()));
}