D and A D) As user I wish to play on a modern day 3D view Description: The current base of the game is run on a 2D dot and line canvas, this will be overhauled and replaced with a 3D view of the game. Acceptance Criteria: - Boats, and markers in the race are now 3D. - Sea has a surface. - Race runs and is reflected on the 3D view. - Skybox must be present. - Boundary must be shown (2D or 3D) - Annotations do not need to be shown. Tests: - When the FXML's are split make sure that the race still changes panes to reflect each stage of the race, prestart, racing, and finish. - The boats headings on the 3D models are the correct orientation (if they aren't facting forward on load please tell Fan-Wu as this should be an issue with the stl file). - The camera is "fixed" for now. - The base pane of the race is a stack pane so we can stack an annotations layer, or control layer, etc in the future. A) [V] As a player I would like for the style of the application to be informative Acceptance criteria: * The direction to the next mark is shown in 3rd person view * The next mark to be rounded is highlighted * Player is notified with a sound effect when the mark is rounded * Collisions are made clear and give visual and audio feedback * The score table overlays on the race and does not affect scaling See merge request !46main
@ -0,0 +1,46 @@
|
||||
package visualiser.Commands.VisualiserRaceCommands;
|
||||
|
||||
import javafx.scene.media.AudioClip;
|
||||
import mock.model.commandFactory.Command;
|
||||
import network.Messages.YachtEvent;
|
||||
import shared.exceptions.BoatNotFoundException;
|
||||
import visualiser.model.VisualiserBoat;
|
||||
import visualiser.model.VisualiserRaceState;
|
||||
|
||||
/**
|
||||
* Created by zwu18 on 4/09/17.
|
||||
*/
|
||||
public class BoatCollisionCommand implements Command {
|
||||
|
||||
YachtEvent yachtEvent;
|
||||
|
||||
VisualiserRaceState visualiserRace;
|
||||
|
||||
public BoatCollisionCommand(YachtEvent yachtEvent, VisualiserRaceState visualiserRace){
|
||||
this.yachtEvent = yachtEvent;
|
||||
this.visualiserRace = visualiserRace;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
|
||||
if(visualiserRace.getPlayerBoatID()==yachtEvent.getSourceID()){
|
||||
//System.out.println("I crashed!");
|
||||
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/collision.wav").toExternalForm());
|
||||
sound.play();
|
||||
|
||||
} else {
|
||||
//System.out.println("Someone else crashed!");
|
||||
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/quietcollision.wav").toExternalForm());
|
||||
sound.play();
|
||||
}
|
||||
|
||||
try {
|
||||
VisualiserBoat boat = visualiserRace.getBoat(yachtEvent.getSourceID());
|
||||
boat.setHasCollided(true);
|
||||
} catch (BoatNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package visualiser.layout;
|
||||
|
||||
import javafx.scene.shape.Shape3D;
|
||||
|
||||
/**
|
||||
* Created by connortaylorbrown on 13/09/17.
|
||||
*/
|
||||
public class Annotation3D extends Subject3D {
|
||||
/**
|
||||
* Constructor for view subject wrapper
|
||||
*
|
||||
* @param mesh to be rendered
|
||||
*/
|
||||
public Annotation3D(Shape3D mesh) {
|
||||
super(mesh, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent rescaling of this subject
|
||||
* @param scale ignored
|
||||
*/
|
||||
@Override
|
||||
public void setScale(double scale) {}
|
||||
}
|
||||
@ -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 Annotation3D(new Sphere(thickness * 2));
|
||||
bound1.setX(graphCoord1.getX());
|
||||
bound1.setZ(graphCoord1.getY());
|
||||
boundaryNodes.add(bound1);
|
||||
|
||||
Subject3D connector = new Annotation3D(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,17 @@
|
||||
package visualiser.layout;
|
||||
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.PhongMaterial;
|
||||
import javafx.scene.shape.Cylinder;
|
||||
import javafx.scene.transform.Rotate;
|
||||
|
||||
/**
|
||||
* Created by cbt24 on 14/09/17.
|
||||
*/
|
||||
public class Shockwave extends Subject3D {
|
||||
public Shockwave(double radius) {
|
||||
super(new Cylinder(radius,0),0);
|
||||
getMesh().getTransforms().add(new Rotate(-90, Rotate.X_AXIS));
|
||||
getMesh().setMaterial(new PhongMaterial(new Color(0,0,0,0)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,163 @@
|
||||
package visualiser.layout;
|
||||
|
||||
import javafx.geometry.Point3D;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.PhongMaterial;
|
||||
import javafx.scene.shape.MeshView;
|
||||
import javafx.scene.transform.Rotate;
|
||||
|
||||
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 double yshift;
|
||||
private double clipOverlap;
|
||||
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;
|
||||
this.yshift = -size/64;
|
||||
clipOverlap = 0;
|
||||
makeSkyBox();
|
||||
}
|
||||
|
||||
private void makeSkyBox() {
|
||||
addTop();
|
||||
addFront();
|
||||
addBack();
|
||||
addLeft();
|
||||
addRight();
|
||||
//addSeaOverlay();
|
||||
}
|
||||
|
||||
private void addTop() {
|
||||
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterUp2048.png")), size);
|
||||
|
||||
surface.setRotationAxis(new Point3D(0, 0, 1));
|
||||
surface.setRotate(180);
|
||||
|
||||
surface.setTranslateX(x);
|
||||
surface.setTranslateY(y - size + 1);
|
||||
surface.setTranslateZ(z);
|
||||
|
||||
Subject3D top = new SkyBoxPlane(surface,0);
|
||||
skyBoxPlanes.add(top);
|
||||
}
|
||||
|
||||
private void addRight() {
|
||||
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterRight2048.png")), size + 1);
|
||||
|
||||
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 + yshift);
|
||||
surface.setTranslateZ(z + size/2 - clipOverlap);
|
||||
|
||||
|
||||
Subject3D right = new SkyBoxPlane(surface,0);
|
||||
skyBoxPlanes.add(right);
|
||||
}
|
||||
|
||||
private void addLeft() {
|
||||
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterLeft2048.png")), size + 1);
|
||||
|
||||
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 + yshift);
|
||||
surface.setTranslateZ(z - size/2 + clipOverlap);
|
||||
|
||||
|
||||
Subject3D left = new SkyBoxPlane(surface,0);
|
||||
skyBoxPlanes.add(left);
|
||||
}
|
||||
|
||||
private void addBack() {
|
||||
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterBack2048.png")), size);
|
||||
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 + clipOverlap);
|
||||
surface.setTranslateY(y + yshift);
|
||||
surface.setTranslateZ(z);
|
||||
|
||||
Subject3D back = new SkyBoxPlane(surface,0);
|
||||
skyBoxPlanes.add(back);
|
||||
}
|
||||
|
||||
private void addFront() {
|
||||
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterFront2048.png")), size);
|
||||
|
||||
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 - clipOverlap);
|
||||
surface.setTranslateY(y + yshift);
|
||||
surface.setTranslateZ(z);
|
||||
|
||||
Subject3D front = new SkyBoxPlane(surface,0);
|
||||
skyBoxPlanes.add(front);
|
||||
}
|
||||
|
||||
private MeshView makeSurface(Image diffuseMap, int size) {
|
||||
|
||||
PhongMaterial material = new PhongMaterial();
|
||||
material.setDiffuseColor(Color.web("#FFFFFF"));
|
||||
material.setSpecularColor(Color.web("#000000"));
|
||||
material.setDiffuseMap(diffuseMap);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package visualiser.layout;
|
||||
|
||||
import javafx.scene.shape.Shape3D;
|
||||
|
||||
public class SkyBoxPlane extends Subject3D {
|
||||
|
||||
public SkyBoxPlane(Shape3D mesh, int sourceID) {
|
||||
super(mesh,sourceID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScale(double scale) {
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.0 MiB |
|
After Width: | Height: | Size: 3.6 MiB |
|
After Width: | Height: | Size: 4.8 MiB |
|
After Width: | Height: | Size: 4.4 MiB |
|
After Width: | Height: | Size: 4.6 MiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
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>
|
||||