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         /*
 513         Button backButton;
 514         Button stopButton;
 515         Button playButton;
 516         Button pauseButton;
 517         Button forwardButton;
 518         final EventHandler<ActionEvent> backAction = new EventHandler<ActionEvent>() {
 519             @Override
 520             public void handle(ActionEvent e) {
 521                 System.out.println("backAction");
 522                 Duration currentTime;
 523                 currentTime = timeline.getCurrentTime();
 524                 timeline.jumpTo(Duration.seconds(currentTime.toSeconds() - ONE_FRAME));
 525             }
 526         };
 527         final EventHandler<ActionEvent> stopAction = new EventHandler<ActionEvent>() {
 528             @Override
 529             public void handle(ActionEvent e) {
 530                 System.out.println("stopAction");
 531                 timeline.stop();
 532             }
 533         };
 534         final EventHandler<ActionEvent> playAction = new EventHandler<ActionEvent>() {
 535             @Override
 536             public void handle(ActionEvent e) {
 537                 System.out.println("playAction");
 538                 timeline.play();
 539             }
 540         };
 541         final EventHandler<ActionEvent> pauseAction = new EventHandler<ActionEvent>() {
 542             @Override
 543             public void handle(ActionEvent e) {
 544                 System.out.println("pauseAction");
 545                 timeline.pause();
 546             }
 547         };
 548         final EventHandler<ActionEvent> forwardAction = new EventHandler<ActionEvent>() {
 549             @Override
 550             public void handle(ActionEvent e) {
 551                 System.out.println("forwardAction");                
 552                 Duration currentTime;
 553                 currentTime = timeline.getCurrentTime();
 554                 timeline.jumpTo(Duration.seconds(currentTime.toSeconds() + ONE_FRAME));
 555             }
 556         };
 557         */
 558 
 559         //----------------------------------------
 560         // Light (Parented to Camera)
 561 
 562         final ColorPicker lightColorPicker = new ColorPicker(pointLight.getColor());
 563         lightColorPicker.setOnAction(new EventHandler<ActionEvent>() {
 564             @Override
 565             public void handle(ActionEvent t) {
 566                 Color c = lightColorPicker.getValue();
 567                 pointLight.setColor(c);
 568                 //pointLightMaterial.setDiffuseColor(c);
 569                 //pointLightGeo.setMaterial(pointLightMaterial);
 570             }
 571         });
 572 
 573         Color c = Color.WHITE;
 574         Scene scene = world.getScene();
 575         if (scene != null) {
 576             Paint fill = scene.getFill();
 577             if (fill instanceof Color) {
 578                 c = (Color)fill;
 579             }
 580         }
 581         final ColorPicker backColorPicker = new ColorPicker(c);
 582         backColorPicker.setOnAction(new EventHandler<ActionEvent>() {
 583 
 584             @Override
 585             public void handle(ActionEvent event) {
 586                 Color c = backColorPicker.getValue();
 587                 //probably not the best way to get scene
 588                 if (world.getScene() != null) {
 589                     world.getScene().setFill(c);
 590                 }
 591                 
 592             }
 593         });
 594 
 595         final EventHandler<ActionEvent> resetFovAction = new EventHandler<ActionEvent>() {
 596             @Override
 597             public void handle(ActionEvent e) {
 598                 System.out.println("resetFovAction");                
 599                 camera.setFieldOfView(30.0);
 600             }
 601         };
 602         final EventHandler<ActionEvent> resetNearAction = new EventHandler<ActionEvent>() {
 603             @Override
 604             public void handle(ActionEvent e) {
 605                 System.out.println("resetFovAction");                
 606                 camera.setNearClip(0.1);
 607             }
 608         };
 609         final EventHandler<ActionEvent> resetFarAction = new EventHandler<ActionEvent>() {
 610             @Override
 611             public void handle(ActionEvent e) {
 612                 System.out.println("resetFovAction");                
 613                 camera.setFarClip(10000.0);
 614             }
 615         };
 616 
 617         fovLabel = new Label("fov");
 618         fovLabel.setId("fov-label");
 619         nearLabel = new Label("Near Clip");
 620         nearLabel.setId("near-label");
 621         farLabel = new Label("Far Clip");
 622         farLabel.setId("far-label");
 623         resetFovButton = new Button("reset FOV");
 624         resetFovButton.setId("resetFov-button");
 625         resetFovButton.setOnAction(resetFovAction);
 626         resetNearButton = new Button("reset near");
 627         resetNearButton.setId("resetNear-button");
 628         resetNearButton.setOnAction(resetNearAction);
 629         resetFarButton = new Button("reset far");
 630         resetFarButton.setId("resetFar-button");
 631         resetFarButton.setOnAction(resetFarAction);
 632 
 633         VBox vbox = new VBox(0, lightColorPicker, backColorPicker, fovLabel, nearLabel, farLabel,
 634                 resetFovButton, resetNearButton, resetFarButton);
 635         vbox.setId("center");
 636         vbox.setAlignment(Pos.TOP_CENTER);
 637         borderPane.setCenter(vbox);
 638         
 639         handleMouse2(fovLabel);
 640         handleMouse2(nearLabel);
 641         handleMouse2(farLabel);
 642         
 643         //----------------------------------------
 644         // Light 2
 645 
 646         final EventHandler<ActionEvent> light2resetXYZAction = new EventHandler<ActionEvent>() {
 647             @Override
 648             public void handle(ActionEvent e) {
 649                 System.out.println("light2resetXYZAction");                
 650                 pointLight2.setTranslateX(-10.0);
 651                 pointLight2.setTranslateY(10.0);
 652                 pointLight2.setTranslateZ(0.0);
 653                 pointLight2Geo.setTranslateX(pointLight2.getTranslateX());
 654                 pointLight2Geo.setTranslateY(pointLight2.getTranslateY());
 655                 pointLight2Geo.setTranslateZ(pointLight2.getTranslateZ());
 656             }
 657         };
 658         final EventHandler<ActionEvent> light2zeroXYZAction = new EventHandler<ActionEvent>() {
 659             @Override
 660             public void handle(ActionEvent e) {
 661                 System.out.println("light2zeroXYZAction");                
 662                 pointLight2.setTranslateX(0.0);
 663                 pointLight2.setTranslateY(0.0);
 664                 pointLight2.setTranslateZ(0.0);
 665                 pointLight2Geo.setTranslateX(pointLight2.getTranslateX());
 666                 pointLight2Geo.setTranslateY(pointLight2.getTranslateY());
 667                 pointLight2Geo.setTranslateZ(pointLight2.getTranslateZ());
 668             }
 669         };
 670         final EventHandler<ActionEvent> light2showAction = new EventHandler<ActionEvent>() {
 671             @Override
 672             public void handle(ActionEvent e) {
 673                 System.out.println("light2show");                
 674                 pointLight2Geo.setVisible(true);
 675             }
 676         };
 677         final EventHandler<ActionEvent> light2hideAction = new EventHandler<ActionEvent>() {
 678             @Override
 679             public void handle(ActionEvent e) {
 680                 System.out.println("light2hide");                
 681                 pointLight2Geo.setVisible(false);
 682             }
 683         };
 684         
 685         final ColorPicker light2ColorPicker = new ColorPicker(pointLight2.getColor());
 686         light2ColorPicker.setOnAction(new EventHandler<ActionEvent>() {
 687             @Override
 688             public void handle(ActionEvent t) {
 689                 Color c = light2ColorPicker.getValue();
 690                 pointLight2.setColor(c);
 691                 pointLight2Material.setDiffuseColor(c);
 692                 pointLight2Geo.setMaterial(pointLight2Material);
 693             }
 694         });
 695 
 696         light2Label = new Label("light2");
 697         light2Label.setId("light2-label");
 698         light2xyLabel = new Label("light2 XY");
 699         light2xyLabel.setId("light2xy-label");
 700         light2xzLabel = new Label("light2 XZ");
 701         light2xzLabel.setId("light2xz-label");
 702         light2yzLabel = new Label("light2 YZ");
 703         light2yzLabel.setId("light2yz-label");
 704         light2resetXYZButton = new Button("reset XYZ");
 705         light2resetXYZButton.setId("light2-resetXYZ");
 706         light2resetXYZButton.setOnAction(light2resetXYZAction);
 707         light2zeroXYZButton = new Button("zero XYZ");
 708         light2zeroXYZButton.setId("light2-zeroXYZ");
 709         light2zeroXYZButton.setOnAction(light2zeroXYZAction);
 710         light2show = new Button("show");
 711         light2show.setId("light2-show");
 712         light2show.setOnAction(light2showAction);
 713         light2hide = new Button("hide");
 714         light2hide.setId("light2-hide");
 715         light2hide.setOnAction(light2hideAction);
 716         VBox vbox2 = new VBox(0, light2ColorPicker, light2Label, light2xyLabel, light2xzLabel, light2yzLabel,
 717                 light2resetXYZButton, light2zeroXYZButton, light2show, light2hide);
 718         vbox2.setId("left");
 719         vbox2.setAlignment(Pos.TOP_CENTER);
 720         borderPane.setLeft(vbox2);
 721 
 722         handleMouse2(light2Label);
 723         handleMouse2(light2xyLabel);
 724         handleMouse2(light2xzLabel);
 725         handleMouse2(light2yzLabel);
 726 
 727         //----------------------------------------
 728         // Light 3
 729 
 730         final EventHandler<ActionEvent> light3resetXYZAction = new EventHandler<ActionEvent>() {
 731             @Override
 732             public void handle(ActionEvent e) {
 733                 System.out.println("light3resetXYZAction");                
 734                 pointLight3.setTranslateX(-10.0);
 735                 pointLight3.setTranslateY(10.0);
 736                 pointLight3.setTranslateZ(0.0);
 737                 pointLight3Geo.setTranslateX(pointLight3.getTranslateX());
 738                 pointLight3Geo.setTranslateY(pointLight3.getTranslateY());
 739                 pointLight3Geo.setTranslateZ(pointLight3.getTranslateZ());
 740             }
 741         };
 742         final EventHandler<ActionEvent> light3zeroXYZAction = new EventHandler<ActionEvent>() {
 743             @Override
 744             public void handle(ActionEvent e) {
 745                 System.out.println("light3zeroXYZAction");                
 746                 pointLight3.setTranslateX(0.0);
 747                 pointLight3.setTranslateY(0.0);
 748                 pointLight3.setTranslateZ(0.0);
 749                 pointLight3Geo.setTranslateX(pointLight3.getTranslateX());
 750                 pointLight3Geo.setTranslateY(pointLight3.getTranslateY());
 751                 pointLight3Geo.setTranslateZ(pointLight3.getTranslateZ());
 752             }
 753         };
 754         final EventHandler<ActionEvent> light3showAction = new EventHandler<ActionEvent>() {
 755             @Override
 756             public void handle(ActionEvent e) {
 757                 System.out.println("light3show");                
 758                 pointLight3Geo.setVisible(true);
 759             }
 760         };
 761         final EventHandler<ActionEvent> light3hideAction = new EventHandler<ActionEvent>() {
 762             @Override
 763             public void handle(ActionEvent e) {
 764                 System.out.println("light3hide");                
 765                 pointLight3Geo.setVisible(false);
 766             }
 767         };
 768         
 769         final ColorPicker light3ColorPicker = new ColorPicker(pointLight3.getColor());
 770         light3ColorPicker.setOnAction(new EventHandler<ActionEvent>() {
 771             @Override
 772             public void handle(ActionEvent t) {
 773                 Color c = light3ColorPicker.getValue();
 774                 pointLight3.setColor(c);
 775                 pointLight3Material.setDiffuseColor(c);
 776                 pointLight3Geo.setMaterial(pointLight3Material);
 777             }
 778         });
 779 
 780         light3Label = new Label("light3");
 781         light3Label.setId("light3-label");
 782         light3xyLabel = new Label("light3 XY");
 783         light3xyLabel.setId("light3xy-label");
 784         light3xzLabel = new Label("light3 XZ");
 785         light3xzLabel.setId("light3xz-label");
 786         light3yzLabel = new Label("light3 YZ");
 787         light3yzLabel.setId("light3yz-label");
 788         light3resetXYZButton = new Button("reset XYZ");
 789         light3resetXYZButton.setId("light3-resetXYZ");
 790         light3resetXYZButton.setOnAction(light3resetXYZAction);
 791         light3zeroXYZButton = new Button("zero XYZ");
 792         light3zeroXYZButton.setId("light3-zeroXYZ");
 793         light3zeroXYZButton.setOnAction(light3zeroXYZAction);
 794         light3show = new Button("show");
 795         light3show.setId("light3-show");
 796         light3show.setOnAction(light3showAction);
 797         light3hide = new Button("hide");
 798         light3hide.setId("light3-hide");
 799         light3hide.setOnAction(light3hideAction);
 800 
 801         VBox vbox3 = new VBox(0, light3ColorPicker, light3Label, light3xyLabel, light3xzLabel, light3yzLabel,
 802                 light3resetXYZButton, light3zeroXYZButton, light3show, light3hide);
 803         vbox3.setId("right");
 804         vbox3.setAlignment(Pos.TOP_CENTER);
 805         borderPane.setRight(vbox3);
 806 
 807         handleMouse2(light3Label);
 808         handleMouse2(light3xyLabel);
 809         handleMouse2(light3xzLabel);
 810         handleMouse2(light3yzLabel);
 811 /*
 812         backButton = new Button("<-");
 813         backButton.setId("back-button");
 814         backButton.setOnAction(backAction);
 815         stopButton = new Button("Stop");
 816         stopButton.setId("stop-button");
 817         stopButton.setOnAction(stopAction);
 818         playButton = new Button("Play");
 819         playButton.setId("play-button");
 820         playButton.setOnAction(playAction);
 821         pauseButton = new Button("Pause");
 822         pauseButton.setId("pause-button");
 823         pauseButton.setOnAction(pauseAction);
 824         forwardButton = new Button("->");
 825         forwardButton.setId("forward-button");
 826         forwardButton.setOnAction(forwardAction);
 827 
 828         HBox hbox = new HBox(0, backButton, stopButton, playButton, pauseButton, forwardButton);
 829         hbox.setId("bottom");
 830         hbox.setAlignment(Pos.CENTER);
 831         borderPane.setBottom(hbox);
 832 */
 833         stage.show();
 834     }
 835     
 836     private void handleMouse2(final Label label) {
 837 
 838         label.setOnMousePressed(new EventHandler<MouseEvent>() {
 839             @Override public void handle(MouseEvent me) {
 840                 // System.out.println("________________________________");
 841                 System.out.println("label.getId() = " + label.getId());
 842                 // System.out.println("label.getText() = " + label.getText());
 843                 // System.out.println("label.toString() = " + label.toString());
 844                 
 845                 mousePosX = me.getSceneX();
 846                 mousePosY = me.getSceneY();
 847                 mouseOldX = me.getSceneX();
 848                 mouseOldY = me.getSceneY();
 849             }
 850         });
 851         label.setOnMouseDragged(new EventHandler<MouseEvent>() {
 852             @Override public void handle(MouseEvent me) {
 853                 mouseOldX = mousePosX;
 854                 mouseOldY = mousePosY;
 855                 mousePosX = me.getSceneX();
 856                 mousePosY = me.getSceneY();
 857                 mouseDeltaX = (mousePosX - mouseOldX); //*DELTA_MULTIPLIER;
 858                 mouseDeltaY = (mousePosY - mouseOldY); //*DELTA_MULTIPLIER;
 859                 double modifier = 1.0;
 860                 double modifierFactor = 0.1;
 861                 
 862                 if (me.isControlDown()) {
 863                     modifier = 0.1;
 864                 } 
 865                 if (me.isShiftDown()) {
 866                     modifier = 10.0;
 867                 }     
 868                 /*
 869                     .id("fov-label")
 870                     .id("near-label")
 871                     .id("far-label")
 872 */
 873                 if (label.getId().equalsIgnoreCase("fov-label")) {
 874                     if (me.isPrimaryButtonDown()) {
 875                         double fieldOfView = camera.getFieldOfView();
 876                         fieldOfView += mouseDeltaX*modifierFactor*modifier;
 877                         if (fieldOfView < 0.0001) { fieldOfView = 0.001; }
 878                         else if (fieldOfView > 179.99) { fieldOfView = 179.99; }
 879                         System.out.println("fieldOfView = " + fieldOfView);
 880                         camera.setFieldOfView(fieldOfView);
 881                     }
 882                 }
 883                 else if (label.getId().equalsIgnoreCase("near-label")) {
 884                     if (me.isPrimaryButtonDown()) {
 885                         double nearClip = camera.getNearClip();
 886                         nearClip += mouseDeltaX*modifierFactor*modifier;
 887                         if (nearClip < 0.00000000001) { nearClip = 0.00000000001; }
 888                         else if (nearClip > 10000.00) { nearClip = 10000.0; }
 889                         System.out.println("nearClip = " + nearClip);
 890                         camera.setNearClip(nearClip);
 891                     }
 892                 }
 893                 else if (label.getId().equalsIgnoreCase("far-label")) {
 894                     if (me.isPrimaryButtonDown()) {
 895                         double farClip = camera.getFarClip();
 896                         farClip += mouseDeltaX*modifierFactor*modifier*10.0;
 897                         if (farClip < 0.0001) { farClip = 0.001; }
 898                         else if (farClip > 100000000.00) { farClip = 1000000000.0; }
 899                         System.out.println("farClip = " + farClip);
 900                         camera.setFarClip(farClip);
 901                     }
 902                 }
 903 
 904                 else if (label.getId().equalsIgnoreCase("light2-label")) {
 905                     if (me.isPrimaryButtonDown()) {
 906                         pointLight2.setTranslateX(pointLight2.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 907                     }
 908                     else if (me.isMiddleButtonDown()) {
 909                         pointLight2.setTranslateY(pointLight2.getTranslateY() + mouseDeltaX*modifierFactor*modifier);
 910                     }
 911                     else if (me.isSecondaryButtonDown()) {
 912                         pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 913                     }
 914                     // System.out.println("light2 " + pointLight2.getTranslateX() + " " + pointLight2.getTranslateY() + " " + pointLight2.getTranslateZ());
 915                 }
 916                 else if (label.getId().equalsIgnoreCase("light2xy-label")) {
 917                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 918                         pointLight2.setTranslateX(pointLight2.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 919                         pointLight2.setTranslateY(pointLight2.getTranslateY() - mouseDeltaY*modifierFactor*modifier);
 920                     }
 921                     else if (me.isSecondaryButtonDown()) {
 922                         pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 923                     }
 924                     // System.out.println("light2 " + pointLight2.getTranslateX() + " " + pointLight2.getTranslateY() + " " + pointLight2.getTranslateZ());
 925                 }
 926                 else if (label.getId().equalsIgnoreCase("light2xz-label")) {
 927                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 928                         pointLight2.setTranslateX(pointLight2.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 929                         pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaY*modifierFactor*modifier);
 930                     }
 931                     else if (me.isSecondaryButtonDown()) {
 932                         // pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*0.1*modifier);
 933                     }
 934                     // System.out.println("light2 " + pointLight2.getTranslateX() + " " + pointLight2.getTranslateY() + " " + pointLight2.getTranslateZ());
 935                 }
 936                 else if (label.getId().equalsIgnoreCase("light2yz-label")) {
 937                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 938                         pointLight2.setTranslateY(pointLight2.getTranslateY() - mouseDeltaY*modifierFactor*modifier);
 939                         pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 940                     }
 941                     else if (me.isSecondaryButtonDown()) {
 942                         // pointLight2.setTranslateZ(pointLight2.getTranslateZ() + mouseDeltaX*0.1*modifier);
 943                     }
 944                     // System.out.println("light2 " + pointLight2.getTranslateX() + " " + pointLight2.getTranslateY() + " " + pointLight2.getTranslateZ());
 945                 }
 946 
 947                 if (label.getId().equalsIgnoreCase("light3-label")) {
 948                     if (me.isPrimaryButtonDown()) {
 949                         pointLight3.setTranslateX(pointLight3.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 950                     }
 951                     else if (me.isMiddleButtonDown()) {
 952                         pointLight3.setTranslateY(pointLight3.getTranslateY() + mouseDeltaX*modifierFactor*modifier);
 953                     }
 954                     else if (me.isSecondaryButtonDown()) {
 955                         pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 956                     }
 957                     // System.out.println("light3 " + pointLight3.getTranslateX() + " " + pointLight3.getTranslateY() + " " + pointLight3.getTranslateZ());
 958                 }
 959                 else if (label.getId().equalsIgnoreCase("light3xy-label")) {
 960                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 961                         pointLight3.setTranslateX(pointLight3.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 962                         pointLight3.setTranslateY(pointLight3.getTranslateY() - mouseDeltaY*modifierFactor*modifier);
 963                     }
 964                     else if (me.isSecondaryButtonDown()) {
 965                         pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 966                     }
 967                     // System.out.println("light3 " + pointLight3.getTranslateX() + " " + pointLight3.getTranslateY() + " " + pointLight3.getTranslateZ());
 968                 }
 969                 else if (label.getId().equalsIgnoreCase("light3xz-label")) {
 970                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 971                         pointLight3.setTranslateX(pointLight3.getTranslateX() + mouseDeltaX*modifierFactor*modifier);
 972                         pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaY*modifierFactor*modifier);
 973                     }
 974                     else if (me.isSecondaryButtonDown()) {
 975                         // pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*0.1*modifier);
 976                     }
 977                     // System.out.println("light3 " + pointLight3.getTranslateX() + " " + pointLight3.getTranslateY() + " " + pointLight3.getTranslateZ());
 978                 }
 979                 else if (label.getId().equalsIgnoreCase("light3yz-label")) {
 980                     if (me.isPrimaryButtonDown() || me.isMiddleButtonDown()) {
 981                         pointLight3.setTranslateY(pointLight3.getTranslateY() - mouseDeltaY*modifierFactor*modifier);
 982                         pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*modifierFactor*modifier);
 983                     }
 984                     else if (me.isSecondaryButtonDown()) {
 985                         // pointLight3.setTranslateZ(pointLight3.getTranslateZ() + mouseDeltaX*0.1*modifier);
 986                     }
 987                     // System.out.println("light3 " + pointLight3.getTranslateX() + " " + pointLight3.getTranslateY() + " " + pointLight3.getTranslateZ());
 988                 }
 989 
 990                 pointLight2Geo.setTranslateX(pointLight2.getTranslateX());
 991                 pointLight2Geo.setTranslateY(pointLight2.getTranslateY());
 992                 pointLight2Geo.setTranslateZ(pointLight2.getTranslateZ());
 993 
 994                 pointLight3Geo.setTranslateX(pointLight3.getTranslateX());
 995                 pointLight3Geo.setTranslateY(pointLight3.getTranslateY());
 996                 pointLight3Geo.setTranslateZ(pointLight3.getTranslateZ());
 997             }
 998             
 999 
1000         });
1001     }
1002 
1003     //=============================================================================
1004     // start
1005     //=============================================================================
1006     @Override
1007     public void start(Stage primaryStage) {
1008         sessionManager = SessionManager.createSessionManager("OldTestViewer");
1009         System.out.println("new File().getAbsolutePath() = " + new File("").getAbsolutePath());
1010 
1011         buildScene();
1012         buildLights();
1013         buildCamera();
1014         buildSpheres();
1015         buildAxes();
1016         
1017         Scene scene = new Scene(root, 1024, 768, true);
1018         scene.setFill(Color.GREY);
1019         handleKeyboard(scene, world);
1020         handleMouse(scene, world);
1021         
1022         primaryStage.setTitle("Test (Maya) Viewer");
1023         primaryStage.setScene(scene);
1024         primaryStage.show();
1025         
1026         primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
1027             @Override
1028             public void handle(WindowEvent event) {
1029                 if (loadedPath != null) {
1030                     sessionManager.getProperties().setProperty(PATH_PROPERTY, loadedPath.getAbsolutePath());
1031                 }
1032                 sessionManager.saveSession();
1033             }
1034         });
1035         
1036         sessionManager.loadSession();
1037 
1038         String path = sessionManager.getProperties().getProperty(PATH_PROPERTY);
1039         if (path != null) {
1040             loadNewNode(new File(path));
1041         }
1042 
1043 
1044         scene.setCamera(camera);
1045                 
1046         // ScenicView.show(scene);
1047         
1048         // Eventually move the below to an AnimationTimer
1049         updateLight();
1050     }
1051     
1052     // Eventually move the below to an AnimationTimer
1053     // This causes the light to be updated properly
1054     // Currently there is a bug that doesn't update the lights that have parent transforms
1055     private void updateLight() {
1056         Transform transform = camera.getLocalToSceneTransform();
1057         double x = transform.getTx();
1058         double y = transform.getTy();
1059         double z = transform.getTz();
1060         // System.out.println("x = " + x + ", y = " + y + ", z = " + z);
1061         pointLight.setTranslateX(x);
1062         pointLight.setTranslateY(y);
1063         pointLight.setTranslateZ(z);
1064     }
1065 
1066     private void handleMouse(Scene scene, final Node root) {
1067         scene.setOnMousePressed(new EventHandler<MouseEvent>() {
1068             @Override public void handle(MouseEvent me) {
1069                 mousePosX = me.getSceneX();
1070                 mousePosY = me.getSceneY();
1071                 mouseOldX = me.getSceneX();
1072                 mouseOldY = me.getSceneY();
1073             }
1074         });
1075         scene.setOnMouseDragged(new EventHandler<MouseEvent>() {
1076             @Override public void handle(MouseEvent me) {
1077                 mouseOldX = mousePosX;
1078                 mouseOldY = mousePosY;
1079                 mousePosX = me.getSceneX();
1080                 mousePosY = me.getSceneY();
1081                 mouseDeltaX = (mousePosX - mouseOldX); //*DELTA_MULTIPLIER;
1082                 mouseDeltaY = (mousePosY - mouseOldY); //*DELTA_MULTIPLIER;
1083                 
1084                 double modifier = 1.0;
1085                 double modifierFactor = 0.1;
1086                 
1087                 if (me.isControlDown()) {
1088                     modifier = 0.1;
1089                 } 
1090                 if (me.isShiftDown()) {
1091                     modifier = 10.0;
1092                 }     
1093                 if (me.isAltDown() && me.isPrimaryButtonDown()) {
1094 //                    System.out.println("(MouseEvent.getX() = " + me.getSceneX() + ", MouseEvent.getY() = " + me.getSceneY() + ") (mouseDeltaX = " + mouseDeltaX + ", mouseDeltaY = " + mouseDeltaY + ")");
1095                     cameraXform.ry.setAngle(cameraXform.ry.getAngle() - mouseDeltaX*modifierFactor*modifier*2.0);  // +
1096                     cameraXform.rx.setAngle(cameraXform.rx.getAngle() + mouseDeltaY*modifierFactor*modifier*2.0);  // -
1097                     // Eventually move the below to an AnimationTimer
1098                     updateLight();
1099                 }
1100                 else if (me.isAltDown() && me.isSecondaryButtonDown()) {
1101                     double z = camera.getTranslateZ();
1102                     double newZ = z + mouseDeltaX*modifierFactor*modifier;
1103                     camera.setTranslateZ(newZ);
1104                     // Eventually move the below to an AnimationTimer
1105                     updateLight();
1106                 }
1107                 else if (me.isAltDown() && me.isMiddleButtonDown()) {
1108                     cameraXform2.t.setX(cameraXform2.t.getX() + mouseDeltaX*modifierFactor*modifier*0.3);  // -
1109                     cameraXform2.t.setY(cameraXform2.t.getY() + mouseDeltaY*modifierFactor*modifier*0.3);  // -
1110                     // System.out.println("cameraXform2.t : " + cameraXform2.t.getX() + ", " + cameraXform2.t.getY());
1111                     // Eventually move the below to an AnimationTimer
1112                     updateLight();
1113                 }
1114             }
1115         });
1116     }
1117     
1118     private void handleKeyboard(Scene scene, final Node root) {
1119         //System.out.println("--> handleKeyboard");
1120         final boolean moveCamera = true;
1121         scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
1122             @Override
1123             public void handle(KeyEvent event) {
1124                 Duration currentTime;
1125                 //System.out.println("--> handleKeyboard>handle");
1126                 switch (event.getCode()) {
1127                     case F:
1128                         if (event.isControlDown()) {
1129                             onButtonSave();
1130                         }
1131                         break;
1132                     case O:
1133                         if (event.isControlDown()) {
1134                             onButtonLoad();
1135                         }
1136                         break;
1137                     case Z:
1138                         if (event.isShiftDown()) {
1139                             cameraXform.ry.setAngle(0.0);
1140                             cameraXform.rx.setAngle(0.0);
1141                             camera.setTranslateZ(-300.0);
1142                         }   
1143                         cameraXform2.t.setX(0.0);
1144                         cameraXform2.t.setY(0.0);
1145                         break;
1146                     case X:
1147                         if (event.isControlDown()) {
1148                             if (axisGroup.isVisible()) {
1149                                 axisGroup.setVisible(false);
1150                             }
1151                             else {
1152                                 axisGroup.setVisible(true);
1153                             }
1154                         }   
1155                         break;
1156                     case W:
1157                         if (event.isControlDown()) {
1158                             if (loadedNode.isVisible()) {
1159                                 loadedNode.setVisible(false);
1160                             }
1161                             else {
1162                                 loadedNode.setVisible(true);
1163                             }
1164                         }   
1165                         break;
1166                     case S:
1167                         if (event.isControlDown()) {
1168                             if (spheresGroup.isVisible()) {
1169                                 spheresGroup.setVisible(false);
1170                             }
1171                             else {
1172                                 spheresGroup.setVisible(true);
1173                             }
1174                         }   
1175                         break;
1176                     case U:
1177                         createStage2();
1178                         break;
1179                     case SPACE:
1180                         if (timelinePlaying) {
1181                             timeline.pause();
1182                             timelinePlaying = false;
1183                         }
1184                         else {
1185                             timeline.play();
1186                             timelinePlaying = true;
1187                         }
1188                         break;
1189                         
1190                         /*
1191                          *     double CONTROL_MULTIPLIER = 0.1;
1192     double SHIFT_MULTIPLIER = 0.1;
1193     double ALT_MULTIPLIER = 0.5;
1194                          */
1195                     case UP:
1196                         if (event.isControlDown() && event.isShiftDown()) {
1197                             cameraXform2.t.setY(cameraXform2.t.getY() - 10.0*CONTROL_MULTIPLIER);  
1198                             updateLight();
1199                         }  
1200                         else if (event.isAltDown() && event.isShiftDown()) {
1201                             cameraXform.rx.setAngle(cameraXform.rx.getAngle() - 10.0*ALT_MULTIPLIER);  
1202                             updateLight();
1203                         }
1204                         else if (event.isControlDown()) {
1205                             cameraXform2.t.setY(cameraXform2.t.getY() - 1.0*CONTROL_MULTIPLIER);  
1206                             updateLight();
1207                         }
1208                         else if (event.isAltDown()) {
1209                             cameraXform.rx.setAngle(cameraXform.rx.getAngle() - 2.0*ALT_MULTIPLIER);  
1210                             updateLight();
1211                         }
1212                         else if (event.isShiftDown()) {
1213                             double z = camera.getTranslateZ();
1214                             double newZ = z + 5.0*SHIFT_MULTIPLIER;
1215                             camera.setTranslateZ(newZ);
1216                             updateLight();
1217                         }
1218                         break;
1219                     case DOWN:
1220                         if (event.isControlDown() && event.isShiftDown()) {
1221                             cameraXform2.t.setY(cameraXform2.t.getY() + 10.0*CONTROL_MULTIPLIER);  
1222                             updateLight();
1223                         }  
1224                         else if (event.isAltDown() && event.isShiftDown()) {
1225                             cameraXform.rx.setAngle(cameraXform.rx.getAngle() + 10.0*ALT_MULTIPLIER);  
1226                             updateLight();
1227                         }
1228                         else if (event.isControlDown()) {
1229                             cameraXform2.t.setY(cameraXform2.t.getY() + 1.0*CONTROL_MULTIPLIER);  
1230                             updateLight();
1231                         }
1232                         else if (event.isAltDown()) {
1233                             cameraXform.rx.setAngle(cameraXform.rx.getAngle() + 2.0*ALT_MULTIPLIER);  
1234                             updateLight();
1235                         }
1236                         else if (event.isShiftDown()) {
1237                             double z = camera.getTranslateZ();
1238                             double newZ = z - 5.0*SHIFT_MULTIPLIER;
1239                             camera.setTranslateZ(newZ);
1240                             updateLight();
1241                         }
1242                         break;
1243                     case RIGHT:
1244                         if (event.isControlDown() && event.isShiftDown()) {
1245                             cameraXform2.t.setX(cameraXform2.t.getX() + 10.0*CONTROL_MULTIPLIER);  
1246                             updateLight();
1247                         }  
1248                         else if (event.isAltDown() && event.isShiftDown()) {
1249                             cameraXform.ry.setAngle(cameraXform.ry.getAngle() - 10.0*ALT_MULTIPLIER);  
1250                             updateLight();
1251                         }
1252                         else if (event.isControlDown()) {
1253                             cameraXform2.t.setX(cameraXform2.t.getX() + 1.0*CONTROL_MULTIPLIER);  
1254                             updateLight();
1255                         }
1256                         else if (event.isShiftDown()) {
1257                             currentTime = timeline.getCurrentTime();
1258                             timeline.jumpTo(Frame.frame(Math.round(Frame.toFrame(currentTime)/10.0)*10 + 10));
1259                             // timeline.jumpTo(Duration.seconds(currentTime.toSeconds() + ONE_FRAME));
1260                         }
1261                         else if (event.isAltDown()) {
1262                             cameraXform.ry.setAngle(cameraXform.ry.getAngle() - 2.0*ALT_MULTIPLIER);  
1263                             updateLight();
1264                         }
1265                         else {
1266                             currentTime = timeline.getCurrentTime();
1267                             timeline.jumpTo(Frame.frame(Frame.toFrame(currentTime) + 1));
1268                             // timeline.jumpTo(Duration.seconds(currentTime.toSeconds() + ONE_FRAME));
1269                         }
1270                         break;
1271                     case LEFT:
1272                         if (event.isControlDown() && event.isShiftDown()) {
1273                             cameraXform2.t.setX(cameraXform2.t.getX() - 10.0*CONTROL_MULTIPLIER);  
1274                             updateLight();
1275                         }  
1276                         else if (event.isAltDown() && event.isShiftDown()) {
1277                             cameraXform.ry.setAngle(cameraXform.ry.getAngle() + 10.0*ALT_MULTIPLIER);  // -
1278                             updateLight();
1279                         }
1280                         else if (event.isControlDown()) {
1281                             cameraXform2.t.setX(cameraXform2.t.getX() - 1.0*CONTROL_MULTIPLIER);  
1282                             updateLight();
1283                         }
1284                         else if (event.isShiftDown()) {
1285                             currentTime = timeline.getCurrentTime();
1286                             timeline.jumpTo(Frame.frame(Math.round(Frame.toFrame(currentTime)/10.0)*10 - 10));
1287                             // timeline.jumpTo(Duration.seconds(currentTime.toSeconds() - ONE_FRAME));
1288                         }
1289                         else if (event.isAltDown()) {
1290                             cameraXform.ry.setAngle(cameraXform.ry.getAngle() + 2.0*ALT_MULTIPLIER);  // -
1291                             updateLight();
1292                         }
1293                         else {
1294                             currentTime = timeline.getCurrentTime();
1295                             timeline.jumpTo(Frame.frame(Frame.toFrame(currentTime) - 1));
1296                             // timeline.jumpTo(Duration.seconds(currentTime.toSeconds() - ONE_FRAME));
1297                         }
1298                         break;
1299                 }
1300                 //System.out.println(cameraXform.getTranslateX() + ", " + cameraXform.getTranslateY() + ", " + cameraXform.getTranslateZ());
1301             }
1302 
1303         });
1304     }
1305     
1306     private void onButtonSave() {
1307         new FXMLExporter("output.fxml").export(loadedNode);
1308     }
1309 
1310     /**
1311      * The main() method is ignored in correctly deployed JavaFX application.
1312      * main() serves only as fallback in case the application can not be
1313      * launched through deployment artifacts, e.g., in IDEs with limited FX
1314      * support. NetBeans ignores main().
1315      *
1316      * @param args the command line arguments
1317      */
1318     public static void main(String[] args) {
1319         System.setProperty("prism.dirtyopts", "false");
1320         //System.setProperty("prism.dirtyopts", "false");
1321         launch(args);
1322     }
1323 
1324     private void applyRecursively(Node node, Action a) {
1325         if (node instanceof MeshView) {
1326             a.apply((MeshView) node);
1327         }
1328         if (node instanceof Parent) {
1329             for (Node n : ((Parent) node).getChildrenUnmodifiable()) {
1330                 applyRecursively(n, a);
1331             }
1332         }
1333     }
1334 
1335     private interface Action {
1336         void apply(MeshView mv);
1337     }
1338     
1339     private Action applyWireframe = new Action() {
1340         @Override
1341         public void apply(MeshView mv) {
1342             mv.drawModeProperty().bind(Bindings.when(wireframe).then(DrawMode.LINE).otherwise(DrawMode.FILL));
1343         }
1344     };
1345 
1346     private Action addToCollisionScene = new Action() {
1347         private Vec3d pointSize;
1348         private double size;
1349         private PhongMaterial material = new PhongMaterial(Color.RED);
1350 
1351         @Override
1352         public void apply(MeshView mv) {
1353         }
1354     };
1355     
1356 }