package visualiser.model; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.SubScene; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Shape3D; import javafx.scene.transform.Rotate; import javafx.scene.transform.Translate; /** * Control for rendering 3D objects visible through a PerspectiveCamera. Implements Adapter Pattern to * interface with camera, and allows clients to add shapes to the scene. All scenes contain sea plane and * sky box, whose textures are set with special methods. */ public class View3D extends Pane { /** * Observable list of renderable items */ private ObservableList items; /** * Rendering container for shapes */ private Group world; /** * Near limit of view frustum */ private double nearClip; /** * Far limit of view frustum */ private double farClip; /** * Position camera pivots around */ private Translate pivot; /** * Distance of camera from pivot point */ private Translate distance; /** * Angle along ground between z-axis and camera */ private Rotate yaw; /** * Angle between ground plane and camera direction */ private Rotate pitch; /** * Default constructor for View3D. Sets up Scene and PerspectiveCamera. */ public View3D() { world = new Group(); SubScene scene = new SubScene(world, 300, 300); scene.widthProperty().bind(this.widthProperty()); scene.heightProperty().bind(this.heightProperty()); scene.setFill(Color.BLACK); scene.setCamera(buildCamera()); this.getChildren().add(scene); } /** * Sets up camera view frustum and binds transformations * @return perspective camera */ private PerspectiveCamera buildCamera() { PerspectiveCamera camera = new PerspectiveCamera(true); // Set up view frustum nearClip = 0.1; farClip = 1000.0; camera.setNearClip(nearClip); camera.setFarClip(farClip); // Set up transformations pivot = new Translate(); distance = new Translate(); yaw = new Rotate(0, Rotate.Y_AXIS); pitch = new Rotate(0, Rotate.X_AXIS); camera.getTransforms().addAll(pivot, yaw, pitch, distance); return camera; } public void setItems(ObservableList items) { this.items = items; this.items.addListener((ListChangeListener) c -> { while(c.next()) { if (c.wasRemoved() || c.wasAdded()) { for (Shape3D shape : c.getRemoved()) world.getChildren().remove(shape); for (Shape3D shape : c.getAddedSubList()) world.getChildren().add(shape); } } }); } public void setNearClip(double nearClip) { this.nearClip = nearClip; } public void setFarClip(double farClip) { this.farClip = farClip; } /** * Set object to centre on camera * @param pivot centred object */ public void setPivot(Shape3D pivot) { this.pivot.setX(pivot.getTranslateX()); this.pivot.setY(pivot.getTranslateY()); this.pivot.setZ(pivot.getTranslateZ()); } /** * Set distance of camera from pivot * @param distance in units */ public void setDistance(double distance) { this.distance.setZ(-distance); } /** * Set angle of camera from z-axis along ground * @param yaw in degrees */ public void setYaw(double yaw) { this.yaw.setAngle(yaw); } /** * Set elevation of camera * @param pitch in degrees */ public void setPitch(double pitch) { this.pitch.setAngle(-pitch); } }