001//License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.plugins.streetside.gui.imageinfo;
003
004import org.openstreetmap.josm.plugins.streetside.cubemap.CameraTransformer;
005import org.openstreetmap.josm.plugins.streetside.cubemap.CubemapBox;
006
007import javafx.application.Platform;
008import javafx.embed.swing.JFXPanel;
009import javafx.scene.Group;
010import javafx.scene.PerspectiveCamera;
011import javafx.scene.PointLight;
012import javafx.scene.Scene;
013import javafx.scene.control.Label;
014import javafx.scene.SceneAntialiasing;
015import javafx.scene.control.TextArea;
016import javafx.scene.image.Image;
017import javafx.scene.input.KeyCode;
018import javafx.scene.input.MouseEvent;
019import javafx.scene.layout.VBox;
020import javafx.scene.paint.Color;
021import javafx.scene.text.Font;
022import javafx.scene.text.FontWeight;
023
024@SuppressWarnings("restriction")
025public class ThreeSixtyDegreeViewerPanel extends JFXPanel {
026
027  private static final long serialVersionUID = -4940350009018422000L;
028
029  private static Scene cubemapScene;
030
031  private static Scene defaultScene;
032  private static Scene loadingScene;
033
034  private static Group root;
035  private static Group subGroup;
036  private static CubemapBox cubemapBox;
037  private static PerspectiveCamera camera;
038  private static CameraTransformer cameraTransform = new CameraTransformer();
039
040  private static double mousePosX;
041  private static double mousePosY;
042  private static double mouseOldX;
043  private static double mouseOldY;
044  private static double mouseDeltaX;
045  private static double mouseDeltaY;
046  private static double cameraDistance = 5000;
047
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
057  }
058
059  public 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    final PointLight light = new PointLight(Color.WHITE);
071
072    cameraTransform.getChildren().add(light);
073    light.setTranslateX(camera.getTranslateX());
074    light.setTranslateY(camera.getTranslateY());
075    light.setTranslateZ(camera.getTranslateZ());
076
077    root.getChildren().add(cameraTransform);
078
079    final double size = 100000D;
080
081    cubemapBox = new CubemapBox(front, right, back, left, up, down, size, camera);
082
083    subGroup = new Group();
084    subGroup.getChildren().add(cameraTransform);
085
086    createLoadingScene();
087
088    Platform.runLater(new Runnable() {
089      @Override
090      public void run() {
091        setScene(createDefaultScene());
092      }
093    });
094  }
095
096  private static Scene createDefaultScene() {
097
098    TextArea textArea = new TextArea();
099    textArea.setText("No Streetside image selected.");
100
101    VBox vbox = new VBox(textArea);
102
103    root = new Group();
104
105    camera = new PerspectiveCamera(true);
106    cameraTransform.setTranslate(0, 0, 0);
107    cameraTransform.getChildren().addAll(camera);
108    camera.setNearClip(0.1);
109    camera.setFarClip(1000000.0);
110    camera.setFieldOfView(42);
111    camera.setTranslateZ(-cameraDistance);
112    final PointLight light = new PointLight(Color.WHITE);
113
114    cameraTransform.getChildren().add(light);
115    light.setTranslateX(camera.getTranslateX());
116    light.setTranslateY(camera.getTranslateY());
117    light.setTranslateZ(camera.getTranslateZ());
118
119    root.getChildren().add(cameraTransform);
120
121    final double size = 100000D;
122
123    cubemapBox = new CubemapBox(null, null, null, null, null, null, size, camera);
124
125    subGroup = new Group();
126    subGroup.getChildren().add(cameraTransform);
127
128    cubemapScene = new Scene(new Group(root), 1024, 668, true, SceneAntialiasing.BALANCED);
129    cubemapScene.setFill(Color.TRANSPARENT);
130    cubemapScene.setCamera(camera);
131
132    cubemapScene.setOnKeyPressed(event -> {
133      double change = 10.0;
134      if (event.isShiftDown()) {
135        change = 50.0;
136      }
137      final KeyCode keycode = event.getCode();
138
139      if (keycode == KeyCode.W) {
140        camera.setTranslateZ(camera.getTranslateZ() + change);
141      }
142      if (keycode == KeyCode.S) {
143        camera.setTranslateZ(camera.getTranslateZ() - change);
144      }
145
146      if (keycode == KeyCode.A) {
147        camera.setTranslateX(camera.getTranslateX() - change);
148      }
149      if (keycode == KeyCode.D) {
150        camera.setTranslateX(camera.getTranslateX() + change);
151      }
152    });
153
154    cubemapScene.setOnMousePressed((MouseEvent me) -> {
155      mousePosX = me.getSceneX();
156      mousePosY = me.getSceneY();
157      mouseOldX = me.getSceneX();
158      mouseOldY = me.getSceneY();
159    });
160    cubemapScene.setOnMouseDragged((MouseEvent me) -> {
161      mouseOldX = mousePosX;
162      mouseOldY = mousePosY;
163      mousePosX = me.getSceneX();
164      mousePosY = me.getSceneY();
165      mouseDeltaX = mousePosX - mouseOldX;
166      mouseDeltaY = mousePosY - mouseOldY;
167
168      double modifier = 10.0;
169      final double modifierFactor = 0.1;
170
171      if (me.isControlDown()) {
172        modifier = 0.1;
173      }
174      if (me.isShiftDown()) {
175        modifier = 50.0;
176      }
177      if (me.isPrimaryButtonDown()) {
178        cameraTransform.ry.setAngle(
179          ((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540) % 360 - 180
180        ); // +
181        cameraTransform.rx.setAngle(
182          ((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540) % 360 - 180
183        ); // -
184
185      } else if (me.isSecondaryButtonDown()) {
186        final double z = camera.getTranslateZ();
187        final double newZ = z + mouseDeltaX * modifierFactor * modifier;
188        camera.setTranslateZ(newZ);
189      } else if (me.isMiddleButtonDown()) {
190        cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // -
191        cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // -
192      }
193    });
194
195    root.getChildren().addAll(cubemapBox, subGroup);
196    root.setAutoSizeChildren(true);
197
198    subGroup.setAutoSizeChildren(true);
199
200    // prevent content from disappearing after resizing
201    Platform.setImplicitExit(false);
202
203    defaultScene = new Scene(vbox, 200, 100);
204    return defaultScene;
205  }
206
207  private static void createLoadingScene() {
208    Label label = new Label(" Loading...");
209    label.setFont(Font.font(null, FontWeight.BOLD, 14));
210    VBox vbox = new VBox(label);
211    loadingScene = new Scene(vbox, 200, 100);
212  }
213
214  public CubemapBox getCubemapBox() {
215    if (cubemapBox == null) {
216      // shouldn't happen
217      initialize();
218    }
219    return cubemapBox;
220  }
221
222  public Scene getDefaultScene() {
223    return defaultScene;
224  }
225
226  public Scene getCubemapScene() {
227    return cubemapScene;
228  }
229
230  public Scene getLoadingScene() {
231    return loadingScene;
232  }
233}