I am doing Animation of array of Images using Timeline Animation. The array is 299 images. It iterates once from 0 to 298 images and then animation stops.
It should continuously animate but does not work. I am using opacityProperty() for each imageview using timeline animation. Once one image animation is complete then it goes to next image. But I cannot loop continously when it reaches 298 image. The variable x should become 0 and then again start the animation.
public class Animation_Program_version3 extends Application {
Timeline timeline = null;
Group rootGroup = null;
int x = 0;
Image [] images = new Image[299];;
ArrayList imageview = null;
public Animation_Program_version3() {
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("JavaFX Welcome");
rootGroup = new Group();
final Scene scene = new Scene(rootGroup, 800, 400, Color.BEIGE);
imageview = new ArrayList();
int y = 0;
for(int x = -50; x < 100; x=x+1){
images[y] = new Image("/Image"+x+".jpg", true);
imageview.add(new ImageView(images[y]));
y = y+1;
}
int y1 = 150;
for(int x = 99; x > -50; x=x-1){
images[y1] = new Image("/Image"+x+".jpg", true);
imageview.add(new ImageView(images[y1]));
y1 = y1+1;
}
rootGroup.getChildren().addAll(imageview);
int x = 0;
timeline = new Timeline();
doAnimation();
primaryStage.setScene(scene);
`primaryStage.show(); `
}
<code>
public void doAnimation(){
KeyFrame[] kf = new KeyFrame[images.length];
ImageView im = (ImageView)imageview.get(x);
<code>
im.setImage(images[x]);
kf[x] = new KeyFrame(Duration.millis(1), new KeyValue(im.opacityProperty(), 0));
timeline.getKeyFrames().add(kf[x]);
// When timeline animation is finished it executes the seetOnFinished Event
timeline.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if( x == 298){
System.out.println("VALUE OF x:"+x);
x=0; -------> This is where code does not work When it reaches end of array and x initialize to 0 then animation stops.
Collections.reverse(imageview);
doAnimation();
}
/* This if loop works fine animation iterates through 0 to 298 images. */
if( x < 298){
x++;
doAnimation();
}
}
});
timeline.play();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I have corrected my program and now I am not getting this error java.lang.IllegalArgumentException: Children: duplicate children.
But still the problem is I do not see program running on the screen. I have added root Group in scene but I see nothing on screen. My new Program:
public class Animation_Program_version3 extends Application {
Timeline timeline = null;
Group rootGroup = null;
int x = 0;
Image [] images = new Image[299];;
ArrayList imageview = null;
ImageView im = new ImageView();
public Animation_Program_version3() {
// this.imageview = new TreeSet();
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("JavaFX Welcome");
rootGroup = new Group();
final Scene scene =
new Scene(rootGroup, 800, 400, Color.BEIGE);
//
// final Scene scene =
// new Scene(rootGroup, 800, 400, Color.BEIGE);
// int x = 0;
//Image [] images =
imageview = new ArrayList();
int y = 0;
for(int x = -50; x < 100; x=x+1){
images[y] = new Image("/Image"+x+".jpg", true);
imageview.add(new ImageView(images[y]));
y = y+1;
}
int y1 = 150;
for(int x = 99; x > -50; x=x-1){
images[y1] = new Image("/Image"+x+".jpg", true);
imageview.add(new ImageView(images[y1]));
// imageview[y1] = new ImageView(images[y1]);
y1 = y1+1;
}
//for (int i = 0; i < 299; i++) {
// rootGroup.getChildren().addAll(imageview);
//}
int x = 0;
timeline = new Timeline();
doAnimation();
primaryStage.setScene(scene);
primaryStage.show();
}
public void doAnimation(){
KeyFrame[] kf = new KeyFrame[images.length];
// im = (ImageView)imageview.get(x);
im.setImage(images[x]);
rootGroup.getChildren().setAll(im);
kf[x] = new KeyFrame(Duration.millis(1), new KeyValue(im.opacityProperty(), 0));
timeline.getKeyFrames().add(kf[x]);
timeline.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// timeline = null;
if( x == 298){
System.out.println("VALUE OF x:"+x);
x=0;
// Collections.reverse(imageview);
// rootGroup.getChildren().setAll(imageview);
//
doAnimation();
}
if( x < 298){
System.out.println("Inside 298 OF x:"+x);
x++;
// Animation_Program_version3.rootGroup = null;
// Animation_Program_version3.rootGroup = new Group();
doAnimation();
}
}
});
timeline.play();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
kf[x] = new KeyFrame(Duration.millis(10), new KeyValue(im.opacityProperty(), 1));
This piece of code does all the work. Here opacity goes from im.opacityProperty() which is 1 and goes to 1. So there is no fading out.
doAnimation() is recursive method and loops. We have used timeline animation to make animation smooth or else if i had used for loop then animation would flicker. This took me almost the month to make it work as I am no expert of animation used in JavaFx api.
The animation that i have done is similar to animated gif where you have series of images which when run one after another gives an animated effect.
The code still needs some work. In doAnimation() method, I have created array of KeyFrame: KeyFrame[] kf = new KeyFrame[images.length]; which i feel is not required.
Also there is no need to use Group class. I have used HBox to include ImageView. ImageView is being animated.
public class Animation_Program_version3 extends Application {
Timeline timeline = null;
Group rootGroup = null;
int x = 0;
Image [] images = new Image[299];;
ArrayList imageview = null;
ImageView im = new ImageView();
public Animation_Program_version3() {
// this.imageview = new TreeSet();
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("JavaFX Welcome");
rootGroup = new Group();
//
// final Scene scene =
// new Scene(rootGroup, 800, 400, Color.BEIGE);
// int x = 0;
//Image [] images =
imageview = new ArrayList();
int y = 0;
for(int x = -50; x < 100; x=x+1){
images[y] = new Image("/Image"+x+".jpg", true);
imageview.add(new ImageView(images[y]));
y = y+1;
}
int y1 = 150;
for(int x = 99; x > -50; x=x-1){
images[y1] = new Image("/Image"+x+".jpg", true);
imageview.add(new ImageView(images[y1]));
// imageview[y1] = new ImageView(images[y1]);
y1 = y1+1;
}
//for (int i = 0; i < 299; i++) {
// rootGroup.getChildren().addAll(imageview);
//}
HBox layout2 = new HBox();
layout2.getChildren().add(im);
Scene scene =
new Scene(layout2, 800, 400);
int x = 0;
timeline = new Timeline();
primaryStage.setScene(scene);
primaryStage.show();
doAnimation();
}
public void doAnimation(){
KeyFrame[] kf = new KeyFrame[images.length];
// im = (ImageView)imageview.get(x);
System.out.println("WHAT IS THE VALUE OF:"+x);
im.setImage(images[x]);
System.out.println(images[x]);
// rootGroup.getChildren().setAll(im);
kf[x] = new KeyFrame(Duration.millis(10), new KeyValue(im.opacityProperty(), 1));
timeline.getKeyFrames().add(kf[x]);
timeline.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// timeline = null;
if( x == 298){
System.out.println("VALUE OF x:"+x);
x=0;
// Collections.reverse(imageview);
// rootGroup.getChildren().setAll(imageview);
//
doAnimation();
}
if( x < 298){
System.out.println("Inside 298 OF x:"+x);
x++;
// im.setImage(images[x]);
// Animation_Program_version3.rootGroup = null;
// Animation_Program_version3.rootGroup = new Group();
doAnimation();
}
}
});
timeline.play();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Related
I have an object, like a box in this example.
I want this box to move in a sine to the left on the sphere when the Z-axis is rotated. But after the box has made a curve, i.e. the rotation of the Z-axis is back to 0. The sphere no longer rotates downwards as it did at the start, but upwards to the right.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.*;
import javafx.stage.Stage;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import java.util.ArrayList;
public class TestFX extends Application {
public static final float WIDTH = 1400;
public static final float HEIGHT = 1000;
private final ArrayList<String> input = new ArrayList<>();
private final Sphere sphere = new Sphere(500);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Flugzeug flieger = new Flugzeug(30, 30, 50);
flieger.setMaterial(new PhongMaterial(Color.GREEN));
flieger.setTranslateZ(330);
flieger.getTransforms().add(new Rotate(20, Rotate.X_AXIS));
sphere.translateZProperty().set(710);
sphere.translateYProperty().set(420);
PhongMaterial phongMaterial = new PhongMaterial();
phongMaterial.setDiffuseMap(new Image(getClass().getResourceAsStream("Earth_diffuse_8k.png")));
sphere.setMaterial(phongMaterial);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(40000);
Group root = new Group(flieger, sphere);
Scene scene = new Scene(root, WIDTH, HEIGHT, true);
scene.setCamera(camera);
primaryStage.setTitle("FliegenFX");
primaryStage.setScene(scene);
primaryStage.show();
new AnimationTimer() {
#Override
public void handle(long now) {
initGameLogic(flieger);
dreheErde(flieger, sphere);
}
}.start();
scene.setOnKeyPressed(event -> {
String code = event.getCode().toString();
if (!input.contains(code))
input.add(code);
});
scene.setOnKeyReleased(event -> {
String code = event.getCode().toString();
input.remove(code);
});
}
// TODO Erddrehung nach Drehung beibehalten
private void dreheErde(Flugzeug flieger, Sphere erde) {
double rotationFactor = 0.005;
double drehung = flieger.getDrehung() * rotationFactor;
double amplitude = 2;
double frequency = 0.001;
float angle = (float) (Math.sin(drehung * frequency) * amplitude);
Quaternionf quat = new Quaternionf();
Matrix4f matrix4f = new Matrix4f();
Affine affine = new Affine();
quat.rotateY(angle);
quat.rotateZ(angle);
quat.rotateX(-0.001f);
quat.normalize();
quat.get(matrix4f);
float[] matrixArray = new float[16];
matrix4f.get(matrixArray);
double[] matrix = new double[16];
for (int i = 0 ; i < matrixArray.length; i++)
{
matrix[i] = matrixArray[i];
}
affine.append(matrix, MatrixType.MT_3D_4x4, 0);
erde.getTransforms().add(affine);
}
private void initGameLogic(Flugzeug flieger) {
if (input.contains("LEFT")) {
flieger.rotateByZ(-0.5);
}
if (input.contains("RIGHT")) {
flieger.rotateByZ(0.5);
}
}
private class Flugzeug extends Box{
private double drehung = 0;
private Rotate r;
private Transform t = new Rotate();
public Flugzeug(double width, double height, double depth){
super(width, height, depth);
}
public void rotateByZ(double ang){
drehung += ang;
r = new Rotate(ang, Rotate.Z_AXIS);
t = t.createConcatenation(r);
getTransforms().clear();
getTransforms().addAll(t);
}
public double getDrehung() {
return drehung;
}
}
}
I have tried many different ways with trigonometry but they have not been successful. This was for example to move the X-axis of the sphere also according to the sine or to subtract the movements of the Y and Z axis of the sphere as cosine.
I think it has to be transformed in a Rotation Matrix, but i‘ve never had matrix calculation.
Rotating a Group inside another Group in 3d scene
In this approach Box instance doesn't seem to move 'cause it's moving with the camera . Camera and box are moving together because is it's Group parent who is rotating with keyboard event . The Sphere node is in another group and is not affected . Sphere itself is rotating in Y axis
App.java
public class App extends Application {
#Override
public void start(Stage stage) {
Shape3D sphere = new Sphere(10);
PhongMaterial mat = new PhongMaterial();
mat.setDiffuseMap(new Image("https://www.h-schmidt.net/map/map.jpg"));
sphere.setMaterial(mat);
RotateTransition sphereRotation = new RotateTransition(Duration.seconds(40), sphere);
sphereRotation.setAxis(Rotate.Y_AXIS);
sphereRotation.setToAngle(360);
sphereRotation.setInterpolator(Interpolator.LINEAR);
sphereRotation.setCycleCount(Animation.INDEFINITE);
sphereRotation.play();
Shape3D box = new Box(2, 2, 2);
box.setMaterial(new PhongMaterial(Color.ORANGE));
box.setTranslateZ(-11);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-30);
var planeGroup = new Group(box, camera);
Group sphereGroup = new Group(sphere);
Group group3d = new Group(planeGroup, sphereGroup);
Scene scene = new Scene(group3d, 640, 480, true, SceneAntialiasing.BALANCED);
scene.setOnKeyPressed((t) -> {
if (t.getCode() == KeyCode.UP) {
Rotate r = new Rotate(-2);
r.setAxis(Rotate.X_AXIS);
planeGroup.getTransforms().add(r);
}
if (t.getCode() == KeyCode.LEFT) {
Rotate r = new Rotate(2);
r.setAxis(Rotate.Y_AXIS);
planeGroup.getTransforms().add(r);
}
if (t.getCode() == KeyCode.RIGHT) {
Rotate r = new Rotate(-2);
r.setAxis(Rotate.Y_AXIS);
planeGroup.getTransforms().add(r);
}
if (t.getCode() == KeyCode.DOWN) {
Rotate r = new Rotate(2);
r.setAxis(Rotate.X_AXIS);
planeGroup.getTransforms().add(r);
}
});
scene.setCamera(camera);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I wanna move 3 Shapes at the same time in my scene. I already tried a lot with Threats but nothing worked so far. I hope you can help me, so I am able to see my 3 shapes moving from one side to the other side. Here is my Class:
public class TestingArea {
private Stage stage;
private Scene scene;
private Group root;
private ObservableList<Shape> shapes;
public TestingArea() {
shapeTest();
stage = new Stage();
stage.setScene(scene);
stage.show();
}
//This method is called by the main class, right after
//this stage here is shown
public void startAnimation() {
animationGo();
}
private void shapeTest() {
root = new Group();
shapes = FXCollections.observableArrayList();
int startX = 100;
int startY = 100;
int Size = 10;
//Here i just draw 3 triangles
Polygon poly = new Polygon(startX, startY, startX+Size*10, startY,
startX+Size*10/2, startY+Size*10);
for(Double doub: poly.getPoints()) {
System.out.println(doub);
}
int newStartX = poly.getPoints().get(2).intValue();
int newStartY = poly.getPoints().get(3).intValue();
Polygon poly2 = new Polygon(newStartX, newStartY
,newStartX+Size*10, newStartY
,newStartX+Size*10/2, startY+Size*10
);
int nextStartX = startX+Size*10/2;
int nextStartY = startY+Size*10;
Polygon poly3 = new Polygon(nextStartX, nextStartY
,nextStartX+Size*10, nextStartY
,nextStartX+Size*10/2, nextStartY+Size*10 );
//Add shapes to a List
shapes.add(poly);
shapes.add(poly2);
shapes.add(poly3);
//Add shapes to root
root.getChildren().add(poly);
root.getChildren().add(poly2);
root.getChildren().add(poly3);
scene = new Scene(root);
}
/**
* This method should move my 3 triangles 10 times and
* i wanna see every move.
*/
private void animationGo() {
for(int i = 0; i < 10; i++) {
for(Shape shape: shapes) {
double x = shape.getLayoutX();
shape.setLayoutX(x-10);
}
}
}
}
I create Shape and I need to position on a Canvas
Class Square to draw a square and insert into a canvas position
public class Square{
//calculate the position of the rand column to
//draw and insert in the position of the canvas
public void drawSquare(int posX, int posY, GraphicsContext gc) {
//Square Shadow
//gc.rect(posX, posY, w, h);
gc.rect(posX + 1, posY + 53, 50, 50);
gc.fill();
gc.beginPath();
//Square
gc.beginPath();
gc.setFill(Color.WHITE);
gc.setStroke(Color.BLACK);
gc.setLineWidth(2);
//gc.rect(posX, posY, w, h);
gc.rect(posX + 1, posY + 53, 48, 48);
gc.fill();
gc.stroke();
}
}
New Canvas instance with height = 450 and width = 600
Canvas canvas = new Canvas();
canvas.setHeight(450);
canvas.setWidth(600);
and GraphicsContext to draw square
GraphicsContext gc = canvas.getGraphicsContext2D();
with this loop, draw 4 rows and 6 columns with square in canvas,
and my doubt is how to calculate the position of the line and column to draw square and insert in the position of the canvas when I call pieces.drawSquare(i, j, gc);, and method drawSquare creates the shape but the doubt is how to position them if it is more than one shape
for (int i = 0; i < 4; i++) { //4 rows
for (int j = 0; j < 6; i++) { //6 columns
Piece pieces = new Piece();
pieces.drawSquare(i, j, gc);
}
this image is the example,
and the objective is to fill in 4 rows and 6 columns
I have already thought about dividing the size and width of Canvas with the size and width of the shape but it is not working, maybe can have another solution
I think this can get you jump starting
public class Main extends Application {
private SimpleIntegerProperty rowProperty = new SimpleIntegerProperty(4); //default
private SimpleIntegerProperty columnProperty = new SimpleIntegerProperty(6);//default
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
root.setPadding(new Insets(5));
HBox top;
TextField rowField = new TextField();
rowField.setMaxWidth(60);
rowField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
try{ rowProperty.setValue(Integer.valueOf(newValue));}catch(NumberFormatException e){}
}
});
TextField colField = new TextField();
colField.setMaxWidth(60);
colField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
try{ columnProperty.setValue(Integer.valueOf(newValue));}catch(NumberFormatException e){}
}
});
top = new HBox(10,new Label("ROW FIELD"),rowField, new Label("COLUMN FIELD"),colField);
top.setAlignment(Pos.CENTER);
root.setStyle("-fx-background-color: white;");
root.setTop(top);
////////////////////////////////////////////////////////////////////////////////////
Canvas canvas = new Canvas(500,400);
canvas.getGraphicsContext2D().setFill(Color.BLACK);
canvas.getGraphicsContext2D().setStroke(Color.GOLD);
ChangeListener<Number> chan = new ChangeListener<Number>() {
int space = 2;
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
///i will draw here
canvas.getGraphicsContext2D().clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
int rectW = (int) canvas.getWidth();
rectW = rectW/columnProperty.intValue();
int rectH = (int) canvas.getHeight();
rectH = rectH/rowProperty.intValue();
System.out.println(rectW);
System.out.println(rectH);
for(int k = 0; k < canvas.getHeight()/rectH; k++){
for(int i =0; i< canvas.getWidth()/rectW; i++){
canvas.getGraphicsContext2D().fillRect((i*rectW) + (i*space),
(k*rectH) + (k*space),
rectW, rectH);
}
}
}
};
rowProperty.addListener(chan);
columnProperty.addListener(chan);
//////////////////////////////////////////////////////////////////////////////////////////
root.setCenter(canvas);
Label l = new Label("ENTER NUMBERS TO FIELDS TO SEE IT");
l.setStyle("-fx-background-color: blueviolet; -fx-text-fill: white;");
l.setPrefWidth(Double.MAX_VALUE);
l.setAlignment(Pos.CENTER);
root.setBottom(l);
Scene scene = new Scene(root,500,500);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) { e.printStackTrace(); }
}
public static void main(String[] args) {
launch(args);
}
}
in my Application below, red blocks are falling from the top of the screen. My problem is, that after 3-5 minutes the animation begins to lag and I can't figure out why.
I used a profiler and found out, that it's not a memory leak but it must be a resource leak, I am pretty sure because the Cpu Load increases over time.
If you want to, you can just implement the necessary classes and compile and run the code, then you should probably see the problem. (after 3-5 minutes)
Thanks for your help and have a great day! :)
public class RBYDebug extends Application {
Group root = new Group();
ArrayList<Rectangle> entities = new ArrayList<>();
Random random = new Random();
int sW = 1600; //screen Width
int sH = 980; //screen Height
int dy = 4; //vertical move speed
public static void main(String[] args){
Application.launch(args);
}
public void start(Stage primaryStage){
//Setup Entities
for (int i = 0; i < 40; i++){
double x = random.nextDouble() * sW;
double y = i * 50;
Rectangle rect = new Rectangle(x, -y -50, 50, 50);
rect.setFill(Color.RED);
entities.add(rect);
}
//Root-Group setup
root.getChildren().setAll(entities);
//Setup Scene
Scene scene = new Scene(root, sW, sH, Color.BLACK);
//Stage setup
primaryStage.setTitle("RBY Debug");
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
//Setup Timer
AnimationTimer timer = new AnimationTimer(){
public void handle(long now){
update();
}
};
timer.start();
}
public void update(){
for (Rectangle i : entities){
if (i.getY() <= sH){
i.setY(i.getY() + dy);
}
else {
i.setY(-i.getHeight());
}
}
root.getChildren().setAll(entities);
}
}
Due to new features in JavaFX 8, it became possible to combine 3D objects with 2D UI controls.
I used this documents as manuals: JavaFX Tutorial, Exploring JavaFX 3D.
So, I made this code:
public class CastAnalytics extends Application {
final Group root = new Group();
final Group axisGroup = new Group();
final XForm world = new XForm();
final PerspectiveCamera camera = new PerspectiveCamera(true);
final PerspectiveCamera subSceneCamera = new PerspectiveCamera(false);
final XForm cameraXForm = new XForm();
final XForm cameraXForm2 = new XForm();
final XForm cameraXForm3 = new XForm();
final double cameraDistance = 450;
final XForm moleculeGroup = new XForm();
private Timeline timeline;
boolean timelinePlaying = false;
double CONTROL_MULTIPLIER = 0.1;
double SHIFT_MULTIPLIER = 0.1;
double ALT_MULTIPLIER = 0.5;
double mousePosX;
double mousePosY;
double mouseOldX;
double mouseOldY;
double mouseDeltaX;
double mouseDeltaY;
#Override
public void start(Stage primaryStage) throws Exception{
buildScene();
buildCamera();
buildAxes();
Scene scene = new Scene(root, 1024, 768, true);
scene.setFill(Color.GREY);
handleKeyboard(scene, world);
handleMouse(scene, world);
primaryStage.setTitle("Sample Application");
primaryStage.setScene(scene);
primaryStage.show();
scene.setCamera(subSceneCamera);
scene.setCamera(camera);
}
private void buildScene() {
root.getChildren().add(world);
Label label = new Label("123");
HBox hBox = new HBox();
hBox.getChildren().add(label);
SubScene subScene = new SubScene(hBox, 200, 200);
subScene.setLayoutX(100);
subScene.setLayoutY(100);
root.getChildren().addAll(subScene);
}
private void buildCamera() {
root.getChildren().addAll(cameraXForm);
cameraXForm.getChildren().add(cameraXForm2);
cameraXForm2.getChildren().add(cameraXForm3);
cameraXForm3.getChildren().add(camera);
cameraXForm3.setRotateZ(180.0);
camera.setNearClip(0.1);
camera.setFarClip(10000.0);
camera.setTranslateZ(-cameraDistance);
cameraXForm.ry.setAngle(320.0);
cameraXForm.rx.setAngle(40);
}
private void buildAxes() {
Box box = new Box(200,200,200);
axisGroup.getChildren().addAll(box);
world.getChildren().addAll(axisGroup);
}
private void handleMouse(Scene scene, final Node root) {
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent me) {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
}
});
scene.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
double modifier = 1.0;
double modifierFactor = 0.1;
if (me.isControlDown()) {
modifier = 0.1;
}
if (me.isShiftDown()) {
modifier = 10.0;
}
if (me.isPrimaryButtonDown()) {
cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() - mouseDeltaX * modifierFactor * modifier * 2.0); // +
cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() + mouseDeltaY * modifierFactor * modifier * 2.0); // -
} else if (me.isSecondaryButtonDown()) {
double z = camera.getTranslateZ();
double newZ = z + mouseDeltaX * modifierFactor * modifier;
camera.setTranslateZ(newZ);
} else if (me.isMiddleButtonDown()) {
cameraXForm2.t.setX(cameraXForm2.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
cameraXForm2.t.setY(cameraXForm2.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
}
}
});
}
private void handleKeyboard(Scene scene, final Node root) {
final boolean moveCamera = true;
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
Duration currentTime;
switch (event.getCode()) {
case Z:
if (event.isShiftDown()) {
cameraXForm.ry.setAngle(0.0);
cameraXForm.rx.setAngle(0.0);
camera.setTranslateZ(-300.0);
}
cameraXForm2.t.setX(0.0);
cameraXForm2.t.setY(0.0);
break;
case X:
if (event.isControlDown()) {
if (axisGroup.isVisible()) {
axisGroup.setVisible(false);
} else {
axisGroup.setVisible(true);
}
}
break;
case S:
if (event.isControlDown()) {
if (moleculeGroup.isVisible()) {
moleculeGroup.setVisible(false);
} else {
moleculeGroup.setVisible(true);
}
}
break;
case SPACE:
if (timelinePlaying) {
timeline.pause();
timelinePlaying = false;
} else {
timeline.play();
timelinePlaying = true;
}
break;
case UP:
if (event.isControlDown() && event.isShiftDown()) {
cameraXForm2.t.setY(cameraXForm2.t.getY() - 10.0 * CONTROL_MULTIPLIER);
} else if (event.isAltDown() && event.isShiftDown()) {
cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() - 10.0 * ALT_MULTIPLIER);
} else if (event.isControlDown()) {
cameraXForm2.t.setY(cameraXForm2.t.getY() - 1.0 * CONTROL_MULTIPLIER);
} else if (event.isAltDown()) {
cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() - 2.0 * ALT_MULTIPLIER);
} else if (event.isShiftDown()) {
double z = camera.getTranslateZ();
double newZ = z + 5.0 * SHIFT_MULTIPLIER;
camera.setTranslateZ(newZ);
}
break;
case DOWN:
if (event.isControlDown() && event.isShiftDown()) {
cameraXForm2.t.setY(cameraXForm2.t.getY() + 10.0 * CONTROL_MULTIPLIER);
} else if (event.isAltDown() && event.isShiftDown()) {
cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() + 10.0 * ALT_MULTIPLIER);
} else if (event.isControlDown()) {
cameraXForm2.t.setY(cameraXForm2.t.getY() + 1.0 * CONTROL_MULTIPLIER);
} else if (event.isAltDown()) {
cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() + 2.0 * ALT_MULTIPLIER);
} else if (event.isShiftDown()) {
double z = camera.getTranslateZ();
double newZ = z - 5.0 * SHIFT_MULTIPLIER;
camera.setTranslateZ(newZ);
}
break;
case RIGHT:
if (event.isControlDown() && event.isShiftDown()) {
cameraXForm2.t.setX(cameraXForm2.t.getX() + 10.0 * CONTROL_MULTIPLIER);
} else if (event.isAltDown() && event.isShiftDown()) {
cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() - 10.0 * ALT_MULTIPLIER);
} else if (event.isControlDown()) {
cameraXForm2.t.setX(cameraXForm2.t.getX() + 1.0 * CONTROL_MULTIPLIER);
} else if (event.isAltDown()) {
cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() - 2.0 * ALT_MULTIPLIER);
}
break;
case LEFT:
if (event.isControlDown() && event.isShiftDown()) {
cameraXForm2.t.setX(cameraXForm2.t.getX() - 10.0 * CONTROL_MULTIPLIER);
} else if (event.isAltDown() && event.isShiftDown()) {
cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() + 10.0 * ALT_MULTIPLIER); // -
} else if (event.isControlDown()) {
cameraXForm2.t.setX(cameraXForm2.t.getX() - 1.0 * CONTROL_MULTIPLIER);
} else if (event.isAltDown()) {
cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() + 2.0 * ALT_MULTIPLIER); // -
}
break;
}
}
});
}
public static void main(String[] args) {
launch(args);
}
}
But te result isn't that what I expected. I wanted to have Pane for UI controls above the 3D object, but what I get is this:
What am I doing wrong?
From what I understand from the (limited) tests I have done, there are two options:
Set a camera for a sub-scene and add that sub-scene to the root. You will be using only one camera. Your world will have to be a separate group and flying/pivoting camera view will have to be accomplished by transforming the world group.
File a bug report with JavaFX jira.
I was not successful using a separate camera as a sub-scene camera. No transforms applied to the camera or a sub-scene itself ever rotated a sub-scene from the default position similar to the one in your screenshot. At this point with Oracle not releasing any sub-scene documentation, we can only wait till they come clean and fill the gaps. Until then we can consider subscene support in JavaFX 3D broken.
The problem is here:
scene.setCamera(subSceneCamera);
scene.setCamera(camera);
You can set only one camera in Scene (or SubScene).
You need to set second Camera in SubScene. Try something like this:
#Override
public void start(Stage primaryStage) throws Exception{
...
primaryStage.show();
scene.setCamera(camera);
}
private void buildScene() {
...
subScene.setLayoutX(100);
subScene.setLayoutY(100);
subScene.setCamera(subSceneCamera);
root.getChildren().addAll(subScene);
}
Here is the Solution
public class rotate3Dwithpanel extends Application
{
private double mouseOldX, mouseOldY = 0;
private Rotate rotateX = new Rotate(0, Rotate.X_AXIS);
private Rotate rotateY = new Rotate(0, Rotate.Y_AXIS);
private Rotate rotateZ = new Rotate(0, Rotate.Z_AXIS);
#Override
public void start(Stage stage) throws Exception
{
final PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setSpecularColor(Color.ORANGE);
redMaterial.setDiffuseColor(Color.RED);
Box myBox = new Box(100, 100, 100);
myBox.setTranslateX(400);
myBox.setTranslateY(300);
myBox.setTranslateZ(400);
myBox.setMaterial(redMaterial);
Rectangle rectangle = new Rectangle();
rectangle.setX(200);
rectangle.setY(600);
rectangle.setWidth(200);
rectangle.setHeight(100);
rectangle.setFill(Color.GREY);
// to Set pivot points
rotateX.setPivotX(400);
rotateX.setPivotY(300);
rotateX.setPivotZ(400);
rotateY.setPivotX(400);
rotateY.setPivotY(300);
rotateY.setPivotZ(400);
rotateZ.setPivotX(400);
rotateZ.setPivotY(300);
rotateZ.setPivotZ(400);
// initialize the camera
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.getTransforms().addAll (rotateX, rotateY, new Translate(0, 0, 0));
Group root = new Group();
Group subRoot = new Group();
root.getChildren().add(rectangle);
Scene scene = new Scene(root, 1000, 1000, true);
SubScene subScene = new SubScene(subRoot, 800, 800, true, SceneAntialiasing.BALANCED);
subScene.setCamera(camera);
subRoot.getChildren().add(myBox);
root.getChildren().add(subScene);
// events for rotation
rectangle.setOnMousePressed(event -> {
mouseOldX = event.getSceneX();
mouseOldY = event.getSceneY();
});
rectangle.setOnMouseDragged(event -> {
if(event.isPrimaryButtonDown())
{
rotateX.setAngle(rotateX.getAngle() - (event.getSceneY() - mouseOldY));
rotateY.setAngle(rotateY.getAngle() + (event.getSceneX() - mouseOldX));
mouseOldX = event.getSceneX();
mouseOldY = event.getSceneY();
}
});
stage.setTitle("JavaFX 3D Object");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}