Fixed merge-related bugs

main
Connor Taylor-Brown 8 years ago
commit 70bbc89239

2
.gitignore vendored

@ -183,3 +183,5 @@ local.properties
# IntelliJDEA ignore
*.iml
dedicatedServer/.idea/
.idea/copyright/
settings/keyBindings.xml

@ -18,7 +18,6 @@ import shared.exceptions.InvalidRegattaDataException;
import shared.exceptions.XMLReaderException;
import shared.model.Bearing;
import shared.model.Constants;
import shared.xml.XMLUtilities;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
@ -71,8 +70,6 @@ public class Event {
private Thread connectionThread;
private int mapIndex;
@ -82,32 +79,11 @@ public class Event {
* @param singlePlayer Whether or not to create a single player event.
* @throws EventConstructionException Thrown if we cannot create an Event for any reason.
*/
public Event(boolean singlePlayer, int mapIndex) throws EventConstructionException {
public Event(boolean singlePlayer) throws EventConstructionException {
// System.out.println(XMLUtilities.validateXML(this.getClass().getClassLoader().getResource("mock/mockXML/iMapLayout.xml").toString()
// , this.getClass().getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd")));
this.mapIndex = mapIndex;
String raceXMLFile;
String raceXMLFile = "mock/mockXML/raceThreePlayers.xml";
String boatsXMLFile = "mock/mockXML/boatTest.xml";
String regattaXMLFile = "mock/mockXML/regattaTest.xml";
switch (mapIndex){
case 0:raceXMLFile = "mock/mockXML/raceSixPlayers.xml";
break;
case 1:raceXMLFile = "mock/mockXML/oMapLayout.xml";
break;
case 2: raceXMLFile = "mock/mockXML/iMapLayout.xml";
break;
case 3: raceXMLFile = "mock/mockXML/mMapLayout.xml";
break;
case 4:
raceXMLFile = "mock/mockXML/raceTutorial.xml";
boatsXMLFile = "mock/mockXML/boatTutorial.xml";
regattaXMLFile = "mock/mockXML/regattaTutorial.xml";
break;
default: raceXMLFile = "mock/mockXML/raceSixPlayers.xml";
}
if (singlePlayer) {
raceXMLFile = "mock/mockXML/raceSinglePlayer.xml";
@ -119,9 +95,7 @@ public class Event {
//this.raceXML = RaceXMLCreator.alterRaceToWind(raceXMLFile, 90);
this.raceXML = XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8);
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8));
if(mapIndex==4){
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8), 1000, 5000);
}
this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8);
this.regattaXML = XMLReader.readXMLFileToString(regattaXMLFile, StandardCharsets.UTF_8);
@ -204,15 +178,9 @@ public class Event {
* @return String containing edited xml
*/
public static String setRaceXMLAtCurrentTimeToNow(String raceXML) {
return setRaceXMLAtCurrentTimeToNow(raceXML, Constants.RacePreStartTime, Constants.RacePreparatoryTime);
}
public static String setRaceXMLAtCurrentTimeToNow(String raceXML, long racePreStartTime, long racePreparatoryTime){
//The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute.
long millisecondsToAdd = racePreStartTime + racePreparatoryTime;
long millisecondsToAdd = Constants.RacePreStartTime + 1 * 60 * 1000;
long secondsToAdd = millisecondsToAdd / 1000;
//Scale the time using our time scalar.
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
@ -222,6 +190,7 @@ public class Event {
raceXML = raceXML.replace("RACE_CREATION_TIME", dateFormat.format(creationTime));
raceXML = raceXML.replace("RACE_START_TIME", dateFormat.format(creationTime.plusSeconds(secondsToAdd)));
return raceXML;
}

@ -42,12 +42,14 @@ public class SourceIdAllocator {
}
List<Integer> allocatedIDs = mockRace.getRaceDataSource().getParticipants();
System.out.println(allocatedIDs);
List<Integer> allIDs = new ArrayList<>(mockRace.getBoatDataSource().getBoats().keySet());
System.out.println(allIDs);
//Get list of unallocated ids.
List<Integer> unallocatedIDs = new ArrayList<>(allIDs);
unallocatedIDs.removeAll(allocatedIDs);
System.out.println(unallocatedIDs.isEmpty());
if (!unallocatedIDs.isEmpty()) {

@ -74,7 +74,7 @@ public class RaceXMLCreator {
* @throws SAXException error in schema file
* @throws ParserConfigurationException error in parsing the schema file
*/
public static String alterRaceToWind(String s, double degrees, boolean tutorial) throws XMLReaderException, InvalidRaceDataException, JAXBException, IOException, SAXException, ParserConfigurationException {
public static String alterRaceToWind(String s, double degrees) throws XMLReaderException, InvalidRaceDataException, JAXBException, IOException, SAXException, ParserConfigurationException {
RaceXMLReader reader = new RaceXMLReader(s, XMLFileType.ResourcePath);
XMLRace race = XMLUtilities.xmlToClass(
@ -82,11 +82,7 @@ public class RaceXMLCreator {
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
XMLRace.class);
if(tutorial){
setRaceXMLAtCurrentTimeToNow(race, 1000l, 5000l);
} else {
setRaceXMLAtCurrentTimeToNow(race);
}
setRaceXMLAtCurrentTimeToNow(race);
double raceOriginalBearing = getLineAngle(getLeewardGate(reader).getMark1Position(), getWindwardGate(reader).getMark1Position());
@ -97,7 +93,6 @@ public class RaceXMLCreator {
return XMLUtilities.classToXML(race);
}
/**
* Rotate the features in a race such as the boundary, and the marks.
* @param race the race to alter
@ -185,11 +180,14 @@ public class RaceXMLCreator {
}
/**
* Sets the xml description of the race to show the race was created now, and starts in 4 minutes
* @param raceXML The race.xml contents.
*/
public static void setRaceXMLAtCurrentTimeToNow(XMLRace raceXML) {
public static void setRaceXMLAtCurrentTimeToNow(XMLRace raceXML, long racePrestartTime, long racePreparatoryTime){
//The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute.
long millisecondsToAdd = racePrestartTime + racePreparatoryTime;
long millisecondsToAdd = Constants.RacePreStartTime + 1 * 60 * 1000;
long secondsToAdd = millisecondsToAdd / 1000;
//Scale the time using our time scalar.
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
@ -200,13 +198,4 @@ public class RaceXMLCreator {
raceXML.getRaceStartTime().setTime(dateFormat.format(creationTime.plusSeconds(secondsToAdd)));
}
/**
* Sets the xml description of the race to show the race was created now, and starts in 4 minutes
* @param raceXML The race.xml contents.
*/
public static void setRaceXMLAtCurrentTimeToNow(XMLRace raceXML) {
setRaceXMLAtCurrentTimeToNow(raceXML, Constants.RacePreStartTime, Constants.RacePreparatoryTime);
}
}

@ -411,14 +411,14 @@ public class Boat extends Collider {
@Override
public boolean rayCast(Boat boat) {
if(boat != this) {
return rayCast(boat, 100);
return rayCast(boat, 15);
} else return false;
}
@Override
public void onCollisionEnter(Boat collider, Collision e) {
if(e.getBearing().degrees() > 270 || e.getBearing().degrees() < 90) {
collider.bounce(100);
collider.bounce(15);
}
}
}

@ -39,14 +39,14 @@ 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 = 1000;
public static final long RacePreStartTime = 6 * 1000;
/**
* The race preparatory time, in milliseconds. 1 minute.
*/
// public static final long RacePreparatoryTime = 60 * 1000;
public static final long RacePreparatoryTime = 1 * 60 * 1000;
public static final long RacePreparatoryTime = 6 * 1000;

@ -29,11 +29,6 @@ public class Mark extends Collider{
*/
private GPSCoordinate position;
/**
* Repulsion radius of the mark
*/
private double repulsionRadius = 50;
/**
* Constructs a mark with a given source ID, name, and position.
* @param sourceID The source ID of the mark.
@ -97,11 +92,11 @@ public class Mark extends Collider{
@Override
public boolean rayCast(Boat boat) {
return rayCast(boat, repulsionRadius);
return rayCast(boat, 15);
}
@Override
public void onCollisionEnter(Boat collider, Collision e) {
collider.bounce(repulsionRadius);
collider.bounce(15);
}
}

@ -8,11 +8,13 @@ import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.scene.AmbientLight;
import javafx.scene.PointLight;
import javafx.scene.chart.LineChart;
import javafx.scene.control.*;
import javafx.scene.effect.Light;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
@ -32,8 +34,7 @@ import visualiser.app.App;
import visualiser.enums.TutorialState;
import visualiser.gameController.ControllerClient;
import visualiser.gameController.Keys.ControlKey;
import visualiser.layout.Subject3D;
import visualiser.layout.View3D;
import visualiser.layout.*;
import visualiser.model.*;
import visualiser.utils.GPSConverter;
@ -208,46 +209,85 @@ public class RaceController extends Controller {
private void initialiseView3D(VisualiserRaceEvent race) {
viewSubjects = FXCollections.observableArrayList();
AmbientLight ambientLight = new AmbientLight(new Color(1, 1, 1, 0.75));
ambientLight.setTranslateX(250);
ambientLight.setTranslateZ(210);
ambientLight.setLightOn(true);
PointLight pointLight = new PointLight();
ambientLight.setTranslateX(250);
ambientLight.setTranslateZ(210);
ambientLight.setLightOn(true);
// 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();
URL markerAsset = HostController.class.getClassLoader().getResource("assets/Bouy V1.1.stl");
StlMeshImporter importerMark = new StlMeshImporter();
importerMark.read(markerAsset);
URL alternateBoatAsset = HostController.class.getClassLoader().getResource("assets/V1.3 BurgerBoat.stl");
StlMeshImporter importerBurgerBoat = new StlMeshImporter();
importerBurgerBoat.read(alternateBoatAsset);
view3D = new View3D(false);
view3D.setItems(viewSubjects);
view3D.setDistance(1050);
view3D.setYaw(0);
view3D.setPitch(60);
view3D.setBirdsEye();
view3D.enableTracking();
//newPane.getChildren().add(view3D);
view3D.addAmbientLight(ambientLight);
view3D.addPointLight(pointLight);
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);
// Set up sea surface
SeaSurface sea = new SeaSurface(750, 200);
sea.setX(250);
sea.setZ(210);
viewSubjects.add(sea);
SkyBox skyBox = new SkyBox(750, 200, 250, 0, 210);
viewSubjects.addAll(skyBox.getSkyBoxPlanes());
Boundary3D boundary3D = new Boundary3D(visualiserRace.getVisualiserRaceState().getRaceDataSource().getBoundary(), gpsConverter);
for (Subject3D subject3D: boundary3D.getBoundaryNodes()){
viewSubjects.add(subject3D);
}
// Position and add each mark to view
for(Mark mark: race.getVisualiserRaceState().getMarks()) {
Subject3D subject = new Subject3D(new Sphere(2), mark.getSourceID());
subject.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
subject.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
MeshView mesh = new MeshView(importerMark.getImport());
Subject3D markModel = new Subject3D(mesh, mark.getSourceID());
markModel.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
markModel.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
viewSubjects.add(subject);
viewSubjects.add(markModel);
}
// Position and add each boat to view
for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
MeshView mesh = new MeshView(importer.getImport());
Subject3D subject = new Subject3D(mesh, boat.getSourceID());
viewSubjects.add(subject);
MeshView mesh;
if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) {
mesh = new MeshView(importer.getImport());
} else {
mesh = new MeshView(importerBurgerBoat.getImport());
}
Subject3D boatModel = new Subject3D(mesh, boat.getSourceID());
viewSubjects.add(boatModel);
// Track this boat's movement with the new subject
AnimationTimer trackBoat = new AnimationTimer() {
@Override
public void handle(long now) {
subject.setHeading(boat.getBearing().degrees());
subject.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
subject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
boatModel.setHeading(boat.getBearing().degrees());
boatModel.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
boatModel.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
}
};
trackBoat.start();

@ -0,0 +1,89 @@
package visualiser.layout;
import com.sun.corba.se.impl.orbutil.graph.Graph;
import javafx.scene.shape.Box;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Sphere;
import shared.model.GPSCoordinate;
import visualiser.model.GraphCoordinate;
import visualiser.utils.GPSConverter;
import java.util.ArrayList;
import java.util.List;
/**
* Class that creates a 3d boundary based on gps coordinates
*/
public class Boundary3D {
public static double thickness = 1;
private List<Subject3D> boundaryNodes = new ArrayList<>();
private List<Subject3D> boundaryConnectors = new ArrayList<>();
private GPSConverter gpsConverter;
public Boundary3D(List<GPSCoordinate> points, GPSConverter gpsConverter){
this.gpsConverter = gpsConverter;
setBoundaryNodes(points);
}
/**
* Splits up the list so that it generates a edge of the boundary
* @param points boundary gpscoordinate
*/
private void setBoundaryNodes(List<GPSCoordinate> points){
if (points.size() < 2){
return;
}
System.out.println(points.size());
for (int i = 0; i < points.size(); i++){
if (i + 1 != points.size()){
addBound(points.get(i), points.get(i + 1));
} else {
addBound(points.get(i), points.get(0));
}
}
}
/**
* Add a two point boundary this will create a sphere at coord1 and a line to coord 2
* this is to reduce double up (2 spheres in one point).
* @param coord1 point to make sphere and start the line.
* @param coord2 point to end the line.
*/
private void addBound(GPSCoordinate coord1, GPSCoordinate coord2){
GraphCoordinate graphCoord1 = gpsConverter.convertGPS(coord1);
GraphCoordinate graphCoord2 = gpsConverter.convertGPS(coord2);
GraphCoordinate avgCoord = new GraphCoordinate((graphCoord1.getX() + graphCoord2.getX()) / 2,
(graphCoord1.getY() + graphCoord2.getY()) / 2);
double a = (graphCoord1.getX() - graphCoord2.getX());
double b = (graphCoord1.getY() - graphCoord2.getY());
double c = Math.sqrt(a * a + b * b);
Subject3D bound1 = new Subject3D(new Sphere(thickness * 2));
bound1.setX(graphCoord1.getX());
bound1.setZ(graphCoord1.getY());
boundaryNodes.add(bound1);
Subject3D connector = new Subject3D(new Box(c, thickness, thickness));
connector.setX(avgCoord.getX());
connector.setZ(avgCoord.getY());
double angle = 90 + Math.toDegrees(GPSConverter.getAngle(graphCoord2, graphCoord1));
connector.setHeading(angle);
boundaryConnectors.add(connector);
}
/**
* get the 3d objects to draw
* @return 3d boundary to draw
*/
public List<Subject3D> getBoundaryNodes(){
//these two must be concatenated with nodes after connectors else the spheres will not overlap the lines
ArrayList<Subject3D> result = new ArrayList<>(boundaryConnectors);
result.addAll(boundaryNodes);
return result;
}
}

@ -0,0 +1,130 @@
package visualiser.layout;
import com.sun.javafx.geom.PickRay;
import com.sun.javafx.scene.input.PickResultChooser;
import com.sun.javafx.sg.prism.NGNode;
import javafx.scene.Node;
import javafx.scene.shape.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 3D plane
*/
public class Plane3D extends TriangleMesh{
/**
* Length is up down, and width is left right. Drawn on the x-y plane with z kept at 0.
* @param width width of the plane
* @param length length of the plane
* @param subdivisionsWidth number of divisions along the width of the plane
* @param subdivisionsLength number of division along the length of the plane
*/
public Plane3D(float width, float length, int subdivisionsWidth, int subdivisionsLength){
//add texture points and vertex points
float subWidth = width / (float) subdivisionsWidth;
float subLength = length / (float) subdivisionsLength;
ArrayList<Float> pointsList = new ArrayList<>();
ArrayList<Float> textureCoord = new ArrayList<>();
float startW = -width/2;
float startL = -length/2;
for (float l = 0; l <= length; l += subLength) {
for (float w = 0; w <= width; w += subWidth){
//add points
pointsList.add(w + startW);
pointsList.add(l + startL);
pointsList.add(0f);
//addTexture coords
textureCoord.add(1 - w/width);
textureCoord.add(1 - l/length);
}
}
this.getPoints().setAll(copyListToArray(pointsList));
this.getTexCoords().setAll(copyListToArray(textureCoord));
//connect points to make faces
ArrayList<Integer> faces = new ArrayList<>();
int listSize = pointsList.size()/3;
int divsInRow = subdivisionsWidth + 1;
for (int i = 0; i < listSize; i++){
int row = i/divsInRow;
if (row < 1){
continue;
}
boolean notFirstCol = (i) % divsInRow != 0;
boolean notLastCol = (i + 1) % divsInRow != 0;
if (notFirstCol){
faces.add(i);
faces.add(i);
// printPointAtIndex(i);
faces.add(i - divsInRow);
faces.add(i - divsInRow);
// printPointAtIndex(i - divsInRow);
faces.add(i - 1);
faces.add(i - 1);
// printPointAtIndex(i-1);
}
if (notLastCol) {
faces.add(i - divsInRow + 1);
faces.add(i - divsInRow + 1);
// printPointAtIndex(i - divsInRow + 1);
faces.add(i - divsInRow);
faces.add(i - divsInRow);
// printPointAtIndex(i - divsInRow);
faces.add(i);
faces.add(i);
// printPointAtIndex(i);
}
}
this.getFaces().setAll(copyListToIntArray(faces));
}
/**
* Testing function to see if the points are correct
* @param index index that the points correspond to (remember 3 is a point)
*/
private void printPointAtIndex(int index){
int i = index * 3;
float x = this.getPoints().get(i);
float y = this.getPoints().get(i + 1);
float z = this.getPoints().get(i + 2);
System.out.println(String.format("Point at %d is x:%f, y:%f, z:%f", index, x, y, z));
}
/**
* copies the list to a float array because java List.toArray isn't working
* @param list list to copy
* @return array
*/
private static float[] copyListToArray(List<Float> list){
float[] res = new float[list.size()];
for (int i = 0; i < list.size(); i++){
res[i] = list.get(i);
}
return res;
}
/**
* copies the list to an integer array because java List.toArray isn't working
* @param list list to copy
* @return array
*/
private static int[] copyListToIntArray(List<Integer> list){
int[] res = new int[list.size()];
for (int i = 0; i < list.size(); i++){
res[i] = list.get(i);
}
return res;
}
}

@ -0,0 +1,126 @@
package visualiser.layout;
import javafx.geometry.Point3D;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.Shape3D;
import javafx.scene.shape.TriangleMesh;
import visualiser.utils.PerlinNoiseGenerator;
/**
* Creates a SeaSurface
*/
public class SeaSurface extends Subject3D {
/**
* Sea Surface Constructor
* @param size size of the sea surface (has to be square for simplicity's sake)
* @param freq frequency the perlin noise is to be generated at
*/
public SeaSurface(int size, double freq){
super(createSurface(size, freq));
}
/**
* Creates the sea surface
*/
private static Shape3D createSurface(int size, double freq){
float[][] noiseArray = PerlinNoiseGenerator.createNoise(size, freq);
Image diffuseMap = createImage(noiseArray.length, noiseArray);
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(diffuseMap);
Plane3D seaPlane = new Plane3D(noiseArray.length, noiseArray.length, 10, 10);
MeshView seaSurface = new MeshView(seaPlane);
seaSurface.setMaterial(material);
seaSurface.setMouseTransparent(true);
seaSurface.toFront();
return seaSurface;
}
/**
* Create texture for uv mapping
* @param size size of the image to make
* @param noise array of noise
* @return image that is created
*/
private static Image createImage(double size, float[][] noise) {
int width = (int) size;
int height = (int) size;
WritableImage wr = new WritableImage(width, height);
PixelWriter pw = wr.getPixelWriter();
//interpolate colours based on noise
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
float value = noise[x][y];
double gray = normalizeValue(value, -.5, .5, 0., 1.);
gray = clamp(gray, 0, 1);
//values to interpolate on
Color brightBlue = new Color(0.06, 0.5, .78, 1);
Color lightBlue = new Color(0.15, 0.68, .88, 1);
Color lighterBlue = new Color(0.28, 0.73, .91, 1);
Color colour = Color.WHITE.interpolate(brightBlue, gray).interpolate(lighterBlue, gray).interpolate(lightBlue, gray);
pw.setColor(x, y, colour);
}
}
return wr;
}
/**
* Nomalises the values so that the colours are correct
* @param value value to normalise
* @param min current min
* @param max current max
* @param newMin new min
* @param newMax new max
* @return returns normalised value
*/
private static double normalizeValue(double value, double min, double max, double newMin, double newMax) {
return (value - min) * (newMax - newMin) / (max - min) + newMin;
}
/**
* clamps a value between a min and max
* @param value value to clamp
* @param min minimum value it can be
* @param max maximum value it can be
* @return result after clamp
*/
private static double clamp(double value, double min, double max) {
if (Double.compare(value, min) < 0)
return min;
if (Double.compare(value, max) > 0)
return max;
return value;
}
/**
* Prevent rescaling of sea surface
* @param scale ignored
*/
@Override
public void setScale(double scale) {}
}

@ -0,0 +1,162 @@
package visualiser.layout;
import javafx.geometry.Point3D;
import javafx.scene.image.Image;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.MeshView;
import javafx.scene.transform.Rotate;
import visualiser.utils.PerlinNoiseGenerator;
import java.util.ArrayList;
import java.util.List;
/**
* Creates a skyBox
*/
public class SkyBox {
private int size;
private double x;
private double y;
private double z;
private double freq;
private List<Subject3D> skyBoxPlanes = new ArrayList<>();
public SkyBox(int edgeLength, double freq, double x, double y, double z) {
this.size = edgeLength;
this.x = x;
this.y = y;
this.z = z;
this.freq = freq;
makeSkyBox();
}
private void makeSkyBox() {
addTop();
addFront();
addBack();
addLeft();
addRight();
addSeaOverlay();
}
private void addTop() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/skyTop.png")));
surface.setRotationAxis(new Point3D(0, 0, 1));
surface.setRotate(180);
surface.setTranslateX(x);
surface.setTranslateY(y - size + 1);
surface.setTranslateZ(z);
Subject3D top = new Subject3D(surface);
skyBoxPlanes.add(top);
}
private void addRight() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/skyRight.png")));
surface.setTranslateX(size/2);
surface.setTranslateY(size/2);
surface.setRotationAxis(new Point3D(1, 0, 0));
surface.setRotate(90);
surface.setTranslateX(-size/2);
surface.setTranslateY(-size/2);
surface.setTranslateX(x);
surface.setTranslateY(y - size/2);
surface.setTranslateZ(z + size/2 - 1);
Subject3D right = new Subject3D(surface);
skyBoxPlanes.add(right);
}
private void addLeft() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/skyLeft.png")));
surface.setTranslateX(size/2);
surface.setTranslateY(size/2);
surface.setRotationAxis(new Point3D(1, 0, 0));
surface.setRotate(-90);
surface.setTranslateX(-size/2);
surface.setTranslateY(-size/2);
surface.setScaleX(-1);
surface.setScaleZ(-1);
surface.setTranslateX(x);
surface.setTranslateY(y - size/2);
surface.setTranslateZ(z - size/2 + 1);
Subject3D left = new Subject3D(surface);
skyBoxPlanes.add(left);
}
private void addBack() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/skyBack.png")));
surface.getTransforms().add(new Rotate(90, 0, 0));
surface.setRotationAxis(new Point3D(1, 0, 0));
surface.setRotate(-90);
surface.setScaleY(-1);
surface.setScaleZ(-1);
surface.setTranslateX(x - size/2 + 1);
surface.setTranslateY(y - size/2);
surface.setTranslateZ(z);
Subject3D back = new Subject3D(surface);
skyBoxPlanes.add(back);
}
private void addFront() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/skyFront.png")));
surface.setTranslateX(size/2);
surface.setTranslateY(size/2);
surface.setRotationAxis(new Point3D(0, 0, 1));
surface.setRotate(-90);
surface.setTranslateX(-size/2);
surface.setTranslateY(-size/2);
surface.setTranslateX(x + size/2 - 1);
surface.setTranslateY(y - size/2);
surface.setTranslateZ(z);
Subject3D front = new Subject3D(surface);
skyBoxPlanes.add(front);
}
private MeshView makeSurface(Image diffuseMap) {
PhongMaterial material = new PhongMaterial();
//material.setDiffuseColor(Color.BLUE);
material.setDiffuseMap(diffuseMap);
//material.setSpecularColor(Color.WHITE);
Plane3D plane = new Plane3D(size, size, 10, 10);
MeshView surface = new MeshView(plane);
surface.setMaterial(material);
surface.setMouseTransparent(true);
return surface;
}
private void addSeaOverlay() {
SeaSurface seaSurface = new SeaSurface(size * 3, freq);
seaSurface.setX(x);
seaSurface.setY(y - size/4 + 1);
seaSurface.setZ(z);
skyBoxPlanes.add(seaSurface);
}
public List<Subject3D> getSkyBoxPlanes() {
return skyBoxPlanes;
}
}

@ -2,6 +2,7 @@ package visualiser.layout;
import javafx.scene.shape.Shape3D;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
/**
@ -27,6 +28,8 @@ public class Subject3D {
*/
private Rotate heading;
private Scale scale;
/**
* Constructor for view subject wrapper
* @param mesh to be rendered
@ -34,10 +37,11 @@ public class Subject3D {
public Subject3D(Shape3D mesh, int sourceID) {
this.mesh = mesh;
this.sourceID = sourceID;
this.scale = new Scale();
this.position = new Translate();
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.mesh.getTransforms().addAll(position, scale, heading, new Rotate(90, Rotate.X_AXIS), new Rotate(180, Rotate.Y_AXIS));
}
public Shape3D getMesh() {
@ -56,6 +60,12 @@ public class Subject3D {
return heading;
}
public void setScale(double scale) {
this.scale.setX(scale);
this.scale.setY(scale);
this.scale.setZ(scale);
}
public void setX(double x) {
position.setX(x);
}

@ -3,9 +3,7 @@ package visualiser.layout;
import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.SubScene;
import javafx.scene.*;
import javafx.scene.input.PickResult;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
@ -89,12 +87,17 @@ public class View3D extends Pane {
/**
* Distance to switch from third person to bird's eye
*/
private double THIRD_PERSON_LIMIT = 100;
private final double THIRD_PERSON_LIMIT = 100;
/**
* Distance to stop zoom
*/
private final double FIRST_PERSON_LIMIT = 2;
/**
* Default constructor for View3D. Sets up Scene and PerspectiveCamera.
* @param fill whether or not to fill the background of the view.
*/
public View3D() {
public View3D(boolean fill) {
this.world = new Group();
this.shapeMap = new HashMap<>();
this.sourceMap = new HashMap<>();
@ -103,13 +106,18 @@ public class View3D extends Pane {
scene.widthProperty().bind(this.widthProperty());
scene.heightProperty().bind(this.heightProperty());
scene.setFill(new Color(0.2, 0.6, 1, 1));
if (fill) {
scene.setFill(new Color(0.2, 0.6, 1, 1));
}
scene.setCamera(buildCamera());
this.getChildren().add(scene);
}
public View3D(){
this(true);
}
/**
* Sets up camera view frustum and binds transformations
* @return perspective camera
@ -169,11 +177,37 @@ public class View3D extends Pane {
scene.setOnMousePressed(e -> {
PickResult result = e.getPickResult();
if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) {
untrackSubject();
trackSubject(shapeMap.get(result.getIntersectedNode()));
setThirdPerson();
}
});
}
/**
* Configures camera to third person view
*/
public void setThirdPerson() {
this.setDistance(THIRD_PERSON_LIMIT / 2);
this.setPitch(10);
for(Subject3D item: items) {
item.setScale(0.1);
}
}
/**
* Configures camera to bird's eye view
*/
public void setBirdsEye() {
this.setYaw(0);
this.setPitch(60);
for(Subject3D item: items) {
item.setScale(1);
}
}
/**
* Stop camera from following the last selected subject
*/
@ -191,7 +225,6 @@ public class View3D extends Pane {
* @param subject to track
*/
private void trackSubject(Subject3D subject) {
untrackSubject();
target = subject;
updatePivot(target.getPosition());
@ -201,9 +234,6 @@ public class View3D extends Pane {
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) {
@ -241,13 +271,12 @@ public class View3D extends Pane {
public void updateDistance(double delta) {
double distance = -this.distance.getZ() + delta;
if(distance <= 0) {
this.setDistance(0);
if(distance <= FIRST_PERSON_LIMIT) {
this.setDistance(FIRST_PERSON_LIMIT);
} else if(distance > THIRD_PERSON_LIMIT) {
untrackSubject();
this.setYaw(0);
this.setPitch(60);
this.setDistance(distance);
untrackSubject();
setBirdsEye();
} else {
this.setDistance(distance);
}
@ -268,4 +297,12 @@ public class View3D extends Pane {
public void setPitch(double pitch) {
this.pitch.setAngle(-pitch);
}
public void addAmbientLight(AmbientLight ambientLight) {
this.world.getChildren().add(ambientLight);
}
public void addPointLight(PointLight pointLight) {
this.world.getChildren().add(pointLight);
}
}

@ -101,4 +101,14 @@ public class GPSConverter {
return convertGPS(coordinate.getLatitude(), coordinate.getLongitude());
}
/**
* Gets the bearing between two coordinates
* @param coord1 coordinate 1
* @param coord2 coordinate 2
* @return return the bearing between the two.
*/
public static double getAngle(GraphCoordinate coord1, GraphCoordinate coord2){
return Math.atan2(coord2.getX() - coord1.getX(), coord2.getY() - coord1.getY());
}
}

@ -0,0 +1,88 @@
package visualiser.utils;
/**
* Perlin Noise Generator
*/
public class PerlinNoiseGenerator {
/**
* Create an array of the given size with values of perlin noise
* @param size size of array that you wish to create
* @param freq frequency that the noise is to be generated at.
* @return noise generated
*/
public static float[][] createNoise( int size, double freq) {
float[][] noiseArray = new float[(int) size][(int) size];
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
double frequency = freq / (double) size;
double noise = ImprovedNoise.noise(x * frequency, y * frequency, 0);
noiseArray[x][y] = (float) noise;
}
}
return noiseArray;
}
/**
* Perlin noise generator
*
* // JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
* // http://mrl.nyu.edu/~perlin/paper445.pdf
* // http://mrl.nyu.edu/~perlin/noise/
*/
public final static class ImprovedNoise {
static public double noise(double x, double y, double z) {
int X = (int)Math.floor(x) & 255, // FIND UNIT CUBE THAT
Y = (int)Math.floor(y) & 255, // CONTAINS POINT.
Z = (int)Math.floor(z) & 255;
x -= Math.floor(x); // FIND RELATIVE X,Y,Z
y -= Math.floor(y); // OF POINT IN CUBE.
z -= Math.floor(z);
double u = fade(x), // COMPUTE FADE CURVES
v = fade(y), // FOR EACH OF X,Y,Z.
w = fade(z);
int A = p[X ]+Y, AA = p[A]+Z, AB = p[A+1]+Z, // HASH COORDINATES OF
B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z; // THE 8 CUBE CORNERS,
return lerp(w, lerp(v, lerp(u, grad(p[AA ], x , y , z ), // AND ADD
grad(p[BA ], x-1, y , z )), // BLENDED
lerp(u, grad(p[AB ], x , y-1, z ), // RESULTS
grad(p[BB ], x-1, y-1, z ))),// FROM 8
lerp(v, lerp(u, grad(p[AA+1], x , y , z-1 ), // CORNERS
grad(p[BA+1], x-1, y , z-1 )), // OF CUBE
lerp(u, grad(p[AB+1], x , y-1, z-1 ),
grad(p[BB+1], x-1, y-1, z-1 ))));
}
static double fade(double t) { return t * t * t * (t * (t * 6 - 15) + 10); }
static double lerp(double t, double a, double b) { return a + t * (b - a); }
static double grad(int hash, double x, double y, double z) {
int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
double u = h<8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
v = h<4 ? y : h==12||h==14 ? x : z;
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}
static final int p[] = new int[512], permutation[] = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
static { for (int i=0; i < 256 ; i++) p[256+i] = p[i] = permutation[i]; }
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Race>
<RaceID>5326</RaceID>
<RaceType>FLEET</RaceType>
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
<Participants>
</Participants>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3" />
<Corner SeqID="3" CompoundMarkID="4" Rounding="Port" ZoneSize="3" />
<Corner SeqID="4" CompoundMarkID="3" Rounding="Starboard" ZoneSize="3" />
<Corner SeqID="5" CompoundMarkID="4" Rounding="Port" ZoneSize="3" />
<Corner SeqID="6" CompoundMarkID="5" Rounding="SP" ZoneSize="3"/>
</CompoundMarkSequence>
<Course>
<CompoundMark CompoundMarkID="1" Name="Start Line">
<Mark SeqId="1" Name="PRO" TargetLat="32.296577" TargetLng="-64.854304" SourceID="101"/>
<Mark SeqId="2" Name="PIN" TargetLat="32.293771" TargetLng="-64.855242" SourceID="102"/>
</CompoundMark>
<CompoundMark CompoundMarkID="2" Name="Marker 1">
<Mark Name="Marker1" TargetLat="32.293039" TargetLng="-64.843983" SourceID="103"/>
</CompoundMark>
<CompoundMark CompoundMarkID="3" Name="Windward Gate">
<Mark Name="WGL" SeqId="1" TargetLat="32.28468" TargetLng="-64.850045" SourceID="104"/>
<Mark Name="WGR" SeqId="2" TargetLat="32.280164" TargetLng="-64.847591" SourceID="105"/>
</CompoundMark>
<CompoundMark CompoundMarkID="4" Name="Leeward Gate">
<Mark Name="LGL" SeqId="1" TargetLat="32.309693" TargetLng="-64.835249" SourceID="106"/>
<Mark Name="LGR" SeqId="2" TargetLat="32.308046" TargetLng="-64.831785" SourceID="107"/>
</CompoundMark>
<CompoundMark CompoundMarkID="5" Name="Finish Line">
<Mark Name="FL" SeqId="1" TargetLat="32.317379" TargetLng="-64.839291" SourceID="108"/>
<Mark Name="FR" SeqId="2" TargetLat="32.317257" TargetLng="-64.83626" SourceID="109"/>
</CompoundMark>
</Course>
<CourseLimit>
<Limit Lat="32.313922" Lon="-64.837168" SeqID="1"/>
<Limit Lat="32.317379" Lon="-64.839291" SeqID="2"/>
<Limit Lat="32.317911" Lon="-64.836996" SeqID="3"/>
<Limit Lat="32.317257" Lon="-64.83626" SeqID="4"/>
<Limit Lat="32.304273" Lon="-64.822834" SeqID="5"/>
<Limit Lat="32.279097" Lon="-64.841545" SeqID="6"/>
<Limit Lat="32.279604" Lon="-64.849871" SeqID="7"/>
<Limit Lat="32.289545" Lon="-64.854162" SeqID="8"/>
<Limit Lat="32.290198" Lon="-64.858711" SeqID="9"/>
<Limit Lat="32.297164" Lon="-64.856394" SeqID="10"/>
<Limit Lat="32.296148" Lon="-64.849184" SeqID="11"/>
</CourseLimit>
</Race>
Loading…
Cancel
Save