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}