    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;
    009import org.openstreetmap.josm.tools.I18n;
    010import org.openstreetmap.josm.tools.Logging;
    011
    012import javafx.application.Platform;
    013import javafx.embed.swing.JFXPanel;
    014import javafx.scene.Group;
    015import javafx.scene.PerspectiveCamera;
    016import javafx.scene.PointLight;
    017import javafx.scene.Scene;
    018import javafx.scene.SceneAntialiasing;
    019import javafx.scene.control.TextArea;
    020import javafx.scene.image.Image;
    021import javafx.scene.input.KeyCode;
    022import javafx.scene.input.MouseEvent;
    023import javafx.scene.layout.VBox;
    024import javafx.scene.paint.Color;
    025import javafx.scene.transform.NonInvertibleTransformException;
    026
    027
    028@SuppressWarnings("restriction")
    029public class ThreeSixtyDegreeViewerPanel extends JFXPanel {
    030
    031        private static final long serialVersionUID = -4940350009018422000L;
    032
    033        private static Group root;
    034        private static Group subGroup;
    035        private static CubemapBox cubemapBox;
    036        private static PerspectiveCamera camera;
    037        private static CameraTransformer cameraTransform = new CameraTransformer();
    038
    039        private static double mousePosX;
    040        private static double mousePosY;
    041        private static double mouseOldX;
    042        private static double mouseOldY;
    043        private static double mouseDeltaX;
    044        private static double mouseDeltaY;
    045        private static double cameraDistance = 5000;
    046
    047        // Supply Image Paths or a NullPointer will occur
    048        private static Image front;
    049        private static Image right;
    050        private static Image back;
    051        private static Image left;
    052        private static Image up;
    053        private static Image down;
    054
    055        public ThreeSixtyDegreeViewerPanel() {
    056                // constructor
    057        }
    058
    059        void initialize() {
    060
    061                root = new Group();
    062
    063                camera = new PerspectiveCamera(true);
    064                cameraTransform.setTranslate(0, 0, 0);
    065                cameraTransform.getChildren().addAll(camera);
    066                camera.setNearClip(0.1);
    067                camera.setFarClip(1000000.0);
    068                camera.setFieldOfView(42);
    069                camera.setTranslateZ(-cameraDistance);
    070                // cameraTransform.ry.setAngle(-45.0);
    071                // cameraTransform.rx.setAngle(-10.0);
    072                // add a Point Light for better viewing of the grid coordinate system
    073                final PointLight light = new PointLight(Color.WHITE);
    074
    075                cameraTransform.getChildren().add(light);
    076                light.setTranslateX(camera.getTranslateX());
    077                light.setTranslateY(camera.getTranslateY());
    078                light.setTranslateZ(camera.getTranslateZ());
    079
    080                root.getChildren().add(cameraTransform);
    081
    082                final double size = 100000D;
    083
    084                cubemapBox = new CubemapBox(front, right, back, left, up, down, size, camera);
    085
    086                subGroup = new Group();
    087                subGroup.getChildren().add(cameraTransform);
    088
    089                Platform.runLater(new Runnable() {
    090                        @Override
    091                        public void run() {
    092                                try {
    093                                        // TODO: create Default Scene and replace with 360 degree scene @rrh
    094                                        setScene(createScene());
    095                                } catch (NonInvertibleTransformException e) {
    096                                        Logging.error(I18n.tr("Error initializing StreetsideViewerPanel - JavaFX {0}", e.getMessage()));
    097                                }
    098                        }
    099                });
    100        }
    101
    102        private static Scene createScene() throws NonInvertibleTransformException {
    103
    104                /*root = new Group();
    105
    106                camera = new PerspectiveCamera(true);
    107                cameraTransform.setTranslate(0, 0, 0);
    108                cameraTransform.getChildren().addAll(camera);
    109                camera.setNearClip(0.1);
    110                camera.setFarClip(1000000.0);
    111                camera.setFieldOfView(42);
    112                camera.setTranslateZ(-cameraDistance);
    113                final PointLight light = new PointLight(Color.WHITE);
    114
    115                cameraTransform.getChildren().add(light);
    116                light.setTranslateX(camera.getTranslateX());
    117                light.setTranslateY(camera.getTranslateY());
    118                light.setTranslateZ(camera.getTranslateZ());
    119
    120                root.getChildren().add(cameraTransform);
    121
    122                // Load Cubemap box AFTER camera is initialized
    123                final double size = 100000D;
    124
    125                cubemapBox = new CubemapBox(null, null, null, null, null, null, size, camera);
    126
    127                subGroup = new Group();
    128                subGroup.getChildren().add(cameraTransform);*/
    129
    130                final Scene scene = new Scene(new Group(root), 1024, 668, true, SceneAntialiasing.BALANCED);
    131                scene.setFill(Color.TRANSPARENT);
    132                scene.setCamera(camera);
    133
    134                // First person shooter keyboard movement
    135                scene.setOnKeyPressed(event -> {
    136                        double change = 10.0;
    137                        // Add shift modifier to simulate "Running Speed"
    138                        if (event.isShiftDown()) {
    139                                change = 50.0;
    140                        }
    141                        // What key did the user press?
    142                        final KeyCode keycode = event.getCode();
    143                        // Step 2c: Add Zoom controls
    144                        if (keycode == KeyCode.W) {
    145                                camera.setTranslateZ(camera.getTranslateZ() + change);
    146                        }
    147                        if (keycode == KeyCode.S) {
    148                                camera.setTranslateZ(camera.getTranslateZ() - change);
    149                        }
    150                        // Step 2d: Add Strafe controls
    151                        if (keycode == KeyCode.A) {
    152                                camera.setTranslateX(camera.getTranslateX() - change);
    153                        }
    154                        if (keycode == KeyCode.D) {
    155                                camera.setTranslateX(camera.getTranslateX() + change);
    156                        }
    157                });
    158
    159                scene.setOnMousePressed((MouseEvent me) -> {
    160                        mousePosX = me.getSceneX();
    161                        mousePosY = me.getSceneY();
    162                        mouseOldX = me.getSceneX();
    163                        mouseOldY = me.getSceneY();
    164                });
    165                scene.setOnMouseDragged((MouseEvent me) -> {
    166                        mouseOldX = mousePosX;
    167                        mouseOldY = mousePosY;
    168                        mousePosX = me.getSceneX();
    169                        mousePosY = me.getSceneY();
    170                        mouseDeltaX = mousePosX - mouseOldX;
    171                        mouseDeltaY = mousePosY - mouseOldY;
    172
    173                        double modifier = 10.0;
    174                        final double modifierFactor = 0.1;
    175
    176                        if (me.isControlDown()) {
    177                                modifier = 0.1;
    178                        }
    179                        if (me.isShiftDown()) {
    180                                modifier = 50.0;
    181                        }
    182                        if (me.isPrimaryButtonDown()) {
    183                                cameraTransform.ry.setAngle(
    184                                                ((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540)
    185                                                                % 360 - 180); // +
    186                                cameraTransform.rx.setAngle(
    187                                                ((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540)
    188                                                                % 360 - 180); // -
    189
    190                        } else if (me.isSecondaryButtonDown()) {
    191                                final double z = camera.getTranslateZ();
    192                                final double newZ = z + mouseDeltaX * modifierFactor * modifier;
    193                                camera.setTranslateZ(newZ);
    194                        } else if (me.isMiddleButtonDown()) {
    195                                cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
    196                                cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
    197                        }
    198                });
    199
    200                /*scene.widthProperty().addListener(new ChangeListener<Number>() {
    201                    @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) {
    202                        System.out.println("Width: " + newSceneWidth);
    203                    }
    204
    205                        @Override
    206                        public void changed(ObservableValue<? extends Number> observable, Number oldSceneWidth, Number newSceneWidth) {
    207                                draw();
    208                        }
    209                });*/
    210                /*scene.heightProperty().addListener(new ChangeListener<Number>() {
    211                    @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
    212                        //System.out.println("Height: " + newSceneHeight);
    213                        draw();
    214                    }
    215                });*/
    216
    217                root.getChildren().addAll(cubemapBox, subGroup);
    218                root.setAutoSizeChildren(true);
    219
    220                subGroup.setAutoSizeChildren(true);
    221
    222                // TODO: attempt to prevent content from disappearing after resizing
    223                Platform.setImplicitExit(false);
    224
    225                return scene;
    226        }
    227
    228        private static Scene createDefaultScene() {
    229                // TODO: default scene with message? @rrh
    230
    231                // Load Cubemap box AFTER camera is initialized
    232                final double size = 100000D;
    233
    234                TextArea textArea = new TextArea();
    235                textArea.setText("No Streetside image selected.");
    236
    237                VBox vbox = new VBox(textArea);
    238
    239                Scene scene = new Scene(vbox, 200, 100);
    240                return scene;
    241        }
    242
    243        public static Scene createScene(BufferedImage img0, BufferedImage img1, BufferedImage img2, BufferedImage img3,
    244                        BufferedImage img4, BufferedImage img5) throws NonInvertibleTransformException {
    245                front = GraphicsUtils.convertBufferedImage2JavaFXImage(img0);
    246                right = GraphicsUtils.convertBufferedImage2JavaFXImage(img1);
    247                back = GraphicsUtils.convertBufferedImage2JavaFXImage(img2);
    248                left = GraphicsUtils.convertBufferedImage2JavaFXImage(img3);
    249                up = GraphicsUtils.convertBufferedImage2JavaFXImage(img4);
    250                down = GraphicsUtils.convertBufferedImage2JavaFXImage(img5);
    251
    252                root = new Group();
    253
    254                camera = new PerspectiveCamera(true);
    255                cameraTransform.setTranslate(0, 0, 0);
    256                cameraTransform.getChildren().addAll(camera);
    257                camera.setNearClip(0.1);
    258                camera.setFarClip(1000000.0);
    259                camera.setFieldOfView(42);
    260                camera.setTranslateZ(-cameraDistance);
    261                // cameraTransform.ry.setAngle(-45.0);
    262                // cameraTransform.rx.setAngle(-10.0);
    263                // add a Point Light for better viewing of the grid coordinate system
    264                final PointLight light = new PointLight(Color.WHITE);
    265
    266                cameraTransform.getChildren().add(light);
    267                light.setTranslateX(camera.getTranslateX());
    268                light.setTranslateY(camera.getTranslateY());
    269                light.setTranslateZ(camera.getTranslateZ());
    270
    271                root.getChildren().add(cameraTransform);
    272
    273                // Load Cubemap box AFTER camera is initialized
    274                final double size = 100000D;
    275
    276                cubemapBox = new CubemapBox(front, right, back, left, up, down, size, camera);
    277
    278                final Group torusGroup = new Group();
    279                torusGroup.getChildren().add(cameraTransform);
    280
    281                final Scene scene = new Scene(new Group(root), 1024, 668, true, SceneAntialiasing.BALANCED);
    282                scene.setFill(Color.TRANSPARENT);
    283                scene.setCamera(camera);
    284
    285                // First person shooter keyboard movement
    286                scene.setOnKeyPressed(event -> {
    287                        double change = 10.0;
    288                        // Add shift modifier to simulate "Running Speed"
    289                        if (event.isShiftDown()) {
    290                                change = 50.0;
    291                        }
    292                        // What key did the user press?
    293                        final KeyCode keycode = event.getCode();
    294                        // Step 2c: Add Zoom controls
    295                        if (keycode == KeyCode.W) {
    296                                camera.setTranslateZ(camera.getTranslateZ() + change);
    297                        }
    298                        if (keycode == KeyCode.S) {
    299                                camera.setTranslateZ(camera.getTranslateZ() - change);
    300                        }
    301                        // Step 2d: Add Strafe controls
    302                        if (keycode == KeyCode.A) {
    303                                camera.setTranslateX(camera.getTranslateX() - change);
    304                        }
    305                        if (keycode == KeyCode.D) {
    306                                camera.setTranslateX(camera.getTranslateX() + change);
    307                        }
    308
    309                });
    310
    311                scene.setOnMousePressed((MouseEvent me) -> {
    312                        mousePosX = me.getSceneX();
    313                        mousePosY = me.getSceneY();
    314                        mouseOldX = me.getSceneX();
    315                        mouseOldY = me.getSceneY();
    316                });
    317                scene.setOnMouseDragged((MouseEvent me) -> {
    318                        mouseOldX = mousePosX;
    319                        mouseOldY = mousePosY;
    320                        mousePosX = me.getSceneX();
    321                        mousePosY = me.getSceneY();
    322                        mouseDeltaX = mousePosX - mouseOldX;
    323                        mouseDeltaY = mousePosY - mouseOldY;
    324
    325                        double modifier = 10.0;
    326                        final double modifierFactor = 0.1;
    327
    328                        if (me.isControlDown()) {
    329                                modifier = 0.1;
    330                        }
    331                        if (me.isShiftDown()) {
    332                                modifier = 50.0;
    333                        }
    334                        if (me.isPrimaryButtonDown()) {
    335                                cameraTransform.ry.setAngle(
    336                                                ((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540)
    337                                                                % 360 - 180); // +
    338                                cameraTransform.rx.setAngle(
    339                                                ((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540)
    340                                                                % 360 - 180); // -
    341
    342                        } else if (me.isSecondaryButtonDown()) {
    343                                final double z = camera.getTranslateZ();
    344                                final double newZ = z + mouseDeltaX * modifierFactor * modifier;
    345                                camera.setTranslateZ(newZ);
    346                        } else if (me.isMiddleButtonDown()) {
    347                                cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
    348                                cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
    349                        }
    350                });
    351
    352                root.getChildren().addAll(cubemapBox, torusGroup);
    353                root.setAutoSizeChildren(true);
    354
    355                return scene;
    356        }
    357
    358        /*public void setCubemapImages(BufferedImage img, BufferedImage img1, BufferedImage img2, BufferedImage img3,
    359                        BufferedImage img4, BufferedImage img5) {
    360                cubemapBox = null;
    361
    362                GraphicsUtils.PlatformHelper.run(new Runnable() {
    363                        @Override
    364                        public void run() {
    365                                try {
    366                                        // initialize without imagery.
    367                                        scene = createScene(img, img1, img2, img3, img4, img5);
    368                                        setScene(scene);
    369                                } catch (NonInvertibleTransformException e) {
    370                                        Logging.error(I18n.tr("Error initializing StreetsideViewerPanel - JavaFX {0}", e.getMessage()));
    371                                }
    372                        }
    373                });
    374        }*/
    375
    376        public CubemapBox getCubemapBox() {
    377                if (cubemapBox == null) {
    378                        // shouldn't happen
    379                        initialize();
    380                }
    381                return cubemapBox;
    382        }
    383}