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                                setScene(createDefaultScene());
095                        }
096                });
097        }
098
099        public static Scene createScene() /*throws NonInvertibleTransformException*/ {
100
101                root = new Group();
102
103                camera = new PerspectiveCamera(true);
104                cameraTransform.setTranslate(0, 0, 0);
105                cameraTransform.getChildren().addAll(camera);
106                camera.setNearClip(0.1);
107                camera.setFarClip(1000000.0);
108                camera.setFieldOfView(42);
109                camera.setTranslateZ(-cameraDistance);
110                final PointLight light = new PointLight(Color.WHITE);
111
112                cameraTransform.getChildren().add(light);
113                light.setTranslateX(camera.getTranslateX());
114                light.setTranslateY(camera.getTranslateY());
115                light.setTranslateZ(camera.getTranslateZ());
116
117                root.getChildren().add(cameraTransform);
118
119                // Load Cubemap box AFTER camera is initialized
120                final double size = 100000D;
121
122                cubemapBox = new CubemapBox(null, null, null, null, null, null, size, camera);
123
124                subGroup = new Group();
125                subGroup.getChildren().add(cameraTransform);
126
127                final Scene scene = new Scene(new Group(root), 1024, 668, true, SceneAntialiasing.BALANCED);
128                scene.setFill(Color.TRANSPARENT);
129                scene.setCamera(camera);
130
131                // First person shooter keyboard movement
132                scene.setOnKeyPressed(event -> {
133                        double change = 10.0;
134                        // Add shift modifier to simulate "Running Speed"
135                        if (event.isShiftDown()) {
136                                change = 50.0;
137                        }
138                        // What key did the user press?
139                        final KeyCode keycode = event.getCode();
140                        // Step 2c: Add Zoom controls
141                        if (keycode == KeyCode.W) {
142                                camera.setTranslateZ(camera.getTranslateZ() + change);
143                        }
144                        if (keycode == KeyCode.S) {
145                                camera.setTranslateZ(camera.getTranslateZ() - change);
146                        }
147                        // Step 2d: Add Strafe controls
148                        if (keycode == KeyCode.A) {
149                                camera.setTranslateX(camera.getTranslateX() - change);
150                        }
151                        if (keycode == KeyCode.D) {
152                                camera.setTranslateX(camera.getTranslateX() + change);
153                        }
154                });
155
156                scene.setOnMousePressed((MouseEvent me) -> {
157                        mousePosX = me.getSceneX();
158                        mousePosY = me.getSceneY();
159                        mouseOldX = me.getSceneX();
160                        mouseOldY = me.getSceneY();
161                });
162                scene.setOnMouseDragged((MouseEvent me) -> {
163                        mouseOldX = mousePosX;
164                        mouseOldY = mousePosY;
165                        mousePosX = me.getSceneX();
166                        mousePosY = me.getSceneY();
167                        mouseDeltaX = mousePosX - mouseOldX;
168                        mouseDeltaY = mousePosY - mouseOldY;
169
170                        double modifier = 10.0;
171                        final double modifierFactor = 0.1;
172
173                        if (me.isControlDown()) {
174                                modifier = 0.1;
175                        }
176                        if (me.isShiftDown()) {
177                                modifier = 50.0;
178                        }
179                        if (me.isPrimaryButtonDown()) {
180                                cameraTransform.ry.setAngle(
181                                                ((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540)
182                                                                % 360 - 180); // +
183                                cameraTransform.rx.setAngle(
184                                                ((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540)
185                                                                % 360 - 180); // -
186
187                        } else if (me.isSecondaryButtonDown()) {
188                                final double z = camera.getTranslateZ();
189                                final double newZ = z + mouseDeltaX * modifierFactor * modifier;
190                                camera.setTranslateZ(newZ);
191                        } else if (me.isMiddleButtonDown()) {
192                                cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
193                                cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
194                        }
195                });
196
197                root.getChildren().addAll(cubemapBox, subGroup);
198                root.setAutoSizeChildren(true);
199
200                subGroup.setAutoSizeChildren(true);
201
202                // prevent content from disappearing after resizing
203                Platform.setImplicitExit(false);
204
205                return scene;
206        }
207
208        private static Scene createDefaultScene() {
209                // Load Cubemap box AFTER camera is initialized
210
211                TextArea textArea = new TextArea();
212                textArea.setText("No Streetside image selected.");
213
214                VBox vbox = new VBox(textArea);
215
216                root = new Group();
217
218    camera = new PerspectiveCamera(true);
219    cameraTransform.setTranslate(0, 0, 0);
220    cameraTransform.getChildren().addAll(camera);
221    camera.setNearClip(0.1);
222    camera.setFarClip(1000000.0);
223    camera.setFieldOfView(42);
224    camera.setTranslateZ(-cameraDistance);
225    final PointLight light = new PointLight(Color.WHITE);
226
227    cameraTransform.getChildren().add(light);
228    light.setTranslateX(camera.getTranslateX());
229    light.setTranslateY(camera.getTranslateY());
230    light.setTranslateZ(camera.getTranslateZ());
231
232    root.getChildren().add(cameraTransform);
233
234    // Load Cubemap box AFTER camera is initialized
235    final double size = 100000D;
236
237    cubemapBox = new CubemapBox(null, null, null, null, null, null, size, camera);
238
239    subGroup = new Group();
240    subGroup.getChildren().add(cameraTransform);
241
242    /*final Scene*/ cubemapScene = new Scene(new Group(root), 1024, 668, true, SceneAntialiasing.BALANCED);
243    cubemapScene.setFill(Color.TRANSPARENT);
244    cubemapScene.setCamera(camera);
245
246    // First person shooter keyboard movement
247    cubemapScene.setOnKeyPressed(event -> {
248      double change = 10.0;
249      // Add shift modifier to simulate "Running Speed"
250      if (event.isShiftDown()) {
251        change = 50.0;
252      }
253      // What key did the user press?
254      final KeyCode keycode = event.getCode();
255      // Step 2c: Add Zoom controls
256      if (keycode == KeyCode.W) {
257        camera.setTranslateZ(camera.getTranslateZ() + change);
258      }
259      if (keycode == KeyCode.S) {
260        camera.setTranslateZ(camera.getTranslateZ() - change);
261      }
262      // Step 2d: Add Strafe controls
263      if (keycode == KeyCode.A) {
264        camera.setTranslateX(camera.getTranslateX() - change);
265      }
266      if (keycode == KeyCode.D) {
267        camera.setTranslateX(camera.getTranslateX() + change);
268      }
269    });
270
271    cubemapScene.setOnMousePressed((MouseEvent me) -> {
272      mousePosX = me.getSceneX();
273      mousePosY = me.getSceneY();
274      mouseOldX = me.getSceneX();
275      mouseOldY = me.getSceneY();
276    });
277    cubemapScene.setOnMouseDragged((MouseEvent me) -> {
278      mouseOldX = mousePosX;
279      mouseOldY = mousePosY;
280      mousePosX = me.getSceneX();
281      mousePosY = me.getSceneY();
282      mouseDeltaX = mousePosX - mouseOldX;
283      mouseDeltaY = mousePosY - mouseOldY;
284
285      double modifier = 10.0;
286      final double modifierFactor = 0.1;
287
288      if (me.isControlDown()) {
289        modifier = 0.1;
290      }
291      if (me.isShiftDown()) {
292        modifier = 50.0;
293      }
294      if (me.isPrimaryButtonDown()) {
295        cameraTransform.ry.setAngle(
296            ((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540)
297                % 360 - 180); // +
298        cameraTransform.rx.setAngle(
299            ((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540)
300                % 360 - 180); // -
301
302      } else if (me.isSecondaryButtonDown()) {
303        final double z = camera.getTranslateZ();
304        final double newZ = z + mouseDeltaX * modifierFactor * modifier;
305        camera.setTranslateZ(newZ);
306      } else if (me.isMiddleButtonDown()) {
307        cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
308        cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
309      }
310    });
311
312    root.getChildren().addAll(cubemapBox, subGroup);
313    root.setAutoSizeChildren(true);
314
315    subGroup.setAutoSizeChildren(true);
316
317    // prevent content from disappearing after resizing
318    Platform.setImplicitExit(false);
319
320    defaultScene = new Scene(vbox, 200, 100);
321                return defaultScene;
322        }
323
324        public static Scene createScene(BufferedImage img0, BufferedImage img1, BufferedImage img2, BufferedImage img3,
325                        BufferedImage img4, BufferedImage img5) throws NonInvertibleTransformException {
326                front = GraphicsUtils.convertBufferedImage2JavaFXImage(img0);
327                right = GraphicsUtils.convertBufferedImage2JavaFXImage(img1);
328                back = GraphicsUtils.convertBufferedImage2JavaFXImage(img2);
329                left = GraphicsUtils.convertBufferedImage2JavaFXImage(img3);
330                up = GraphicsUtils.convertBufferedImage2JavaFXImage(img4);
331                down = GraphicsUtils.convertBufferedImage2JavaFXImage(img5);
332
333                root = new Group();
334
335                camera = new PerspectiveCamera(true);
336                cameraTransform.setTranslate(0, 0, 0);
337                cameraTransform.getChildren().addAll(camera);
338                camera.setNearClip(0.1);
339                camera.setFarClip(1000000.0);
340                camera.setFieldOfView(42);
341                camera.setTranslateZ(-cameraDistance);
342                final PointLight light = new PointLight(Color.WHITE);
343
344                cameraTransform.getChildren().add(light);
345                light.setTranslateX(camera.getTranslateX());
346                light.setTranslateY(camera.getTranslateY());
347                light.setTranslateZ(camera.getTranslateZ());
348
349                root.getChildren().add(cameraTransform);
350
351                // Load Cubemap box AFTER camera is initialized
352                final double size = 100000D;
353
354                cubemapBox = new CubemapBox(front, right, back, left, up, down, size, camera);
355
356                final Group torusGroup = new Group();
357                torusGroup.getChildren().add(cameraTransform);
358
359                final Scene scene = new Scene(new Group(root), 1024, 668, true, SceneAntialiasing.BALANCED);
360                scene.setFill(Color.TRANSPARENT);
361                scene.setCamera(camera);
362
363                // First person shooter keyboard movement
364                scene.setOnKeyPressed(event -> {
365                        double change = 10.0;
366                        // Add shift modifier to simulate "Running Speed"
367                        if (event.isShiftDown()) {
368                                change = 50.0;
369                        }
370                        // What key did the user press?
371                        final KeyCode keycode = event.getCode();
372                        // Step 2c: Add Zoom controls
373                        if (keycode == KeyCode.W) {
374                                camera.setTranslateZ(camera.getTranslateZ() + change);
375                        }
376                        if (keycode == KeyCode.S) {
377                                camera.setTranslateZ(camera.getTranslateZ() - change);
378                        }
379                        // Step 2d: Add Strafe controls
380                        if (keycode == KeyCode.A) {
381                                camera.setTranslateX(camera.getTranslateX() - change);
382                        }
383                        if (keycode == KeyCode.D) {
384                                camera.setTranslateX(camera.getTranslateX() + change);
385                        }
386
387                });
388
389                scene.setOnMousePressed((MouseEvent me) -> {
390                        mousePosX = me.getSceneX();
391                        mousePosY = me.getSceneY();
392                        mouseOldX = me.getSceneX();
393                        mouseOldY = me.getSceneY();
394                });
395                scene.setOnMouseDragged((MouseEvent me) -> {
396                        mouseOldX = mousePosX;
397                        mouseOldY = mousePosY;
398                        mousePosX = me.getSceneX();
399                        mousePosY = me.getSceneY();
400                        mouseDeltaX = mousePosX - mouseOldX;
401                        mouseDeltaY = mousePosY - mouseOldY;
402
403                        double modifier = 10.0;
404                        final double modifierFactor = 0.1;
405
406                        if (me.isControlDown()) {
407                                modifier = 0.1;
408                        }
409                        if (me.isShiftDown()) {
410                                modifier = 50.0;
411                        }
412                        if (me.isPrimaryButtonDown()) {
413                                cameraTransform.ry.setAngle(
414                                                ((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540)
415                                                                % 360 - 180); // +
416                                cameraTransform.rx.setAngle(
417                                                ((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540)
418                                                                % 360 - 180); // -
419
420                        } else if (me.isSecondaryButtonDown()) {
421                                final double z = camera.getTranslateZ();
422                                final double newZ = z + mouseDeltaX * modifierFactor * modifier;
423                                camera.setTranslateZ(newZ);
424                        } else if (me.isMiddleButtonDown()) {
425                                cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
426                                cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
427                        }
428                });
429
430                root.getChildren().addAll(cubemapBox, torusGroup);
431                root.setAutoSizeChildren(true);
432
433                return scene;
434        }
435
436        public CubemapBox getCubemapBox() {
437                if (cubemapBox == null) {
438                        // shouldn't happen
439                        initialize();
440                }
441                return cubemapBox;
442        }
443
444  public Scene getDefaultScene() {
445    return defaultScene;
446  }
447
448  public Scene getCubemapScene() {
449    return cubemapScene;
450  }
451}