From 0ee448e17da5490c51ec2edeef423d34eab1218d Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Mon, 4 Sep 2017 01:27:29 +1200 Subject: [PATCH 01/16] Created wrapper for controlling the position and heading of rendered 3D models. - Updated View3D to use Subject3D wrappers instead of Shape3D - Fixed HostController for the new architecture - Made position and heading observable in VisualiserBoat - Attached listeners to each VisualiserBoat in RaceController #story[1261] --- .../Controllers/HostController.java | 23 +++--- .../Controllers/RaceController.java | 11 +++ .../java/visualiser/layout/Subject3D.java | 72 +++++++++++++++++++ .../visualiser/{model => layout}/View3D.java | 20 +++--- .../java/visualiser/model/BoatDisplay3D.java | 18 ----- .../java/visualiser/model/VisualiserBoat.java | 44 ++++++++++-- 6 files changed, 141 insertions(+), 47 deletions(-) create mode 100644 racevisionGame/src/main/java/visualiser/layout/Subject3D.java rename racevisionGame/src/main/java/visualiser/{model => layout}/View3D.java (85%) delete mode 100644 racevisionGame/src/main/java/visualiser/model/BoatDisplay3D.java diff --git a/racevisionGame/src/main/java/visualiser/Controllers/HostController.java b/racevisionGame/src/main/java/visualiser/Controllers/HostController.java index 008276ef..e7583252 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/HostController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/HostController.java @@ -11,14 +11,11 @@ import javafx.scene.control.SplitPane; import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; -import javafx.scene.shape.Box; -import javafx.scene.shape.Mesh; import javafx.scene.shape.MeshView; -import javafx.scene.shape.Shape3D; -import javafx.scene.transform.Rotate; import mock.app.Event; import mock.exceptions.EventConstructionException; -import visualiser.model.View3D; +import visualiser.layout.Subject3D; +import visualiser.layout.View3D; import java.io.IOException; import java.net.Socket; @@ -64,31 +61,29 @@ public class HostController extends Controller { @Override public void initialize(URL location, ResourceBundle resources) { - ObservableList shapes = FXCollections.observableArrayList(); + ObservableList subjects = FXCollections.observableArrayList(); view3D = new View3D(); - view3D.setItems(shapes); + view3D.setItems(subjects); playerContainer.add(view3D, 0,0); URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); StlMeshImporter importer = new StlMeshImporter(); importer.read(asset); - MeshView mesh = new MeshView(importer.getImport()); - shapes.add(mesh); + Subject3D subject = new Subject3D(new MeshView(importer.getImport())); - view3D.setPivot(mesh); + subjects.add(subject); + + view3D.setPivot(subject); view3D.setDistance(50); view3D.setYaw(45); view3D.setPitch(20); - Rotate rotation = new Rotate(0, Rotate.Y_AXIS); - mesh.getTransforms().addAll(rotation, new Rotate(-90, Rotate.X_AXIS)); - AnimationTimer rotate = new AnimationTimer() { @Override public void handle(long now) { - rotation.setAngle(rotation.getAngle() + 0.1); + subject.setHeading(subject.getHeading() + 0.1); } }; rotate.start(); diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index b6a53f0b..9d7e36c9 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -28,6 +28,7 @@ import visualiser.model.*; import java.io.IOException; import java.net.URL; +import java.util.List; import java.util.Optional; import java.util.ResourceBundle; import java.util.logging.Level; @@ -185,6 +186,8 @@ public class RaceController extends Controller { //Race canvas. initialiseRaceCanvas(this.visualiserRace); + initialiseView3D(this.visualiserRace); + //Race timezone label. initialiseRaceTimezoneLabel(this.visualiserRace); @@ -196,6 +199,14 @@ public class RaceController extends Controller { raceTimer(); } + private void initialiseView3D(VisualiserRaceEvent race) { + List boats = race.getVisualiserRaceState().getBoats(); + for(VisualiserBoat boat: boats) { + boat.positionProperty().addListener((o, prev, curr) -> { + System.out.println(boat.getCountry() + " is at " + curr.toString()); + }); + } + } /** diff --git a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java new file mode 100644 index 00000000..17736843 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java @@ -0,0 +1,72 @@ +package visualiser.layout; + +import javafx.scene.shape.Shape3D; +import javafx.scene.transform.Rotate; +import javafx.scene.transform.Translate; + +/** + * Wrapper for controlling the position and heading of rendered 3D models. + */ +public class Subject3D { + /** + * Rendered mesh + */ + private Shape3D mesh; + + /** + * Position translation updated by state listeners + */ + private Translate position; + /** + * Heading rotation updated by state listeners + */ + private Rotate heading; + + /** + * Constructor for view subject wrapper + * @param mesh to be rendered + */ + public Subject3D(Shape3D mesh) { + this.mesh = mesh; + this.position = new Translate(); + this.heading = new Rotate(0, Rotate.Y_AXIS); + + this.mesh.getTransforms().addAll(heading, new Rotate(-90, Rotate.X_AXIS), position); + } + + public Shape3D getMesh() { + return mesh; + } + + public double getX() { + return this.position.getX(); + } + + public double getY() { + return this.position.getY(); + } + + public double getZ() { + return this.position.getZ(); + } + + public void setX(double x) { + this.position.setX(x); + } + + public void setY(double y) { + this.position.setY(y); + } + + public void setZ(double z) { + this.position.setZ(z); + } + + public double getHeading() { + return this.heading.getAngle(); + } + + public void setHeading(double angle) { + this.heading.setAngle(angle); + } +} diff --git a/racevisionGame/src/main/java/visualiser/model/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java similarity index 85% rename from racevisionGame/src/main/java/visualiser/model/View3D.java rename to racevisionGame/src/main/java/visualiser/layout/View3D.java index 6affb906..2aae9f1a 100644 --- a/racevisionGame/src/main/java/visualiser/model/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -1,4 +1,4 @@ -package visualiser.model; +package visualiser.layout; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; @@ -20,7 +20,7 @@ public class View3D extends Pane { /** * Observable list of renderable items */ - private ObservableList items; + private ObservableList items; /** * Rendering container for shapes */ @@ -89,13 +89,13 @@ public class View3D extends Pane { return camera; } - public void setItems(ObservableList items) { + public void setItems(ObservableList items) { this.items = items; - this.items.addListener((ListChangeListener) c -> { + 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); + for (Subject3D shape : c.getRemoved()) world.getChildren().remove(shape.getMesh()); + for (Subject3D shape : c.getAddedSubList()) world.getChildren().add(shape.getMesh()); } } }); @@ -113,10 +113,10 @@ public class View3D extends Pane { * 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()); + public void setPivot(Subject3D pivot) { + this.pivot.setX(pivot.getX()); + this.pivot.setY(pivot.getY()); + this.pivot.setZ(pivot.getZ()); } /** diff --git a/racevisionGame/src/main/java/visualiser/model/BoatDisplay3D.java b/racevisionGame/src/main/java/visualiser/model/BoatDisplay3D.java deleted file mode 100644 index 9314f5cd..00000000 --- a/racevisionGame/src/main/java/visualiser/model/BoatDisplay3D.java +++ /dev/null @@ -1,18 +0,0 @@ -package visualiser.model; - -import com.interactivemesh.jfx.importer.Importer; -import javafx.scene.layout.Pane; - -/** - * Created by fwy13 on 29/08/17. - */ -public class BoatDisplay3D extends Pane { - - - public BoatDisplay3D(String filePath){ - super(); -// Shape3D -// this.getChildren().add(); - } - -} diff --git a/racevisionGame/src/main/java/visualiser/model/VisualiserBoat.java b/racevisionGame/src/main/java/visualiser/model/VisualiserBoat.java index 6cbfdaa3..555def15 100644 --- a/racevisionGame/src/main/java/visualiser/model/VisualiserBoat.java +++ b/racevisionGame/src/main/java/visualiser/model/VisualiserBoat.java @@ -1,11 +1,10 @@ package visualiser.model; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; import javafx.scene.paint.Color; import network.Messages.Enums.BoatStatusEnum; -import shared.model.Azimuth; -import shared.model.Boat; -import shared.model.Constants; -import shared.model.GPSCoordinate; +import shared.model.*; import java.time.Duration; import java.time.ZonedDateTime; @@ -61,7 +60,8 @@ public class VisualiserBoat extends Boat { private boolean isClientBoat = false; - + private ObjectProperty positionProperty; + private ObjectProperty bearingProperty; /** @@ -239,4 +239,38 @@ public class VisualiserBoat extends Boat { public void setClientBoat(boolean clientBoat) { isClientBoat = clientBoat; } + + @Override + public GPSCoordinate getPosition() { + return positionProperty.get(); + } + + @Override + public void setPosition(GPSCoordinate position) { + if(this.positionProperty == null) { + this.positionProperty = new SimpleObjectProperty<>(); + } + this.positionProperty.set(position); + } + + public ObjectProperty positionProperty() { + return positionProperty; + } + + @Override + public Bearing getBearing() { + return bearingProperty.get(); + } + + @Override + public void setBearing(Bearing bearing) { + if(this.bearingProperty == null) { + this.bearingProperty = new SimpleObjectProperty<>(); + } + this.bearingProperty.set(bearing); + } + + public ObjectProperty bearingProperty() { + return bearingProperty; + } } From 971a061499e7135d570e531687ed854a9b65dbeb Mon Sep 17 00:00:00 2001 From: cbt24 Date: Mon, 4 Sep 2017 18:51:37 +1200 Subject: [PATCH 02/16] View3D within RaceController now tracks boat heading - Race leaves lobby on Warning status for development - Latest subject added is used as camera pivot #story[1261] --- .../Controllers/RaceController.java | 86 +++++++------------ .../Controllers/StartController.java | 8 +- 2 files changed, 36 insertions(+), 58 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 9d7e36c9..885a4371 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -1,6 +1,7 @@ package visualiser.Controllers; +import com.interactivemesh.jfx.importer.stl.StlMeshImporter; import javafx.animation.AnimationTimer; import javafx.application.Platform; import javafx.collections.FXCollections; @@ -17,6 +18,7 @@ import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; +import javafx.scene.shape.MeshView; import javafx.util.Callback; import network.Messages.Enums.RaceStatusEnum; import shared.model.Leg; @@ -24,6 +26,8 @@ import visualiser.app.App; import visualiser.gameController.ControllerClient; import visualiser.gameController.Keys.ControlKey; import visualiser.gameController.Keys.KeyFactory; +import visualiser.layout.Subject3D; +import visualiser.layout.View3D; import visualiser.model.*; import java.io.IOException; @@ -40,27 +44,18 @@ import java.util.logging.Logger; * Controller used to display a running race. */ public class RaceController extends Controller { - - /** * The race object which describes the currently occurring race. */ private VisualiserRaceEvent visualiserRace; - /** * Service for sending keystrokes to server */ private ControllerClient controllerClient; - private boolean isHost; - /** - * The canvas that draws the race. - */ - private ResizableRaceCanvas raceCanvas; - /** * The sparkline graph. */ @@ -71,16 +66,16 @@ public class RaceController extends Controller { */ private boolean infoTableShow; + private View3D view3D; + private ObservableList viewSubjects; + /** * The arrow controller. */ @FXML private ArrowController arrowController; - - @FXML private GridPane canvasBase; - @FXML private SplitPane race; /** @@ -102,9 +97,6 @@ public class RaceController extends Controller { @FXML private TableColumn boatMarkColumn; @FXML private TableColumn boatSpeedColumn; @FXML private LineChart sparklineChart; - @FXML private AnchorPane annotationPane; - - /** * Ctor. @@ -183,9 +175,6 @@ public class RaceController extends Controller { //Arrow. initialiseArrow(this.visualiserRace); - //Race canvas. - initialiseRaceCanvas(this.visualiserRace); - initialiseView3D(this.visualiserRace); //Race timezone label. @@ -194,18 +183,42 @@ public class RaceController extends Controller { //Race clock. initialiseRaceClock(this.visualiserRace); - //Start the race animation timer. raceTimer(); } private void initialiseView3D(VisualiserRaceEvent race) { + URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); + StlMeshImporter importer = new StlMeshImporter(); + importer.read(asset); + List boats = race.getVisualiserRaceState().getBoats(); + viewSubjects = FXCollections.observableArrayList(); + + //Create View3D + view3D = new View3D(); + view3D.setItems(viewSubjects); for(VisualiserBoat boat: boats) { + System.out.println("Adding " + boat.getCountry()); + Subject3D subject = new Subject3D(new MeshView(importer.getImport())); + viewSubjects.add(subject); + view3D.setPivot(subject); // Filthy hack makes last added boat the pivot + boat.positionProperty().addListener((o, prev, curr) -> { System.out.println(boat.getCountry() + " is at " + curr.toString()); }); + + boat.bearingProperty().addListener((o, prev, curr) -> { + subject.setHeading(curr.degrees()); + }); } + + // Display View3D + view3D.setVisible(true); + view3D.setDistance(50); + view3D.setYaw(45); + view3D.setPitch(20); + canvasBase.getChildren().add(0, view3D); } @@ -359,32 +372,8 @@ public class RaceController extends Controller { this.sparkline = new Sparkline(this.visualiserRace.getVisualiserRaceState(), this.sparklineChart); } - - /** - * Initialises the {@link ResizableRaceCanvas}, provides the race to read data from. - * @param race Race to read data from. - */ - private void initialiseRaceCanvas(VisualiserRaceEvent race) { - - //Create canvas. - raceCanvas = new ResizableRaceCanvas(race.getVisualiserRaceState()); - - //Set properties. - raceCanvas.setMouseTransparent(true); - raceCanvas.widthProperty().bind(canvasBase.widthProperty()); - raceCanvas.heightProperty().bind(canvasBase.heightProperty()); - - //Draw it and show it. - raceCanvas.draw(); - raceCanvas.setVisible(true); - - //Add to scene. - canvasBase.getChildren().add(0, raceCanvas); - } - - /** - * Intialises the race time zone label with the race's time zone. + * Initialises the race time zone label with the race's time zone. * @param race The race to get time zone from. */ private void initialiseRaceTimezoneLabel(VisualiserRaceEvent race) { @@ -423,10 +412,6 @@ public class RaceController extends Controller { //Display this controller. race.setVisible(true); - - - // set up annotation displays - new Annotations(annotationPane, raceCanvas); } /** @@ -469,13 +454,8 @@ public class RaceController extends Controller { finishRace(visualiserRace.getVisualiserRaceState().getBoats()); } else { - //Otherwise, render the canvas. - raceCanvas.drawRace(); - - //Sort the tableview. Doesn't automatically work for all columns. boatInfoTable.sort(); - } //Return to main screen if we lose connection. diff --git a/racevisionGame/src/main/java/visualiser/Controllers/StartController.java b/racevisionGame/src/main/java/visualiser/Controllers/StartController.java index 890eb816..b46bf79f 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/StartController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/StartController.java @@ -225,12 +225,10 @@ public class StartController extends Controller { //Get the current race status. RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum(); - //Display it. - raceStatusLabel.setText("Race Status: " + raceStatus.name()); - - //If the race has reached the preparatory phase, or has started... - if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) { + if (raceStatus == RaceStatusEnum.WARNING + || raceStatus == RaceStatusEnum.PREPARATORY + || raceStatus == RaceStatusEnum.STARTED) { //Stop this timer. stop(); From c74cc26bf13b0823debfbf61b5bb5c345df667b4 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Tue, 5 Sep 2017 12:17:55 +1200 Subject: [PATCH 03/16] Merged with master and changed View3D default background - Removed sparkline from RaceController #story[1261] --- .../Controllers/RaceController.java | 26 ------------------- .../main/java/visualiser/layout/View3D.java | 2 +- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 06b92177..f9347eab 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -1,6 +1,5 @@ package visualiser.Controllers; - import com.interactivemesh.jfx.importer.stl.StlMeshImporter; import javafx.animation.AnimationTimer; import javafx.application.Platform; @@ -13,7 +12,6 @@ import javafx.scene.chart.LineChart; import javafx.scene.control.*; 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; @@ -24,7 +22,6 @@ import shared.model.Leg; import visualiser.app.App; import visualiser.gameController.ControllerClient; import visualiser.gameController.Keys.ControlKey; -import visualiser.gameController.Keys.KeyFactory; import visualiser.layout.Subject3D; import visualiser.layout.View3D; import visualiser.model.*; @@ -56,11 +53,6 @@ public class RaceController extends Controller { private boolean isHost; - /** - * The sparkline graph. - */ - private Sparkline sparkline; - /** * state of the info table */ @@ -78,11 +70,6 @@ public class RaceController extends Controller { @FXML private SplitPane race; - /** - * This is the root node of the arrow control. - */ - @FXML private Pane arrow; - /** * This is the pane we place the actual arrow control inside of. */ @@ -169,9 +156,6 @@ public class RaceController extends Controller { //Information table. initialiseInfoTable(this.visualiserRace); - //Sparkline. - initialiseSparkline(this.visualiserRace); - //Arrow. initialiseArrow(this.visualiserRace); @@ -362,16 +346,6 @@ public class RaceController extends Controller { } - - /** - * Initialises the {@link Sparkline}, and listens to a specified {@link VisualiserRaceEvent}. - * @param race The race to listen to. - */ - private void initialiseSparkline(VisualiserRaceEvent race) { - //The race.getBoats() we are passing in is sorted by position in race inside the race class. - this.sparkline = new Sparkline(this.visualiserRace.getVisualiserRaceState(), this.sparklineChart); - } - /** * Initialises the race time zone label with the race's time zone. * @param race The race to get time zone from. diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index 2aae9f1a..492b1e1f 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -59,7 +59,7 @@ public class View3D extends Pane { SubScene scene = new SubScene(world, 300, 300); scene.widthProperty().bind(this.widthProperty()); scene.heightProperty().bind(this.heightProperty()); - scene.setFill(Color.BLACK); + scene.setFill(new Color(0.2, 0.6, 1, 1)); scene.setCamera(buildCamera()); From 3ecf203cba90e30b5a2c8ff6fe71675d0f120f49 Mon Sep 17 00:00:00 2001 From: cbt24 Date: Tue, 5 Sep 2017 15:19:04 +1200 Subject: [PATCH 04/16] Recovered stable rotation of the player's boat - Uses AnimationTimer rather than listeners - Reduced pre-start time to speed up testing #story[1261] --- .../src/main/java/shared/model/Constants.java | 6 +-- .../Controllers/RaceController.java | 51 ++++++++++--------- .../java/visualiser/layout/Subject3D.java | 16 +++--- .../main/java/visualiser/layout/View3D.java | 7 +++ 4 files changed, 45 insertions(+), 35 deletions(-) diff --git a/racevisionGame/src/main/java/shared/model/Constants.java b/racevisionGame/src/main/java/shared/model/Constants.java index bb7ec598..f18ec111 100644 --- a/racevisionGame/src/main/java/shared/model/Constants.java +++ b/racevisionGame/src/main/java/shared/model/Constants.java @@ -36,15 +36,15 @@ public class Constants { public static final int RaceTimeScale = 2;//10; /** - * The race pre-start time, in milliseconds. 3 minutes. + * The race pre-start time, in milliseconds. 3 minutes (30 seconds for development). */ - public static final long RacePreStartTime = 3 * 60 * 1000; + public static final long RacePreStartTime = 30 * 1000; /** * The race preparatory time, in milliseconds. 1 minute. */ - public static final long RacePreparatoryTime = 1 * 60 * 1000; + public static final long RacePreparatoryTime = 60 * 1000; diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index f9347eab..52147309 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -16,9 +16,13 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.shape.MeshView; +import javafx.scene.shape.Sphere; import javafx.util.Callback; import network.Messages.Enums.RaceStatusEnum; +import shared.exceptions.BoatNotFoundException; +import shared.exceptions.MarkNotFoundException; import shared.model.Leg; +import shared.model.Mark; import visualiser.app.App; import visualiser.gameController.ControllerClient; import visualiser.gameController.Keys.ControlKey; @@ -93,7 +97,6 @@ public class RaceController extends Controller { @Override public void initialize(URL location, ResourceBundle resources) { -// KeyFactory keyFactory = KeyFactory.getFactory(); infoTableShow = true; // Initialise keyboard handler @@ -172,37 +175,37 @@ public class RaceController extends Controller { } private void initialiseView3D(VisualiserRaceEvent race) { + ObservableList subjects = FXCollections.observableArrayList(); + + view3D = new View3D(); + view3D.setItems(subjects); + canvasBase.getChildren().add(0, view3D); + URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); + StlMeshImporter importer = new StlMeshImporter(); importer.read(asset); + Subject3D subject = new Subject3D(new MeshView(importer.getImport())); - List boats = race.getVisualiserRaceState().getBoats(); - viewSubjects = FXCollections.observableArrayList(); - - //Create View3D - view3D = new View3D(); - view3D.setItems(viewSubjects); - for(VisualiserBoat boat: boats) { - System.out.println("Adding " + boat.getCountry()); - Subject3D subject = new Subject3D(new MeshView(importer.getImport())); - viewSubjects.add(subject); - view3D.setPivot(subject); // Filthy hack makes last added boat the pivot - - boat.positionProperty().addListener((o, prev, curr) -> { - System.out.println(boat.getCountry() + " is at " + curr.toString()); - }); + subjects.add(subject); - boat.bearingProperty().addListener((o, prev, curr) -> { - subject.setHeading(curr.degrees()); - }); - } - - // Display View3D - view3D.setVisible(true); + view3D.setPivot(subject); view3D.setDistance(50); view3D.setYaw(45); view3D.setPitch(20); - canvasBase.getChildren().add(0, view3D); + + try { + VisualiserBoat boat = race.getVisualiserRaceState().getBoat(race.getVisualiserRaceState().getPlayerBoatID()); + AnimationTimer rotate = new AnimationTimer() { + @Override + public void handle(long now) { + subject.setHeading(boat.getBearing().degrees()); + } + }; + rotate.start(); + } catch(BoatNotFoundException e) { + e.printStackTrace(); + } } diff --git a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java index 17736843..9abd3b5b 100644 --- a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java @@ -39,34 +39,34 @@ public class Subject3D { } public double getX() { - return this.position.getX(); + return position.getTx(); } public double getY() { - return this.position.getY(); + return position.getTy(); } public double getZ() { - return this.position.getZ(); + return position.getTz(); } public void setX(double x) { - this.position.setX(x); + position.setX(x); } public void setY(double y) { - this.position.setY(y); + position.setY(y); } public void setZ(double z) { - this.position.setZ(z); + position.setZ(z); } public double getHeading() { - return this.heading.getAngle(); + return heading.getAngle(); } public void setHeading(double angle) { - this.heading.setAngle(angle); + heading.setAngle(angle); } } diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index 492b1e1f..62c913b9 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -50,6 +50,8 @@ public class View3D extends Pane { */ private Rotate pitch; + private PerspectiveCamera camera; + /** * Default constructor for View3D. Sets up Scene and PerspectiveCamera. */ @@ -86,6 +88,7 @@ public class View3D extends Pane { pitch = new Rotate(0, Rotate.X_AXIS); camera.getTransforms().addAll(pivot, yaw, pitch, distance); + this.camera = camera; return camera; } @@ -142,4 +145,8 @@ public class View3D extends Pane { public void setPitch(double pitch) { this.pitch.setAngle(-pitch); } + + public PerspectiveCamera getCamera() { + return camera; + } } From 05a0614246efb492e8979fe90f09ac82bb63b473 Mon Sep 17 00:00:00 2001 From: cbt24 Date: Tue, 5 Sep 2017 16:02:20 +1200 Subject: [PATCH 05/16] Boats now rotate correctly in position rather than about the origin #story[1261] --- .../java/visualiser/Controllers/RaceController.java | 12 +++++++++++- .../src/main/java/visualiser/layout/Subject3D.java | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 52147309..ee6f665d 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -181,6 +181,14 @@ public class RaceController extends Controller { view3D.setItems(subjects); canvasBase.getChildren().add(0, view3D); + /*for(Mark mark: race.getVisualiserRaceState().getMarks()) { + Subject3D subject = new Subject3D(new Sphere(2)); + subject.setX(mark.getPosition().getLongitude() * 1000); + subject.setZ(mark.getPosition().getLatitude() * 1000); + + subjects.add(subject); + }*/ + URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); StlMeshImporter importer = new StlMeshImporter(); @@ -190,7 +198,7 @@ public class RaceController extends Controller { subjects.add(subject); view3D.setPivot(subject); - view3D.setDistance(50); + view3D.setDistance(500); view3D.setYaw(45); view3D.setPitch(20); @@ -200,6 +208,8 @@ public class RaceController extends Controller { @Override public void handle(long now) { subject.setHeading(boat.getBearing().degrees()); + subject.setX(boat.getPosition().getLongitude()); + subject.setZ(boat.getPosition().getLatitude()); } }; rotate.start(); diff --git a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java index 9abd3b5b..2812f980 100644 --- a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java @@ -31,7 +31,7 @@ public class Subject3D { this.position = new Translate(); this.heading = new Rotate(0, Rotate.Y_AXIS); - this.mesh.getTransforms().addAll(heading, new Rotate(-90, Rotate.X_AXIS), position); + this.mesh.getTransforms().addAll(position, heading, new Rotate(-90, Rotate.X_AXIS)); } public Shape3D getMesh() { From 19f4d0fc06bf3f8cf3d656e11b16eda86c361093 Mon Sep 17 00:00:00 2001 From: cbt24 Date: Tue, 5 Sep 2017 16:50:26 +1200 Subject: [PATCH 06/16] Added marks back to the race view #story[1261] --- .../Controllers/RaceController.java | 34 +++++++++++-------- .../java/visualiser/layout/Subject3D.java | 12 ++----- .../main/java/visualiser/layout/View3D.java | 4 +-- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index ee6f665d..2a2b7cb7 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -181,29 +181,33 @@ public class RaceController extends Controller { view3D.setItems(subjects); canvasBase.getChildren().add(0, view3D); - /*for(Mark mark: race.getVisualiserRaceState().getMarks()) { + for(Mark mark: race.getVisualiserRaceState().getMarks()) { Subject3D subject = new Subject3D(new Sphere(2)); - subject.setX(mark.getPosition().getLongitude() * 1000); - subject.setZ(mark.getPosition().getLatitude() * 1000); + subject.setX(mark.getPosition().getLongitude()); + subject.setZ(mark.getPosition().getLatitude()); + System.out.println(subject.getPosition().toString()); subjects.add(subject); - }*/ + } - URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); + try { + VisualiserBoat boat = race.getVisualiserRaceState().getBoat(race.getVisualiserRaceState().getPlayerBoatID()); - StlMeshImporter importer = new StlMeshImporter(); - importer.read(asset); - Subject3D subject = new Subject3D(new MeshView(importer.getImport())); + URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); - subjects.add(subject); + StlMeshImporter importer = new StlMeshImporter(); + importer.read(asset); + Subject3D subject = new Subject3D(new MeshView(importer.getImport())); + subject.setX(boat.getPosition().getLongitude()); + subject.setZ(boat.getPosition().getLatitude()); - view3D.setPivot(subject); - view3D.setDistance(500); - view3D.setYaw(45); - view3D.setPitch(20); + System.out.println(subject.getPosition().toString()); + subjects.add(subject); - try { - VisualiserBoat boat = race.getVisualiserRaceState().getBoat(race.getVisualiserRaceState().getPlayerBoatID()); + view3D.setPivot(subject); + view3D.setDistance(500); + view3D.setYaw(0); + view3D.setPitch(20); AnimationTimer rotate = new AnimationTimer() { @Override public void handle(long now) { diff --git a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java index 2812f980..f472ea12 100644 --- a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java @@ -38,16 +38,8 @@ public class Subject3D { return mesh; } - public double getX() { - return position.getTx(); - } - - public double getY() { - return position.getTy(); - } - - public double getZ() { - return position.getTz(); + public Translate getPosition() { + return this.position; } public void setX(double x) { diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index 62c913b9..00066a4a 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -117,9 +117,7 @@ public class View3D extends Pane { * @param pivot centred object */ public void setPivot(Subject3D pivot) { - this.pivot.setX(pivot.getX()); - this.pivot.setY(pivot.getY()); - this.pivot.setZ(pivot.getZ()); + this.pivot = pivot.getPosition(); } /** From d89186d8bf9ff35eea287e8a54310b39907a1df3 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Wed, 6 Sep 2017 14:36:24 +1200 Subject: [PATCH 07/16] Finished playable 3D race with first person camera - Camera pivot must be manually updated - GPS coordinates are scaled by an arbitrary amount to make movement visible - Model has to be flipped 180 degrees to move forward #story[1261] --- .../Controllers/HostController.java | 1 - .../Controllers/RaceController.java | 47 +++++++++---------- .../java/visualiser/layout/Subject3D.java | 3 +- .../main/java/visualiser/layout/View3D.java | 17 ++----- 4 files changed, 29 insertions(+), 39 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/HostController.java b/racevisionGame/src/main/java/visualiser/Controllers/HostController.java index e7583252..2855a770 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/HostController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/HostController.java @@ -75,7 +75,6 @@ public class HostController extends Controller { subjects.add(subject); - view3D.setPivot(subject); view3D.setDistance(50); view3D.setYaw(45); view3D.setPitch(20); diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 2a2b7cb7..46afe65e 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -175,50 +175,47 @@ public class RaceController extends Controller { } private void initialiseView3D(VisualiserRaceEvent race) { + int scale = 5000; + ObservableList subjects = FXCollections.observableArrayList(); + URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); + StlMeshImporter importer = new StlMeshImporter(); + importer.read(asset); + view3D = new View3D(); + view3D.setDistance(100); + view3D.setYaw(0); + view3D.setPitch(20); + view3D.setItems(subjects); canvasBase.getChildren().add(0, view3D); for(Mark mark: race.getVisualiserRaceState().getMarks()) { - Subject3D subject = new Subject3D(new Sphere(2)); - subject.setX(mark.getPosition().getLongitude()); - subject.setZ(mark.getPosition().getLatitude()); + Subject3D subject = new Subject3D(new Sphere(5)); + subject.setX(mark.getPosition().getLongitude() * scale); + subject.setZ(mark.getPosition().getLatitude() * scale); - System.out.println(subject.getPosition().toString()); subjects.add(subject); } - try { - VisualiserBoat boat = race.getVisualiserRaceState().getBoat(race.getVisualiserRaceState().getPlayerBoatID()); - - URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); - - StlMeshImporter importer = new StlMeshImporter(); - importer.read(asset); + for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) { Subject3D subject = new Subject3D(new MeshView(importer.getImport())); - subject.setX(boat.getPosition().getLongitude()); - subject.setZ(boat.getPosition().getLatitude()); - - System.out.println(subject.getPosition().toString()); subjects.add(subject); - view3D.setPivot(subject); - view3D.setDistance(500); - view3D.setYaw(0); - view3D.setPitch(20); - AnimationTimer rotate = new AnimationTimer() { + AnimationTimer trackBoat = new AnimationTimer() { @Override public void handle(long now) { subject.setHeading(boat.getBearing().degrees()); - subject.setX(boat.getPosition().getLongitude()); - subject.setZ(boat.getPosition().getLatitude()); + subject.setX(boat.getPosition().getLongitude() * scale); + subject.setZ(boat.getPosition().getLatitude()* scale); + if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) { + view3D.updatePivot(subject.getPosition()); + } + view3D.setYaw(boat.getBearing().degrees()); } }; - rotate.start(); - } catch(BoatNotFoundException e) { - e.printStackTrace(); + trackBoat.start(); } } diff --git a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java index f472ea12..4bb6f6af 100644 --- a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java @@ -17,6 +17,7 @@ public class Subject3D { * Position translation updated by state listeners */ private Translate position; + /** * Heading rotation updated by state listeners */ @@ -31,7 +32,7 @@ public class Subject3D { this.position = new Translate(); this.heading = new Rotate(0, Rotate.Y_AXIS); - this.mesh.getTransforms().addAll(position, heading, new Rotate(-90, Rotate.X_AXIS)); + this.mesh.getTransforms().addAll(position, heading, new Rotate(90, Rotate.X_AXIS), new Rotate(180, Rotate.Y_AXIS)); } public Shape3D getMesh() { diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index 00066a4a..002f0b28 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -34,7 +34,7 @@ public class View3D extends Pane { */ private double farClip; /** - * Position camera pivots around + * Camera origin */ private Translate pivot; /** @@ -112,14 +112,11 @@ public class View3D extends Pane { this.farClip = farClip; } - /** - * Set object to centre on camera - * @param pivot centred object - */ - public void setPivot(Subject3D pivot) { - this.pivot = pivot.getPosition(); + public void updatePivot(Translate pivot) { + this.pivot.setX(pivot.getX()); + this.pivot.setY(pivot.getY()); + this.pivot.setZ(pivot.getZ()); } - /** * Set distance of camera from pivot * @param distance in units @@ -143,8 +140,4 @@ public class View3D extends Pane { public void setPitch(double pitch) { this.pitch.setAngle(-pitch); } - - public PerspectiveCamera getCamera() { - return camera; - } } From 18a689b1107db32d76729ad7ab4372e475bdbc1c Mon Sep 17 00:00:00 2001 From: Fan-Wu Yang Date: Thu, 7 Sep 2017 19:29:08 +1200 Subject: [PATCH 08/16] Changed a constant to be using the one in the constants file (prepartory start time) and temporarily made it so that the race can start in 3 seconds. #story[1261] --- racevisionGame/src/main/java/mock/app/Event.java | 2 +- racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java | 2 +- racevisionGame/src/main/java/shared/model/Constants.java | 7 +++++-- racevisionGame/src/main/java/visualiser/layout/View3D.java | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/racevisionGame/src/main/java/mock/app/Event.java b/racevisionGame/src/main/java/mock/app/Event.java index 6f778852..703686bc 100644 --- a/racevisionGame/src/main/java/mock/app/Event.java +++ b/racevisionGame/src/main/java/mock/app/Event.java @@ -180,7 +180,7 @@ public class Event { public static String setRaceXMLAtCurrentTimeToNow(String raceXML) { //The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute. - long millisecondsToAdd = Constants.RacePreStartTime + (1 * 60 * 1000); + long millisecondsToAdd = Constants.RacePreStartTime + Constants.RacePreparatoryTime; long secondsToAdd = millisecondsToAdd / 1000; //Scale the time using our time scalar. secondsToAdd = secondsToAdd / Constants.RaceTimeScale; diff --git a/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java b/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java index 5cb74625..b168862f 100644 --- a/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java +++ b/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java @@ -187,7 +187,7 @@ public class RaceXMLCreator { public static void setRaceXMLAtCurrentTimeToNow(XMLRace raceXML) { //The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute. - long millisecondsToAdd = Constants.RacePreStartTime + (1 * 60 * 1000); + long millisecondsToAdd = Constants.RacePreStartTime + Constants.RacePreparatoryTime; long secondsToAdd = millisecondsToAdd / 1000; //Scale the time using our time scalar. secondsToAdd = secondsToAdd / Constants.RaceTimeScale; diff --git a/racevisionGame/src/main/java/shared/model/Constants.java b/racevisionGame/src/main/java/shared/model/Constants.java index f18ec111..18a2aaed 100644 --- a/racevisionGame/src/main/java/shared/model/Constants.java +++ b/racevisionGame/src/main/java/shared/model/Constants.java @@ -38,13 +38,16 @@ public class Constants { /** * The race pre-start time, in milliseconds. 3 minutes (30 seconds for development). */ - public static final long RacePreStartTime = 30 * 1000; +// public static final long RacePreStartTime = 30 * 1000; + public static final long RacePreStartTime = 1000; /** * The race preparatory time, in milliseconds. 1 minute. */ - public static final long RacePreparatoryTime = 60 * 1000; +// public static final long RacePreparatoryTime = 60 * 1000; + public static final long RacePreparatoryTime = 3 * 1000; + diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index 002f0b28..f3607ce0 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -87,6 +87,7 @@ public class View3D extends Pane { yaw = new Rotate(0, Rotate.Y_AXIS); pitch = new Rotate(0, Rotate.X_AXIS); camera.getTransforms().addAll(pivot, yaw, pitch, distance); + //camera.setTranslateZ(-1000); this.camera = camera; return camera; From 7bad0e53ff41b1d082f46a8a3dbb5f135ddbe3b0 Mon Sep 17 00:00:00 2001 From: Fan-Wu Yang Date: Thu, 7 Sep 2017 22:03:24 +1200 Subject: [PATCH 09/16] The boat is now viewable in top down view (60 degrees for isometric) and the race is playable #story[1261] --- .../Controllers/RaceController.java | 43 +++++++-- .../java/visualiser/layout/Subject3D.java | 3 + .../main/java/visualiser/layout/View3D.java | 21 ++++- .../java/visualiser/utils/GPSConverter.java | 91 +++++++++++++++++++ 4 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 racevisionGame/src/main/java/visualiser/utils/GPSConverter.java diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 46afe65e..cd450f18 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -17,8 +17,10 @@ import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.shape.MeshView; import javafx.scene.shape.Sphere; +import javafx.scene.transform.Translate; import javafx.util.Callback; import network.Messages.Enums.RaceStatusEnum; +import shared.dataInput.RaceDataSource; import shared.exceptions.BoatNotFoundException; import shared.exceptions.MarkNotFoundException; import shared.model.Leg; @@ -29,6 +31,7 @@ import visualiser.gameController.Keys.ControlKey; import visualiser.layout.Subject3D; import visualiser.layout.View3D; import visualiser.model.*; +import visualiser.utils.GPSConverter; import java.io.IOException; import java.net.URL; @@ -175,7 +178,7 @@ public class RaceController extends Controller { } private void initialiseView3D(VisualiserRaceEvent race) { - int scale = 5000; + int scale = 1; ObservableList subjects = FXCollections.observableArrayList(); @@ -184,39 +187,59 @@ public class RaceController extends Controller { importer.read(asset); view3D = new View3D(); - view3D.setDistance(100); + view3D.setDistance(1050); view3D.setYaw(0); - view3D.setPitch(20); + view3D.setPitch(60); + //view3D.rotateCamera(-90, 1, 0, 0); + //view3D.updatePosition(0, 200, 0); + RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource(); + + double lat1 = raceData.getMapTopLeft().getLatitude(); + double long1 = raceData.getMapTopLeft().getLongitude(); + double lat2 = raceData.getMapBottomRight().getLatitude(); + double long2 = raceData.getMapBottomRight().getLongitude(); + System.out.println(view3D.getWidth()); + System.out.println(view3D.getHeight()); + final GPSConverter gpsConverter = new GPSConverter(lat1, long1, lat2, long2, (int)450, (int)450); view3D.setItems(subjects); canvasBase.getChildren().add(0, view3D); for(Mark mark: race.getVisualiserRaceState().getMarks()) { Subject3D subject = new Subject3D(new Sphere(5)); - subject.setX(mark.getPosition().getLongitude() * scale); - subject.setZ(mark.getPosition().getLatitude() * scale); +// subject.setX(mark.getPosition().getLongitude() * scale); +// subject.setZ(mark.getPosition().getLatitude()* scale); + subject.setX(gpsConverter.convertGPS(mark.getPosition()).getX() * scale); + subject.setZ(gpsConverter.convertGPS(mark.getPosition()).getY() * scale); subjects.add(subject); } for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) { - Subject3D subject = new Subject3D(new MeshView(importer.getImport())); + MeshView mesh = new MeshView(importer.getImport()); + Subject3D subject = new Subject3D(mesh); subjects.add(subject); AnimationTimer trackBoat = new AnimationTimer() { @Override public void handle(long now) { subject.setHeading(boat.getBearing().degrees()); - subject.setX(boat.getPosition().getLongitude() * scale); - subject.setZ(boat.getPosition().getLatitude()* scale); +// subject.setHeading(0); +// subject.setX(boat.getPosition().getLongitude() * scale); +// subject.setZ(boat.getPosition().getLatitude()* scale); + double x = gpsConverter.convertGPS(boat.getPosition()).getX() * scale; + //System.out.println(x); + subject.setX(x); + subject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY() * scale); if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) { - view3D.updatePivot(subject.getPosition()); + //view3D.updatePivot(subject.getPosition()); } - view3D.setYaw(boat.getBearing().degrees()); + //view3D.setYaw(boat.getBearing().degrees()); } }; trackBoat.start(); } + view3D.updatePivot(new Translate(250, 0, 210)); } diff --git a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java index 4bb6f6af..adfd93ab 100644 --- a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java @@ -33,6 +33,9 @@ public class Subject3D { this.heading = new Rotate(0, Rotate.Y_AXIS); this.mesh.getTransforms().addAll(position, heading, new Rotate(90, Rotate.X_AXIS), new Rotate(180, Rotate.Y_AXIS)); + this.position.xProperty().addListener(((observable, oldValue, newValue) -> System.out.println("Boat x: " + newValue))); + this.position.yProperty().addListener(((observable, oldValue, newValue) -> System.out.println("Boat y: " + newValue))); + this.position.zProperty().addListener(((observable, oldValue, newValue) -> System.out.println("Boat z: " + newValue))); } public Shape3D getMesh() { diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index f3607ce0..95a6cfeb 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -2,6 +2,7 @@ package visualiser.layout; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; +import javafx.geometry.Point3D; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.SubScene; @@ -77,7 +78,7 @@ public class View3D extends Pane { // Set up view frustum nearClip = 0.1; - farClip = 1000.0; + farClip = 3000.0; camera.setNearClip(nearClip); camera.setFarClip(farClip); @@ -87,7 +88,7 @@ public class View3D extends Pane { yaw = new Rotate(0, Rotate.Y_AXIS); pitch = new Rotate(0, Rotate.X_AXIS); camera.getTransforms().addAll(pivot, yaw, pitch, distance); - //camera.setTranslateZ(-1000); + centerCamera(); this.camera = camera; return camera; @@ -118,6 +119,13 @@ public class View3D extends Pane { this.pivot.setY(pivot.getY()); this.pivot.setZ(pivot.getZ()); } + + public void updatePosition(double x, double y, double z) { + this.distance.setX(x); + this.distance.setY(y); + this.distance.setZ(z); + } + /** * Set distance of camera from pivot * @param distance in units @@ -141,4 +149,13 @@ public class View3D extends Pane { public void setPitch(double pitch) { this.pitch.setAngle(-pitch); } + + public void centerCamera(){ + } + + public void rotateCamera(double angle, double x, double y, double z){ + camera.setRotationAxis(new Point3D(x, y, z)); + camera.setRotate(-90); + } + } diff --git a/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java b/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java new file mode 100644 index 00000000..55a162b2 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java @@ -0,0 +1,91 @@ +package visualiser.utils; + +import shared.model.GPSCoordinate; +import visualiser.model.GraphCoordinate; + +/** + * Created by fwy13 on 7/09/17. + */ +public class GPSConverter { + double longRight; + double longLeft; + double latBottom; + double latTop; + int paneWidth; + int paneHeight; + + public GPSConverter(double latTop, double longLeft, double latBottom, double longRight, double paneWidth, double paneHeight){ + this.longRight = longRight; + this.longLeft = longLeft; + this.latBottom = latBottom; + this.latTop = latTop; + this.paneWidth = (int)paneWidth; + this.paneHeight = (int)paneHeight; + } + + /** + * Converts GPS coordinates to coordinates for container. + * It is assumed that the provided GPSCoordinate will always be within the GPSCoordinate boundaries of the RaceMap. + * + * @param lat GPS latitude + * @param lon GPS longitude + * @return GraphCoordinate (pair of doubles) + * @see GraphCoordinate + */ + private GraphCoordinate convertGPS(double lat, double lon) { + + //Calculate the width/height, in gps coordinates, of the map. + double longWidth = longRight - longLeft; + double latHeight = latBottom - latTop; + + //Calculate the distance between the specified coordinate and the edge of the map. + double longDelta = lon - longLeft; + double latDelta = lat - latTop; + + //Calculate the proportion along horizontally, from the left, the coordinate should be. + double longProportion = longDelta / longWidth; + //Calculate the proportion along vertically, from the top, the coordinate should be. + double latProportion = latDelta / latHeight; + //System.out.println(latProportion + " " + longProportion); + + + //Check which pixel dimension of our map is smaller. We use this to ensure that any rendered stuff retains its correct aspect ratio, and that everything is visible on screen. + int smallerDimension = Math.min(paneWidth, paneHeight); + + //Calculate the x and y pixel coordinates. + //We take the complement of latProportion to flip it. + int x = (int) (longProportion * smallerDimension); + int y = (int) (latProportion * smallerDimension); + + //Because we try to maintain the correct aspect ratio, we will end up with "spare" pixels along the larger dimension (e.g., width 800, height 600, 200 extra pixels along width). + int extraPixels = Math.abs(paneWidth - paneHeight); + //We therefore "center" the coordinates along this larger dimension, by adding half of the extra pixels. + if (paneWidth > paneHeight) { + x += extraPixels / 2; + } else { + y += extraPixels / 2; + } + + + //Finally, create the GraphCoordinate. + GraphCoordinate graphCoordinate = new GraphCoordinate(x, y); + + + return graphCoordinate; + + } + + /** + * Converts the GPS Coordinate to GraphCoordinate. + * It is assumed that the provided GPSCoordinate will always be within the GPSCoordinate boundaries of the RaceMap. + * + * @param coordinate GPSCoordinate representation of Latitude and Longitude. + * @return GraphCoordinate that the GPS is coordinates are to be displayed on the map. + * @see GraphCoordinate + * @see GPSCoordinate + */ + public GraphCoordinate convertGPS(GPSCoordinate coordinate) { + return convertGPS(coordinate.getLatitude(), coordinate.getLongitude()); + } + +} From 54baf4f884117f9d63ab2bb77fe6d81fd7a8eb63 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Thu, 7 Sep 2017 23:22:27 +1200 Subject: [PATCH 10/16] Added scroll-wheel zooming - Race pane listens to scroll delta and sets View3D distance accordingly - GPSConverter has more applicable property names for infinite 3D #story[1190] --- .../Controllers/RaceController.java | 49 ++++++++----------- .../main/java/visualiser/layout/View3D.java | 23 ++------- .../java/visualiser/utils/GPSConverter.java | 41 +++++++++------- .../resources/visualiser/scenes/race.fxml | 2 +- 4 files changed, 48 insertions(+), 67 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index cd450f18..4880fcaf 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -12,8 +12,8 @@ import javafx.scene.chart.LineChart; import javafx.scene.control.*; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; +import javafx.scene.input.ScrollEvent; import javafx.scene.layout.GridPane; -import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.shape.MeshView; import javafx.scene.shape.Sphere; @@ -21,8 +21,6 @@ import javafx.scene.transform.Translate; import javafx.util.Callback; import network.Messages.Enums.RaceStatusEnum; import shared.dataInput.RaceDataSource; -import shared.exceptions.BoatNotFoundException; -import shared.exceptions.MarkNotFoundException; import shared.model.Leg; import shared.model.Mark; import visualiser.app.App; @@ -35,7 +33,6 @@ import visualiser.utils.GPSConverter; import java.io.IOException; import java.net.URL; -import java.util.List; import java.util.Optional; import java.util.ResourceBundle; import java.util.logging.Level; @@ -75,7 +72,7 @@ public class RaceController extends Controller { @FXML private GridPane canvasBase; - @FXML private SplitPane race; + @FXML private SplitPane racePane; /** * This is the pane we place the actual arrow control inside of. @@ -103,7 +100,7 @@ public class RaceController extends Controller { infoTableShow = true; // Initialise keyboard handler - race.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + racePane.addEventFilter(KeyEvent.KEY_PRESSED, event -> { String codeString = event.getCode().toString(); if (codeString.equals("TAB")){toggleTable();} @@ -129,7 +126,7 @@ public class RaceController extends Controller { Optional result = alert.showAndWait(); if (result.get() == ButtonType.OK) { parent.endEvent(); - race.setVisible(false); + racePane.setVisible(false); App.app.showMainStage(App.getStage()); } } else { @@ -138,7 +135,7 @@ public class RaceController extends Controller { alert.setContentText("Do you wish to quit the race?"); Optional result = alert.showAndWait(); if (result.get() == ButtonType.OK) { - race.setVisible(false); + racePane.setVisible(false); App.app.showMainStage(App.getStage()); } } @@ -180,7 +177,7 @@ public class RaceController extends Controller { private void initialiseView3D(VisualiserRaceEvent race) { int scale = 1; - ObservableList subjects = FXCollections.observableArrayList(); + viewSubjects = FXCollections.observableArrayList(); URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); StlMeshImporter importer = new StlMeshImporter(); @@ -190,45 +187,35 @@ public class RaceController extends Controller { view3D.setDistance(1050); view3D.setYaw(0); view3D.setPitch(60); - //view3D.rotateCamera(-90, 1, 0, 0); - //view3D.updatePosition(0, 200, 0); RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource(); double lat1 = raceData.getMapTopLeft().getLatitude(); double long1 = raceData.getMapTopLeft().getLongitude(); double lat2 = raceData.getMapBottomRight().getLatitude(); double long2 = raceData.getMapBottomRight().getLongitude(); - System.out.println(view3D.getWidth()); - System.out.println(view3D.getHeight()); - final GPSConverter gpsConverter = new GPSConverter(lat1, long1, lat2, long2, (int)450, (int)450); + final GPSConverter gpsConverter = new GPSConverter(lat1, long1, lat2, long2, 450, 450); - view3D.setItems(subjects); + view3D.setItems(viewSubjects); canvasBase.getChildren().add(0, view3D); for(Mark mark: race.getVisualiserRaceState().getMarks()) { Subject3D subject = new Subject3D(new Sphere(5)); -// subject.setX(mark.getPosition().getLongitude() * scale); -// subject.setZ(mark.getPosition().getLatitude()* scale); subject.setX(gpsConverter.convertGPS(mark.getPosition()).getX() * scale); subject.setZ(gpsConverter.convertGPS(mark.getPosition()).getY() * scale); - subjects.add(subject); + viewSubjects.add(subject); } for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) { MeshView mesh = new MeshView(importer.getImport()); Subject3D subject = new Subject3D(mesh); - subjects.add(subject); + viewSubjects.add(subject); AnimationTimer trackBoat = new AnimationTimer() { @Override public void handle(long now) { subject.setHeading(boat.getBearing().degrees()); -// subject.setHeading(0); -// subject.setX(boat.getPosition().getLongitude() * scale); -// subject.setZ(boat.getPosition().getLatitude()* scale); double x = gpsConverter.convertGPS(boat.getPosition()).getX() * scale; - //System.out.println(x); subject.setX(x); subject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY() * scale); if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) { @@ -240,6 +227,10 @@ public class RaceController extends Controller { trackBoat.start(); } view3D.updatePivot(new Translate(250, 0, 210)); + + racePane.setOnScroll(e -> { + view3D.updateDistance(e.getDeltaY()); + }); } @@ -422,7 +413,7 @@ public class RaceController extends Controller { initialiseRace(); //Display this controller. - race.setVisible(true); + racePane.setVisible(true); } /** @@ -430,7 +421,7 @@ public class RaceController extends Controller { * @param boats boats there are in the race. */ public void finishRace(ObservableList boats) { - race.setVisible(false); + racePane.setVisible(false); parent.enterFinish(boats); } @@ -471,7 +462,7 @@ public class RaceController extends Controller { //Return to main screen if we lose connection. if (!visualiserRace.getServerConnection().isAlive()) { - race.setVisible(false); + racePane.setVisible(false); //parent.enterTitle(); try { App.app.showMainStage(App.getStage()); @@ -493,10 +484,10 @@ public class RaceController extends Controller { * toggles if the info table is shown */ private void toggleTable() { - double tablePercent = 1 - (boatPlacingColumn.getPrefWidth() + boatTeamColumn.getPrefWidth() + boatMarkColumn.getPrefWidth() + boatSpeedColumn.getPrefWidth())/race.getWidth(); + double tablePercent = 1 - (boatPlacingColumn.getPrefWidth() + boatTeamColumn.getPrefWidth() + boatMarkColumn.getPrefWidth() + boatSpeedColumn.getPrefWidth())/racePane.getWidth(); if (infoTableShow){ - race.setDividerPositions(tablePercent); + racePane.setDividerPositions(tablePercent); arrowPane.setScaleX(0.5); arrowPane.setScaleY(0.5); @@ -504,7 +495,7 @@ public class RaceController extends Controller { arrowPane.setTranslateY(0 - arrowPane.getScene().getHeight()/4); }else{ - race.setDividerPositions(1); + racePane.setDividerPositions(1); arrowPane.setScaleX(1); arrowPane.setScaleY(1); diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index 95a6cfeb..118317f4 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -51,8 +51,6 @@ public class View3D extends Pane { */ private Rotate pitch; - private PerspectiveCamera camera; - /** * Default constructor for View3D. Sets up Scene and PerspectiveCamera. */ @@ -88,9 +86,7 @@ public class View3D extends Pane { yaw = new Rotate(0, Rotate.Y_AXIS); pitch = new Rotate(0, Rotate.X_AXIS); camera.getTransforms().addAll(pivot, yaw, pitch, distance); - centerCamera(); - this.camera = camera; return camera; } @@ -120,12 +116,6 @@ public class View3D extends Pane { this.pivot.setZ(pivot.getZ()); } - public void updatePosition(double x, double y, double z) { - this.distance.setX(x); - this.distance.setY(y); - this.distance.setZ(z); - } - /** * Set distance of camera from pivot * @param distance in units @@ -134,6 +124,10 @@ public class View3D extends Pane { this.distance.setZ(-distance); } + public void updateDistance(double delta) { + this.distance.setZ(this.distance.getZ() - delta); + } + /** * Set angle of camera from z-axis along ground * @param yaw in degrees @@ -149,13 +143,4 @@ public class View3D extends Pane { public void setPitch(double pitch) { this.pitch.setAngle(-pitch); } - - public void centerCamera(){ - } - - public void rotateCamera(double angle, double x, double y, double z){ - camera.setRotationAxis(new Point3D(x, y, z)); - camera.setRotate(-90); - } - } diff --git a/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java b/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java index 55a162b2..c7e4760a 100644 --- a/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java +++ b/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java @@ -7,20 +7,27 @@ import visualiser.model.GraphCoordinate; * Created by fwy13 on 7/09/17. */ public class GPSConverter { - double longRight; - double longLeft; - double latBottom; - double latTop; - int paneWidth; - int paneHeight; - - public GPSConverter(double latTop, double longLeft, double latBottom, double longRight, double paneWidth, double paneHeight){ + private double longRight; + private double longLeft; + private double latBottom; + private double latTop; + + /** + * Conversion factor from longitude to view units + */ + private double longitudeFactor; + /** + * Conversion factor from latitude to view units + */ + private double latitudeFactor; + + public GPSConverter(double latTop, double longLeft, double latBottom, double longRight, double longitudeFactor, double latitudeFactor){ this.longRight = longRight; this.longLeft = longLeft; this.latBottom = latBottom; this.latTop = latTop; - this.paneWidth = (int)paneWidth; - this.paneHeight = (int)paneHeight; + this.longitudeFactor = (int)longitudeFactor; + this.latitudeFactor = (int)latitudeFactor; } /** @@ -46,11 +53,9 @@ public class GPSConverter { double longProportion = longDelta / longWidth; //Calculate the proportion along vertically, from the top, the coordinate should be. double latProportion = latDelta / latHeight; - //System.out.println(latProportion + " " + longProportion); - - //Check which pixel dimension of our map is smaller. We use this to ensure that any rendered stuff retains its correct aspect ratio, and that everything is visible on screen. - int smallerDimension = Math.min(paneWidth, paneHeight); + //Check which metric dimension of our map is smaller. We use this to ensure that any rendered stuff retains its correct aspect ratio, and that everything is visible on screen. + double smallerDimension = Math.min(longitudeFactor, latitudeFactor); //Calculate the x and y pixel coordinates. //We take the complement of latProportion to flip it. @@ -58,12 +63,12 @@ public class GPSConverter { int y = (int) (latProportion * smallerDimension); //Because we try to maintain the correct aspect ratio, we will end up with "spare" pixels along the larger dimension (e.g., width 800, height 600, 200 extra pixels along width). - int extraPixels = Math.abs(paneWidth - paneHeight); + double extraDistance = Math.abs(longitudeFactor - latitudeFactor); //We therefore "center" the coordinates along this larger dimension, by adding half of the extra pixels. - if (paneWidth > paneHeight) { - x += extraPixels / 2; + if (longitudeFactor > latitudeFactor) { + x += extraDistance / 2; } else { - y += extraPixels / 2; + y += extraDistance / 2; } diff --git a/racevisionGame/src/main/resources/visualiser/scenes/race.fxml b/racevisionGame/src/main/resources/visualiser/scenes/race.fxml index bbc1c077..b6496743 100644 --- a/racevisionGame/src/main/resources/visualiser/scenes/race.fxml +++ b/racevisionGame/src/main/resources/visualiser/scenes/race.fxml @@ -22,7 +22,7 @@ - + From b59eca1a8b69834b90522e6d50a5797db3a3cc5a Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Fri, 8 Sep 2017 12:43:36 +1200 Subject: [PATCH 11/16] Added Subject3D selection to View3D - Changed how View3D is added to canvasBase to allow events to get through #story[1190] --- .../Controllers/RaceController.java | 16 ++++------- .../java/visualiser/layout/Subject3D.java | 3 --- .../main/java/visualiser/layout/View3D.java | 27 +++++++++++++++++-- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 4880fcaf..0bbc2ad6 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -175,8 +175,6 @@ public class RaceController extends Controller { } private void initialiseView3D(VisualiserRaceEvent race) { - int scale = 1; - viewSubjects = FXCollections.observableArrayList(); URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); @@ -196,12 +194,12 @@ public class RaceController extends Controller { final GPSConverter gpsConverter = new GPSConverter(lat1, long1, lat2, long2, 450, 450); view3D.setItems(viewSubjects); - canvasBase.getChildren().add(0, view3D); + canvasBase.add(view3D, 0, 0); for(Mark mark: race.getVisualiserRaceState().getMarks()) { Subject3D subject = new Subject3D(new Sphere(5)); - subject.setX(gpsConverter.convertGPS(mark.getPosition()).getX() * scale); - subject.setZ(gpsConverter.convertGPS(mark.getPosition()).getY() * scale); + subject.setX(gpsConverter.convertGPS(mark.getPosition()).getX()); + subject.setZ(gpsConverter.convertGPS(mark.getPosition()).getY()); viewSubjects.add(subject); } @@ -215,9 +213,9 @@ public class RaceController extends Controller { @Override public void handle(long now) { subject.setHeading(boat.getBearing().degrees()); - double x = gpsConverter.convertGPS(boat.getPosition()).getX() * scale; + double x = gpsConverter.convertGPS(boat.getPosition()).getX(); subject.setX(x); - subject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY() * scale); + subject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY()); if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) { //view3D.updatePivot(subject.getPosition()); } @@ -227,10 +225,6 @@ public class RaceController extends Controller { trackBoat.start(); } view3D.updatePivot(new Translate(250, 0, 210)); - - racePane.setOnScroll(e -> { - view3D.updateDistance(e.getDeltaY()); - }); } diff --git a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java index adfd93ab..4bb6f6af 100644 --- a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java @@ -33,9 +33,6 @@ public class Subject3D { this.heading = new Rotate(0, Rotate.Y_AXIS); this.mesh.getTransforms().addAll(position, heading, new Rotate(90, Rotate.X_AXIS), new Rotate(180, Rotate.Y_AXIS)); - this.position.xProperty().addListener(((observable, oldValue, newValue) -> System.out.println("Boat x: " + newValue))); - this.position.yProperty().addListener(((observable, oldValue, newValue) -> System.out.println("Boat y: " + newValue))); - this.position.zProperty().addListener(((observable, oldValue, newValue) -> System.out.println("Boat z: " + newValue))); } public Shape3D getMesh() { diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index 118317f4..a981ca89 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -6,12 +6,17 @@ import javafx.geometry.Point3D; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.SubScene; +import javafx.scene.input.PickResult; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; +import javafx.scene.shape.Shape; import javafx.scene.shape.Shape3D; import javafx.scene.transform.Rotate; import javafx.scene.transform.Translate; +import java.util.HashMap; +import java.util.Map; + /** * 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 @@ -22,6 +27,10 @@ public class View3D extends Pane { * Observable list of renderable items */ private ObservableList items; + /** + * Map for selecting Subject3D from Shape3D + */ + private Map selectionMap; /** * Rendering container for shapes */ @@ -56,6 +65,7 @@ public class View3D extends Pane { */ public View3D() { world = new Group(); + selectionMap = new HashMap<>(); SubScene scene = new SubScene(world, 300, 300); scene.widthProperty().bind(this.widthProperty()); @@ -64,6 +74,13 @@ public class View3D extends Pane { scene.setCamera(buildCamera()); + scene.setOnMousePressed(e -> { + PickResult result = e.getPickResult(); + if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) { + System.out.println(selectionMap.get(result.getIntersectedNode())); + } + }); + this.getChildren().add(scene); } @@ -95,8 +112,14 @@ public class View3D extends Pane { this.items.addListener((ListChangeListener) c -> { while(c.next()) { if (c.wasRemoved() || c.wasAdded()) { - for (Subject3D shape : c.getRemoved()) world.getChildren().remove(shape.getMesh()); - for (Subject3D shape : c.getAddedSubList()) world.getChildren().add(shape.getMesh()); + for (Subject3D shape : c.getRemoved()) { + world.getChildren().remove(shape.getMesh()); + selectionMap.remove(shape.getMesh()); + } + for (Subject3D shape : c.getAddedSubList()) { + world.getChildren().add(shape.getMesh()); + selectionMap.put(shape.getMesh(), shape); + } } } }); From 23eabed3ff5eb2e7d4c5959769b2cfc024a0ef04 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Fri, 8 Sep 2017 12:58:06 +1200 Subject: [PATCH 12/16] View3D tracks selected boat at fixed distance, pitch, and relative yaw #story[1190] --- .../main/java/visualiser/Controllers/HostController.java | 2 +- .../src/main/java/visualiser/layout/Subject3D.java | 8 ++++---- .../src/main/java/visualiser/layout/View3D.java | 9 ++++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/HostController.java b/racevisionGame/src/main/java/visualiser/Controllers/HostController.java index 2855a770..10bc9dbe 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/HostController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/HostController.java @@ -82,7 +82,7 @@ public class HostController extends Controller { AnimationTimer rotate = new AnimationTimer() { @Override public void handle(long now) { - subject.setHeading(subject.getHeading() + 0.1); + subject.setHeading(subject.getHeading().getAngle() + 0.1); } }; rotate.start(); diff --git a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java index 4bb6f6af..af76f4f4 100644 --- a/racevisionGame/src/main/java/visualiser/layout/Subject3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/Subject3D.java @@ -43,6 +43,10 @@ public class Subject3D { return this.position; } + public Rotate getHeading() { + return heading; + } + public void setX(double x) { position.setX(x); } @@ -55,10 +59,6 @@ public class Subject3D { position.setZ(z); } - public double getHeading() { - return heading.getAngle(); - } - public void setHeading(double angle) { heading.setAngle(angle); } diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index a981ca89..a6afb439 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -77,7 +77,14 @@ public class View3D extends Pane { scene.setOnMousePressed(e -> { PickResult result = e.getPickResult(); if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) { - System.out.println(selectionMap.get(result.getIntersectedNode())); + Subject3D target = selectionMap.get(result.getIntersectedNode()); + target.getPosition().xProperty().addListener((o, prev, curr) -> pivot.setX((double)curr)); + target.getPosition().yProperty().addListener((o, prev, curr) -> pivot.setY((double)curr)); + target.getPosition().zProperty().addListener((o, prev, curr) -> pivot.setZ((double)curr)); + target.getHeading().angleProperty().addListener((o, prev, curr) -> setYaw((double)curr)); + + this.setDistance(100); + this.setPitch(30); } }); From 1efec06bdcc8f332da2ab22e048e9a675cf7839f Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Fri, 8 Sep 2017 14:21:03 +1200 Subject: [PATCH 13/16] View3D can untrack objects and zoom in different modes - updateDistance zooms between 0 and infinity - Camera switches from third person to bird's eye when reaching a set distance - Only one subject can be tracked at a time #story[1190] --- .../Controllers/RaceController.java | 4 ++ .../main/java/visualiser/layout/View3D.java | 64 ++++++++++++++++--- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 0bbc2ad6..4a8addb7 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -225,6 +225,10 @@ public class RaceController extends Controller { trackBoat.start(); } view3D.updatePivot(new Translate(250, 0, 210)); + + view3D.setOnScroll(e -> { + view3D.updateDistance(e.getDeltaY()); + }); } diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index a6afb439..3f867791 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -1,5 +1,7 @@ package visualiser.layout; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.geometry.Point3D; @@ -31,6 +33,10 @@ public class View3D extends Pane { * Map for selecting Subject3D from Shape3D */ private Map selectionMap; + /** + * Subject tracked by camera + */ + private Subject3D target; /** * Rendering container for shapes */ @@ -60,12 +66,23 @@ public class View3D extends Pane { */ private Rotate pitch; + private ChangeListener pivotHeading = (o, prev, curr) -> yaw.setAngle((double)curr); + + private ChangeListener pivotX = (o, prev, curr) -> pivot.setX((double)curr); + + private ChangeListener pivotY = (o, prev, curr) -> pivot.setY((double)curr); + + private ChangeListener pivotZ = (o, prev, curr) -> pivot.setZ((double)curr); + + private double THIRD_PERSON_LIMIT = 100; + /** * Default constructor for View3D. Sets up Scene and PerspectiveCamera. */ public View3D() { world = new Group(); selectionMap = new HashMap<>(); + target = null; SubScene scene = new SubScene(world, 300, 300); scene.widthProperty().bind(this.widthProperty()); @@ -77,14 +94,7 @@ public class View3D extends Pane { scene.setOnMousePressed(e -> { PickResult result = e.getPickResult(); if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) { - Subject3D target = selectionMap.get(result.getIntersectedNode()); - target.getPosition().xProperty().addListener((o, prev, curr) -> pivot.setX((double)curr)); - target.getPosition().yProperty().addListener((o, prev, curr) -> pivot.setY((double)curr)); - target.getPosition().zProperty().addListener((o, prev, curr) -> pivot.setZ((double)curr)); - target.getHeading().angleProperty().addListener((o, prev, curr) -> setYaw((double)curr)); - - this.setDistance(100); - this.setPitch(30); + trackSubject(selectionMap.get(result.getIntersectedNode())); } }); @@ -132,6 +142,31 @@ public class View3D extends Pane { }); } + private void untrackSubject() { + if(target != null) { + target.getPosition().xProperty().removeListener(pivotX); + target.getPosition().yProperty().removeListener(pivotY); + target.getPosition().zProperty().removeListener(pivotZ); + target.getHeading().angleProperty().removeListener(pivotHeading); + } + } + + private void trackSubject(Subject3D subject) { + untrackSubject(); + target = subject; + + updatePivot(target.getPosition()); + setYaw(target.getHeading().getAngle()); + + target.getPosition().xProperty().addListener(pivotX); + target.getPosition().yProperty().addListener(pivotY); + target.getPosition().zProperty().addListener(pivotZ); + target.getHeading().angleProperty().addListener(pivotHeading); + + this.setDistance(THIRD_PERSON_LIMIT); + this.setPitch(20); + } + public void setNearClip(double nearClip) { this.nearClip = nearClip; } @@ -155,7 +190,18 @@ public class View3D extends Pane { } public void updateDistance(double delta) { - this.distance.setZ(this.distance.getZ() - delta); + double distance = -this.distance.getZ() + delta; + + if(distance <= 0) { + this.setDistance(0); + } else if(distance > THIRD_PERSON_LIMIT) { + untrackSubject(); + this.setYaw(0); + this.setPitch(60); + this.setDistance(distance); + } else { + this.setDistance(distance); + } } /** From 68f434b6b6c17ad38cad25044d904ef3886b1c11 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Fri, 8 Sep 2017 15:49:53 +1200 Subject: [PATCH 14/16] Bound zooming action to zoom keys - View3D subject selection is no longer enabled by default - Added documentation #story[1190] --- .../Controllers/RaceController.java | 24 ++++---- .../gameController/Keys/ControlKey.java | 3 +- .../main/java/visualiser/layout/View3D.java | 57 ++++++++++++------- .../java/visualiser/utils/GPSConverter.java | 15 ++--- 4 files changed, 59 insertions(+), 40 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 4a8addb7..a7cf88b3 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -26,6 +26,7 @@ import shared.model.Mark; import visualiser.app.App; import visualiser.gameController.ControllerClient; import visualiser.gameController.Keys.ControlKey; +import visualiser.gameController.Keys.KeyFactory; import visualiser.layout.Subject3D; import visualiser.layout.View3D; import visualiser.model.*; @@ -185,15 +186,12 @@ public class RaceController extends Controller { view3D.setDistance(1050); view3D.setYaw(0); view3D.setPitch(60); - RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource(); - double lat1 = raceData.getMapTopLeft().getLatitude(); - double long1 = raceData.getMapTopLeft().getLongitude(); - double lat2 = raceData.getMapBottomRight().getLatitude(); - double long2 = raceData.getMapBottomRight().getLongitude(); - final GPSConverter gpsConverter = new GPSConverter(lat1, long1, lat2, long2, 450, 450); + RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource(); + final GPSConverter gpsConverter = new GPSConverter(raceData, 450, 450); view3D.setItems(viewSubjects); + view3D.enableTracking(); canvasBase.add(view3D, 0, 0); for(Mark mark: race.getVisualiserRaceState().getMarks()) { @@ -213,13 +211,8 @@ public class RaceController extends Controller { @Override public void handle(long now) { subject.setHeading(boat.getBearing().degrees()); - double x = gpsConverter.convertGPS(boat.getPosition()).getX(); - subject.setX(x); + subject.setX(gpsConverter.convertGPS(boat.getPosition()).getX()); subject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY()); - if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) { - //view3D.updatePivot(subject.getPosition()); - } - //view3D.setYaw(boat.getBearing().degrees()); } }; trackBoat.start(); @@ -229,6 +222,13 @@ public class RaceController extends Controller { view3D.setOnScroll(e -> { view3D.updateDistance(e.getDeltaY()); }); + + racePane.addEventFilter(KeyEvent.KEY_PRESSED, e -> { + switch(keyFactory.getKey(e.getCode().toString()).toString()) { + case "Zoom In": view3D.updateDistance(-10); break; + case "Zoom Out": view3D.updateDistance(10); break; + } + }); } diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java index dd489f73..ce4b341e 100644 --- a/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java @@ -1,6 +1,5 @@ package visualiser.gameController.Keys; -import javafx.scene.input.KeyCode; import network.Messages.Enums.BoatActionEnum; /** @@ -45,7 +44,7 @@ public abstract class ControlKey { /** * What this key should do when the command is issued for it to do its job. */ - public abstract void onAction();//may want to make it take in a visualiser and stuff in the future. + public abstract void onAction(); /** * What to do when the key is held diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index 3f867791..db5ee85b 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -1,17 +1,14 @@ package visualiser.layout; import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; -import javafx.geometry.Point3D; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.SubScene; import javafx.scene.input.PickResult; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; -import javafx.scene.shape.Shape; import javafx.scene.shape.Shape3D; import javafx.scene.transform.Rotate; import javafx.scene.transform.Translate; @@ -25,6 +22,10 @@ import java.util.Map; * sky box, whose textures are set with special methods. */ public class View3D extends Pane { + /** + * Container for group and camera + */ + private SubScene scene; /** * Observable list of renderable items */ @@ -65,39 +66,42 @@ public class View3D extends Pane { * Angle between ground plane and camera direction */ private Rotate pitch; - + /** + * Single listener for subject heading changes + */ private ChangeListener pivotHeading = (o, prev, curr) -> yaw.setAngle((double)curr); - + /** + * Single listener for subject position (x) changes + */ private ChangeListener pivotX = (o, prev, curr) -> pivot.setX((double)curr); - + /** + * Single listener for subject position (y) changes + */ private ChangeListener pivotY = (o, prev, curr) -> pivot.setY((double)curr); - + /** + * Single listener for subject position (z) changes + */ private ChangeListener pivotZ = (o, prev, curr) -> pivot.setZ((double)curr); - + /** + * Distance to switch from third person to bird's eye + */ private double THIRD_PERSON_LIMIT = 100; /** * Default constructor for View3D. Sets up Scene and PerspectiveCamera. */ public View3D() { - world = new Group(); - selectionMap = new HashMap<>(); - target = null; + this.world = new Group(); + this.selectionMap = new HashMap<>(); + this.target = null; + this.scene = new SubScene(world, 300, 300); - SubScene scene = new SubScene(world, 300, 300); scene.widthProperty().bind(this.widthProperty()); scene.heightProperty().bind(this.heightProperty()); scene.setFill(new Color(0.2, 0.6, 1, 1)); scene.setCamera(buildCamera()); - scene.setOnMousePressed(e -> { - PickResult result = e.getPickResult(); - if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) { - trackSubject(selectionMap.get(result.getIntersectedNode())); - } - }); - this.getChildren().add(scene); } @@ -142,6 +146,15 @@ public class View3D extends Pane { }); } + public void enableTracking() { + scene.setOnMousePressed(e -> { + PickResult result = e.getPickResult(); + if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) { + trackSubject(selectionMap.get(result.getIntersectedNode())); + } + }); + } + private void untrackSubject() { if(target != null) { target.getPosition().xProperty().removeListener(pivotX); @@ -189,6 +202,12 @@ public class View3D extends Pane { this.distance.setZ(-distance); } + /** + * Adds delta to current distance and changes camera mode if applicable. + * Third person limit specifies the distance at which a third person camera + * switches to bird's-eye, remaining focused on the same position. + * @param delta amount to change distance by + */ public void updateDistance(double delta) { double distance = -this.distance.getZ() + delta; diff --git a/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java b/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java index c7e4760a..49ef7bcb 100644 --- a/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java +++ b/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java @@ -1,5 +1,6 @@ package visualiser.utils; +import shared.dataInput.RaceDataSource; import shared.model.GPSCoordinate; import visualiser.model.GraphCoordinate; @@ -21,13 +22,13 @@ public class GPSConverter { */ private double latitudeFactor; - public GPSConverter(double latTop, double longLeft, double latBottom, double longRight, double longitudeFactor, double latitudeFactor){ - this.longRight = longRight; - this.longLeft = longLeft; - this.latBottom = latBottom; - this.latTop = latTop; - this.longitudeFactor = (int)longitudeFactor; - this.latitudeFactor = (int)latitudeFactor; + public GPSConverter(RaceDataSource source, double longitudeFactor, double latitudeFactor) { + this.latTop = source.getMapTopLeft().getLatitude(); + this.longLeft = source.getMapTopLeft().getLongitude(); + this.latBottom = source.getMapBottomRight().getLatitude(); + this.longRight = source.getMapBottomRight().getLongitude(); + this.longitudeFactor = longitudeFactor; + this.latitudeFactor = latitudeFactor; } /** From b034a452b60503dd9696f7968e7fefff59c2e7c0 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Fri, 8 Sep 2017 16:30:03 +1200 Subject: [PATCH 15/16] Fixed problem with race pane registering non-control keys #story[1190] --- .../java/visualiser/Controllers/RaceController.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index a7cf88b3..35e20cb7 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -224,9 +224,16 @@ public class RaceController extends Controller { }); racePane.addEventFilter(KeyEvent.KEY_PRESSED, e -> { - switch(keyFactory.getKey(e.getCode().toString()).toString()) { - case "Zoom In": view3D.updateDistance(-10); break; - case "Zoom Out": view3D.updateDistance(10); break; + ControlKey key = keyFactory.getKey(e.getCode().toString()); + if(key != null) { + switch (key.toString()) { + case "Zoom In": + view3D.updateDistance(-10); + break; + case "Zoom Out": + view3D.updateDistance(10); + break; + } } }); } From 93883ee8b7e9bca352f438ed46f1566039013661 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Fri, 8 Sep 2017 23:48:47 +1200 Subject: [PATCH 16/16] Added more descriptive documentation to new classes and functionality #story[1190] --- .../Controllers/RaceController.java | 17 ++++++++++++----- .../main/java/visualiser/layout/View3D.java | 19 +++++++++++++++++++ .../java/visualiser/utils/GPSConverter.java | 11 +++++++++-- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 35e20cb7..e864993d 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -178,35 +178,39 @@ public class RaceController extends Controller { private void initialiseView3D(VisualiserRaceEvent race) { viewSubjects = FXCollections.observableArrayList(); + // Import boat mesh URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl"); StlMeshImporter importer = new StlMeshImporter(); importer.read(asset); + // Configure camera angles and control view3D = new View3D(); view3D.setDistance(1050); view3D.setYaw(0); view3D.setPitch(60); + view3D.enableTracking(); + canvasBase.add(view3D, 0, 0); + // Set up projection from GPS to view RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource(); final GPSConverter gpsConverter = new GPSConverter(raceData, 450, 450); view3D.setItems(viewSubjects); - view3D.enableTracking(); - canvasBase.add(view3D, 0, 0); - + // Position and add each mark to view for(Mark mark: race.getVisualiserRaceState().getMarks()) { - Subject3D subject = new Subject3D(new Sphere(5)); + Subject3D subject = new Subject3D(new Sphere(2)); subject.setX(gpsConverter.convertGPS(mark.getPosition()).getX()); subject.setZ(gpsConverter.convertGPS(mark.getPosition()).getY()); viewSubjects.add(subject); } - + // Position and add each boat to view for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) { MeshView mesh = new MeshView(importer.getImport()); Subject3D subject = new Subject3D(mesh); viewSubjects.add(subject); + // Track this boat's movement with the new subject AnimationTimer trackBoat = new AnimationTimer() { @Override public void handle(long now) { @@ -217,12 +221,15 @@ public class RaceController extends Controller { }; trackBoat.start(); } + // Fix initial bird's-eye position view3D.updatePivot(new Translate(250, 0, 210)); + // Bind zooming to scrolling view3D.setOnScroll(e -> { view3D.updateDistance(e.getDeltaY()); }); + // Bind zooming to keypress (Z/X default) racePane.addEventFilter(KeyEvent.KEY_PRESSED, e -> { ControlKey key = keyFactory.getKey(e.getCode().toString()); if(key != null) { diff --git a/racevisionGame/src/main/java/visualiser/layout/View3D.java b/racevisionGame/src/main/java/visualiser/layout/View3D.java index db5ee85b..27fe6086 100644 --- a/racevisionGame/src/main/java/visualiser/layout/View3D.java +++ b/racevisionGame/src/main/java/visualiser/layout/View3D.java @@ -128,6 +128,11 @@ public class View3D extends Pane { return camera; } + /** + * Provide the list of subjects to be automatically added or removed from the view as the list + * changes. + * @param items list managed by client + */ public void setItems(ObservableList items) { this.items = items; this.items.addListener((ListChangeListener) c -> { @@ -146,6 +151,9 @@ public class View3D extends Pane { }); } + /** + * Intercept mouse clicks on subjects in view. The applied listener cannot be removed. + */ public void enableTracking() { scene.setOnMousePressed(e -> { PickResult result = e.getPickResult(); @@ -155,6 +163,9 @@ public class View3D extends Pane { }); } + /** + * Stop camera from following the last selected subject + */ private void untrackSubject() { if(target != null) { target.getPosition().xProperty().removeListener(pivotX); @@ -164,6 +175,10 @@ public class View3D extends Pane { } } + /** + * Set camera to follow the selected subject + * @param subject to track + */ private void trackSubject(Subject3D subject) { untrackSubject(); target = subject; @@ -188,6 +203,10 @@ public class View3D extends Pane { this.farClip = farClip; } + /** + * Sets the coordinates of the camera pivot once. + * @param pivot source of coordinates + */ public void updatePivot(Translate pivot) { this.pivot.setX(pivot.getX()); this.pivot.setY(pivot.getY()); diff --git a/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java b/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java index 49ef7bcb..22dd937f 100644 --- a/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java +++ b/racevisionGame/src/main/java/visualiser/utils/GPSConverter.java @@ -5,14 +5,15 @@ import shared.model.GPSCoordinate; import visualiser.model.GraphCoordinate; /** - * Created by fwy13 on 7/09/17. + * Converts GPS coordinates to view volume coordinates. Longitudes are equally spaced at all latitudes, + * which leads to inaccurate distance measurements close to the poles. This is acceptable as races are + * not likely to be set there. */ public class GPSConverter { private double longRight; private double longLeft; private double latBottom; private double latTop; - /** * Conversion factor from longitude to view units */ @@ -22,6 +23,12 @@ public class GPSConverter { */ private double latitudeFactor; + /** + * Set up projection with default view boundaries from RaceDataSource + * @param source for view boundaries + * @param longitudeFactor separation of a degree of longitude in view units + * @param latitudeFactor separation of a degree of latitude in view units + */ public GPSConverter(RaceDataSource source, double longitudeFactor, double latitudeFactor) { this.latTop = source.getMapTopLeft().getLatitude(); this.longLeft = source.getMapTopLeft().getLongitude();