JavaFX enormous lag with larger Canvas dimensions - java

This is kind of a vague question, but I'm trying to create a code editor using JavaFX Canvas technologies and its being incredibly slow for what I'd like.
Take the following code for example
public class JavaFXApplication13 extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
int extent = 6300;
System.out.println(System.getProperty("java.version"));
Canvas cvs = new Canvas(extent,extent);
ScrollPane scpn = new ScrollPane();
root.setTop(cvs);
scpn.setContent(root);
root.autosize();
scpn.autosize();
GraphicsContext ctx = cvs.getGraphicsContext2D();
for(int i = 0; extent / 300 > i; i++){
ctx.setFill(Color.RED);
ctx.fillRect(i*300, 0, 100, extent);
ctx.setFill(Color.BLUE);
ctx.fillRect(i*300+100, 0, 100, extent);
ctx.setFill(Color.GREEN);
ctx.fillRect(i*300+200, 0, 100, extent);
}
//// root.getChildren().add(btn);
Scene scene = new Scene(scpn, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
If you try to slide pane around, theres a good few seconds between when you move the cursor and when the scrollbar and the scroll pane update. This has to do with the size of the canvas, which is set to 6300, which is nothing. I can open NotePad and get line heights in the million and its able to draw them with ease.
Performance is even worse when trying to draw on a large sized canvas, simple
onKeyPress((a) -> drawText(a.getText(), ...));
takes seconds to process.
I guess what I'm trying to say is, is this performance normal? or should I just suck it up and move on to something more powerful such as OpenGL?

A code editor should be virtual and only draw the lines you see on screen! So IMHO your use of canvas is completely incorrect!
Canvas at its heart can be seen like a buffered image you can draw on and on the OpenGL / Directx side only sees a image.
Why reinvent the wheel there are at least 2 opensource javafx code editors. See https://tomasmikula.github.io/blog/ and http://tomsondev.bestsolution.at/2014/08/11/efxclipse-1-0-new-features-styledtext-control-to-build-a-code-editor-framework/

I tested JavaFX for a game project about 6 months ago, I was drawing 10000 rectangles and using an animation timer to change the color of each rectangle 60 times a second.
I found that using the canvas for this was really slow and was getting about a frame a second. I changed to just using the scene graph by just adding JavaFX rectangle nodes to a Group node and it worked with no lag.
I was surprised that using JavaFX objects was way more efficient that using the canvas, I am now working on my second JavaFX 2 game using nodes in the scene graph.

Related

JavaFX 3D - Is it possible to change the width of Line in DrawMode.Line?

I would like to change the linewidth of any mesh with DrawMode.LINE, but I don't know if it is possible.
I added some code for reference.
#Override
public void start(Stage primaryStage) throws Exception{
PerspectiveCamera camera= new PerspectiveCamera(true);
Group root = new Group();
Scene scene = new Scene(root, 1024, 768, true);
primaryStage.setScene(scene);
primaryStage.show();
scene.setFill(Color.BLACK);
camera.setFarClip(10000);
camera.setTranslateZ(-10);
scene.setCamera(camera);
Box box = new Box();
box.setDrawMode(DrawMode.LINE);
root.getChildren().addAll(camera,box);
}
public static void main(String[] args) {
launch(args);
}
It is not possible. The DrawMode is passed down to the native renderer whose default wireframe is a width 1 line. For the Direct3D pipeline, see Outline and Fill State. Even if you use the line drawing support library it will tell you that:
The library uses native hardware line drawing support (if available in
the device) only if:
Line width is 1.
No line pattern is enabled.
Line widths that are different than 1 cannot be drawn with line primitives, but have to be drawn with triangles instead:
The line drawing library emulates lines using texture triangles
In other words, a width of 1 is special because it has specific hardware support.

Javafx Scene.snapshot method with slow performance

I am making a sliding animation to switch a scene to another scene, but when I call this method, it has a delay for switching scene. I found that the cause is a method snapshot() of class Scene.
Does anyone have a solution?
code:
public void switchScene(Scene target) {
Scene current = getPrimaryStage().getScene();
WritableImage beforeImage;
WritableImage afterImage;
int width = ((int) ((Region) current.getRoot()).getWidth());
int height = ((int) ((Region) current.getRoot()).getHeight());
beforeImage = new WritableImage(width, height);
ImageView leftImage = new ImageView(current.snapshot(beforeImage));
afterImage = new WritableImage(width, height);
ImageView rightImage = new ImageView(target.snapshot(afterImage));
leftImage.setTranslateX(0);
rightImage.setTranslateX(width);
StackPane animation = new StackPane(leftImage, rightImage);
animation.setPrefSize(target.getWidth(), target.getHeight());
primaryStage.setScene(new Scene(animation));
Timeline timeline = new Timeline();
KeyValue kv = new KeyValue(rightImage.translateXProperty(), 0, Interpolator.EASE_BOTH);
KeyFrame kf = new KeyFrame(Duration.seconds(0.75), kv);
timeline.getKeyFrames().add(kf);
timeline.setOnFinished(t -> {
// remove pane and restore scene 1
primaryStage.setScene(target);
});
timeline.play();
}
Taking a snapshot in this way is an inherently slow operation, there's not a great deal that can be done to speed it up while staying in Java land. As suggested in the comment, if you really want to take a snapshot then a better approach would be to use the asynchronous method, which won't block the UI thread while it runs (so while they'll still be a delay, your app will still remain responsive.)
However, if I've understood your example correctly, there's absolutely no need to use screenshots at all - why are you using images rather than just animating the nodes themselves? Remember that all JavaFX elements are nodes of the scenegraph, so can be animated in the same way. So instead of:
StackPane animation = new StackPane(leftImage, rightImage);
You should just be able to do:
StackPane animation = new StackPane(source, target);
...then use this to animate the panes directly without going through the slow process of taking screenshots.

Texturing an Imported Triangle Mesh Javafx

I created a design in blender exported to STL and used the StlModelImporterJFX to import it into my JavaFX program and run it. Everything runs fine, the application works, there is just one thing missing...texture, so basically, I want to take my imported mesh and create an image as seen below for a smaller design.
Is there any program or algorithm that I can use to create an image such as that below that I can later edit manually and use as a texture for the entire Triangle Mesh? Also on a side note is it possible to edit this image live in the program and swap out colours while running? Sorry if this is poorly worded, if you want any clarification, I can provide it.
When you import a 3D model with a third-party 3D importer you have less control of the resulting TriangleMesh. If you want to provide texture features to your model you'll have to edit the exported file and add the texture coordinates, which is not the best approach.
But if you could generate the mesh from scratch, you could easily apply textures over it.
This question shows how you can define the texture coordinates and uses the same net image you have to provide the texture of an icosahedron.
Based on the answer on that question, the texture can be defined without an actual image, just with a palette of colors.
And you can easily change those on runtime, i.e. when you click on one face you can change the color on that face.
The Fxyz library makes use of a TexturedMesh, designed to easily apply textures to 3D shapes.
You can find many primitives there, like the icosahedron.
This question shows the result of different texture modes over an icosahedron.
This short snippet shows how you can apply a texture over faces, and change it on runtime:
private int numColors = 10;
#Override
public void start(Stage primaryStage) {
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-5);
IcosahedronMesh icoFaces = new IcosahedronMesh(100, 0);
icoFaces.setTextureModeFaces(numColors);
icoFaces.getTransforms().addAll(new Rotate(20, Rotate.X_AXIS), new Rotate(-10, Rotate.Y_AXIS));
final Group group = new Group(icoFaces);
Scene scene = new Scene(group, 600, 400, true, SceneAntialiasing.BALANCED);
scene.setCamera(camera);
primaryStage.setScene(scene);
primaryStage.setTitle(("Icosahedron - FXyz3D"));
primaryStage.show();
icoFaces.setOnMouseClicked(e -> {
ObservableFaceArray faces = ((TriangleMesh) icoFaces.getMesh()).getFaces();
int selectedFace = e.getPickResult().getIntersectedFace();
int colorId = faces.get(6 * selectedFace + 1);
int newColorId = colorId + 1 >= numColors ? 0 : colorId + 1;
faces.set(6 * selectedFace + 1, newColorId);
faces.set(6 * selectedFace + 3, newColorId);
faces.set(6 * selectedFace + 5, newColorId);
});
}
Running the application:
And after clicking in the frontal green face:

How to fill a JavaFX Sphere with two Colors

How can I fill in JavaFX a 3D Sphere with a linear gradient like a 2d Circle?
I work with the JavaFX Scene Builder.
As #mohsenmadi has pointed out, the diffuse color doesn't allow you using other than one single color.
But you can have different colors on the sphere by using an image as a diffuse map.
Based on your first image, I've created this texture image (called diffuse.jpg, and placed under the same folder as the JavaFX class):
You can create now your bicolored sphere:
#Override
public void start(Stage primaryStage) throws Exception {
// 3D
Sphere sphere = new Sphere(5);
PhongMaterial phongMaterial = new PhongMaterial();
phongMaterial.setDiffuseMap(new Image(getClass().getResource("diffuse.jpg").toExternalForm()));
sphere.setMaterial(phongMaterial);
...
}
So you will see this:
Note that you may have some side effects on the poles.
You can also have a look at the FXyz project, a library with aditional JavaFX 3D complex shapes, and also complex texture options.
For instance, you can use a density map to create the same effect you want, but without providing the texture image.
Under org/fxyz/shapes/primitives you can find several primitives like SegmentedSphereMesh.
Like an sphere you can create one giving the number of divisions, the crop divisions (0 in this case for x and y), the radiuos, and the center:
SegmentedSphereMesh sphere = new SegmentedSphereMesh(200,0,0,100,new Point3D(0f,0f,0f));
Now you can define the function:
Function<Point3D, Number> dens = p->p.y>0?1:0;
and apply it, with the number of colors (2 in this case):
sphere.setTextureModeVertices3D(2,dens);
Now you will have this:
Now you won't have side effects on the poles, and you could modify this function easily to other cases.
Note that you can add create your own palette of colors or play with the HSB function under org/fxyz/utils/Palette.
The way to achieve gradient-like effects on 3D shapes is by applying lighting material and lighting position. You can't simply apply two colours that gradually transform into each other. I cooked for you a small app that shows just how to achieve this.
public class ShadedSphere extends Application {
public void start(Stage stage) {
StackPane layout = new StackPane();
layout.setPrefSize(300, 300);
Scene scene = new Scene(layout);
createScene(scene);
stage.setScene(scene);
stage.show();
}
private void createScene(Scene scene) {
PhongMaterial material = new PhongMaterial();
material.setDiffuseColor(Color.ORANGE);
material.setSpecularColor(Color.BLACK);
Sphere sphere = new Sphere(100);
sphere.setMaterial(material);
Pane root = (Pane) scene.getRoot();
root.getChildren().add(sphere);
}
public static void main(String[] args) {
launch(args);
}
}
Which will give you this:
If you change the location of the sphere (e.g., using setTranslateX() and same for Y and Z), you should notice different effects of lighting on it; so the next thing for you to grasp is how to control location of lighting fixtures. Also, lights can have colour! Which means you can achieve even Northern Lights effects if you want to see cool stuff.
To learn a bit more about lighting, camera and effects, see this link.

Disable automatic resizing, without disabling manual resizing

Coding a GUI in a Java project I've encountered a problem with JavaFX.
I've not found any soulutions for my specific problem, so here I am:)
Is it possible to let a JavaFX scene be resizeable by the user and at the same time have it not resized by child nodes, that are bigger, than the window?
Here is some sample code:
#Override
public void startGUI(int width, int height) {
this.main = new MainWindow(this, this.logic);
this.scene = new Scene(this.main.getRoot());
this.main.setScene(this.scene);
this.primaryStage.setScene(this.scene);
this.primaryStage.setMinHeight(height);
this.primaryStage.setMinWidth(width);
}
The 'MainWindow' has got a childnode, that can be very big (>1024x768).
I want the window not to be resized by this node, but at the same time, the user should be able to resize the window by dragging its borders.
Use a Scene constructor which specifies initial size constraints.
For example:
Scene scene = new Scene(root, 600, 400);
That way the initial size of the Scene will be taken from these constraints rather than calculated from the preferred size of the root node.

Categories