package com.jpl.games.model; import com.javafx.experiments.jfx3dviewer.AutoScalingGroup; import com.javafx.experiments.jfx3dviewer.Xform; import javafx.event.EventHandler; import javafx.scene.AmbientLight; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.PerspectiveCamera; import javafx.scene.PointLight; import javafx.scene.SubScene; import javafx.scene.input.*; import javafx.scene.paint.Color; import javafx.scene.paint.PhongMaterial; import javafx.scene.shape.Box; import javafx.scene.shape.Sphere; import javafx.scene.transform.Rotate; import javafx.scene.transform.Translate; /** * 3D Content Model for Viewer App. * Contains the 3D subscene and everything related to it: light, cameras, axis * Based on com.javafx.experiments.jfx3dviewer.ContentModel * @author jpereda, April 2014 - @JPeredaDnr */ public class ContentModel { private SubScene subScene; private final Group root3D = new Group(); private final PerspectiveCamera camera = new PerspectiveCamera(true); private final Rotate yUpRotate = new Rotate(180,0,0,0,Rotate.X_AXIS); // y Up private final Rotate cameraLookXRotate = new Rotate(0,0,0,0,Rotate.X_AXIS); private final Rotate cameraLookZRotate = new Rotate(0,0,0,0,Rotate.Z_AXIS); private final Translate cameraPosition = new Translate(0,0,0); private final Xform cameraXform = new Xform(); private final Xform cameraXform2 = new Xform(); private final AutoScalingGroup autoScalingGroup = new AutoScalingGroup(2); private final AmbientLight ambientLight = new AmbientLight(Color.WHITE); private final PointLight light1 = new PointLight(Color.WHITE); private final double paneW, paneH; private double dimModel=100d; private double mousePosX, mousePosY; private double mouseOldX, mouseOldY; private double mouseDeltaX, mouseDeltaY; private final double modifierFactor = 0.3; public ContentModel(double paneW, double paneH, double dimModel) { this.paneW=paneW; this.paneH=paneH; this.dimModel=dimModel; buildCamera(); buildSubScene(); buildAxes(); addLights(); } private void buildCamera() { camera.setNearClip(1.0); camera.setFarClip(10000.0); camera.setFieldOfView(2d*dimModel/3d); camera.getTransforms().addAll(yUpRotate,cameraPosition, cameraLookXRotate,cameraLookZRotate); cameraXform.getChildren().add(cameraXform2); cameraXform2.getChildren().add(camera); cameraPosition.setZ(-2d*dimModel); root3D.getChildren().add(cameraXform); /* Rotate camera to show isometric view X right, Y top, Z 120ยบ left-down from each Three ways: One: Calculate vector and angle of rotation to combine two rotations: Qy[Pi/6].Qx[-Pi/6] (note angles for camera are opposite to model rotations) With some calculations: (http://jperedadnr.blogspot.com.es/2013/06/leap-motion-controller-and-javafx-new.html) * camera.setRotationAxis(new Point3D(-0.694747,0.694747,0.186157)); * camera.setRotate(42.1812); Two: with rotation matrix, taking all the previous transformations, by appending all of them in a single matrix before prepending these two rotations: * Affine affineCamIni=new Affine(); * camera.getTransforms().stream().forEach(affineCamIni::append); * affineCamIni.prepend(new Rotate(-30, Rotate.X_AXIS)); * affineCamIni.prepend(new Rotate(30, Rotate.Y_AXIS)); * camera.getTransforms().setAll(affineCamIni); Three: setting first RX rotation, then RY: */ cameraXform.setRx(-30.0); cameraXform.setRy(30); } private void buildSubScene() { root3D.getChildren().add(autoScalingGroup); subScene = new SubScene(root3D,paneW,paneH,true,javafx.scene.SceneAntialiasing.BALANCED); subScene.setCamera(camera); subScene.setFill(Color.CADETBLUE); setListeners(true); } private void buildAxes() { double length = 2d*dimModel; double width = dimModel/100d; double radius = 2d*dimModel/100d; final PhongMaterial redMaterial = new PhongMaterial(); redMaterial.setDiffuseColor(Color.DARKRED); redMaterial.setSpecularColor(Color.RED); final PhongMaterial greenMaterial = new PhongMaterial(); greenMaterial.setDiffuseColor(Color.DARKGREEN); greenMaterial.setSpecularColor(Color.GREEN); final PhongMaterial blueMaterial = new PhongMaterial(); blueMaterial.setDiffuseColor(Color.DARKBLUE); blueMaterial.setSpecularColor(Color.BLUE); Sphere xSphere = new Sphere(radius); Sphere ySphere = new Sphere(radius); Sphere zSphere = new Sphere(radius); xSphere.setMaterial(redMaterial); ySphere.setMaterial(greenMaterial); zSphere.setMaterial(blueMaterial); xSphere.setTranslateX(dimModel); ySphere.setTranslateY(dimModel); zSphere.setTranslateZ(dimModel); Box xAxis = new Box(length, width, width); Box yAxis = new Box(width, length, width); Box zAxis = new Box(width, width, length); xAxis.setMaterial(redMaterial); yAxis.setMaterial(greenMaterial); zAxis.setMaterial(blueMaterial); autoScalingGroup.getChildren().addAll(xAxis, yAxis, zAxis); autoScalingGroup.getChildren().addAll(xSphere, ySphere, zSphere); } private void addLights(){ root3D.getChildren().add(ambientLight); root3D.getChildren().add(light1); light1.setTranslateX(dimModel*0.6); light1.setTranslateY(dimModel*0.6); light1.setTranslateZ(dimModel*0.6); } private final EventHandler<MouseEvent> mouseEventHandler = event -> { double xFlip = -1.0, yFlip=1.0; // yUp if (event.getEventType() == MouseEvent.MOUSE_PRESSED) { mousePosX = event.getSceneX(); mousePosY = event.getSceneY(); mouseOldX = event.getSceneX(); mouseOldY = event.getSceneY(); } else if (event.getEventType() == MouseEvent.MOUSE_DRAGGED) { double modifier = event.isControlDown()?0.1:event.isShiftDown()?3.0:1.0; mouseOldX = mousePosX; mouseOldY = mousePosY; mousePosX = event.getSceneX(); mousePosY = event.getSceneY(); mouseDeltaX = (mousePosX - mouseOldX); mouseDeltaY = (mousePosY - mouseOldY); if(event.isMiddleButtonDown() || (event.isPrimaryButtonDown() && event.isSecondaryButtonDown())) { cameraXform2.setTx(cameraXform2.t.getX() + xFlip*mouseDeltaX*modifierFactor*modifier*0.3); cameraXform2.setTy(cameraXform2.t.getY() + yFlip*mouseDeltaY*modifierFactor*modifier*0.3); } else if(event.isPrimaryButtonDown()) { cameraXform.setRy(cameraXform.ry.getAngle() - yFlip*mouseDeltaX*modifierFactor*modifier*2.0); cameraXform.setRx(cameraXform.rx.getAngle() + xFlip*mouseDeltaY*modifierFactor*modifier*2.0); } else if(event.isSecondaryButtonDown()) { double z = cameraPosition.getZ(); double newZ = z - xFlip*(mouseDeltaX+mouseDeltaY)*modifierFactor*modifier; cameraPosition.setZ(newZ); } } }; private final EventHandler<ScrollEvent> scrollEventHandler = event -> { if (event.getTouchCount() > 0) { // touch pad scroll cameraXform2.setTx(cameraXform2.t.getX() - (0.01*event.getDeltaX())); // - cameraXform2.setTy(cameraXform2.t.getY() + (0.01*event.getDeltaY())); // - } else { // mouse wheel double z = cameraPosition.getZ()-(event.getDeltaY()*0.2); z = Math.max(z,-10d*dimModel); z = Math.min(z,0); cameraPosition.setZ(z); } }; private final EventHandler<ZoomEvent> zoomEventHandler = event -> { if (!Double.isNaN(event.getZoomFactor()) && event.getZoomFactor() > 0.8 && event.getZoomFactor() < 1.2) { double z = cameraPosition.getZ()/event.getZoomFactor(); z = Math.max(z,-10d*dimModel); z = Math.min(z,0); cameraPosition.setZ(z); } }; private void setListeners(boolean addListeners){ if(addListeners){ subScene.addEventHandler(MouseEvent.ANY, mouseEventHandler); subScene.addEventHandler(ZoomEvent.ANY, zoomEventHandler); subScene.addEventHandler(ScrollEvent.ANY, scrollEventHandler); } else { subScene.removeEventHandler(MouseEvent.ANY, mouseEventHandler); subScene.removeEventHandler(ZoomEvent.ANY, zoomEventHandler); subScene.removeEventHandler(ScrollEvent.ANY, scrollEventHandler); } } /* Public methods */ public SubScene getSubScene() { return subScene; } public void setContent(Node content) { autoScalingGroup.getChildren().add(content); } public void stopEventHandling(){ setListeners(false); } public void resumeEventHandling(){ setListeners(true); } public void resetCam(){ cameraXform.ry.setAngle(0.0); cameraXform.rx.setAngle(0.0); cameraPosition.setZ(-2d*dimModel); cameraXform2.t.setX(0.0); cameraXform2.t.setY(0.0); cameraXform.setRx(-30.0); cameraXform.setRy(30); } }