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}