/* * Copyright (C) 2013-2015 F(X)yz, * Sean Phillips, Jason Pollastrini and Jose Pereda * All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.fxyz.tools; import javafx.animation.AnimationTimer; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.geometry.Rectangle2D; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.SnapshotParameters; import javafx.scene.SubScene; import javafx.scene.image.ImageView; import javafx.scene.image.WritableImage; import javafx.scene.input.KeyCode; import javafx.scene.input.MouseEvent; import javafx.scene.input.ScrollEvent; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.Border; import javafx.scene.layout.BorderStroke; import javafx.scene.layout.BorderStrokeStyle; import javafx.scene.layout.BorderWidths; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; import javafx.scene.transform.Rotate; import javafx.scene.transform.Translate; import org.fxyz.cameras.CameraTransformer; import org.fxyz.events.CloseCutawayEvent; /** * Builds upon base CameraView class but provides event handling for * dragging and closing the cutaway * * @author Sean */ public final class Cutaway extends VBox { private double mousePosX; private double mousePosY; private double mouseOldX; private double mouseOldY; private double mouseDeltaX; private double mouseDeltaY; private double cutawayPosX; private double cutawayPosY; private double cutawayOldX; private double cutawayOldY; private double cutawayDeltaX; private double cutawayDeltaY; private final SnapshotParameters params = new SnapshotParameters(); private WritableImage image = null; private double startX = 0; private double startY = 0; public CameraTransformer cameraTransform = new CameraTransformer(); public PerspectiveCamera camera; public Rotate rx = new Rotate(0,0,0,0,Rotate.X_AXIS), ry = new Rotate(0,0,0,0,Rotate.X_AXIS), rz = new Rotate(0,0,0,0,Rotate.X_AXIS); public Translate t = new Translate(0,0,0); private Group worldToView; private AnimationTimer viewTimer = null; public ImageView imageView = new ImageView(); private double controlSize = 15; public Cutaway(SubScene scene, double width, double height) { // Make sure "world" is a group assert scene.getRoot().getClass().equals(Group.class); setFillWidth(true); setPrefSize(width, height+controlSize); setMaxSize(width, height+controlSize); this.setBorder(new Border( new BorderStroke(Color.DARKKHAKI, BorderStrokeStyle.SOLID, new CornerRadii(25), new BorderWidths(5) ))); Rectangle closeSquare = new Rectangle(controlSize, controlSize, Color.LIGHTCORAL); closeSquare.setOnMouseClicked((MouseEvent me) -> { this.fireEvent(new CloseCutawayEvent(this)); }); Circle dragCircle = new Circle(controlSize/2, Color.STEELBLUE); HBox top = new HBox(20,closeSquare,dragCircle); dragCircle.setVisible(false); StackPane.setAlignment(closeSquare, Pos.TOP_RIGHT); StackPane.setMargin(closeSquare, new Insets(50)); top.setBackground(new Background( new BackgroundFill(Color.KHAKI, new CornerRadii(20,20,0,0,false), Insets.EMPTY))); top.setAlignment(Pos.TOP_RIGHT); top.setOnMousePressed((MouseEvent me) -> { cutawayPosX = me.getSceneX(); cutawayPosY = me.getSceneY(); cutawayOldX = me.getSceneX(); cutawayOldY = me.getSceneY(); }); top.setOnMouseDragged((MouseEvent me) -> { cutawayOldX = cutawayPosX; cutawayOldY = cutawayPosY; cutawayPosX = me.getSceneX(); cutawayPosY = me.getSceneY(); cutawayDeltaX = (cutawayPosX - cutawayOldX); cutawayDeltaY = (cutawayPosY - cutawayOldY); setTranslateX(getTranslateX() + cutawayDeltaX); setTranslateY(getTranslateY() + cutawayDeltaY); }); getChildren().addAll(top,imageView); worldToView = (Group)scene.getRoot(); camera = new PerspectiveCamera(true); cameraTransform.getChildren().add(camera); camera.setNearClip(0.1); camera.setFarClip(15000.0); camera.setTranslateZ(-1500); params.setCamera(camera); params.setDepthBuffer(true); params.setFill(Color.rgb(0, 0, 0, 0.5)); viewTimer = new AnimationTimer() { @Override public void handle(long now) { redraw(); } }; setOnMouseEntered(e->{ requestFocus(); }); } public void startViewing() { viewTimer.start(); } public void pause() { viewTimer.stop(); } public void setFirstPersonNavigationEabled(boolean b) { if (b) { // Navigation setMouseTransparent(false); //First person shooter keyboard movement imageView.setOnKeyPressed(event -> { double change = 10.0; //Add shift modifier to simulate "Running Speed" if(event.isShiftDown()) { change = 50.0; } //What key did the user press? KeyCode keycode = event.getCode(); //Step 2c: Add Zoom controls if(keycode == KeyCode.W) { camera.setTranslateZ(camera.getTranslateZ() + change); } if(keycode == KeyCode.S) { camera.setTranslateZ(camera.getTranslateZ() - change); } //Step 2d: Add Strafe controls if(keycode == KeyCode.A) { camera.setTranslateX(camera.getTranslateX() - change); } if(keycode == KeyCode.D) { camera.setTranslateX(camera.getTranslateX() + change); } }); imageView.setOnScroll((ScrollEvent event) -> { event.consume(); if (event.getDeltaY() == 0) { return; } double change = event.getDeltaY(); //Add shift modifier to simulate "Running Speed" if (event.isShiftDown()) { change *= 2; } camera.setTranslateZ(camera.getTranslateZ() + change); }); imageView.setOnMousePressed((MouseEvent me) -> { mousePosX = me.getSceneX(); mousePosY = me.getSceneY(); mouseOldX = me.getSceneX(); mouseOldY = me.getSceneY(); }); imageView.setOnMouseDragged((MouseEvent me) -> { mouseOldX = mousePosX; mouseOldY = mousePosY; mousePosX = me.getSceneX(); mousePosY = me.getSceneY(); mouseDeltaX = (mousePosX - mouseOldX); mouseDeltaY = (mousePosY - mouseOldY); double modifier = 10.0; double modifierFactor = 0.1; if (me.isControlDown()) { modifier = 0.1; } if (me.isShiftDown()) { modifier = 50.0; } if (me.isPrimaryButtonDown()) { cameraTransform.ry.setAngle(((cameraTransform.ry.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0) % 360 + 540) % 360 - 180); // + cameraTransform.rx.setAngle(((cameraTransform.rx.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0) % 360 + 540) % 360 - 180); // - } else if (me.isSecondaryButtonDown()) { double z = camera.getTranslateZ(); double newZ = z + mouseDeltaX * modifierFactor * modifier; camera.setTranslateZ(newZ); } else if (me.isMiddleButtonDown() ) { cameraTransform.t.setX(cameraTransform.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3); // - cameraTransform.t.setY(cameraTransform.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3); // - } }); }else{ imageView.setOnMouseDragged(null); imageView.setOnScroll(null); imageView.setOnMousePressed(null); imageView.setOnKeyPressed(null); imageView.setMouseTransparent(true); } } private void redraw() { params.setViewport(new Rectangle2D(0, 0, imageView.getFitWidth(), imageView.getFitHeight())); if (image == null || image.getWidth() != imageView.getFitWidth() || image.getHeight() != imageView.getFitHeight()) { image = worldToView.snapshot(params, null); } else { worldToView.snapshot(params, image); } // set a clip to apply rounded border to the original image. Rectangle clip = new Rectangle( imageView.getFitWidth(), imageView.getFitHeight() ); clip.setArcWidth(20); clip.setArcHeight(20); imageView.setClip(clip); imageView.setImage(image); } public PerspectiveCamera getCamera() { return camera; } public Rotate getRx() { return rx; } public Rotate getRy() { return ry; } public Rotate getRz() { return rz; } public Translate getT() { return t; } }