1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package com.javafx.experiments.jfx3dviewer;
  33 
  34 
  35 // import testviewer.Frame;
  36 
  37 import java.io.File;
  38 import java.io.IOException;
  39 import java.net.MalformedURLException;
  40 import java.net.URL;
  41 import java.util.logging.Level;
  42 import java.util.logging.Logger;
  43 import javafx.animation.Timeline;
  44 import javafx.application.Application;
  45 import javafx.beans.binding.Bindings;
  46 import javafx.beans.property.BooleanProperty;
  47 import javafx.beans.property.SimpleBooleanProperty;
  48 import javafx.event.ActionEvent;
  49 import javafx.event.EventHandler;
  50 import javafx.fxml.FXMLLoader;
  51 import javafx.geometry.Pos;
  52 import javafx.scene.DepthTest;
  53 import javafx.scene.Group;
  54 import javafx.scene.Node;
  55 import javafx.scene.Parent;
  56 import javafx.scene.PerspectiveCamera;
  57 import javafx.scene.PointLight;
  58 import javafx.scene.Scene;
  59 import javafx.scene.control.Button;
  60 import javafx.scene.control.ColorPicker;
  61 import javafx.scene.control.Label;
  62 import javafx.scene.input.KeyEvent;
  63 import javafx.scene.input.MouseEvent;
  64 import javafx.scene.layout.BorderPane;
  65 import javafx.scene.layout.HBox;
  66 import javafx.scene.layout.VBox;
  67 import javafx.scene.paint.Color;
  68 import javafx.scene.paint.Paint;
  69 import javafx.scene.paint.PhongMaterial;
  70 import javafx.scene.shape.Box;
  71 import javafx.scene.shape.Cylinder;
  72 import javafx.scene.shape.DrawMode;
  73 import javafx.scene.shape.MeshView;
  74 import javafx.scene.shape.Sphere;
  75 import javafx.scene.transform.Transform;
  76 import javafx.stage.FileChooser;
  77 import javafx.stage.Stage;
  78 import javafx.stage.WindowEvent;
  79 import javafx.util.Duration;
  80 import com.javafx.experiments.exporters.fxml.FXMLExporter;
  81 import com.javafx.experiments.importers.max.MaxLoader;
  82 import com.javafx.experiments.importers.maya.MayaGroup;
  83 import com.javafx.experiments.importers.maya.MayaImporter;
  84 import com.javafx.experiments.importers.maya.Xform;
  85 import com.javafx.experiments.importers.obj.ObjImporter;
  86 import com.sun.javafx.geom.Vec3d;
  87 
  88 public class OldTestViewer extends Application {
  89     public static final String PATH_PROPERTY = "file";
  90 
  91     final private PointLight pointLight = new PointLight();
  92     final private PointLight pointLight2 = new PointLight();
  93     final private PointLight pointLight3 = new PointLight();
  94     final private Sphere pointLight2Geo = new Sphere(0.1);
  95     final private Sphere pointLight3Geo = new Sphere(0.1);
  96     final PhongMaterial pointLight2Material = new PhongMaterial();
  97     final PhongMaterial pointLight3Material = new PhongMaterial();
  98         
  99     private Node loadedNode = null;
 100     private File loadedPath = null;
 101     private Xform vertexes;
 102     private BooleanProperty wireframe = new SimpleBooleanProperty(false);
 103     final PerspectiveCamera camera = new PerspectiveCamera(true);
 104     final Group root = new Group();
 105     final Group axisGroup = new Group();
 106     final Xform spheresGroup = new Xform();
 107     final Xform world = new Xform();
 108     final Xform cameraXform = new Xform();
 109     final Xform cameraXform2 = new Xform();
 110     final Xform cameraXform3 = new Xform();
 111     final double cameraDistance = 30;
 112     private Timeline timeline;
 113     boolean timelinePlaying = false;
 114     double ONE_FRAME = 1.0/24.0;
 115     double DELTA_MULTIPLIER = 200.0;
 116     double CONTROL_MULTIPLIER = 0.1;
 117     double SHIFT_MULTIPLIER = 0.1;
 118     double ALT_MULTIPLIER = 0.5;
 119     
 120     private SessionManager sessionManager;
 121     
 122     double mousePosX;
 123     double mousePosY;
 124     double mouseOldX;
 125     double mouseOldY;
 126     double mouseDeltaX;
 127     double mouseDeltaY;
 128 
 129     // [*] root (Group)
 130     //     [*] world (Xform)
 131     //         [*] axisGroup
 132     //         [*] spheresGroup
 133     //     [*] pointLight
 134     //     [*] pointLight2
 135     //     [*] pointLight3
 136     //     [*] cameraXform
 137     //         [*] cameraXform2
 138     //             [*] cameraXform3
 139     //                 [*] camera
 140 
 141     private void buildCamera() {
 142         root.getChildren().add(cameraXform);
 143         cameraXform.getChildren().add(cameraXform2);
 144         cameraXform2.getChildren().add(cameraXform3);
 145         cameraXform3.getChildren().add(camera);
 146         cameraXform3.setRotateZ(180.0);
 147 
 148         camera.setNearClip(0.1);
 149         camera.setFarClip(10000.0);
 150         camera.setTranslateZ(-cameraDistance);
 151         cameraXform.ry.setAngle(180.0);
 152         cameraXform.rx.setAngle(20);
 153     }
 154     
 155     private void buildSpheres() {
 156         final PhongMaterial redMaterial = new PhongMaterial();
 157         redMaterial.setDiffuseColor(Color.DARKRED);
 158         redMaterial.setSpecularColor(Color.RED);
 159 
 160         final PhongMaterial greenMaterial = new PhongMaterial();
 161         greenMaterial.setDiffuseColor(Color.DARKGREEN);
 162         greenMaterial.setSpecularColor(Color.GREEN);
 163 
 164         final PhongMaterial blueMaterial = new PhongMaterial();
 165         blueMaterial.setDiffuseColor(Color.DARKBLUE);
 166         blueMaterial.setSpecularColor(Color.BLUE);
 167         /*
 168         final PhongMaterial redMaterial = new PhongMaterial();
 169         redMaterial.setDiffuseColor(Color.RED);
 170         redMaterial.setSpecularColor(Color.ORANGE);
 171 
 172         final PhongMaterial greenMaterial = new PhongMaterial();
 173         greenMaterial.setDiffuseColor(Color.GREEN);
 174         greenMaterial.setSpecularColor(Color.YELLOW);
 175 
 176         final PhongMaterial blueMaterial = new PhongMaterial();
 177         blueMaterial.setDiffuseColor(Color.BLUE);
 178         blueMaterial.setSpecularColor(Color.VIOLET);
 179 */
 180         
 181         // red
 182         final Box box1 = new Box(8, 8, 8);
 183         box1.setMaterial(redMaterial);
 184         final Box box2 = new Box();
 185         box2.setMaterial(redMaterial);
 186 
 187         box1.setTranslateX(-10);
 188         //box1.setTranslateY(10);
 189 
 190         //box1.setScaleX(0.10);
 191         //box1.setScaleY(0.04);
 192         //box1.setScaleZ(0.04);
 193         
 194         box2.setTranslateX(-12);
 195         //box2.setScaleX(0.04);
 196         //box2.setScaleY(0.10);
 197         //box2.setScaleZ(0.10);
 198 
 199         // green
 200         final Cylinder cylinder1 = new Cylinder(4, 8);
 201         cylinder1.setMaterial(greenMaterial);
 202         final Cylinder cylinder2 = new Cylinder();
 203         cylinder2.setMaterial(greenMaterial);
 204 
 205         cylinder1.setTranslateX(0);
 206         //cylinder1.setTranslateY(10);
 207         //cylinder1.setScaleX(0.04);
 208         //cylinder1.setScaleY(0.10);
 209         //cylinder1.setScaleZ(0.04);
 210         
 211         cylinder2.setTranslateY(-12);
 212         //cylinder2.setScaleX(0.10);
 213         //cylinder2.setScaleY(0.04);
 214         //cylinder2.setScaleZ(0.10);
 215 
 216         // blue
 217         final Sphere sphere1 = new Sphere(4.5);
 218         sphere1.setMaterial(blueMaterial);
 219         final Sphere sphere2 = new Sphere(4);
 220         sphere2.setMaterial(blueMaterial);
 221 
 222         sphere1.setTranslateX(10);
 223         //sphere1.setTranslateY(10);
 224         //sphere1.setScaleX(0.04);
 225         //sphere1.setScaleY(0.04);
 226         //sphere1.setScaleZ(0.10);
 227         
 228         sphere2.setTranslateZ(-12);
 229         //sphere2.setScaleX(0.10);
 230         //sphere2.setScaleY(0.10);
 231         //sphere2.setScaleZ(0.04);
 232 
 233         //spheresGroup.getChildren().addAll(box1, box2, cylinder1, cylinder2, sphere1, sphere2);
 234         spheresGroup.getChildren().addAll(box1, cylinder1, sphere1);
 235         spheresGroup.setRotateX(180.0);
 236 
 237         /*
 238         // red
 239         final Sphere redPos = new Sphere(4);
 240         redPos.setMaterial(redMaterial);
 241         final Sphere redNeg = new Sphere(4);
 242         redNeg.setMaterial(redMaterial);
 243 
 244         redPos.setTranslateX(12);
 245         redPos.setScaleX(0.10);
 246         redPos.setScaleY(0.04);
 247         redPos.setScaleZ(0.04);
 248         
 249         redNeg.setTranslateX(-12);
 250         redNeg.setScaleX(0.04);
 251         redNeg.setScaleY(0.10);
 252         redNeg.setScaleZ(0.10);
 253 
 254         // green
 255         final Sphere greenPos = new Sphere(4);
 256         greenPos.setMaterial(greenMaterial);
 257         final Sphere greenNeg = new Sphere(4);
 258         greenNeg.setMaterial(greenMaterial);
 259 
 260         greenPos.setTranslateY(12);
 261         greenPos.setScaleX(0.04);
 262         greenPos.setScaleY(0.10);
 263         greenPos.setScaleZ(0.04);
 264         
 265         greenNeg.setTranslateY(-12);
 266         greenNeg.setScaleX(0.10);
 267         greenNeg.setScaleY(0.04);
 268         greenNeg.setScaleZ(0.10);
 269 
 270         // blue
 271         final Sphere bluePos = new Sphere(4);
 272         bluePos.setMaterial(blueMaterial);
 273         final Sphere blueNeg = new Sphere(4);
 274         blueNeg.setMaterial(blueMaterial);
 275 
 276         bluePos.setTranslateZ(12);
 277         bluePos.setScaleX(0.04);
 278         bluePos.setScaleY(0.04);
 279         bluePos.setScaleZ(0.10);
 280         
 281         blueNeg.setTranslateZ(-12);
 282         blueNeg.setScaleX(0.10);
 283         blueNeg.setScaleY(0.10);
 284         blueNeg.setScaleZ(0.04);
 285 
 286         spheresGroup.getChildren().addAll(redPos, redNeg, greenPos, greenNeg, bluePos, blueNeg);
 287         */
 288         // Hide spheres by default for now
 289         spheresGroup.setVisible(false);
 290 
 291         world.getChildren().addAll(spheresGroup);
 292     }
 293 
 294     private void buildAxes() {
 295         final PhongMaterial redMaterial = new PhongMaterial();
 296         redMaterial.setDiffuseColor(Color.DARKRED);
 297         redMaterial.setSpecularColor(Color.RED);
 298 
 299         final PhongMaterial greenMaterial = new PhongMaterial();
 300         greenMaterial.setDiffuseColor(Color.DARKGREEN);
 301         greenMaterial.setSpecularColor(Color.GREEN);
 302 
 303         final PhongMaterial blueMaterial = new PhongMaterial();
 304         blueMaterial.setDiffuseColor(Color.DARKBLUE);
 305         blueMaterial.setSpecularColor(Color.BLUE);
 306 
 307         final Sphere red = new Sphere(50);
 308         red.setMaterial(redMaterial);
 309 
 310         final Sphere blue = new Sphere(50);
 311         blue.setMaterial(blueMaterial);
 312 
 313         final Box xAxis = new Box(24.0, 0.05, 0.05);
 314         final Box yAxis = new Box(0.05, 24.0, 0.05);
 315         final Box zAxis = new Box(0.05, 0.05, 24.0);
 316         
 317         xAxis.setMaterial(redMaterial);
 318         yAxis.setMaterial(greenMaterial);
 319         zAxis.setMaterial(blueMaterial);
 320 
 321         // blue.setTranslateZ(100);
 322         // red.setTranslateZ(-100);
 323 
 324         axisGroup.getChildren().addAll(xAxis, yAxis, zAxis);
 325         world.getChildren().addAll(axisGroup);
 326     }
 327 
 328     private void buildLights() {
 329         pointLight.setColor(Color.GREY);
 330         //pointLight.setColor(Color.RED);
 331 
 332         pointLight2.setColor(Color.LIGHTGREY);
 333         pointLight2.setTranslateX(-7.0);
 334         pointLight2.setTranslateY(7.0);
 335         pointLight2.setTranslateZ(7.0);
 336         
 337         pointLight3.setColor(Color.DARKGREY);
 338         pointLight3.setTranslateX(7.0);
 339         pointLight3.setTranslateY(7.0);
 340         pointLight3.setTranslateZ(-7.0);
 341         
 342         final PhongMaterial pointLight2Material = new PhongMaterial();
 343         pointLight2Material.setDiffuseColor(Color.LIGHTGREY);
 344         pointLight2Geo.setMaterial(pointLight2Material);
 345 
 346         final PhongMaterial pointLight3Material = new PhongMaterial();
 347         pointLight3Material.setDiffuseColor(Color.DARKGREY);
 348         pointLight3Geo.setMaterial(pointLight3Material);
 349         
 350         root.getChildren().add(pointLight);
 351         root.getChildren().add(pointLight2);
 352         root.getChildren().add(pointLight3);
 353         root.getChildren().add(pointLight2Geo);
 354         root.getChildren().add(pointLight3Geo);       
 355     }
 356 
 357     private void buildScene() {
 358         vertexes = new Xform();
 359         
 360         root.getChildren().add(world);
 361         world.getChildren().add(vertexes);
 362       
 363         world.setDepthTest(DepthTest.ENABLE);
 364         root.setDepthTest(DepthTest.ENABLE);
 365     }
 366     
 367     private void loadNewNode(File file) {
 368         if (!file.exists()) {
 369             return;
 370         }
 371         Node newNode = null;
 372         try {
 373             switch (getExtension(file)) {
 374                 case "ma":
 375                     newNode = loadMayaFile(file);
 376                     break;
 377                 case "ase":
 378                     newNode = loadMaxFile(file);
 379                     break;
 380                 case "com/javafx/importers/obj":
 381                     newNode = loadObjFile(file);
 382                     break;
 383                 case "fxml":
 384                     newNode = FXMLLoader.load(file.toURI().toURL());
 385                     break;
 386             }
 387         } catch (Exception ex) {
 388             Logger.getLogger(OldTestViewer.class.getName()).log(Level.SEVERE, null, ex);
 389         }
 390         if (newNode != null) {
 391             vertexes.getChildren().clear();
 392             world.getChildren().remove(loadedNode);
 393             loadedNode = newNode;
 394             loadedPath = file;
 395             applyRecursively(newNode, applyWireframe);
 396             applyRecursively(newNode, addToCollisionScene);
 397             world.getChildren().add(loadedNode);
 398         }
 399     }
 400     
 401     private Node loadMaxFile(File file) {
 402         MaxLoader loader = new MaxLoader();
 403         return loader.loadMaxFile(file);
 404     }
 405     
 406     private Node loadObjFile(File file) {
 407         try {
 408 //            ObjImporter.setDebug(true);
 409             ObjImporter reader = new ObjImporter(file.getAbsolutePath());
 410             Group res = new Group();
 411             for (String key : reader.getMeshes()) {
 412                 res.getChildren().add(reader.buildMeshView(key));
 413             }
 414             return res;
 415         } catch (IOException ex) {
 416             Logger.getLogger(OldTestViewer.class.getName()).log(Level.SEVERE, null, ex);
 417             return new Group();
 418         }
 419     }
 420     
 421     private String getExtension(File file) {
 422         String name = file.getName();
 423 
 424         int dot = name.lastIndexOf('.');
 425         if (dot <= 0) {
 426             return file.getPath().toLowerCase();
 427         } else {
 428             return name.substring(dot + 1, name.length()).toLowerCase();
 429         }
 430     }
 431     
 432     private void onButtonLoad() {
 433         FileChooser chooser = new FileChooser();
 434         
 435         chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Supported files", "*.ma", "*.ase", "*.obj", "*.fxml"));
 436         chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All files", "*.*"));
 437         if (loadedPath != null) {
 438             chooser.setInitialDirectory(loadedPath.getAbsoluteFile().getParentFile());
 439         }
 440         chooser.setTitle("Select file to load");
 441         
 442         //probably not the best way to get window
 443         File newFile = chooser.showOpenDialog(world.getScene().getWindow());
 444         if (newFile != null) {
 445             loadNewNode(newFile);
 446         }
 447     }
 448 
 449     private MayaGroup loadMayaFile(File file) {
 450         MayaImporter mayaImporter = new MayaImporter();
 451 
 452         URL url;
 453         try {
 454             url = file.toURI().toURL();
 455         } catch (MalformedURLException ex) {
 456             Logger.getLogger(OldTestViewer.class.getName()).log(Level.SEVERE, null, ex);
 457             return new MayaGroup();
 458         }
 459         mayaImporter.load(url.toString(), false);
 460         timeline = mayaImporter.getTimeline();
 461         timeline.setCycleCount(Timeline.INDEFINITE);
 462         //timeline.setAutoReverse(true);
 463         //timeline.setRate(0.1);
 464         timeline.play();
 465         timelinePlaying = true;
 466 
 467         return mayaImporter.getRoot();
 468     }
 469     
 470     
 471     private void createStage2() 
 472     {
 473         final Stage stage = new Stage();
 474         BorderPane borderPane = new BorderPane();
 475         Scene scene2 = new Scene(borderPane, 310, 170, false);
 476         // Scene scene2 = new Scene(borderPane, 600, 200, true);  
 477         
 478         handleKeyboard(scene2, world);
 479         
 480         //stage.setTitle("Test Controls");
 481         scene2.setFill(Color.DARKGREY);
 482         stage.setScene(scene2);
 483         handleMouse(scene2, world);
 484         
 485         Label fovLabel;
 486         Label nearLabel;
 487         Label farLabel;
 488         Button resetFovButton;
 489         Button resetNearButton;
 490         Button resetFarButton;
 491                 
 492         // light2
 493         Label light2Label;
 494         Label light2xyLabel;
 495         Label light2xzLabel;
 496         Label light2yzLabel;
 497         Button light2resetXYZButton;
 498         Button light2zeroXYZButton;
 499         Button light2show;
 500         Button light2hide;
 501         
 502         // light3
 503         Label light3Label;
 504         Label light3xyLabel;
 505         Label light3xzLabel;
 506         Label light3yzLabel;
 507         Button light3resetXYZButton;
 508         Button light3zeroXYZButton;
 509         Button light3show;
 510         Button light3hide;
 511 
 512         Button backButton;
 513         Button stopButton;
 514         Button playButton;
 515         Button pauseButton;
 516         Button forwardButton;
 517         final EventHandler<ActionEvent> backAction = new EventHandler<ActionEvent>() {
 518             @Override
 519             public void handle(ActionEvent e) {
 520                 System.out.println("backAction");
 521                 Duration currentTime;
 522                 currentTime = timeline.getCurrentTime();
 523                 timeline.jumpTo(Duration.seconds(currentTime.toSeconds() - ONE_FRAME));
 524             }
 525         };
 526         final EventHandler<ActionEvent> stopAction = new EventHandler<ActionEvent>() {
 527             @Override
 528             public void handle(ActionEvent e) {
 529                 System.out.println("stopAction");
 530                 timeline.stop();
 531             }
 532         };
 533         final EventHandler<ActionEvent> playAction = new EventHandler<ActionEvent>() {
 534             @Override
 535             public void handle(ActionEvent e) {
 536                 System.out.println("playAction");
 537                 timeline.play();
 538             }
 539         };
 540         final EventHandler<ActionEvent> pauseAction = new EventHandler<ActionEvent>() {
 541             @Override
 542             public void handle(ActionEvent e) {
 543                 System.out.println("pauseAction");
 544                 timeline.pause();
 545             }
 546         };
 547         final EventHandler<ActionEvent> forwardAction = new EventHandler<ActionEvent>() {
 548             @Override
 549             public void handle(ActionEvent e) {
 550                 System.out.println("forwardAction");                
 551                 Duration currentTime;
 552                 currentTime = timeline.getCurrentTime();
 553                 timeline.jumpTo(Duration.seconds(currentTime.toSeconds() + ONE_FRAME));
 554             }
 555         };
 556 
 557         //----------------------------------------
 558         // Light (Parented to Camera)
 559 
 560         final ColorPicker lightColorPicker = new ColorPicker(pointLight.getColor());
 561         lightColorPicker.setOnAction(new EventHandler<ActionEvent>() {
 562             @Override
 563             public void handle(ActionEvent t) {
 564                 Color c = lightColorPicker.getValue();
 565                 pointLight.setColor(c);
 566                 //pointLightMaterial.setDiffuseColor(c);
 567                 //pointLightGeo.setMaterial(pointLightMaterial);
 568             }
 569         });
 570 
 571         Color c = Color.WHITE;
 572         Scene scene = world.getScene();
 573         if (scene != null) {
 574             Paint fill = scene.getFill();
 575             if (fill instanceof Color) {
 576                 c = (Color)fill;
 577             }
 578         }
 579         final ColorPicker backColorPicker = new ColorPicker(c);
 580         backColorPicker.setOnAction(new EventHandler<ActionEvent>() {
 581 
 582             @Override
 583             public void handle(ActionEvent event) {
 584                 Color c = backColorPicker.getValue();
 585                 //probably not the best way to get scene
 586                 if (world.getScene() != null) {
 587                     world.getScene().setFill(c);
 588                 }
 589                 
 590             }
 591         });
 592 
 593         final EventHandler<ActionEvent> resetFovAction = new EventHandler<ActionEvent>() {
 594             @Override
 595             public void handle(ActionEvent e) {
 596                 System.out.println("resetFovAction");                
 597                 camera.setFieldOfView(30.0);
 598             }
 599         };
 600         final EventHandler<ActionEvent> resetNearAction = new EventHandler<ActionEvent>() {
 601             @Override
 602             public void handle(ActionEvent e) {
 603                 System.out.println("resetFovAction");                
 604                 camera.setNearClip(0.1);
 605             }
 606         };
 607         final EventHandler<ActionEvent> resetFarAction = new EventHandler<ActionEvent>() {
 608             @Override
 609             public void handle(ActionEvent e) {
 610                 System.out.println("resetFovAction");                
 611                 camera.setFarClip(10000.0);
 612             }
 613         };
 614 
 615         fovLabel = new Label("fov");
 616         fovLabel.setId("fov-label");
 617         nearLabel = new Label("Near Clip");
 618         nearLabel.setId("near-label");
 619         farLabel = new Label("Far Clip");
 620         farLabel.setId("far-label");
 621         resetFovButton = new Button("reset FOV");
 622         resetFovButton.setId("resetFov-button");
 623         resetFovButton.setOnAction(resetFovAction);
 624         resetNearButton = new Button("reset near");
 625         resetNearButton.setId("resetNear-button");
 626         resetNearButton.setOnAction(resetNearAction);
 627         resetFarButton = new Button("reset far");
 628         resetFarButton.setId("resetFar-button");
 629         resetFarButton.setOnAction(resetFarAction);
 630 
 631         VBox vbox = new VBox(0, lightColorPicker, backColorPicker, fovLabel, nearLabel, farLabel,
 632                 resetFovButton, resetNearButton, resetFarButton);
 633         vbox.setId("center");
 634         vbox.setAlignment(Pos.TOP_CENTER);
 635         borderPane.setCenter(vbox);
 636         
 637         handleMouse2(fovLabel);
 638         handleMouse2(nearLabel);
 639         handleMouse2(farLabel);
 640         
 641         //----------------------------------------
 642         // Light 2
 643 
 644         final EventHandler<ActionEvent> light2resetXYZAction = new EventHandler<ActionEvent>() {
 645             @Override
 646             public void handle(ActionEvent e) {
 647                 System.out.println("light2resetXYZAction");                
 648                 pointLight2.setTranslateX(-10.0);
 649                 pointLight2.setTranslateY(10.0);
 650                 pointLight2.setTranslateZ(0.0);
 651                 pointLight2Geo.setTranslateX(pointLight2.getTranslateX());
 652                 pointLight2Geo.setTranslateY(pointLight2.getTranslateY());
 653                 pointLight2Geo.setTranslateZ(pointLight2.getTranslateZ());
 654             }
 655         };
 656         final EventHandler<ActionEvent> light2zeroXYZAction = new EventHandler<ActionEvent>() {
 657             @Override
 658             public void handle(ActionEvent e) {
 659                 System.out.println("light2zeroXYZAction");                
 660                 pointLight2.setTranslateX(0.0);
 661                 pointLight2.setTranslateY(0.0);
 662                 pointLight2.setTranslateZ(0.0);
 663                 pointLight2Geo.setTranslateX(pointLight2.getTranslateX());
 664                 pointLight2Geo.setTranslateY(pointLight2.getTranslateY());
 665                 pointLight2Geo.setTranslateZ(pointLight2.getTranslateZ());
 666             }
 667         };
 668         final EventHandler<ActionEvent> light2showAction = new EventHandler<ActionEvent>() {
 669             @Override
 670             public void handle(ActionEvent e) {
 671                 System.out.println("light2show");                
 672                 pointLight2Geo.setVisible(true);
 673             }
 674         };
 675         final EventHandler<ActionEvent> light2hideAction = new EventHandler<ActionEvent>() {
 676             @Override
 677             public void handle(ActionEvent e) {
 678                 System.out.println("light2hide");                
 679                 pointLight2Geo.setVisible(false);
 680             }
 681         };
 682         
 683         final ColorPicker light2ColorPicker = new ColorPicker(pointLight2.getColor());
 684         light2ColorPicker.setOnAction(new EventHandler<ActionEvent>() {
 685             @Override
 686             public void handle(ActionEvent t) {
 687                 Color c = light2ColorPicker.getValue();
 688                 pointLight2.setColor(c);
 689                 pointLight2Material.setDiffuseColor(c);
 690                 pointLight2Geo.setMaterial(pointLight2Material);
 691             }
 692         });
 693 
 694         light2Label = new Label("light2");
 695         light2Label.setId("light2-label");
 696         light2xyLabel = new Label("light2 XY");
 697         light2xyLabel.setId("light2xy-label");
 698         light2xzLabel = new Label("light2 XZ");
 699         light2xzLabel.setId("light2xz-label");
 700         light2yzLabel = new Label("light2 YZ");
 701         light2yzLabel.setId("light2yz-label");
 702         light2resetXYZButton = new Button("reset XYZ");
 703         light2resetXYZButton.setId("light2-resetXYZ");
 704         light2resetXYZButton.setOnAction(light2resetXYZAction);
 705         light2zeroXYZButton = new Button("zero XYZ");
 706         light2zeroXYZButton.setId("light2-zeroXYZ");
 707         light2zeroXYZButton.setOnAction(light2zeroXYZAction);
 708         light2show = new Button("show");
 709         light2show.setId("light2-show");
 710         light2show.setOnAction(light2showAction);
 711         light2hide = new Button("hide");
 712         light2hide.setId("light2-hide");
 713         light2hide.setOnAction(light2hideAction);
 714         VBox vbox2 = new VBox(0, light2ColorPicker, light2Label, light2xyLabel, light2xzLabel, light2yzLabel,
 715                 light2resetXYZButton, light2zeroXYZButton, light2show, light2hide);
 716         vbox2.setId("left");
 717         vbox2.setAlignment(Pos.TOP_CENTER);
 718         borderPane.setLeft(vbox2);
 719 
 720         handleMouse2(light2Label);
 721         handleMouse2(light2xyLabel);
 722         handleMouse2(light2xzLabel);
 723         handleMouse2(light2yzLabel);
 724 
 725         //----------------------------------------
 726         // Light 3
 727 
 728         final EventHandler<ActionEvent> light3resetXYZAction = new EventHandler<ActionEvent>() {
 729             @Override
 730             public void handle(ActionEvent e) {
 731                 System.out.println("light3resetXYZAction");                
 732                 pointLight3.setTranslateX(-10.0);
 733                 pointLight3.setTranslateY(10.0);
 734                 pointLight3.setTranslateZ(0.0);
 735                 pointLight3Geo.setTranslateX(pointLight3.getTranslateX());
 736                 pointLight3Geo.setTranslateY(pointLight3.getTranslateY());
 737                 pointLight3Geo.setTranslateZ(pointLight3.getTranslateZ());
 738             }
 739         };
 740         final EventHandler<ActionEvent> light3zeroXYZAction = new EventHandler<ActionEvent>() {
 741             @Override
 742             public void handle(ActionEvent e) {
 743                 System.out.println("light3zeroXYZAction");                
 744                 pointLight3.setTranslateX(0.0);
 745                 pointLight3.setTranslateY(0.0);
 746                 pointLight3.setTranslateZ(0.0);
 747                 pointLight3Geo.setTranslateX(pointLight3.getTranslateX());
 748                 pointLight3Geo.setTranslateY(pointLight3.getTranslateY());
 749                 pointLight3Geo.setTranslateZ(pointLight3.getTranslateZ());
 750             }
 751         };
 752         final EventHandler<ActionEvent> light3showAction = new EventHandler<ActionEvent>() {
 753             @Override
 754             public void handle(ActionEvent e) {
 755                 System.out.println("light3show");                
 756                 pointLight3Geo.setVisible(true);
 757             }
 758         };
 759         final EventHandler<ActionEvent> light3hideAction = new EventHandler<ActionEvent>() {
 760             @Override
 761             public void handle(ActionEvent e) {
 762                 System.out.println("light3hide");                
 763                 pointLight3Geo.setVisible(false);
 764             }
 765         };
 766         
 767         final ColorPicker light3ColorPicker = new ColorPicker(pointLight3.getColor());
 768         light3ColorPicker.setOnAction(new EventHandler<ActionEvent>() {
 769             @Override
 770             public void handle(ActionEvent t) {
 771                 Color c = light3ColorPicker.getValue();
 772                 pointLight3.setColor(c);
 773                 pointLight3Material.setDiffuseColor(c);
 774                 pointLight3Geo.setMaterial(pointLight3Material);
 775             }
 776         });
 777 
 778         light3Label = new Label("light3");
 779         light3Label.setId("light3-label");
 780         light3xyLabel = new Label("light3 XY");
 781         light3xyLabel.setId("light3xy-label");
 782         light3xzLabel = new Label("light3 XZ");
 783         light3xzLabel.setId("light3xz-label");
 784         light3yzLabel = new Label("light3 YZ");
 785         light3yzLabel.setId("light3yz-label");
 786         light3resetXYZButton = new Button("reset XYZ");
 787         light3resetXYZButton.setId("light3-resetXYZ");
 788         light3resetXYZButton.setOnAction(light3resetXYZAction);
 789         light3zeroXYZButton = new Button("zero XYZ");
 790         light3zeroXYZButton.setId("light3-zeroXYZ");
 791         light3zeroXYZButton.setOnAction(light3zeroXYZAction);
 792         light3show = new Button("show");
 793         light3show.setId("light3-show");
 794         light3show.setOnAction(light3showAction);
 795         light3hide = new Button("hide");
 796         light3hide.setId("light3-hide");
 797         light3hide.setOnAction(light3hideAction);
 798 
 799         VBox vbox3 = new VBox(0, light3ColorPicker, light3Label, light3xyLabel, light3xzLabel, light3yzLabel,
 800                 light3resetXYZButton, light3zeroXYZButton, light3show, light3hide);
 801         vbox3.setId("right");
 802         vbox3.setAlignment(Pos.TOP_CENTER);
 803         borderPane.setRight(vbox3);
 804 
 805         handleMouse2(light3Label);
 806         handleMouse2(light3xyLabel);
 807         handleMouse2(light3xzLabel);
 808         handleMouse2(light3yzLabel);
 809 
 810         backButton = new Button("<-");
 811         backButton.setId("back-button");
 812         backButton.setOnAction(backAction);
 813         stopButton = new Button("Stop");
 814         stopButton.setId("stop-button");
 815         stopButton.setOnAction(stopAction);
 816         playButton = new Button("Play");
 817         playButton.setId("play-button");
 818         playButton.setOnAction(playAction);
 819         pauseButton = new Button("Pause");
 820         pauseButton.setId("pause-button");
 821         pauseButton.setOnAction(pauseAction);
 822         forwardButton = new Button("->");
 823         forwardButton.setId("forward-button");
 824         forwardButton.setOnAction(forwardAction);
 825 
 826         HBox hbox = new HBox(0, backButton, stopButton, playButton, pauseButton, forwardButton);
 827         hbox.setId("bottom");
 828         hbox.setAlignment(Pos.CENTER);
 829         borderPane.setBottom(hbox);
 830         stage.show();
 831     }
 832     
 833     private void handleMouse2(final Label label) {
 834 
 835         label.setOnMousePressed(new EventHandler<MouseEvent>() {
 836             @Override public void handle(MouseEvent me) {
 837                 // System.out.println("________________________________");
 838                 System.out.println("label.getId() = " + label.getId());
 839                 // System.out.println("label.getText() = " + label.getText());
 840                 // System.out.println("label.toString() = " + label.toString());
 841                 
 842                 mousePosX = me.getSceneX();
 843                 mousePosY = me.getSceneY();
 844                 mouseOldX = me.getSceneX();
 845                 mouseOldY = me.getSceneY();
 846             }
 847         });
 848         label.setOnMouseDragged(new EventHandler<MouseEvent>() {
 849             @Override public void handle(MouseEvent me) {
 850                 mouseOldX = mousePosX;
 851                 mouseOldY = mousePosY;
 852                 mousePosX = me.getSceneX();
 853                 mousePosY = me.getSceneY();
 854                 mouseDeltaX = (mousePosX - mouseOldX); //*DELTA_MULTIPLIER;
 855                 mouseDeltaY = (mousePosY - mouseOldY); //*DELTA_MULTIPLIER;
 856                 double modifier = 1.0;
 857                 double modifierFactor = 0.1;
 858                 
 859                 if (me.isControlDown()) {
 860                     modifier = 0.1;
 861                 } 
 862                 if (me.isShiftDown()) {
 863                     modifier = 10.0;
 864                 }     
 865                 /*
 866                     .id("fov-label")
 867                     .id("near-label")
 868                     .id("far-label")
 869 */
 870                 if (label.getId().equalsIgnoreCase("fov-label")) {
 871                     if (me.isPrimaryButtonDown()) {
 872                         double fieldOfView = camera.getFieldOfView();
 873                         fieldOfView += mouseDeltaX*modifierFactor*modifier;
 874                         if (fieldOfView < 0.0001) { fieldOfView = 0.001; }
 875                         else if (fieldOfView > 179.99) { fieldOfView = 179.99; }
 876                         System.out.println("fieldOfView = " + fieldOfView);
 877                         camera.setFieldOfView(fieldOfView);
 878                     }
 879                 }
 880                 else if (label.getId().equalsIgnoreCase("near-label")) {
 881                     if (me.isPrimaryButtonDown()) {
 882                         double nearClip = camera.getNearClip();
 883                         nearClip += mouseDeltaX*modifierFactor*modifier;
 884                         if (nearClip < 0.00000000001) { nearClip = 0.00000000001; }
 885                         else if (nearClip > 10000.00) { nearClip = 10000.0; }
 886                         System.out.println("nearClip = " + nearClip);
 887                         camera.setNearClip(nearClip);
 888                     }
 889                 }
 890                 else if (label.getId().equalsIgnoreCase("far-label")) {
 891                     if (me.isPrimaryButtonDown()) {
 892                         double farClip = camera.getFarClip();
 893                         farClip += mouseDeltaX*modifierFactor*modifier*10.0;
 894                         if (farClip < 0.0001) { farClip = 0.001; }
 895                         else if (farClip > 100000000.00) { farClip = 1000000000.0; }
 896                         System.out.println("farClip = " + farClip);
 897                         camera.setFarClip(farClip);
 898                     }
 899                 }
 900 
 901                 else if (label.getId().equalsIgnoreCase("light2-label")) {
 902                     if (me.isPrimaryButtonDown()) {
 903                         pointLight2.setTranslateX(pointLight2.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 904                     }
 905                     else if (me.isMiddleButtonDown()) {
 906                         pointLight2.setTranslateY(pointLight2.getTranslateY() + mouseDeltaX*modifierFactor*modifier);
 907                     }
 908                     else if (me.isSecondaryButtonDown()) {
 909                         pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 910                     }
 911                     // System.out.println("light2 " + pointLight2.getTranslateX() + " " + pointLight2.getTranslateY() + " " + pointLight2.getTranslateZ());
 912                 }
 913                 else if (label.getId().equalsIgnoreCase("light2xy-label")) {
 914                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 915                         pointLight2.setTranslateX(pointLight2.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 916                         pointLight2.setTranslateY(pointLight2.getTranslateY() - mouseDeltaY*modifierFactor*modifier);
 917                     }
 918                     else if (me.isSecondaryButtonDown()) {
 919                         pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 920                     }
 921                     // System.out.println("light2 " + pointLight2.getTranslateX() + " " + pointLight2.getTranslateY() + " " + pointLight2.getTranslateZ());
 922                 }
 923                 else if (label.getId().equalsIgnoreCase("light2xz-label")) {
 924                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 925                         pointLight2.setTranslateX(pointLight2.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 926                         pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaY*modifierFactor*modifier);
 927                     }
 928                     else if (me.isSecondaryButtonDown()) {
 929                         // pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*0.1*modifier);
 930                     }
 931                     // System.out.println("light2 " + pointLight2.getTranslateX() + " " + pointLight2.getTranslateY() + " " + pointLight2.getTranslateZ());
 932                 }
 933                 else if (label.getId().equalsIgnoreCase("light2yz-label")) {
 934                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 935                         pointLight2.setTranslateY(pointLight2.getTranslateY() - mouseDeltaY*modifierFactor*modifier);
 936                         pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 937                     }
 938                     else if (me.isSecondaryButtonDown()) {
 939                         // pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*0.1*modifier);
 940                     }
 941                     // System.out.println("light2 " + pointLight2.getTranslateX() + " " + pointLight2.getTranslateY() + " " + pointLight2.getTranslateZ());
 942                 }
 943 
 944                 if (label.getId().equalsIgnoreCase("light3-label")) {
 945                     if (me.isPrimaryButtonDown()) {
 946                         pointLight3.setTranslateX(pointLight3.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 947                     }
 948                     else if (me.isMiddleButtonDown()) {
 949                         pointLight3.setTranslateY(pointLight3.getTranslateY() + mouseDeltaX*modifierFactor*modifier);
 950                     }
 951                     else if (me.isSecondaryButtonDown()) {
 952                         pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 953                     }
 954                     // System.out.println("light3 " + pointLight3.getTranslateX() + " " + pointLight3.getTranslateY() + " " + pointLight3.getTranslateZ());
 955                 }
 956                 else if (label.getId().equalsIgnoreCase("light3xy-label")) {
 957                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 958                         pointLight3.setTranslateX(pointLight3.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 959                         pointLight3.setTranslateY(pointLight3.getTranslateY() - mouseDeltaY*modifierFactor*modifier);
 960                     }
 961                     else if (me.isSecondaryButtonDown()) {
 962                         pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 963                     }
 964                     // System.out.println("light3 " + pointLight3.getTranslateX() + " " + pointLight3.getTranslateY() + " " + pointLight3.getTranslateZ());
 965                 }
 966                 else if (label.getId().equalsIgnoreCase("light3xz-label")) {
 967                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 968                         pointLight3.setTranslateX(pointLight3.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 969                         pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaY*modifierFactor*modifier);
 970                     }
 971                     else if (me.isSecondaryButtonDown()) {
 972                         // pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*0.1*modifier);
 973                     }
 974                     // System.out.println("light3 " + pointLight3.getTranslateX() + " " + pointLight3.getTranslateY() + " " + pointLight3.getTranslateZ());
 975                 }
 976                 else if (label.getId().equalsIgnoreCase("light3yz-label")) {
 977                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 978                         pointLight3.setTranslateY(pointLight3.getTranslateY() - mouseDeltaY*modifierFactor*modifier);
 979                         pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 980                     }
 981                     else if (me.isSecondaryButtonDown()) {
 982                         // pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*0.1*modifier);
 983                     }
 984                     // System.out.println("light3 " + pointLight3.getTranslateX() + " " + pointLight3.getTranslateY() + " " + pointLight3.getTranslateZ());
 985                 }
 986 
 987                 pointLight2Geo.setTranslateX(pointLight2.getTranslateX());
 988                 pointLight2Geo.setTranslateY(pointLight2.getTranslateY());
 989                 pointLight2Geo.setTranslateZ(pointLight2.getTranslateZ());
 990 
 991                 pointLight3Geo.setTranslateX(pointLight3.getTranslateX());
 992                 pointLight3Geo.setTranslateY(pointLight3.getTranslateY());
 993                 pointLight3Geo.setTranslateZ(pointLight3.getTranslateZ());
 994             }
 995             
 996 
 997         });
 998     }
 999 
1000     //=============================================================================
1001     // start
1002     //=============================================================================
1003     @Override
1004     public void start(Stage primaryStage) {
1005         sessionManager = SessionManager.createSessionManager("OldTestViewer");
1006         System.out.println("new File().getAbsolutePath() = " + new File("").getAbsolutePath());
1007 
1008         buildScene();
1009         buildLights();
1010         buildCamera();
1011         buildSpheres();
1012         buildAxes();
1013         
1014         Scene scene = new Scene(root, 1024, 768, true);
1015         scene.setFill(Color.GREY);
1016         handleKeyboard(scene, world);
1017         handleMouse(scene, world);
1018         
1019         primaryStage.setTitle("Test (Maya) Viewer");
1020         primaryStage.setScene(scene);
1021         primaryStage.show();
1022         
1023         primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
1024             @Override
1025             public void handle(WindowEvent event) {
1026                 if (loadedPath != null) {
1027                     sessionManager.getProperties().setProperty(PATH_PROPERTY, loadedPath.getAbsolutePath());
1028                 }
1029                 sessionManager.saveSession();
1030             }
1031         });
1032         
1033         sessionManager.loadSession();
1034 
1035         String path = sessionManager.getProperties().getProperty(PATH_PROPERTY);
1036         if (path != null) {
1037             loadNewNode(new File(path));
1038         }
1039 
1040 
1041         scene.setCamera(camera);
1042                 
1043         // ScenicView.show(scene);
1044         
1045         // Eventually move the below to an AnimationTimer
1046         updateLight();
1047     }
1048     
1049     // Eventually move the below to an AnimationTimer
1050     // This causes the light to be updated properly
1051     // Currently there is a bug that doesn't update the lights that have parent transforms
1052     private void updateLight() {
1053         Transform transform = camera.getLocalToSceneTransform();
1054         double x = transform.getTx();
1055         double y = transform.getTy();
1056         double z = transform.getTz();
1057         // System.out.println("x = " + x + ", y = " + y + ", z = " + z);
1058         pointLight.setTranslateX(x);
1059         pointLight.setTranslateY(y);
1060         pointLight.setTranslateZ(z);
1061     }
1062 
1063     private void handleMouse(Scene scene, final Node root) {
1064         scene.setOnMousePressed(new EventHandler<MouseEvent>() {
1065             @Override public void handle(MouseEvent me) {
1066                 mousePosX = me.getSceneX();
1067                 mousePosY = me.getSceneY();
1068                 mouseOldX = me.getSceneX();
1069                 mouseOldY = me.getSceneY();
1070             }
1071         });
1072         scene.setOnMouseDragged(new EventHandler<MouseEvent>() {
1073             @Override public void handle(MouseEvent me) {
1074                 mouseOldX = mousePosX;
1075                 mouseOldY = mousePosY;
1076                 mousePosX = me.getSceneX();
1077                 mousePosY = me.getSceneY();
1078                 mouseDeltaX = (mousePosX - mouseOldX); //*DELTA_MULTIPLIER;
1079                 mouseDeltaY = (mousePosY - mouseOldY); //*DELTA_MULTIPLIER;
1080                 
1081                 double modifier = 1.0;
1082                 double modifierFactor = 0.1;
1083                 
1084                 if (me.isControlDown()) {
1085                     modifier = 0.1;
1086                 } 
1087                 if (me.isShiftDown()) {
1088                     modifier = 10.0;
1089                 }     
1090                 if (me.isAltDown() && me.isPrimaryButtonDown()) {
1091 //                    System.out.println("(MouseEvent.getX() = " + me.getSceneX() + ", MouseEvent.getY() = " + me.getSceneY() + ") (mouseDeltaX = " + mouseDeltaX + ", mouseDeltaY = " + mouseDeltaY + ")");
1092                     cameraXform.ry.setAngle(cameraXform.ry.getAngle() - mouseDeltaX*modifierFactor*modifier*2.0);  // +
1093                     cameraXform.rx.setAngle(cameraXform.rx.getAngle() + mouseDeltaY*modifierFactor*modifier*2.0);  // -
1094                     // Eventually move the below to an AnimationTimer
1095                     updateLight();
1096                 }
1097                 else if (me.isAltDown() && me.isSecondaryButtonDown()) {
1098                     double z = camera.getTranslateZ();
1099                     double newZ = z + mouseDeltaX*modifierFactor*modifier;
1100                     camera.setTranslateZ(newZ);
1101                     // Eventually move the below to an AnimationTimer
1102                     updateLight();
1103                 }
1104                 else if (me.isAltDown() && me.isMiddleButtonDown()) {
1105                     cameraXform2.t.setX(cameraXform2.t.getX() + mouseDeltaX*modifierFactor*modifier*0.3);  // -
1106                     cameraXform2.t.setY(cameraXform2.t.getY() + mouseDeltaY*modifierFactor*modifier*0.3);  // -
1107                     // System.out.println("cameraXform2.t : " + cameraXform2.t.getX() + ", " + cameraXform2.t.getY());
1108                     // Eventually move the below to an AnimationTimer
1109                     updateLight();
1110                 }
1111             }
1112         });
1113     }
1114     
1115     private void handleKeyboard(Scene scene, final Node root) {
1116         //System.out.println("--> handleKeyboard");
1117         final boolean moveCamera = true;
1118         scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
1119             @Override
1120             public void handle(KeyEvent event) {
1121                 Duration currentTime;
1122                 //System.out.println("--> handleKeyboard>handle");
1123                 switch (event.getCode()) {
1124                     case F:
1125                         if (event.isControlDown()) {
1126                             onButtonSave();
1127                         }
1128                         break;
1129                     case O:
1130                         if (event.isControlDown()) {
1131                             onButtonLoad();
1132                         }
1133                         break;
1134                     case Z:
1135                         if (event.isShiftDown()) {
1136                             cameraXform.ry.setAngle(0.0);
1137                             cameraXform.rx.setAngle(0.0);
1138                             camera.setTranslateZ(-300.0);
1139                         }   
1140                         cameraXform2.t.setX(0.0);
1141                         cameraXform2.t.setY(0.0);
1142                         break;
1143                     case X:
1144                         if (event.isControlDown()) {
1145                             if (axisGroup.isVisible()) {
1146                                 axisGroup.setVisible(false);
1147                             }
1148                             else {
1149                                 axisGroup.setVisible(true);
1150                             }
1151                         }   
1152                         break;
1153                     case W:
1154                         if (event.isControlDown()) {
1155                             if (loadedNode.isVisible()) {
1156                                 loadedNode.setVisible(false);
1157                             }
1158                             else {
1159                                 loadedNode.setVisible(true);
1160                             }
1161                         }   
1162                         break;
1163                     case S:
1164                         if (event.isControlDown()) {
1165                             if (spheresGroup.isVisible()) {
1166                                 spheresGroup.setVisible(false);
1167                             }
1168                             else {
1169                                 spheresGroup.setVisible(true);
1170                             }
1171                         }   
1172                         break;
1173                     case U:
1174                         createStage2();
1175                         break;
1176                     case SPACE:
1177                         if (timelinePlaying) {
1178                             timeline.pause();
1179                             timelinePlaying = false;
1180                         }
1181                         else {
1182                             timeline.play();
1183                             timelinePlaying = true;
1184                         }
1185                         break;
1186                         
1187                         /*
1188                          *     double CONTROL_MULTIPLIER = 0.1;
1189     double SHIFT_MULTIPLIER = 0.1;
1190     double ALT_MULTIPLIER = 0.5;
1191                          */
1192                     case UP:
1193                         if (event.isControlDown() && event.isShiftDown()) {
1194                             cameraXform2.t.setY(cameraXform2.t.getY() - 10.0*CONTROL_MULTIPLIER);  
1195                             updateLight();
1196                         }  
1197                         else if (event.isAltDown() && event.isShiftDown()) {
1198                             cameraXform.rx.setAngle(cameraXform.rx.getAngle() - 10.0*ALT_MULTIPLIER);  
1199                             updateLight();
1200                         }
1201                         else if (event.isControlDown()) {
1202                             cameraXform2.t.setY(cameraXform2.t.getY() - 1.0*CONTROL_MULTIPLIER);  
1203                             updateLight();
1204                         }
1205                         else if (event.isAltDown()) {
1206                             cameraXform.rx.setAngle(cameraXform.rx.getAngle() - 2.0*ALT_MULTIPLIER);  
1207                             updateLight();
1208                         }
1209                         else if (event.isShiftDown()) {
1210                             double z = camera.getTranslateZ();
1211                             double newZ = z + 5.0*SHIFT_MULTIPLIER;
1212                             camera.setTranslateZ(newZ);
1213                             updateLight();
1214                         }
1215                         break;
1216                     case DOWN:
1217                         if (event.isControlDown() && event.isShiftDown()) {
1218                             cameraXform2.t.setY(cameraXform2.t.getY() + 10.0*CONTROL_MULTIPLIER);  
1219                             updateLight();
1220                         }  
1221                         else if (event.isAltDown() && event.isShiftDown()) {
1222                             cameraXform.rx.setAngle(cameraXform.rx.getAngle() + 10.0*ALT_MULTIPLIER);  
1223                             updateLight();
1224                         }
1225                         else if (event.isControlDown()) {
1226                             cameraXform2.t.setY(cameraXform2.t.getY() + 1.0*CONTROL_MULTIPLIER);  
1227                             updateLight();
1228                         }
1229                         else if (event.isAltDown()) {
1230                             cameraXform.rx.setAngle(cameraXform.rx.getAngle() + 2.0*ALT_MULTIPLIER);  
1231                             updateLight();
1232                         }
1233                         else if (event.isShiftDown()) {
1234                             double z = camera.getTranslateZ();
1235                             double newZ = z - 5.0*SHIFT_MULTIPLIER;
1236                             camera.setTranslateZ(newZ);
1237                             updateLight();
1238                         }
1239                         break;
1240                     case RIGHT:
1241                         if (event.isControlDown() && event.isShiftDown()) {
1242                             cameraXform2.t.setX(cameraXform2.t.getX() + 10.0*CONTROL_MULTIPLIER);  
1243                             updateLight();
1244                         }  
1245                         else if (event.isAltDown() && event.isShiftDown()) {
1246                             cameraXform.ry.setAngle(cameraXform.ry.getAngle() - 10.0*ALT_MULTIPLIER);  
1247                             updateLight();
1248                         }
1249                         else if (event.isControlDown()) {
1250                             cameraXform2.t.setX(cameraXform2.t.getX() + 1.0*CONTROL_MULTIPLIER);  
1251                             updateLight();
1252                         }
1253                         else if (event.isShiftDown()) {
1254                             currentTime = timeline.getCurrentTime();
1255                             timeline.jumpTo(Frame.frame(Math.round(Frame.toFrame(currentTime)/10.0)*10 + 10));
1256                             // timeline.jumpTo(Duration.seconds(currentTime.toSeconds() + ONE_FRAME));
1257                         }
1258                         else if (event.isAltDown()) {
1259                             cameraXform.ry.setAngle(cameraXform.ry.getAngle() - 2.0*ALT_MULTIPLIER);  
1260                             updateLight();
1261                         }
1262                         else {
1263                             currentTime = timeline.getCurrentTime();
1264                             timeline.jumpTo(Frame.frame(Frame.toFrame(currentTime) + 1));
1265                             // timeline.jumpTo(Duration.seconds(currentTime.toSeconds() + ONE_FRAME));
1266                         }
1267                         break;
1268                     case LEFT:
1269                         if (event.isControlDown() && event.isShiftDown()) {
1270                             cameraXform2.t.setX(cameraXform2.t.getX() - 10.0*CONTROL_MULTIPLIER);  
1271                             updateLight();
1272                         }  
1273                         else if (event.isAltDown() && event.isShiftDown()) {
1274                             cameraXform.ry.setAngle(cameraXform.ry.getAngle() + 10.0*ALT_MULTIPLIER);  // -
1275                             updateLight();
1276                         }
1277                         else if (event.isControlDown()) {
1278                             cameraXform2.t.setX(cameraXform2.t.getX() - 1.0*CONTROL_MULTIPLIER);  
1279                             updateLight();
1280                         }
1281                         else if (event.isShiftDown()) {
1282                             currentTime = timeline.getCurrentTime();
1283                             timeline.jumpTo(Frame.frame(Math.round(Frame.toFrame(currentTime)/10.0)*10 - 10));
1284                             // timeline.jumpTo(Duration.seconds(currentTime.toSeconds() - ONE_FRAME));
1285                         }
1286                         else if (event.isAltDown()) {
1287                             cameraXform.ry.setAngle(cameraXform.ry.getAngle() + 2.0*ALT_MULTIPLIER);  // -
1288                             updateLight();
1289                         }
1290                         else {
1291                             currentTime = timeline.getCurrentTime();
1292                             timeline.jumpTo(Frame.frame(Frame.toFrame(currentTime) - 1));
1293                             // timeline.jumpTo(Duration.seconds(currentTime.toSeconds() - ONE_FRAME));
1294                         }
1295                         break;
1296                 }
1297                 //System.out.println(cameraXform.getTranslateX() + ", " + cameraXform.getTranslateY() + ", " + cameraXform.getTranslateZ());
1298             }
1299 
1300         });
1301     }
1302     
1303     private void onButtonSave() {
1304         new FXMLExporter("output.fxml").export(loadedNode);
1305     }
1306 
1307     /**
1308      * The main() method is ignored in correctly deployed JavaFX application.
1309      * main() serves only as fallback in case the application can not be
1310      * launched through deployment artifacts, e.g., in IDEs with limited FX
1311      * support. NetBeans ignores main().
1312      *
1313      * @param args the command line arguments
1314      */
1315     public static void main(String[] args) {
1316         System.setProperty("prism.dirtyopts", "false");
1317         //System.setProperty("prism.dirtyopts", "false");
1318         launch(args);
1319     }
1320 
1321     private void applyRecursively(Node node, Action a) {
1322         if (node instanceof MeshView) {
1323             a.apply((MeshView) node);
1324         }
1325         if (node instanceof Parent) {
1326             for (Node n : ((Parent) node).getChildrenUnmodifiable()) {
1327                 applyRecursively(n, a);
1328             }
1329         }
1330     }
1331 
1332     private interface Action {
1333         void apply(MeshView mv);
1334     }
1335     
1336     private Action applyWireframe = new Action() {
1337         @Override
1338         public void apply(MeshView mv) {
1339             mv.drawModeProperty().bind(Bindings.when(wireframe).then(DrawMode.LINE).otherwise(DrawMode.FILL));
1340         }
1341     };
1342 
1343     private Action addToCollisionScene = new Action() {
1344         private Vec3d pointSize;
1345         private double size;
1346         private PhongMaterial material = new PhongMaterial(Color.RED);
1347 
1348         @Override
1349         public void apply(MeshView mv) {
1350         }
1351     };
1352     
1353 }