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}