    001//License: GPL. For details, see LICENSE file.
    002package org.openstreetmap.josm.plugins.streetside.gui.imageinfo;
    003
    004import java.awt.image.BufferedImage;
    005
    006import org.openstreetmap.josm.plugins.streetside.cubemap.CameraTransformer;
    007import org.openstreetmap.josm.plugins.streetside.cubemap.GraphicsUtils;
    008import org.openstreetmap.josm.plugins.streetside.utils.CubemapBox;
    009
    010import javafx.application.Platform;
    011import javafx.embed.swing.JFXPanel;
    012import javafx.scene.Group;
    013import javafx.scene.PerspectiveCamera;
    014import javafx.scene.PointLight;
    015import javafx.scene.Scene;
    016import javafx.scene.SceneAntialiasing;
    017import javafx.scene.control.TextArea;
    018import javafx.scene.image.Image;
    019import javafx.scene.input.KeyCode;
    020import javafx.scene.input.MouseEvent;
    021import javafx.scene.layout.VBox;
    022import javafx.scene.paint.Color;
    023import javafx.scene.transform.NonInvertibleTransformException;
    024
    025
    026@SuppressWarnings("restriction")
    027public class ThreeSixtyDegreeViewerPanel extends JFXPanel {
    028
    029        private static final long serialVersionUID = -4940350009018422000L;
    030
    031        private static Scene cubemapScene;
    032
    033        private static Scene defaultScene;
    034
    035        private static Group root;
    036        private static Group subGroup;
    037        private static CubemapBox cubemapBox;
    038        private static PerspectiveCamera camera;
    039        private static CameraTransformer cameraTransform = new CameraTransformer();
    040
    041        private static double mousePosX;
    042        private static double mousePosY;
    043        private static double mouseOldX;
    044        private static double mouseOldY;
    045        private static double mouseDeltaX;
    046        private static double mouseDeltaY;
    047        private static double cameraDistance = 5000;
    048
    049        // Supply Image Paths or a NullPointer will occur
    050        private static Image front;
    051        private static Image right;
    052        private static Image back;
    053        private static Image left;
    054        private static Image up;
    055        private static Image down;
    056
    057        public ThreeSixtyDegreeViewerPanel() {
    058                // constructor
    059        }
    060
    061        public void initialize() {
    062
    063                root = new Group();
    064
    065                camera = new PerspectiveCamera(true);
    066                cameraTransform.setTranslate(0, 0, 0);
    067                cameraTransform.getChildren().addAll(camera);
    068                camera.setNearClip(0.1);
    069                camera.setFarClip(1000000.0);
    070                camera.setFieldOfView(42);
    071                camera.setTranslateZ(-cameraDistance);
    072                // cameraTransform.ry.setAngle(-45.0);
    073                // cameraTransform.rx.setAngle(-10.0);
    074                // add a Point Light for better viewing of the grid coordinate system
    075                final PointLight light = new PointLight(Color.WHITE);
    076
    077                cameraTransform.getChildren().add(light);
    078                light.setTranslateX(camera.getTranslateX());
    079                light.setTranslateY(camera.getTranslateY());
    080                light.setTranslateZ(camera.getTranslateZ());
    081
    082                root.getChildren().add(cameraTransform);
    083
    084                final double size = 100000D;
    085
    086                cubemapBox = new CubemapBox(front, right, back, left, up, down, size, camera);
    087
    088                subGroup = new Group();
    089                subGroup.getChildren().add(cameraTransform);
    090
    091                Platform.runLater(new Runnable() {
    092                        @Override
    093                        public void run() {
    094                                //try {
    095                                        setScene(createDefaultScene());
    096                                  //setScene(createScene());
    097                                /*} catch (NonInvertibleTransformException e) {
    098                                        Logging.error(I18n.tr("Error initializing StreetsideViewerPanel - JavaFX {0}", e.getMessage()));
    099                                }*/
    100                        }
    101                });
    102        }
    103
    104        public static Scene createScene() /*throws NonInvertibleTransformException*/ {
    105
    106                root = new Group();
    107
    108                camera = new PerspectiveCamera(true);
    109                cameraTransform.setTranslate(0, 0, 0);
    110                cameraTransform.getChildren().addAll(camera);
    111                camera.setNearClip(0.1);
    112                camera.setFarClip(1000000.0);
    113                camera.setFieldOfView(42);
    114                camera.setTranslateZ(-cameraDistance);
    115                final PointLight light = new PointLight(Color.WHITE);
    116
    117                cameraTransform.getChildren().add(light);
    118                light.setTranslateX(camera.getTranslateX());
    119                light.setTranslateY(camera.getTranslateY());
    120                light.setTranslateZ(camera.getTranslateZ());
    121
    122                root.getChildren().add(cameraTransform);
    123
    124                // Load Cubemap box AFTER camera is initialized
    125                final double size = 100000D;
    126
    127                cubemapBox = new CubemapBox(null, null, null, null, null, null, size, camera);
    128
    129                subGroup = new Group();
    130                subGroup.getChildren().add(cameraTransform);
    131
    132                final Scene scene = new Scene(new Group(root), 1024, 668, true, SceneAntialiasing.BALANCED);
    133                scene.setFill(Color.TRANSPARENT);
    134                scene.setCamera(camera);
    135
    136                // First person shooter keyboard movement
    137                scene.setOnKeyPressed(event -> {
    138                        double change = 10.0;
    139                        // Add shift modifier to simulate "Running Speed"
    140                        if (event.isShiftDown()) {
    141                                change = 50.0;
    142                        }
    143                        // What key did the user press?
    144                        final KeyCode keycode = event.getCode();
    145                        // Step 2c: Add Zoom controls
    146                        if (keycode == KeyCode.W) {
    147                                camera.setTranslateZ(camera.getTranslateZ() + change);
    148                        }
    149                        if (keycode == KeyCode.S) {
    150                                camera.setTranslateZ(camera.getTranslateZ() - change);
    151                        }
    152                        // Step 2d: Add Strafe controls
    153                        if (keycode == KeyCode.A) {
    154                                camera.setTranslateX(camera.getTranslateX() - change);
    155                        }
    156                        if (keycode == KeyCode.D) {
    157                                camera.setTranslateX(camera.getTranslateX() + change);
    158                        }
    159                });
    160
    161                scene.setOnMousePressed((MouseEvent me) -> {
    162                        mousePosX = me.getSceneX();
    163                        mousePosY = me.getSceneY();
    164                        mouseOldX = me.getSceneX();
    165                        mouseOldY = me.getSceneY();
    166                });
    167                scene.setOnMouseDragged((MouseEvent me) -> {
    168                        mouseOldX = mousePosX;
    169                        mouseOldY = mousePosY;
    170                        mousePosX = me.getSceneX();
    171                        mousePosY = me.getSceneY();
    172                        mouseDeltaX = mousePosX - mouseOldX;
    173                        mouseDeltaY = mousePosY - mouseOldY;
    174
    175                        double modifier = 10.0;
    176                        final double modifierFactor = 0.1;
    177
    178                        if (me.isControlDown()) {
    179                                modifier = 0.1;
    180                        }
    181                        if (me.isShiftDown()) {
    182                                modifier = 50.0;
    183                        }
    184                        if (me.isPrimaryButtonDown()) {
    185                                cameraTransform.ry.setAngle(
    186                                                ((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540)
    187                                                                % 360 - 180); // +
    188                                cameraTransform.rx.setAngle(
    189                                                ((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540)
    190                                                                % 360 - 180); // -
    191
    192                        } else if (me.isSecondaryButtonDown()) {
    193                                final double z = camera.getTranslateZ();
    194                                final double newZ = z + mouseDeltaX * modifierFactor * modifier;
    195                                camera.setTranslateZ(newZ);
    196                        } else if (me.isMiddleButtonDown()) {
    197                                cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
    198                                cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
    199                        }
    200                });
    201
    202                /*scene.widthProperty().addListener(new ChangeListener<Number>() {
    203                    @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) {
    204                        System.out.println("Width: " + newSceneWidth);
    205                    }
    206
    207                        @Override
    208                        public void changed(ObservableValue<? extends Number> observable, Number oldSceneWidth, Number newSceneWidth) {
    209                                draw();
    210                        }
    211                });*/
    212                /*scene.heightProperty().addListener(new ChangeListener<Number>() {
    213                    @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
    214                        //System.out.println("Height: " + newSceneHeight);
    215                        draw();
    216                    }
    217                });*/
    218
    219                root.getChildren().addAll(cubemapBox, subGroup);
    220                root.setAutoSizeChildren(true);
    221
    222                subGroup.setAutoSizeChildren(true);
    223
    224                // prevent content from disappearing after resizing
    225                Platform.setImplicitExit(false);
    226
    227                return scene;
    228        }
    229
    230        private static Scene createDefaultScene() {
    231                // TODO: default scene with message? @rrh
    232
    233                // Load Cubemap box AFTER camera is initialized
    234                //final double size = 100000D;
    235
    236                TextArea textArea = new TextArea();
    237                textArea.setText("No Streetside image selected.");
    238
    239                VBox vbox = new VBox(textArea);
    240
    241                root = new Group();
    242
    243    camera = new PerspectiveCamera(true);
    244    cameraTransform.setTranslate(0, 0, 0);
    245    cameraTransform.getChildren().addAll(camera);
    246    camera.setNearClip(0.1);
    247    camera.setFarClip(1000000.0);
    248    camera.setFieldOfView(42);
    249    camera.setTranslateZ(-cameraDistance);
    250    final PointLight light = new PointLight(Color.WHITE);
    251
    252    cameraTransform.getChildren().add(light);
    253    light.setTranslateX(camera.getTranslateX());
    254    light.setTranslateY(camera.getTranslateY());
    255    light.setTranslateZ(camera.getTranslateZ());
    256
    257    root.getChildren().add(cameraTransform);
    258
    259    // Load Cubemap box AFTER camera is initialized
    260    final double size = 100000D;
    261
    262    cubemapBox = new CubemapBox(null, null, null, null, null, null, size, camera);
    263
    264    subGroup = new Group();
    265    subGroup.getChildren().add(cameraTransform);
    266
    267    /*final Scene*/ cubemapScene = new Scene(new Group(root), 1024, 668, true, SceneAntialiasing.BALANCED);
    268    cubemapScene.setFill(Color.TRANSPARENT);
    269    cubemapScene.setCamera(camera);
    270
    271    // First person shooter keyboard movement
    272    cubemapScene.setOnKeyPressed(event -> {
    273      double change = 10.0;
    274      // Add shift modifier to simulate "Running Speed"
    275      if (event.isShiftDown()) {
    276        change = 50.0;
    277      }
    278      // What key did the user press?
    279      final KeyCode keycode = event.getCode();
    280      // Step 2c: Add Zoom controls
    281      if (keycode == KeyCode.W) {
    282        camera.setTranslateZ(camera.getTranslateZ() + change);
    283      }
    284      if (keycode == KeyCode.S) {
    285        camera.setTranslateZ(camera.getTranslateZ() - change);
    286      }
    287      // Step 2d: Add Strafe controls
    288      if (keycode == KeyCode.A) {
    289        camera.setTranslateX(camera.getTranslateX() - change);
    290      }
    291      if (keycode == KeyCode.D) {
    292        camera.setTranslateX(camera.getTranslateX() + change);
    293      }
    294    });
    295
    296    cubemapScene.setOnMousePressed((MouseEvent me) -> {
    297      mousePosX = me.getSceneX();
    298      mousePosY = me.getSceneY();
    299      mouseOldX = me.getSceneX();
    300      mouseOldY = me.getSceneY();
    301    });
    302    cubemapScene.setOnMouseDragged((MouseEvent me) -> {
    303      mouseOldX = mousePosX;
    304      mouseOldY = mousePosY;
    305      mousePosX = me.getSceneX();
    306      mousePosY = me.getSceneY();
    307      mouseDeltaX = mousePosX - mouseOldX;
    308      mouseDeltaY = mousePosY - mouseOldY;
    309
    310      double modifier = 10.0;
    311      final double modifierFactor = 0.1;
    312
    313      if (me.isControlDown()) {
    314        modifier = 0.1;
    315      }
    316      if (me.isShiftDown()) {
    317        modifier = 50.0;
    318      }
    319      if (me.isPrimaryButtonDown()) {
    320        cameraTransform.ry.setAngle(
    321            ((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540)
    322                % 360 - 180); // +
    323        cameraTransform.rx.setAngle(
    324            ((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540)
    325                % 360 - 180); // -
    326
    327      } else if (me.isSecondaryButtonDown()) {
    328        final double z = camera.getTranslateZ();
    329        final double newZ = z + mouseDeltaX * modifierFactor * modifier;
    330        camera.setTranslateZ(newZ);
    331      } else if (me.isMiddleButtonDown()) {
    332        cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
    333        cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
    334      }
    335    });
    336
    337    /*scene.widthProperty().addListener(new ChangeListener<Number>() {
    338        @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) {
    339            System.out.println("Width: " + newSceneWidth);
    340        }
    341
    342      @Override
    343      public void changed(ObservableValue<? extends Number> observable, Number oldSceneWidth, Number newSceneWidth) {
    344        draw();
    345      }
    346    });*/
    347    /*scene.heightProperty().addListener(new ChangeListener<Number>() {
    348        @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
    349            //System.out.println("Height: " + newSceneHeight);
    350          draw();
    351        }
    352    });*/
    353
    354    root.getChildren().addAll(cubemapBox, subGroup);
    355    root.setAutoSizeChildren(true);
    356
    357    subGroup.setAutoSizeChildren(true);
    358
    359    // prevent content from disappearing after resizing
    360    Platform.setImplicitExit(false);
    361
    362    //return scene;
    363
    364                defaultScene = new Scene(vbox, 200, 100);
    365                return defaultScene;
    366        }
    367
    368        public static Scene createScene(BufferedImage img0, BufferedImage img1, BufferedImage img2, BufferedImage img3,
    369                        BufferedImage img4, BufferedImage img5) throws NonInvertibleTransformException {
    370                front = GraphicsUtils.convertBufferedImage2JavaFXImage(img0);
    371                right = GraphicsUtils.convertBufferedImage2JavaFXImage(img1);
    372                back = GraphicsUtils.convertBufferedImage2JavaFXImage(img2);
    373                left = GraphicsUtils.convertBufferedImage2JavaFXImage(img3);
    374                up = GraphicsUtils.convertBufferedImage2JavaFXImage(img4);
    375                down = GraphicsUtils.convertBufferedImage2JavaFXImage(img5);
    376
    377                root = new Group();
    378
    379                camera = new PerspectiveCamera(true);
    380                cameraTransform.setTranslate(0, 0, 0);
    381                cameraTransform.getChildren().addAll(camera);
    382                camera.setNearClip(0.1);
    383                camera.setFarClip(1000000.0);
    384                camera.setFieldOfView(42);
    385                camera.setTranslateZ(-cameraDistance);
    386                // cameraTransform.ry.setAngle(-45.0);
    387                // cameraTransform.rx.setAngle(-10.0);
    388                // add a Point Light for better viewing of the grid coordinate system
    389                final PointLight light = new PointLight(Color.WHITE);
    390
    391                cameraTransform.getChildren().add(light);
    392                light.setTranslateX(camera.getTranslateX());
    393                light.setTranslateY(camera.getTranslateY());
    394                light.setTranslateZ(camera.getTranslateZ());
    395
    396                root.getChildren().add(cameraTransform);
    397
    398                // Load Cubemap box AFTER camera is initialized
    399                final double size = 100000D;
    400
    401                cubemapBox = new CubemapBox(front, right, back, left, up, down, size, camera);
    402
    403                final Group torusGroup = new Group();
    404                torusGroup.getChildren().add(cameraTransform);
    405
    406                final Scene scene = new Scene(new Group(root), 1024, 668, true, SceneAntialiasing.BALANCED);
    407                scene.setFill(Color.TRANSPARENT);
    408                scene.setCamera(camera);
    409
    410                // First person shooter keyboard movement
    411                scene.setOnKeyPressed(event -> {
    412                        double change = 10.0;
    413                        // Add shift modifier to simulate "Running Speed"
    414                        if (event.isShiftDown()) {
    415                                change = 50.0;
    416                        }
    417                        // What key did the user press?
    418                        final KeyCode keycode = event.getCode();
    419                        // Step 2c: Add Zoom controls
    420                        if (keycode == KeyCode.W) {
    421                                camera.setTranslateZ(camera.getTranslateZ() + change);
    422                        }
    423                        if (keycode == KeyCode.S) {
    424                                camera.setTranslateZ(camera.getTranslateZ() - change);
    425                        }
    426                        // Step 2d: Add Strafe controls
    427                        if (keycode == KeyCode.A) {
    428                                camera.setTranslateX(camera.getTranslateX() - change);
    429                        }
    430                        if (keycode == KeyCode.D) {
    431                                camera.setTranslateX(camera.getTranslateX() + change);
    432                        }
    433
    434                });
    435
    436                scene.setOnMousePressed((MouseEvent me) -> {
    437                        mousePosX = me.getSceneX();
    438                        mousePosY = me.getSceneY();
    439                        mouseOldX = me.getSceneX();
    440                        mouseOldY = me.getSceneY();
    441                });
    442                scene.setOnMouseDragged((MouseEvent me) -> {
    443                        mouseOldX = mousePosX;
    444                        mouseOldY = mousePosY;
    445                        mousePosX = me.getSceneX();
    446                        mousePosY = me.getSceneY();
    447                        mouseDeltaX = mousePosX - mouseOldX;
    448                        mouseDeltaY = mousePosY - mouseOldY;
    449
    450                        double modifier = 10.0;
    451                        final double modifierFactor = 0.1;
    452
    453                        if (me.isControlDown()) {
    454                                modifier = 0.1;
    455                        }
    456                        if (me.isShiftDown()) {
    457                                modifier = 50.0;
    458                        }
    459                        if (me.isPrimaryButtonDown()) {
    460                                cameraTransform.ry.setAngle(
    461                                                ((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540)
    462                                                                % 360 - 180); // +
    463                                cameraTransform.rx.setAngle(
    464                                                ((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540)
    465                                                                % 360 - 180); // -
    466
    467                        } else if (me.isSecondaryButtonDown()) {
    468                                final double z = camera.getTranslateZ();
    469                                final double newZ = z + mouseDeltaX * modifierFactor * modifier;
    470                                camera.setTranslateZ(newZ);
    471                        } else if (me.isMiddleButtonDown()) {
    472                                cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
    473                                cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
    474                        }
    475                });
    476
    477                root.getChildren().addAll(cubemapBox, torusGroup);
    478                root.setAutoSizeChildren(true);
    479
    480                return scene;
    481        }
    482
    483        /*public void setCubemapImages(BufferedImage img, BufferedImage img1, BufferedImage img2, BufferedImage img3,
    484                        BufferedImage img4, BufferedImage img5) {
    485                cubemapBox = null;
    486
    487                GraphicsUtils.PlatformHelper.run(new Runnable() {
    488                        @Override
    489                        public void run() {
    490                                try {
    491                                        // initialize without imagery.
    492                                        scene = createScene(img, img1, img2, img3, img4, img5);
    493                                        setScene(scene);
    494                                } catch (NonInvertibleTransformException e) {
    495                                        Logging.error(I18n.tr("Error initializing StreetsideViewerPanel - JavaFX {0}", e.getMessage()));
    496                                }
    497                        }
    498                });
    499        }*/
    500
    501        public CubemapBox getCubemapBox() {
    502                if (cubemapBox == null) {
    503                        // shouldn't happen
    504                        initialize();
    505                }
    506                return cubemapBox;
    507        }
    508
    509  public Scene getDefaultScene() {
    510    return defaultScene;
    511  }
    512
    513  public Scene getCubemapScene() {
    514    return cubemapScene;
    515  }
    516}