diff --git a/racevisionGame/src/main/java/visualiser/Controllers/LobbyController.java b/racevisionGame/src/main/java/visualiser/Controllers/LobbyController.java index 38816a0e..5953bf92 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/LobbyController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/LobbyController.java @@ -40,7 +40,7 @@ public class LobbyController extends Controller { private @FXML TextField addressFld; private @FXML TextField portFld; - private ObservableList connections; + private ObservableList allConnections; private ObservableList customConnections; private AudioClip sound; @@ -51,12 +51,20 @@ public class LobbyController extends Controller { public void initialize() { httpMatchBrowserClient = new HttpMatchBrowserClient(); + httpMatchBrowserClient.connections.addListener(new ListChangeListener() { + @Override + public void onChanged(Change c) { + refreshTable(); + } + }); + new Thread(httpMatchBrowserClient, "Match Client").start(); // set up the connection table customConnections = FXCollections.observableArrayList(); + allConnections = FXCollections.observableArrayList(); //connections.add(new RaceConnection("localhost", 4942, "Local Game")); - lobbyTable.setItems(httpMatchBrowserClient.connections); + lobbyTable.setItems(allConnections); gameNameColumn.setCellValueFactory(cellData -> cellData.getValue().gamenameProperty()); hostNameColumn.setCellValueFactory(cellData -> cellData.getValue().hostnameProperty()); statusColumn.setCellValueFactory(cellData -> cellData.getValue().statusProperty()); @@ -82,8 +90,14 @@ public class LobbyController extends Controller { public void refreshBtnPressed(){ sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm()); sound.play(); + refreshTable(); + } + + private void refreshTable() { + allConnections.clear(); + addCustomGames(); addServerGames(); - for(RaceConnection connection: connections) { + for(RaceConnection connection: allConnections) { connection.check(); } try { @@ -144,9 +158,9 @@ public class LobbyController extends Controller { try { int port = Integer.parseInt(portString); customConnections.add(new RaceConnection(hostName, port, "Boat Game")); - connections.addAll(customConnections); addressFld.clear(); portFld.clear(); + refreshTable(); } catch (NumberFormatException e) { System.err.println("Port number entered is not a number"); } @@ -173,17 +187,14 @@ public class LobbyController extends Controller { * Adds the games received from the server */ private void addServerGames() { - httpMatchBrowserClient.connections.addAll(customConnections); - httpMatchBrowserClient.connections.addListener(new ListChangeListener() { - @Override - public void onChanged(Change c) { - refreshBtnPressed(); - } - }); - + allConnections.addAll(httpMatchBrowserClient.connections); /* for (HostGame game : matchBrowserLobbyInterface.getGames()) { connections.add(new RaceConnection(game.getIp(), 4942, "Boat Game")); }*/ } + + private void addCustomGames() { + allConnections.addAll(customConnections); + } } diff --git a/racevisionGame/src/main/java/visualiser/Controllers/NextMarkController.java b/racevisionGame/src/main/java/visualiser/Controllers/NextMarkController.java new file mode 100644 index 00000000..20da6f3c --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/Controllers/NextMarkController.java @@ -0,0 +1,123 @@ +package visualiser.Controllers; + +import com.interactivemesh.jfx.importer.stl.StlMeshImporter; +import javafx.animation.AnimationTimer; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.geometry.Point3D; +import javafx.scene.AmbientLight; +import javafx.scene.control.Label; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; +import javafx.scene.paint.PhongMaterial; +import javafx.scene.shape.Cylinder; +import javafx.scene.shape.MeshView; +import javafx.scene.shape.Shape3D; +import javafx.scene.transform.Rotate; +import shared.model.Bearing; +import shared.model.CompoundMark; +import shared.model.GPSCoordinate; +import visualiser.layout.Annotation3D; +import visualiser.layout.Assets3D; +import visualiser.layout.Subject3D; +import visualiser.layout.View3D; +import visualiser.model.VisualiserBoat; + +import java.net.URL; +import java.util.Observable; +import java.util.Observer; + +public class NextMarkController { + private @FXML StackPane arrowStackPane2d; + private @FXML StackPane arrowStackPane3d; + private @FXML Pane pane2d; + private @FXML Pane pane3d; + + private VisualiserBoat boat; + + public void initialiseArrowView(VisualiserBoat boat) { + this.boat = boat; + pane2d.setVisible(true); + pane3d.setVisible(false); + initialise2dArrowView(); + initialise3dArrowView(); + } + + private void initialise2dArrowView() { + AnimationTimer arrow2d = new AnimationTimer() { + @Override + public void handle(long now) { + if (boat.getCurrentLeg().getEndCompoundMark() != null) { + CompoundMark target = boat.getCurrentLeg().getEndCompoundMark(); + Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate()); + arrowStackPane2d.setRotate(headingToMark.degrees()); + } else { + stop(); + } + } + }; + arrow2d.start(); + } + + private void initialise3dArrowView() { + ObservableList viewSubjects = FXCollections.observableArrayList(); + String arrowPath = "assets/mark_arrow.x3d"; + + Shape3D arrow = Assets3D.loadX3d(arrowPath); + + arrow.setScaleX(25); + arrow.setScaleY(25); + arrow.setScaleZ(100); + arrow.setRotationAxis(new Point3D(1,0,0)); + + arrowStackPane3d.getChildren().add(arrow); + + AnimationTimer arrow3d = new AnimationTimer() { + @Override + public void handle(long now) { + if (boat.getCurrentLeg().getEndCompoundMark() != null) { + arrow.getTransforms().clear(); + double zRotation = calculateZRotate(); + arrow.setRotate(calculateXRotate(zRotation)); + arrow.getTransforms().add(new Rotate(zRotation, new Point3D(0, 0, 1))); + } else { + stop(); + } + } + }; + arrow3d.start(); + } + + public void show2d() { + pane3d.setVisible(false); + pane2d.setVisible(true); + } + + public void show3d() { + pane2d.setVisible(false); + pane3d.setVisible(true); + } + + private double calculateZRotate() { + CompoundMark target = boat.getCurrentLeg().getEndCompoundMark(); + Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate()); + return -headingToMark.degrees() + boat.getBearing().degrees() + 180; + } + + private double calculateXRotate(double zRotation) { +// if (zRotation > 360) { +// zRotation -=360; +// } else if (zRotation < 0) { +// zRotation += 360; +// } +// +// if (zRotation > 180) { +// zRotation = 360 - zRotation; +// } + + return 70; + //return 90 - 20 * Math.cos(Math.toRadians(zRotation)); + } +} diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceViewController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceViewController.java index d1ef843e..c752a7bc 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceViewController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceViewController.java @@ -19,10 +19,12 @@ import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; +import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.paint.Material; import javafx.scene.paint.PhongMaterial; +import javafx.scene.shape.Cylinder; import javafx.scene.paint.Stop; import javafx.scene.shape.MeshView; import javafx.scene.shape.Shape3D; @@ -82,10 +84,12 @@ public class RaceViewController extends Controller { // note: it says it's not used but it is! do not remove :) private @FXML ArrowController arrowController; + private @FXML NextMarkController nextMarkController; private @FXML GridPane canvasBase; private @FXML GridPane canvasBase1; private @FXML SplitPane racePane; private @FXML StackPane arrowPane; + private @FXML Pane nextMarkPane; private @FXML Label timer; private @FXML Label FPS; private @FXML Label timeZone; @@ -288,19 +292,6 @@ public class RaceViewController extends Controller { * Initialises the various UI components to listen to the {@link #visualiserRace}. */ private void initialiseRaceVisuals() { - - // Import arrow mesh - URL asset = this.getClass().getClassLoader().getResource("assets/arrow V1.0.4.stl"); - StlMeshImporter importer = new StlMeshImporter(); - importer.read(asset); - - MeshView arrow = new MeshView(importer.getImport()); - PhongMaterial arrowMat = new PhongMaterial(Color.RED); - arrow.setMaterial(arrowMat); - - this.nextMarkArrow = new Annotation3D(arrow); - this.nextMarkArrow.setScale(0.1); - // initialise displays initialiseFps(); initialiseInfoTable(); @@ -308,6 +299,7 @@ public class RaceViewController extends Controller { initialiseRaceClock(); initialiseSpeedometer(); raceTimer(); // start the timer + nextMarkPane.toFront(); speedometerLoop(); new Sparkline(this.raceState, this.sparklineChart); timeZone.setText(this.raceState.getRaceClock().getTimeZone()); @@ -317,6 +309,12 @@ public class RaceViewController extends Controller { private void initialiseView3D(VisualiserRaceEvent race) { viewSubjects = FXCollections.observableArrayList(); + try { + nextMarkController.initialiseArrowView(race.getVisualiserRaceState().getBoat(race.getVisualiserRaceState().getPlayerBoatID())); + } catch (BoatNotFoundException e) { + e.printStackTrace(); + } + AmbientLight ambientLight = new AmbientLight(Color.web("#CCCCFF")); ambientLight.setTranslateX(250); ambientLight.setTranslateZ(210); @@ -387,11 +385,29 @@ public class RaceViewController extends Controller { viewSubjects.add(markModel); } - for (VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) { + // Position and add each boat to view + for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) { + Shape3D mesh = Assets3D.getBoat(); + + PhongMaterial boatColorMat = new PhongMaterial(boat.getColor()); + //mesh.setMaterial(boatColorMat); + Subject3D boatModel = new Subject3D(mesh, boat.getSourceID()); + + // Configure visualiser for client's boat if (boat.isClientBoat()) { + // Add player boat highlight Shockwave boatHighlight = new Shockwave(10); boatHighlight.getMesh().setMaterial(new PhongMaterial(new Color(1, 1, 0, 0.1))); viewSubjects.add(boatHighlight); + + // Track player boat with camera + viewSubjects.add(boatModel); + Platform.runLater(() -> { + view3D.trackSubject(boatModel); + view3D.setThirdPerson(); + }); + + // Track player boat with highlight AnimationTimer highlightTrack = new AnimationTimer() { @Override public void handle(long now) { @@ -400,28 +416,23 @@ public class RaceViewController extends Controller { } }; highlightTrack.start(); - } - } - // Position and add each boat to view - for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) { -// MeshView mesh; -// if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) { -// mesh = new MeshView(importer.getImport()); -// } else { -// mesh = new MeshView(importerBurgerBoat.getImport()); -// } - Shape3D mesh = Assets3D.getBoat(); - - PhongMaterial boatColorMat = new PhongMaterial(boat.getColor()); - //mesh.setMaterial(boatColorMat); - Subject3D boatModel = new Subject3D(mesh, boat.getSourceID()); - - viewSubjects.add(boatModel); + // Highlight next mark only for player boat + Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1)); + CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark(); + view3D.getShape(nextMark.getMark1().getSourceID()).getMesh().setMaterial(markColor); + if(nextMark.getMark2() != null) { + view3D.getShape(nextMark.getMark2().getSourceID()).getMesh().setMaterial(markColor); + } + boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> swapColours(curr))); + } else { + viewSubjects.add(boatModel); + } //add sail Sails3D sails3D = new Sails3D(); Subject3D sailsSubject = new Subject3D(sails3D, 0); + sails3D.setMouseTransparent(true); sails3D.setMaterial(boatColorMat); sailsSubject.setXRot(0d); sailsSubject.setHeading(visualiserRace.getVisualiserRaceState().getWindDirection().degrees()); @@ -502,17 +513,9 @@ public class RaceViewController extends Controller { }; trackBoat.start(); - Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1)); - CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark(); - view3D.getShape(nextMark.getMark1().getSourceID()).getMesh().setMaterial(markColor); - if(nextMark.getMark2() != null) { - view3D.getShape(nextMark.getMark2().getSourceID()).getMesh().setMaterial(markColor); - } Subject3D shockwave = new Shockwave(10); - viewSubjects.add(shockwave); - boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> swapColours(curr))); boat.hasCollidedProperty().addListener((o, prev, curr) -> Platform.runLater(() -> showCollision(boat, shockwave))); } // Fix initial bird's-eye position @@ -597,37 +600,32 @@ public class RaceViewController extends Controller { } private void addThirdPersonAnnotations(Subject3D subject3D) { - viewSubjects.add(nextMarkArrow); - final VisualiserBoat boat; - try { - boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID()); - } catch (BoatNotFoundException e) { - e.printStackTrace(); - return; - } - arrowToNextMark = new AnimationTimer() { - @Override - public void handle(long now) { - CompoundMark target = boat.getCurrentLeg().getEndCompoundMark(); - if (target != null) { - Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate()); - - nextMarkArrow.setX(view3D.getPivot().getX()); - nextMarkArrow.setY(view3D.getPivot().getY()); - nextMarkArrow.setZ(view3D.getPivot().getZ() + 15); - nextMarkArrow.setHeading(headingToMark.degrees()); - } - } - }; - arrowToNextMark.start(); + nextMarkController.show3d(); +// viewSubjects.add(nextMarkArrow); +// final VisualiserBoat boat; +// try { +// boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID()); +// } catch (BoatNotFoundException e) { +// e.printStackTrace(); +// return; +// } +// arrowToNextMark = new AnimationTimer() { +// @Override +// public void handle(long now) { +// CompoundMark target = boat.getCurrentLeg().getEndCompoundMark(); +// Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate()); +// +// nextMarkArrow.setX(view3D.getPivot().getX()); +// nextMarkArrow.setY(view3D.getPivot().getY()); +// nextMarkArrow.setZ(view3D.getPivot().getZ() + 15); +// nextMarkArrow.setHeading(headingToMark.degrees()); +// } +// }; +// arrowToNextMark.start(); } private void removeThirdPersonAnnotations() { - viewSubjects.remove(nextMarkArrow); - if (arrowToNextMark != null) { - arrowToNextMark.stop(); - } - + nextMarkController.show2d(); } /** diff --git a/racevisionGame/src/main/java/visualiser/layout/Assets3D.java b/racevisionGame/src/main/java/visualiser/layout/Assets3D.java index 756d5a9e..28bb3497 100644 --- a/racevisionGame/src/main/java/visualiser/layout/Assets3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/Assets3D.java @@ -52,7 +52,7 @@ public class Assets3D { windArrow = new Annotation3D(loadX3d(arrowPath)); } - private static Shape3D loadX3d(String path){ + public static Shape3D loadX3d(String path){ X3dModelImporter x3dModelImporter = new X3dModelImporter(); URL asset = Assets3D.class.getClassLoader().getResource(path); x3dModelImporter.read(asset); diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index 7277ac5f..8501cdaa 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -81,7 +81,7 @@ public class View3D extends Pane { /** * Distance to stop zoom */ - private final double ZOOM_IN_LIMIT = 30; + private final double ZOOM_IN_LIMIT = 3; private final double ZOOM_OUT_LIMIT = 700; private final double MAX_ZOOM_LIMIT = 1500; private final double MAX_PITCH = 60; // birds eye view diff --git a/racevisionGame/src/main/resources/assets/mark_arrow.x3d b/racevisionGame/src/main/resources/assets/mark_arrow.x3d new file mode 100644 index 00000000..1b41caec --- /dev/null +++ b/racevisionGame/src/main/resources/assets/mark_arrow.x3d @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/racevisionGame/src/main/resources/assets/textures/Material Diffuse Color.002 b/racevisionGame/src/main/resources/assets/textures/Material Diffuse Color.002 new file mode 100644 index 00000000..0c5dbaeb Binary files /dev/null and b/racevisionGame/src/main/resources/assets/textures/Material Diffuse Color.002 differ diff --git a/racevisionGame/src/main/resources/images/nextMarkArrow2d.png b/racevisionGame/src/main/resources/images/nextMarkArrow2d.png new file mode 100644 index 00000000..ed9506c4 Binary files /dev/null and b/racevisionGame/src/main/resources/images/nextMarkArrow2d.png differ diff --git a/racevisionGame/src/main/resources/visualiser/scenes/newRaceView.fxml b/racevisionGame/src/main/resources/visualiser/scenes/newRaceView.fxml index cd7a134b..7ddcb840 100644 --- a/racevisionGame/src/main/resources/visualiser/scenes/newRaceView.fxml +++ b/racevisionGame/src/main/resources/visualiser/scenes/newRaceView.fxml @@ -41,6 +41,13 @@ + + + + + + + diff --git a/racevisionGame/src/main/resources/visualiser/scenes/nextMark.fxml b/racevisionGame/src/main/resources/visualiser/scenes/nextMark.fxml new file mode 100644 index 00000000..a2e15c37 --- /dev/null +++ b/racevisionGame/src/main/resources/visualiser/scenes/nextMark.fxml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +