Ported game rendering to 3d environment for boats and markers.

#implement
This commit is contained in:
Calum
2017-09-09 17:34:18 +12:00
parent 0e2946f20b
commit 06a4dde216
9 changed files with 613 additions and 321 deletions
+1 -2
View File
@@ -5,7 +5,6 @@ import javafx.application.Application;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
@@ -69,7 +68,7 @@ public class App extends Application {
public void start(Stage primaryStage) throws Exception { public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/views/StartScreenView.fxml")); Parent root = FXMLLoader.load(getClass().getResource("/views/StartScreenView.fxml"));
primaryStage.setTitle("Party Parrots at Sea"); primaryStage.setTitle("Party Parrots at Sea");
Scene scene = new Scene(root, 1530, 960, false, SceneAntialiasing.BALANCED); Scene scene = new Scene(root, 1530, 960);
scene.getStylesheets().add(getClass().getResource("/css/master.css").toString()); scene.getStylesheets().add(getClass().getResource("/css/master.css").toString());
primaryStage.setScene(scene); primaryStage.setScene(scene);
// primaryStage.setMaxWidth(1530); // primaryStage.setMaxWidth(1530);
+13 -12
View File
@@ -451,6 +451,19 @@ public class GameView extends Pane {
raceBorder.getPoints().setAll(boundaryPoints); raceBorder.getPoints().setAll(boundaryPoints);
} }
/**
* Rescales the race to the size of the window.
*
* @param limitingCoordinates the set of geo points that contains the extremities of the race.
*/
private void rescaleRace(List<GeoPoint> limitingCoordinates) {
//Check is called once to avoid unnecessarily change the course limits once the race is running
findMinMaxPoint(limitingCoordinates);
double minLonToMaxLon = scaleRaceExtremities();
calculateReferencePointLocation(minLonToMaxLon);
// drawGoogleMap();
}
/** /**
* Replaces all tokens in the course with those passed in * Replaces all tokens in the course with those passed in
* *
@@ -487,18 +500,6 @@ public class GameView extends Pane {
}); });
} }
} }
/**
* Rescales the race to the size of the window.
*
* @param limitingCoordinates the set of geo points that contains the extremities of the race.
*/
private void rescaleRace(List<GeoPoint> limitingCoordinates) {
//Check is called once to avoid unnecessarily change the course limits once the race is running
findMinMaxPoint(limitingCoordinates);
double minLonToMaxLon = scaleRaceExtremities();
calculateReferencePointLocation(minLonToMaxLon);
// drawGoogleMap();
}
private void setSelectedBoat(BoatObject bo, Boolean isSelected) { private void setSelectedBoat(BoatObject bo, Boolean isSelected) {
if (this.selectedBoat == bo && !isSelected) { if (this.selectedBoat == bo && !isSelected) {
+433 -52
View File
@@ -1,13 +1,40 @@
package seng302.visualiser; package seng302.visualiser;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.geometry.Point2D;
import javafx.geometry.Point3D; import javafx.geometry.Point3D;
import javafx.scene.*; import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.ParallelCamera;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial; import javafx.scene.paint.Paint;
import javafx.scene.shape.Sphere; import javafx.scene.shape.Polygon;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate; import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate; import javafx.scene.transform.Translate;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; import seng302.gameServer.messages.RoundingSide;
import seng302.model.ClientYacht;
import seng302.model.GeoPoint;
import seng302.model.Limit;
import seng302.model.mark.CompoundMark;
import seng302.model.mark.Corner;
import seng302.model.mark.Mark;
import seng302.utilities.GeoUtility;
import seng302.visualiser.fxObjects.assets_2D.AnnotationBox;
import seng302.visualiser.fxObjects.assets_2D.BoatObject;
import seng302.visualiser.fxObjects.assets_2D.CourseBoundary;
import seng302.visualiser.fxObjects.assets_2D.Gate;
import seng302.visualiser.fxObjects.assets_3D.ModelFactory; import seng302.visualiser.fxObjects.assets_3D.ModelFactory;
import seng302.visualiser.fxObjects.assets_3D.ModelType; import seng302.visualiser.fxObjects.assets_3D.ModelType;
@@ -22,72 +49,373 @@ public class GameView3D {
Group root3D; Group root3D;
SubScene view; SubScene view;
PerspectiveCamera camera; ParallelCamera camera;
Group gameObjects; Group gameObjects;
private double bufferSize = 0;
private double canvasWidth = 200;
private double canvasHeight = 200;
private boolean horizontalInversion = false;
private double distanceScaleFactor;
private ScaleDirection scaleDirection;
private GeoPoint minLatPoint, minLonPoint, maxLatPoint, maxLonPoint;
private double referencePointX, referencePointY;
private double metersPerPixelX, metersPerPixelY;
final double SCALE_DELTA = 1.1;
private Text fpsDisplay = new Text();
private Polygon raceBorder = new CourseBoundary();
/* Note that if either of these is null then values for it have not been added and the other
should be used as the limits of the map. */
private List<Limit> borderPoints;
private Map<Mark, Group> markerObjects;
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
private Map<ClientYacht, AnnotationBox> annotations = new HashMap<>();
private BoatObject selectedBoat = null;
private Group annotationsGroup = new Group();
private Group wakesGroup = new Group();
private Group boatObjectGroup = new Group();
private Group trails = new Group();
private Group markers = new Group();
private Group tokens = new Group();
private List<CompoundMark> course = new ArrayList<>();
private List<Node> mapTokens;
private ImageView mapImage = new ImageView();
//FRAME RATE
private AnimationTimer timer;
private int NUM_SAMPLES = 10;
private final long[] frameTimes = new long[NUM_SAMPLES];
private Double frameRate = 60.0;
private int frameTimeIndex = 0;
private boolean arrayFilled = false;
private ClientYacht playerYacht;
private double windDir = 0.0;
double scaleFactor = 1;
private enum ScaleDirection {
HORIZONTAL,
VERTICAL
}
public GameView3D () { public GameView3D () {
camera = new PerspectiveCamera(true); // camera = new PerspectiveCamera(true);
camera = new ParallelCamera();
camera.getTransforms().addAll( camera.getTransforms().addAll(
new Translate(0,0, -DEFAULT_CAMERA_DEPTH) new Translate(0,0, -DEFAULT_CAMERA_DEPTH)
); );
camera.setFarClip(Double.MAX_VALUE); camera.setFarClip(Double.MAX_VALUE);
camera.setNearClip(0.1); camera.setNearClip(0.1);
camera.setFieldOfView(FOV); // camera.setFieldOfView(FOV);
gameObjects = new Group(); gameObjects = new Group();
gameObjects.getTransforms().add(new Scale(4,4,4));
root3D = new Group(camera, gameObjects); root3D = new Group(camera, gameObjects);
view = new SubScene( view = new SubScene(
root3D, 1000, 1000, true, SceneAntialiasing.BALANCED root3D, 1000, 1000, true, SceneAntialiasing.BALANCED
); );
view.setCamera(camera); view.setCamera(camera);
view.setFill(Color.SKYBLUE); view.setFill(Color.SKYBLUE);
Sphere s = new Sphere(1); camera.getTransforms().add(new Rotate(30, new Point3D(1,0,0)));
s.setMaterial(new PhongMaterial(Color.RED)); camera.setLayoutX(camera.getLayoutX()-400);
Sphere left = new Sphere(1); camera.setLayoutY(camera.getLayoutX()-600);
left.setMaterial(new PhongMaterial(Color.LEMONCHIFFON)); // gameObjects.getChildren().addAll(raceBorder, markers, tokens);
left.getTransforms().add(new Translate(-Math.tan(Math.toRadians(FOV / 2)) * DEFAULT_CAMERA_DEPTH, 0, 0));
Sphere right = new Sphere(1);
right.setMaterial(new PhongMaterial(Color.ROSYBROWN));
right.getTransforms().add(new Translate(Math.tan(Math.toRadians(FOV / 2)) * DEFAULT_CAMERA_DEPTH, 0, 0));
Sphere top = new Sphere(1);
top.setMaterial(new PhongMaterial(Color.TEAL));
top.getTransforms().add(new Translate(0,-Math.tan(Math.toRadians(FOV / 2)) * DEFAULT_CAMERA_DEPTH, 0));
Sphere bottom = new Sphere(1);
bottom.setMaterial(new PhongMaterial(Color.BLANCHEDALMOND));
bottom.getTransforms().add(new Translate(0, Math.tan(Math.toRadians(FOV / 2)) * DEFAULT_CAMERA_DEPTH, 0));
Node boat = ModelFactory.boatGameView(BoatMeshType.DINGHY, Color.BLUE).getAssets();
Node boat2 = ModelFactory.boatGameView(BoatMeshType.DINGHY, Color.BROWN).getAssets();
boat2.getTransforms().add(new Translate(0,20, 0));
Node boat3 = ModelFactory.boatGameView(BoatMeshType.DINGHY, Color.RED).getAssets();
boat3.getTransforms().add(new Translate(0,-20, 0));
Node sMarker = ModelFactory.importModel(ModelType.START_MARKER).getAssets();
sMarker.getTransforms().add(0, new Translate(30, 30, 0));
Node fMarker = ModelFactory.importModel(ModelType.FINISH_MARKER).getAssets();
fMarker.getTransforms().add(0, new Translate(30, -30, 0));
Node marker = ModelFactory.importModel(ModelType.PLAIN_MARKER).getAssets();
marker.getTransforms().add(0, new Translate(30, 0, 0));
Node coin = ModelFactory.importModel(ModelType.VELOCITY_COIN).getAssets();
coin.setTranslateX(coin.getTranslateX() - 30);
gameObjects.getChildren().addAll( gameObjects.getChildren().addAll(
ModelFactory.importModel(ModelType.OCEAN).getAssets(), ModelFactory.importModel(ModelType.OCEAN).getAssets(), markers
s, left, right, top, bottom,
boat, boat2, boat3,
sMarker, fMarker, marker,
coin
); );
// Sphere s = new Sphere(1);
// s.setMaterial(new PhongMaterial(Color.RED));
// Sphere left = new Sphere(1);
// left.setMaterial(new PhongMaterial(Color.LEMONCHIFFON));
// left.getTransforms().add(new Translate(-Math.tan(Math.toRadians(FOV / 2)) * DEFAULT_CAMERA_DEPTH, 0, 0));
// Sphere right = new Sphere(1);
// right.setMaterial(new PhongMaterial(Color.ROSYBROWN));
// right.getTransforms().add(new Translate(Math.tan(Math.toRadians(FOV / 2)) * DEFAULT_CAMERA_DEPTH, 0, 0));
// Sphere top = new Sphere(1);
// top.setMaterial(new PhongMaterial(Color.TEAL));
// top.getTransforms().add(new Translate(0,-Math.tan(Math.toRadians(FOV / 2)) * DEFAULT_CAMERA_DEPTH, 0));
// Sphere bottom = new Sphere(1);
// bottom.setMaterial(new PhongMaterial(Color.BLANCHEDALMOND));
// bottom.getTransforms().add(new Translate(0, Math.tan(Math.toRadians(FOV / 2)) * DEFAULT_CAMERA_DEPTH, 0));
//
// Node boat = ModelFactory.boatGameView(BoatMeshType.DINGHY, Color.BLUE).getAssets();
// Node boat2 = ModelFactory.boatGameView(BoatMeshType.DINGHY, Color.BROWN).getAssets();
// boat2.getTransforms().add(new Translate(0,20, 0));
// Node boat3 = ModelFactory.boatGameView(BoatMeshType.DINGHY, Color.RED).getAssets();
// boat3.getTransforms().add(new Translate(0,-20, 0));
//
// Node sMarker = ModelFactory.importModel(ModelType.START_MARKER).getAssets();
// sMarker.getTransforms().add(0, new Translate(30, 30, 0));
//
// Node fMarker = ModelFactory.importModel(ModelType.FINISH_MARKER).getAssets();
// fMarker.getTransforms().add(0, new Translate(30, -30, 0));
//
// Node marker = ModelFactory.importModel(ModelType.PLAIN_MARKER).getAssets();
// marker.getTransforms().add(0, new Translate(30, 0, 0));
//
// Node coin = ModelFactory.importModel(ModelType.VELOCITY_COIN).getAssets();
// coin.setTranslateX(coin.getTranslateX() - 30);
//
// gameObjects.getChildren().addAll(
// ModelFactory.importModel(ModelType.OCEAN).getAssets(),
// s, left, right, top, bottom,
// boat, boat2, boat3,
// sMarker, fMarker, marker,
// coin
// );
view.sceneProperty().addListener((obs, old, scene) -> { view.sceneProperty().addListener((obs, old, scene) -> {
if (scene!=null) if (scene != null) {
enableCameraMovement(scene); scene.addEventHandler(KeyEvent.KEY_PRESSED, this::cameraMovement);
}
}); });
} }
public void enableCameraMovement(Scene s) { public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) {
s.setOnKeyPressed(event -> { markerObjects = new HashMap<>();
for (Corner corner : sequence) { //Makes course out of all compound marks.
for (CompoundMark compoundMark : newCourse) {
if (corner.getCompoundMarkID() == compoundMark.getId()) {
course.add(compoundMark);
}
}
}
// TODO: 16/08/17 Updating mark roundings here. It should not happen here. Nor should it be done this way.
for (Corner corner : sequence){
CompoundMark compoundMark = course.get(corner.getSeqID() - 1);
compoundMark.setRoundingSide(
RoundingSide.getRoundingSide(corner.getRounding())
);
}
final List<Gate> gates = new ArrayList<>();
Paint colour = Color.BLACK;
//Creates new markers
for (CompoundMark cMark : newCourse) {
//Set start and end colour
// if (cMark.getId() == sequence.get(0).getCompoundMarkID()) {
// colour = Color.GREEN;
// } else if (cMark.getId() == sequence.get(sequence.size() - 1).getCompoundMarkID()) {
// colour = Color.RED;
// }
//Create mark dots
for (Mark mark : cMark.getMarks()) {
makeAndBindMarker(mark);
}
// //Create gate line
// if (cMark.isGate()) {
// for (int i = 1; i < cMark.getMarks().size(); i++) {
// gates.add(
// makeAndBindGate(
// markerObjects.get(cMark.getSubMark(i)),
// markerObjects.get(cMark.getSubMark(i + 1)),
// colour
// )
// );
// }
// }
// colour = Color.BLACK;
}
//Scale race to markers if there is no border.
if (borderPoints == null) {
rescaleRace(new ArrayList<>(markerObjects.keySet()));
}
//Move the Markers to initial position.
markerObjects.forEach(((mark, marker) -> {
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
System.out.println(mark.toString() + " " + p2d.toString());
marker.setLayoutX(p2d.getX());
marker.setLayoutY(p2d.getY());
}));
Platform.runLater(() -> {
markers.getChildren().clear();
markers.getChildren().addAll(gates);
markers.getChildren().addAll(markerObjects.values());
});
}
/**
* Creates a new Marker and binds it's position to the given Mark.
*
* @param observableMark The mark to bind the marker to.
*/
private void makeAndBindMarker(Mark observableMark) {
Group marker = ModelFactory.importModel(ModelType.PLAIN_MARKER).getAssets();
markerObjects.put(observableMark, marker);
observableMark.addPositionListener((mark, lat, lon) -> {
Point2D p2d = findScaledXY(lat, lon);
markerObjects.get(mark).setLayoutX(p2d.getX());
markerObjects.get(mark).setLayoutY(p2d.getY());
});
}
/**
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point with
* the leftmost point, rightmost point, southern most point and northern most point
* respectively.
*/
private void findMinMaxPoint(List<GeoPoint> points) {
List<GeoPoint> sortedPoints = new ArrayList<>(points);
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat));
minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
GeoPoint maxLat = sortedPoints.get(sortedPoints.size() - 1);
maxLatPoint = new GeoPoint(maxLat.getLat(), maxLat.getLng());
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLng));
minLonPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
GeoPoint maxLon = sortedPoints.get(sortedPoints.size() - 1);
maxLonPoint = new GeoPoint(maxLon.getLat(), maxLon.getLng());
if (maxLonPoint.getLng() - minLonPoint.getLng() > 180) {
horizontalInversion = true;
}
}
/**
* Calculates the location of a reference point, this is always the point with minimum latitude,
* in relation to the canvas.
*
* @param minLonToMaxLon The horizontal distance between the point of minimum longitude to
* maximum longitude.
*/
private void calculateReferencePointLocation(double minLonToMaxLon) {
GeoPoint referencePoint = minLatPoint;
double referenceAngle;
if (scaleDirection == ScaleDirection.HORIZONTAL) {
referenceAngle = Math.abs(
GeoUtility.getBearingRad(referencePoint, minLonPoint)
);
referencePointX =
-100 + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
.getDistance(referencePoint, minLonPoint);
referenceAngle = Math.abs(GeoUtility.getDistance(referencePoint, maxLatPoint));
referencePointY = -100 + canvasHeight - (bufferSize + bufferSize);
referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
.getDistance(referencePoint, maxLatPoint);
referencePointY = referencePointY / 2;
referencePointY += bufferSize;
referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
.getDistance(referencePoint, maxLatPoint);
} else {
referencePointY = -100 + canvasHeight - bufferSize;
referenceAngle = Math.abs(
Math.toRadians(
GeoUtility.getDistance(referencePoint, minLonPoint)
)
);
referencePointX = -100 + bufferSize;
referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
.getDistance(referencePoint, minLonPoint);
referencePointX +=
((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor))
/ 2;
}
if (horizontalInversion) {
referencePointX = -100 + canvasWidth - bufferSize - (referencePointX - bufferSize);
}
}
/**
* Finds the scale factor necessary to fit all race markers within the onscreen map and assigns
* it to distanceScaleFactor Returns the max horizontal distance of the map.
*/
private double scaleRaceExtremities() {
double vertAngle = Math.abs(
GeoUtility.getBearingRad(minLatPoint, maxLatPoint)
);
double vertDistance =
Math.cos(vertAngle) * GeoUtility.getDistance(minLatPoint, maxLatPoint);
double horiAngle = Math.abs(
GeoUtility.getBearingRad(minLonPoint, maxLonPoint)
);
if (horiAngle <= (Math.PI / 2)) {
horiAngle = (Math.PI / 2) - horiAngle;
} else {
horiAngle = horiAngle - (Math.PI / 2);
}
double horiDistance =
Math.cos(horiAngle) * GeoUtility.getDistance(minLonPoint, maxLonPoint);
double vertScale = (canvasHeight - (bufferSize + bufferSize)) / vertDistance;
if ((horiDistance * vertScale) > (canvasWidth - (bufferSize + bufferSize))) {
distanceScaleFactor = (canvasWidth - (bufferSize + bufferSize)) / horiDistance;
scaleDirection = ScaleDirection.HORIZONTAL;
} else {
distanceScaleFactor = vertScale;
scaleDirection = ScaleDirection.VERTICAL;
}
return horiDistance;
}
private Point2D findScaledXY(GeoPoint unscaled) {
return findScaledXY(unscaled.getLat(), unscaled.getLng());
}
private Point2D findScaledXY(double unscaledLat, double unscaledLon) {
double distanceFromReference;
double angleFromReference;
double xAxisLocation = referencePointX;
double yAxisLocation = referencePointY;
angleFromReference = GeoUtility.getBearingRad(
minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
);
distanceFromReference = GeoUtility.getDistance(
minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
);
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
xAxisLocation += Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
yAxisLocation -= Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
} else if (angleFromReference >= 0) {
angleFromReference = angleFromReference - Math.PI / 2;
xAxisLocation += Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
yAxisLocation += Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
angleFromReference = Math.abs(angleFromReference);
xAxisLocation -= Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
yAxisLocation -= Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
} else {
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
xAxisLocation -= Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
yAxisLocation += Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
}
if (horizontalInversion) {
xAxisLocation = canvasWidth - bufferSize - (xAxisLocation - bufferSize);
}
return new Point2D(xAxisLocation, yAxisLocation);
}
public void cameraMovement(KeyEvent event) {
switch (event.getCode()) { switch (event.getCode()) {
case UP: case UP:
camera.getTransforms().addAll(new Rotate(0.5, new Point3D(1,0,0))); camera.getTransforms().addAll(new Rotate(0.5, new Point3D(1,0,0)));
@@ -101,11 +429,11 @@ public class GameView3D {
case RIGHT: case RIGHT:
camera.getTransforms().addAll(new Rotate(0.5, new Point3D(0,1,0))); camera.getTransforms().addAll(new Rotate(0.5, new Point3D(0,1,0)));
break; break;
case SPACE: case X:
camera.getTransforms().addAll(new Translate(0, 0, 0.75)); camera.getTransforms().addAll(new Translate(0, 0, 1.5));
break; break;
case Z: case Z:
camera.getTransforms().addAll(new Translate(0, 0, -0.75)); camera.getTransforms().addAll(new Translate(0, 0, -1.5));
break; break;
case W: case W:
camera.getTransforms().addAll(new Translate(0, 1, 0)); camera.getTransforms().addAll(new Translate(0, 1, 0));
@@ -120,6 +448,59 @@ public class GameView3D {
camera.getTransforms().addAll(new Translate(1, 0, 0)); camera.getTransforms().addAll(new Translate(1, 0, 0));
break; break;
} }
}
/**
* Rescales the race to the size of the window.
*
* @param limitingCoordinates the set of geo points that contains the extremities of the race.
*/
private void rescaleRace(List<GeoPoint> limitingCoordinates) {
//Check is called once to avoid unnecessarily change the course limits once the race is running
findMinMaxPoint(limitingCoordinates);
double minLonToMaxLon = scaleRaceExtremities();
calculateReferencePointLocation(minLonToMaxLon);
// drawGoogleMap();
}
/**
* Draws all the boats.
* @param yachts The yachts to set in the race
*/
public void setBoats(List<ClientYacht> yachts) {
BoatObject newBoat;
final List<Group> wakes = new ArrayList<>();
for (ClientYacht clientYacht : yachts) {
Color colour = clientYacht.getColour();
newBoat = new BoatObject();
// newBoat.addSelectedBoatListener(this::setSelectedBoat);
newBoat.setFill(colour);
boatObjects.put(clientYacht, newBoat);
// createAndBindAnnotationBox(clientYacht, colour);
// wakesGroup.getChildren().add(newBoat.getWake());
// wakes.add(newBoat.getWake());
boatObjectGroup.getChildren().add(newBoat);
// trails.getChildren().add(newBoat.getTrail());
clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
BoatObject bo = boatObjects.get(boat);
Point2D p2d = findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
// annotations.get(boat).setLocation(p2d.getX(), p2d.getY());
bo.setTrajectory(
heading,
velocity,
metersPerPixelX,
metersPerPixelY);
});
}
// annotationsGroup.getChildren().addAll(annotations.values());
Platform.runLater(() -> {
gameObjects.getChildren().addAll(boatObjectGroup);
// gameObjects.addAll(trails);
// gameObjects.addAll(wakes);
// gameObjects.addAll(annotationsGroup);
// gameObjects.addAll(boatObjectGroup);
}); });
} }
@@ -42,8 +42,7 @@ import seng302.model.RaceState;
import seng302.model.mark.CompoundMark; import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.visualiser.GameView; import seng302.visualiser.GameView3D;
import seng302.visualiser.controllers.annotations.Annotation;
import seng302.visualiser.controllers.annotations.ImportantAnnotationController; import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate; import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
import seng302.visualiser.controllers.annotations.ImportantAnnotationsState; import seng302.visualiser.controllers.annotations.ImportantAnnotationsState;
@@ -86,7 +85,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private Map<Integer, ClientYacht> participants; private Map<Integer, ClientYacht> participants;
private Map<Integer, CompoundMark> markers; private Map<Integer, CompoundMark> markers;
private RaceXMLData courseData; private RaceXMLData courseData;
private GameView gameView; private GameView3D gameView;
private RaceState raceState; private RaceState raceState;
private Timeline timerTimeline; private Timeline timerTimeline;
@@ -140,32 +139,32 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}); });
updateOrder(raceState.getPlayerPositions()); updateOrder(raceState.getPlayerPositions());
gameView = new GameView(); gameView = new GameView3D();
gameView.setFrameRateFXText(fpsDisplay); // gameView.setFrameRateFXText(fpsDisplay);
Platform.runLater(() -> contentAnchorPane.getChildren().add(0, gameView)); Platform.runLater(() -> contentAnchorPane.getChildren().add(0, gameView.getAssets()));
gameView.setBoats(new ArrayList<>(participants.values())); gameView.setBoats(new ArrayList<>(participants.values()));
gameView.updateBorder(raceData.getCourseLimit()); // gameView.updateBorder(raceData.getCourseLimit());
gameView.updateTokens(raceData.getTokens()); // gameView.updateTokens(raceData.getTokens());
gameView.updateCourse( gameView.updateCourse(
new ArrayList<>(raceData.getCompoundMarks().values()), raceData.getMarkSequence() new ArrayList<>(raceData.getCompoundMarks().values()), raceData.getMarkSequence()
); );
gameView.enableZoom(); // gameView.enableZoom();
gameView.setBoatAsPlayer(player); // gameView.setBoatAsPlayer(player);
gameView.startRace(); // gameView.startRace();
raceState.addCollisionListener(gameView::drawCollision); // raceState.addCollisionListener(gameView::drawCollision);
raceState.windDirectionProperty().addListener((obs, oldDirection, newDirection) -> { // raceState.windDirectionProperty().addListener((obs, oldDirection, newDirection) -> {
gameView.setWindDir(newDirection.doubleValue()); // gameView.setWindDir(newDirection.doubleValue());
Platform.runLater(() -> updateWindDirection(newDirection.doubleValue())); // Platform.runLater(() -> updateWindDirection(newDirection.doubleValue()));
}); // });
raceState.windSpeedProperty().addListener((obs, oldSpeed, newSpeed) -> // raceState.windSpeedProperty().addListener((obs, oldSpeed, newSpeed) ->
Platform.runLater(() -> updateWindSpeed(newSpeed.doubleValue())) // Platform.runLater(() -> updateWindSpeed(newSpeed.doubleValue()))
); // );
Platform.runLater(() -> { // Platform.runLater(() -> {
updateWindDirection(raceState.windDirectionProperty().doubleValue()); // updateWindDirection(raceState.windDirectionProperty().doubleValue());
updateWindSpeed(raceState.getWindSpeed()); // updateWindSpeed(raceState.getWindSpeed());
}); // });
gameView.setWindDir(raceState.windDirectionProperty().doubleValue()); // gameView.setWindDir(raceState.windDirectionProperty().doubleValue());
} }
/** /**
@@ -207,9 +206,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
} }
private void initialiseFPSCheckBox() { private void initialiseFPSCheckBox() {
toggleFps.selectedProperty().addListener((obs, oldVal, newVal) -> // toggleFps.selectedProperty().addListener((obs, oldVal, newVal) ->
gameView.setFPSVisibility(toggleFps.isSelected()) // gameView.setFPSVisibility(toggleFps.isSelected())
); // );
} }
private void initialiseAnnotationSlider() { private void initialiseAnnotationSlider() {
@@ -550,7 +549,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
//Null check is if the listener is fired but nothing selected //Null check is if the listener is fired but nothing selected
yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> { yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
if (selectedBoat != null) { if (selectedBoat != null) {
gameView.selectBoat(selectedBoat); // gameView.selectBoat(selectedBoat);
} }
}); });
} }
@@ -582,31 +581,31 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
} }
private void setAnnotations(Integer annotationLevel) { private void setAnnotations(Integer annotationLevel) {
switch (annotationLevel) { // switch (annotationLevel) {
// No Annotations // // No Annotations
case 0: // case 0:
gameView.setAnnotationVisibilities( // gameView.setAnnotationVisibilities(
false, false, false, false, false, false // false, false, false, false, false, false
); // );
break; // break;
// Important Annotations // // Important Annotations
case 1: // case 1:
gameView.setAnnotationVisibilities( // gameView.setAnnotationVisibilities(
importantAnnotations.getAnnotationState(Annotation.NAME), // importantAnnotations.getAnnotationState(Annotation.NAME),
importantAnnotations.getAnnotationState(Annotation.SPEED), // importantAnnotations.getAnnotationState(Annotation.SPEED),
importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK), // importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK),
importantAnnotations.getAnnotationState(Annotation.LEGTIME), // importantAnnotations.getAnnotationState(Annotation.LEGTIME),
importantAnnotations.getAnnotationState(Annotation.TRACK), // importantAnnotations.getAnnotationState(Annotation.TRACK),
importantAnnotations.getAnnotationState(Annotation.WAKE) // importantAnnotations.getAnnotationState(Annotation.WAKE)
); // );
break; // break;
// All Annotations // // All Annotations
case 2: // case 2:
gameView.setAnnotationVisibilities( // gameView.setAnnotationVisibilities(
true, true, true, true, true, true // true, true, true, true, true, true
); // );
break; // break;
} // }
} }
@@ -630,7 +629,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
} }
public void updateRaceData (RaceXMLData raceData) { public void updateRaceData (RaceXMLData raceData) {
gameView.updateBorder(raceData.getCourseLimit()); // gameView.updateBorder(raceData.getCourseLimit());
gameView.updateTokens(raceData.getTokens()); // gameView.updateTokens(raceData.getTokens());
} }
} }
@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
import javafx.geometry.Point3D;
import javafx.scene.AmbientLight; import javafx.scene.AmbientLight;
import javafx.scene.Group; import javafx.scene.Group;
import javafx.scene.Node; import javafx.scene.Node;
@@ -103,9 +102,9 @@ public class BoatObject extends Group {
private void initChildren(double... points) { private void initChildren(double... points) {
boatPoly = makeBoatPolygon(); boatPoly = makeBoatPolygon();
boatPoly.getAssets().getTransforms().addAll( boatPoly.getAssets().getTransforms().addAll(
new Rotate(-40, new Point3D(1,0,0)), // new Rotate(-40, new Point3D(1,0,0)),
rotation, rotation
new Rotate(-90, new Point3D(0,0,1)) // new Rotate(-90, new Point3D(0,0,1))
); );
boatPoly.getAssets().getTransforms().add(new Scale(5, 5, 5)); boatPoly.getAssets().getTransforms().add(new Scale(5, 5, 5));
// boatPoly.setDrawMode(DrawMode.FILL); // boatPoly.setDrawMode(DrawMode.FILL);
@@ -219,21 +218,21 @@ public class BoatObject extends Group {
rotateTo(rotation, sailIn, windDir); rotateTo(rotation, sailIn, windDir);
boatPoly.getAssets().setLayoutX(x); boatPoly.getAssets().setLayoutX(x);
boatPoly.getAssets().setLayoutY(y); boatPoly.getAssets().setLayoutY(y);
if (sailIn) { // if (sailIn) {
// sail.getPoints().clear(); //// sail.getPoints().clear();
// sail.getPoints().addAll(0.0, 0.0, 4.0, 1.5, 8.0, 3.0, 12.0, 3.5, 16.0, 3.0, 20.0, 1.5, 24.0, 0.0); //// sail.getPoints().addAll(0.0, 0.0, 4.0, 1.5, 8.0, 3.0, 12.0, 3.5, 16.0, 3.0, 20.0, 1.5, 24.0, 0.0);
// sail.getPoints().addAll(0.0, 0.0, 24.0, 0.0); //// sail.getPoints().addAll(0.0, 0.0, 24.0, 0.0);
sail.setLayoutX(x); // sail.setLayoutX(x);
sail.setLayoutY(y); // sail.setLayoutY(y);
} else { // } else {
animateSail(); //// animateSail();
sail.setLayoutX(x); // sail.setLayoutX(x);
sail.setLayoutY(y); // sail.setLayoutY(y);
} // }
wake.setLayoutX(x); // wake.setLayoutX(x);
wake.setLayoutY(y); // wake.setLayoutY(y);
}); });
wake.setRotation(rotation, velocity); // wake.setRotation(rotation, velocity);
// rotateTo(rotation); // rotateTo(rotation);
// boatPoly.setLayoutX(x); // boatPoly.setLayoutX(x);
// boatPoly.setLayoutY(y); // boatPoly.setLayoutY(y);
@@ -263,7 +262,7 @@ public class BoatObject extends Group {
private void rotateTo(double heading, boolean sailsIn, double windDir) { private void rotateTo(double heading, boolean sailsIn, double windDir) {
rotation.setAngle(heading); rotation.setAngle(heading);
if (sailsIn) { if (!sailsIn) {
boatPoly.showSail(); boatPoly.showSail();
Double sailWindOffset = 30.0; Double sailWindOffset = 30.0;
Double upwindAngleLimit = 15.0; Double upwindAngleLimit = 15.0;
@@ -8,6 +8,7 @@ import javafx.scene.AmbientLight;
import javafx.scene.Group; import javafx.scene.Group;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial; import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Circle;
import javafx.scene.shape.MeshView; import javafx.scene.shape.MeshView;
import javafx.scene.transform.Rotate; import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale; import javafx.scene.transform.Scale;
@@ -53,7 +54,8 @@ public class ModelFactory {
public static BoatModel boatGameView(BoatMeshType boatType, Color primaryColour) { public static BoatModel boatGameView(BoatMeshType boatType, Color primaryColour) {
Group boatAssets = getUnmodifiedBoatModel(boatType, primaryColour); Group boatAssets = getUnmodifiedBoatModel(boatType, primaryColour);
boatAssets.getTransforms().setAll( boatAssets.getTransforms().setAll(
new Scale(0.5, 0.5, 0.5) new Rotate(-90, new Point3D(0,0,1)),
new Scale(0.2, 0.2, 0.2)
); );
return new BoatModel(boatAssets, null, boatType); return new BoatModel(boatAssets, null, boatType);
} }
@@ -77,9 +79,14 @@ public class ModelFactory {
} }
public static Model importModel(ModelType tokenType) { public static Model importModel(ModelType tokenType) {
Group assets;
if (tokenType.filename == null) {
assets = new Group();
} else {
ColModelImporter importer = new ColModelImporter(); ColModelImporter importer = new ColModelImporter();
importer.read(ModelFactory.class.getResource("/meshes/" + tokenType.filename)); importer.read(ModelFactory.class.getResource("/meshes/" + tokenType.filename));
Group assets = new Group(importer.getImport()); assets = new Group(importer.getImport());
}
switch (tokenType) { switch (tokenType) {
case VELOCITY_COIN: case VELOCITY_COIN:
return makeCoinPickup(assets); return makeCoinPickup(assets);
@@ -122,9 +129,13 @@ public class ModelFactory {
return new Model(area, null); return new Model(area, null);
} }
private static Model makeOcean(Group plane) { private static Model makeOcean(Group group) {
plane.setScaleY(100); // group.setScaleY(Double.MAX_VALUE);
plane.setScaleX(100); // group.setScaleX(Double.MAX_VALUE);
return new Model(plane, null); // group.getTransforms().add(new Rotate(90, new Point3D(1, 0, 0)));
Circle ocean = new Circle(0,0,1000, Color.DEEPSKYBLUE);
ocean.setStroke(Color.TRANSPARENT);
group.getChildren().add(ocean);
return new Model(group, null);
} }
} }
@@ -2,7 +2,7 @@ package seng302.visualiser.fxObjects.assets_3D;
/** /**
* Enum for models. Values should be the name of the file and files should be .dae files with texture * Enum for models. Values should be the name of the file and files should be .dae files with texture
* information included. * information included. Can be null in which case assets are assumed to be empty.
*/ */
public enum ModelType { public enum ModelType {
@@ -11,7 +11,9 @@ public enum ModelType {
START_MARKER ("start_marker.dae"), START_MARKER ("start_marker.dae"),
PLAIN_MARKER ("plain_marker.dae"), PLAIN_MARKER ("plain_marker.dae"),
MARK_AREA ("mark_area.dae"), MARK_AREA ("mark_area.dae"),
OCEAN ("ocean.dae"); OCEAN (null),
BORDER_PYLON (null),
BORDER_BARRIER (null);
final String filename; final String filename;
File diff suppressed because one or more lines are too long
-100
View File
@@ -1,100 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
<asset>
<contributor>
<author>Blender User</author>
<authoring_tool>Blender 2.78.0 commit date:2016-09-26, commit time:12:42, hash:4bb1e22</authoring_tool>
</contributor>
<created>2017-09-09T12:27:53</created>
<modified>2017-09-09T12:27:53</modified>
<unit name="meter" meter="1"/>
<up_axis>Z_UP</up_axis>
</asset>
<library_images/>
<library_effects>
<effect id="Material_001-effect">
<profile_COMMON>
<technique sid="common">
<phong>
<emission>
<color sid="emission">0 0 0 1</color>
</emission>
<ambient>
<color sid="ambient">0 0 0 1</color>
</ambient>
<diffuse>
<color sid="diffuse">0.64 0.001709759 0 1</color>
</diffuse>
<specular>
<color sid="specular">0.5 0.5 0.5 1</color>
</specular>
<shininess>
<float sid="shininess">50</float>
</shininess>
<index_of_refraction>
<float sid="index_of_refraction">1</float>
</index_of_refraction>
</phong>
</technique>
</profile_COMMON>
</effect>
</library_effects>
<library_materials>
<material id="Material_001-material" name="Material_001">
<instance_effect url="#Material_001-effect"/>
</material>
</library_materials>
<library_geometries>
<geometry id="Plane-mesh" name="Plane">
<mesh>
<source id="Plane-mesh-positions">
<float_array id="Plane-mesh-positions-array" count="24">-1 -1 0 1 -1 0 -1 1 0 1 1 0 -1 -1 0.002058923 1 -1 0.002058923 -1 1 0.002058923 1 1 0.002058923</float_array>
<technique_common>
<accessor source="#Plane-mesh-positions-array" count="8" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<source id="Plane-mesh-normals">
<float_array id="Plane-mesh-normals-array" count="18">0 0 -1 0 0 1 0 1 0 0 -1 0 1 0 0 -1 0 0</float_array>
<technique_common>
<accessor source="#Plane-mesh-normals-array" count="6" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<vertices id="Plane-mesh-vertices">
<input semantic="POSITION" source="#Plane-mesh-positions"/>
</vertices>
<polylist material="Material_001-material" count="12">
<input semantic="VERTEX" source="#Plane-mesh-vertices" offset="0"/>
<input semantic="NORMAL" source="#Plane-mesh-normals" offset="1"/>
<vcount>3 3 3 3 3 3 3 3 3 3 3 3 </vcount>
<p>2 0 1 0 0 0 5 1 6 1 4 1 2 2 7 2 3 2 1 3 4 3 0 3 3 4 5 4 1 4 0 5 6 5 2 5 2 0 3 0 1 0 5 1 7 1 6 1 2 2 6 2 7 2 1 3 5 3 4 3 3 4 7 4 5 4 0 5 4 5 6 5</p>
</polylist>
</mesh>
</geometry>
</library_geometries>
<library_controllers/>
<library_visual_scenes>
<visual_scene id="Scene" name="Scene">
<node id="Plane" name="Plane" type="NODE">
<matrix sid="transform">8 0 0 0 0 8 0 0 0 0 8 0 0 0 0 1</matrix>
<instance_geometry url="#Plane-mesh" name="Plane">
<bind_material>
<technique_common>
<instance_material symbol="Material_001-material" target="#Material_001-material"/>
</technique_common>
</bind_material>
</instance_geometry>
</node>
</visual_scene>
</library_visual_scenes>
<scene>
<instance_visual_scene url="#Scene"/>
</scene>
</COLLADA>