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}