Merge branch 'master' into story1301-ingamelobby
# Conflicts: # racevisionGame/src/main/java/visualiser/Controllers/InGameLobbyController.java # racevisionGame/src/main/resources/visualiser/scenes/gameLobby.fxml
This commit is contained in:
@@ -16,6 +16,9 @@
|
|||||||
# https://www.kernel.org/pub/software/scm/git/docs/git-shortlog.html
|
# https://www.kernel.org/pub/software/scm/git/docs/git-shortlog.html
|
||||||
# http://stacktoheap.com/blog/2013/01/06/using-mailmap-to-fix-authors-list-in-git/
|
# http://stacktoheap.com/blog/2013/01/06/using-mailmap-to-fix-authors-list-in-git/
|
||||||
Erika Savell <[email protected]>
|
Erika Savell <[email protected]>
|
||||||
Connor Taylor-Brown <cbt24@cs17086jp.canterbury.ac.nz> <cbt24@uclive.canterbury.ac.nz>
|
Connor Taylor-Brown <cbt24@uclive.ac.nz> <cbt24@cs17086jp.canterbury.ac.nz>
|
||||||
Fraser Cope <[email protected]>
|
Fraser Cope <[email protected]>
|
||||||
Jessica Syder <[email protected]> Jessica Syder <[email protected]>
|
Jessica Syder <[email protected]> <[email protected]>
|
||||||
|
Joseph Gardner <[email protected]>
|
||||||
|
Hamish Ball <[email protected]>
|
||||||
|
David Wu <[email protected]>
|
||||||
@@ -21,7 +21,7 @@ public class App extends Application {
|
|||||||
public void start(Stage primaryStage) {
|
public void start(Stage primaryStage) {
|
||||||
try {
|
try {
|
||||||
//TODO should read a configuration file to configure server?
|
//TODO should read a configuration file to configure server?
|
||||||
Event raceEvent = new Event(false, 0);
|
Event raceEvent = new Event(false, 0, 5);
|
||||||
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -29,6 +29,12 @@
|
|||||||
<version>2.9.0</version>
|
<version>2.9.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>eu.hansolo</groupId>
|
||||||
|
<artifactId>Medusa</artifactId>
|
||||||
|
<version>7.9</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Maven Dependencies block START-->
|
<!-- Maven Dependencies block START-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.bytebuddy</groupId>
|
<groupId>net.bytebuddy</groupId>
|
||||||
@@ -90,6 +96,18 @@
|
|||||||
<version>20160810</version>
|
<version>20160810</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>JavaInteractiveMesh</groupId>
|
||||||
|
<artifactId>ObjImporter</artifactId>
|
||||||
|
<version>0.8</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>JavaInteractiveMesh</groupId>
|
||||||
|
<artifactId>X3DImporter</artifactId>
|
||||||
|
<version>0.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,53 +40,44 @@ public class Event {
|
|||||||
private String raceXML;
|
private String raceXML;
|
||||||
private String regattaXML;
|
private String regattaXML;
|
||||||
private String boatXML;
|
private String boatXML;
|
||||||
|
|
||||||
private XMLFileType xmlFileType;
|
private XMLFileType xmlFileType;
|
||||||
|
|
||||||
private Polars boatPolars;
|
private Polars boatPolars;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data sources containing data from the xml files.
|
* Data sources containing data from the xml files.
|
||||||
*/
|
*/
|
||||||
RaceDataSource raceDataSource;
|
private RaceDataSource raceDataSource;
|
||||||
BoatDataSource boatDataSource;
|
private BoatDataSource boatDataSource;
|
||||||
RegattaDataSource regattaDataSource;
|
private RegattaDataSource regattaDataSource;
|
||||||
|
|
||||||
|
|
||||||
private ConnectionAcceptor connectionAcceptor;
|
private ConnectionAcceptor connectionAcceptor;
|
||||||
private LatestMessages latestMessages;
|
private LatestMessages latestMessages;
|
||||||
|
|
||||||
private CompositeCommand compositeCommand;
|
private CompositeCommand compositeCommand;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used to allocate source IDs.
|
* This is used to allocate source IDs.
|
||||||
*/
|
*/
|
||||||
private SourceIdAllocator sourceIdAllocator;
|
private SourceIdAllocator sourceIdAllocator;
|
||||||
|
|
||||||
|
private RaceLogic raceLogic;
|
||||||
|
|
||||||
private Thread raceThread;
|
private Thread raceThread;
|
||||||
|
|
||||||
private Thread connectionThread;
|
private Thread connectionThread;
|
||||||
|
|
||||||
private int mapIndex;
|
private int mapIndex;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an event, using various XML files.
|
* Constructs an event, using various XML files.
|
||||||
* @param singlePlayer Whether or not to create a single player event.
|
* @param singlePlayer Whether or not to create a single player event.
|
||||||
* @param mapIndex Specifies which map to use.
|
* @param mapIndex Specifies which map to use.
|
||||||
|
* @param raceLength The length of the race, in milliseconds.
|
||||||
* @throws EventConstructionException Thrown if we cannot create an Event for any reason.
|
* @throws EventConstructionException Thrown if we cannot create an Event for any reason.
|
||||||
*/
|
*/
|
||||||
public Event(boolean singlePlayer, int mapIndex) throws EventConstructionException {
|
public Event(boolean singlePlayer, int mapIndex, int raceLength) throws
|
||||||
|
EventConstructionException {
|
||||||
PolarParser.parseNewPolars("mock/polars/acc_polars.csv");
|
PolarParser.parseNewPolars("mock/polars/acc_polars.csv");
|
||||||
this.mapIndex = mapIndex;
|
this.mapIndex = mapIndex;
|
||||||
String raceXMLFile;
|
String raceXMLFile;
|
||||||
String boatsXMLFile = "mock/mockXML/boatTest.xml";
|
String boatsXMLFile = "mock/mockXML/boatTest.xml";
|
||||||
String regattaXMLFile = "mock/mockXML/regattaTest.xml";
|
String regattaXMLFile = "mock/mockXML/regattaTest.xml";
|
||||||
switch (mapIndex){
|
switch (mapIndex){
|
||||||
case 0:raceXMLFile = "mock/mockXML/raceSixPlayers.xml";
|
case 0:raceXMLFile = "mock/mockXML/ac35MapLayout.xml";
|
||||||
break;
|
break;
|
||||||
case 1:raceXMLFile = "mock/mockXML/oMapLayout.xml";
|
case 1:raceXMLFile = "mock/mockXML/oMapLayout.xml";
|
||||||
break;
|
break;
|
||||||
@@ -99,13 +90,12 @@ public class Event {
|
|||||||
boatsXMLFile = "mock/mockXML/boatTutorial.xml";
|
boatsXMLFile = "mock/mockXML/boatTutorial.xml";
|
||||||
regattaXMLFile = "mock/mockXML/regattaTutorial.xml";
|
regattaXMLFile = "mock/mockXML/regattaTutorial.xml";
|
||||||
break;
|
break;
|
||||||
default: raceXMLFile = "mock/mockXML/raceSixPlayers.xml";
|
default: raceXMLFile = "mock/mockXML/ac35MapLayout.xml";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (singlePlayer) {
|
if (singlePlayer) {
|
||||||
raceXMLFile = "mock/mockXML/raceSinglePlayer.xml";
|
|
||||||
boatsXMLFile = "mock/mockXML/boatsSinglePlayer.xml";
|
boatsXMLFile = "mock/mockXML/boatsSinglePlayer.xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,14 +104,17 @@ public class Event {
|
|||||||
|
|
||||||
//Read XML files.
|
//Read XML files.
|
||||||
try {
|
try {
|
||||||
//this.raceXML = RaceXMLCreator.alterRaceToWind(raceXMLFile, 90);
|
|
||||||
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8));
|
|
||||||
if(mapIndex==4){
|
if(mapIndex==4){
|
||||||
//this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8), 1000, 5000);
|
//This is the tutorial map.
|
||||||
this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, -1, true);
|
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8), 1000, 5000);
|
||||||
|
this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, -1);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, windAngle, false);
|
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8));
|
||||||
this.raceXML = RaceXMLCreator.scaleRaceSize(raceXML, windSpeed, 15 * 60 * 1000);
|
this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, windAngle);
|
||||||
|
this.raceXML = RaceXMLCreator.scaleRaceSize(raceXML,
|
||||||
|
windSpeed, raceLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8);
|
this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8);
|
||||||
@@ -171,20 +164,20 @@ public class Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RaceLogic newRace = new RaceLogic(
|
this.raceLogic = new RaceLogic(
|
||||||
mockRace,
|
mockRace,
|
||||||
this.latestMessages,
|
this.latestMessages,
|
||||||
this.compositeCommand);
|
this.compositeCommand);
|
||||||
|
|
||||||
this.raceThread = new Thread(newRace, "Event.Start()->RaceLogic thread");
|
this.raceThread = new Thread(raceLogic, "Event.Start()->RaceLogic thread");
|
||||||
raceThread.start();
|
raceThread.start();
|
||||||
|
|
||||||
|
|
||||||
//Create connection acceptor.
|
//Create connection acceptor.
|
||||||
this.sourceIdAllocator = new SourceIdAllocator(newRace.getRace());
|
this.sourceIdAllocator = new SourceIdAllocator(raceLogic.getRace());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.connectionAcceptor = new ConnectionAcceptor(latestMessages, compositeCommand, sourceIdAllocator, newRace);
|
this.connectionAcceptor = new ConnectionAcceptor(latestMessages, compositeCommand, sourceIdAllocator, raceLogic);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new EventConstructionException("Could not create ConnectionAcceptor.", e);
|
throw new EventConstructionException("Could not create ConnectionAcceptor.", e);
|
||||||
@@ -217,16 +210,13 @@ public class Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String setRaceXMLAtCurrentTimeToNow(String raceXML, long racePreStartTime, long 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 minutesToAdd = 10;
|
||||||
|
|
||||||
long secondsToAdd = millisecondsToAdd / 1000;
|
|
||||||
|
|
||||||
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
|
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
|
||||||
ZonedDateTime creationTime = ZonedDateTime.now();
|
ZonedDateTime creationTime = ZonedDateTime.now();
|
||||||
raceXML = raceXML.replace("RACE_CREATION_TIME", dateFormat.format(creationTime));
|
raceXML = raceXML.replace("RACE_CREATION_TIME", dateFormat.format(creationTime));
|
||||||
raceXML = raceXML.replace("RACE_START_TIME", dateFormat.format(creationTime.plusSeconds(secondsToAdd)));
|
raceXML = raceXML.replace("RACE_START_TIME", dateFormat.format(creationTime.plusMinutes(minutesToAdd)));
|
||||||
|
|
||||||
return raceXML;
|
return raceXML;
|
||||||
}
|
}
|
||||||
@@ -241,4 +231,9 @@ public class Event {
|
|||||||
return new HostGame(ip, 3779,(byte) 1,(byte) 1, RaceStatusEnum.PRESTART, (byte) 1, (byte) 1);
|
return new HostGame(ip, 3779,(byte) 1,(byte) 1, RaceStatusEnum.PRESTART, (byte) 1, (byte) 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RaceLogic getRaceLogic() {
|
||||||
|
return raceLogic;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class MockBoat extends Boat {
|
|||||||
* 1: passed only first check
|
* 1: passed only first check
|
||||||
* 2: passed first and second check
|
* 2: passed first and second check
|
||||||
*/
|
*/
|
||||||
private Integer roundingStatus = 0;
|
private int roundingStatus = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores whether the boat is on autoVMG or not
|
* Stores whether the boat is on autoVMG or not
|
||||||
@@ -284,8 +284,8 @@ public class MockBoat extends Boat {
|
|||||||
(this.isStarboardSide(mark1) && this.isPortSide(mark2));
|
(this.isStarboardSide(mark1) && this.isPortSide(mark2));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getRoundingStatus() {
|
public int getRoundingStatus() {
|
||||||
return Integer.valueOf(roundingStatus);
|
return roundingStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void increaseRoundingStatus() {
|
public void increaseRoundingStatus() {
|
||||||
|
|||||||
@@ -61,12 +61,17 @@ public class MockRace extends RaceState {
|
|||||||
*/
|
*/
|
||||||
private Polars polars;
|
private Polars polars;
|
||||||
|
|
||||||
private ActiveObserverCommand activeObserverCommand;
|
private Map<Integer, ActiveObserverCommand> activeObserverCommands;
|
||||||
|
|
||||||
private long racePreStartTime = Constants.RacePreStartTime;
|
private long racePreStartTime = Constants.RacePreStartTime;
|
||||||
|
|
||||||
private long racePreparatoryTime = Constants.RacePreparatoryTime;
|
private long racePreparatoryTime = Constants.RacePreparatoryTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the race has been manually started, false otherwise.
|
||||||
|
*/
|
||||||
|
private boolean hasBeenStarted = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and sends events to the given mockOutput.
|
* Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and sends events to the given mockOutput.
|
||||||
* @param boatDataSource Data source for boat related data (yachts and marker boats).
|
* @param boatDataSource Data source for boat related data (yachts and marker boats).
|
||||||
@@ -82,7 +87,7 @@ public class MockRace extends RaceState {
|
|||||||
this.setRaceDataSource(raceDataSource);
|
this.setRaceDataSource(raceDataSource);
|
||||||
this.setRegattaDataSource(regattaDataSource);
|
this.setRegattaDataSource(regattaDataSource);
|
||||||
|
|
||||||
this.activeObserverCommand = new ActiveObserverCommand();
|
this.activeObserverCommands = new HashMap<>();
|
||||||
this.polars = polars;
|
this.polars = polars;
|
||||||
this.scaleFactor = timeScale;
|
this.scaleFactor = timeScale;
|
||||||
|
|
||||||
@@ -104,8 +109,6 @@ public class MockRace extends RaceState {
|
|||||||
colliderRegistry.addCollider(mark.getMark1());
|
colliderRegistry.addCollider(mark.getMark1());
|
||||||
if(mark.getMark2() != null) colliderRegistry.addCollider(mark.getMark2());
|
if(mark.getMark2() != null) colliderRegistry.addCollider(mark.getMark2());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.colliderRegistry.addAllColliders(boats);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -127,9 +130,10 @@ public class MockRace extends RaceState {
|
|||||||
getRaceDataSource().getParticipants().add(sourceID);
|
getRaceDataSource().getParticipants().add(sourceID);
|
||||||
|
|
||||||
this.boats.add(mockBoat);
|
this.boats.add(mockBoat);
|
||||||
|
this.activeObserverCommands.put(boat.getSourceID(), new ActiveObserverCommand());
|
||||||
|
this.colliderRegistry.addCollider(mockBoat);
|
||||||
|
|
||||||
getRaceDataSource().incrementSequenceNumber();
|
getRaceDataSource().incrementSequenceNumber();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -157,6 +161,23 @@ public class MockRace extends RaceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delays the start of the race, if needed, to ensure that the race doesn't start until host wants it to.
|
||||||
|
* If the time until start is less that 5 minutes, it is extended to 10 minutes.
|
||||||
|
*/
|
||||||
|
public void delayRaceStart() {
|
||||||
|
long timeToStart = getRaceDataSource().getStartDateTime().toInstant().toEpochMilli() - System.currentTimeMillis();
|
||||||
|
|
||||||
|
long fiveMinutesMilli = 5 * 60 * 1000;
|
||||||
|
long tenMinutesMilli = 10 * 60 * 1000;
|
||||||
|
|
||||||
|
if ((timeToStart < fiveMinutesMilli) && (timeToStart > 0) && !hasBeenStarted) {
|
||||||
|
startRace(tenMinutesMilli, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the race status enumeration based on the current time.
|
* Updates the race status enumeration based on the current time.
|
||||||
*/
|
*/
|
||||||
@@ -165,7 +186,6 @@ public class MockRace extends RaceState {
|
|||||||
//The millisecond duration of the race. Negative means it hasn't started, so we flip sign.
|
//The millisecond duration of the race. Negative means it hasn't started, so we flip sign.
|
||||||
long timeToStart = - this.getRaceClock().getDurationMilli();
|
long timeToStart = - this.getRaceClock().getDurationMilli();
|
||||||
|
|
||||||
|
|
||||||
if (timeToStart > racePreStartTime) {
|
if (timeToStart > racePreStartTime) {
|
||||||
//Time > 3 minutes is the prestart period.
|
//Time > 3 minutes is the prestart period.
|
||||||
this.setRaceStatusEnum(RaceStatusEnum.PRESTART);
|
this.setRaceStatusEnum(RaceStatusEnum.PRESTART);
|
||||||
@@ -195,6 +215,26 @@ public class MockRace extends RaceState {
|
|||||||
this.racePreparatoryTime = racePreparatoryTime;
|
this.racePreparatoryTime = racePreparatoryTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getRacePreparatoryTime() {
|
||||||
|
return racePreparatoryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the race in #timeToStartMilli milliseconds.
|
||||||
|
* @param timeToStartMilli Millseconds before starting the race.
|
||||||
|
* @param manualStart True if the race has been manually started, false otherwise.
|
||||||
|
*/
|
||||||
|
public void startRace(long timeToStartMilli, boolean manualStart) {
|
||||||
|
this.hasBeenStarted = manualStart;
|
||||||
|
ZonedDateTime startTime = ZonedDateTime.now().plus(timeToStartMilli, ChronoUnit.MILLIS);
|
||||||
|
|
||||||
|
getRaceDataSource().setStartDateTime(startTime);
|
||||||
|
getRaceDataSource().incrementSequenceNumber();
|
||||||
|
|
||||||
|
this.getRaceClock().setStartingTime(getRaceDataSource().getStartDateTime());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the status of all boats in the race to RACING.
|
* Sets the status of all boats in the race to RACING.
|
||||||
*/
|
*/
|
||||||
@@ -377,6 +417,11 @@ public class MockRace extends RaceState {
|
|||||||
boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + updatePeriodMilliseconds);
|
boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + updatePeriodMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove one unit of health for every frame spent outside boundary
|
||||||
|
if(!finish && !GPSCoordinate.isInsideBoundary(boat.getPosition(), getBoundary())) {
|
||||||
|
boat.updateHealth(-0.1);
|
||||||
|
}
|
||||||
|
|
||||||
this.updateEstimatedTime(boat);
|
this.updateEstimatedTime(boat);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -388,7 +433,7 @@ public class MockRace extends RaceState {
|
|||||||
boat.getBearing()
|
boat.getBearing()
|
||||||
), boat.getBearing()) ;
|
), boat.getBearing()) ;
|
||||||
if (vmg.getSpeed() > 0) {
|
if (vmg.getSpeed() > 0) {
|
||||||
boat.setCurrentSpeed(vmg.getSpeed());
|
boat.setCurrentSpeed(vmg.getSpeed() * Math.pow(boat.getHealth() / 100, 0.3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,7 +680,7 @@ public class MockRace extends RaceState {
|
|||||||
for (MockBoat boat : this.boats) {
|
for (MockBoat boat : this.boats) {
|
||||||
|
|
||||||
//If the boat is currently racing, count it.
|
//If the boat is currently racing, count it.
|
||||||
if (boat.getStatus() == BoatStatusEnum.RACING) {
|
if (boat.getStatus() == BoatStatusEnum.RACING && boat.getHealth()>=1) {
|
||||||
numberOfActiveBoats++;
|
numberOfActiveBoats++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -711,11 +756,11 @@ public class MockRace extends RaceState {
|
|||||||
super.setChanged();
|
super.setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addVelocityCommand(ObserverCommand c) {
|
public void addVelocityCommand(ObserverCommand c, int boatId) {
|
||||||
this.activeObserverCommand.changeVelocityCommand(this, c);
|
this.activeObserverCommands.get(boatId).changeVelocityCommand(this, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAngularCommand(ObserverCommand c) {
|
public void addAngularCommand(ObserverCommand c, int boatId) {
|
||||||
this.activeObserverCommand.changeAngularCommand(this, c);
|
this.activeObserverCommands.get(boatId).changeAngularCommand(this, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
|
|||||||
//Update race time.
|
//Update race time.
|
||||||
race.updateRaceTime(currentTime);
|
race.updateRaceTime(currentTime);
|
||||||
|
|
||||||
|
race.delayRaceStart();
|
||||||
|
|
||||||
//Update the race status based on the current time.
|
//Update the race status based on the current time.
|
||||||
race.updateRaceStatusEnum();
|
race.updateRaceStatusEnum();
|
||||||
|
|
||||||
@@ -161,7 +163,7 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
|
|||||||
|
|
||||||
//As long as there is at least one boat racing, we still simulate the race.
|
//As long as there is at least one boat racing, we still simulate the race.
|
||||||
if (race.getNumberOfActiveBoats() != 0) {
|
if (race.getNumberOfActiveBoats() != 0) {
|
||||||
|
//System.out.println(race.getNumberOfActiveBoats());
|
||||||
//Get the time period of this frame.
|
//Get the time period of this frame.
|
||||||
long framePeriod = currentTime - previousFrameTime;
|
long framePeriod = currentTime - previousFrameTime;
|
||||||
|
|
||||||
@@ -173,12 +175,9 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
|
|||||||
race.updatePosition(boat, framePeriod, race.getRaceClock().getDurationMilli());
|
race.updatePosition(boat, framePeriod, race.getRaceClock().getDurationMilli());
|
||||||
|
|
||||||
if(race.getColliderRegistry().rayCast(boat)){
|
if(race.getColliderRegistry().rayCast(boat)){
|
||||||
//System.out.println("Collision!");
|
|
||||||
//Add boat to list
|
//Add boat to list
|
||||||
collisionBoats.add(boat);
|
collisionBoats.add(boat);
|
||||||
}
|
}
|
||||||
|
|
||||||
//System.out.println(race.getColliderRegistry().rayCast(boat));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ public class RaceServer {
|
|||||||
//Parse the boat locations.
|
//Parse the boat locations.
|
||||||
snapshotMessages.addAll(parseBoatLocations());
|
snapshotMessages.addAll(parseBoatLocations());
|
||||||
|
|
||||||
|
//Parse the boat states
|
||||||
|
snapshotMessages.addAll(parseBoatStates());
|
||||||
|
|
||||||
//Parse the marks.
|
//Parse the marks.
|
||||||
snapshotMessages.addAll(parseMarks());
|
snapshotMessages.addAll(parseMarks());
|
||||||
|
|
||||||
@@ -243,7 +246,29 @@ public class RaceServer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates BoatState messages for every boat in the race
|
||||||
|
* @return list of BoatState messages
|
||||||
|
*/
|
||||||
|
private List<BoatState> parseBoatStates() {
|
||||||
|
List<BoatState> boatStates = new ArrayList<>();
|
||||||
|
for(MockBoat boat: race.getBoats()) {
|
||||||
|
boatStates.add(parseIndividualBoatState(boat));
|
||||||
|
}
|
||||||
|
return boatStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BoatState message for the current state of the given boat
|
||||||
|
* @param boat to generate message for
|
||||||
|
* @return BoatState message
|
||||||
|
*/
|
||||||
|
private BoatState parseIndividualBoatState(MockBoat boat) {
|
||||||
|
return new BoatState(
|
||||||
|
boat.getSourceID(),
|
||||||
|
(int)boat.getHealth()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the race status, and returns it.
|
* Parses the race status, and returns it.
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public class CollisionCommand extends ObserverCommand {
|
|||||||
@Override
|
@Override
|
||||||
public void update(Observable o, Object arg) {
|
public void update(Observable o, Object arg) {
|
||||||
if(GPSCoordinate.calculateDistanceMeters(boat.getPosition(), startingPosition) < distance) {
|
if(GPSCoordinate.calculateDistanceMeters(boat.getPosition(), startingPosition) < distance) {
|
||||||
|
boat.setVelocityDefault(false);
|
||||||
boat.setPosition(GPSCoordinate.calculateNewPosition(boat.getPosition(), 3, azimuth));
|
boat.setPosition(GPSCoordinate.calculateNewPosition(boat.getPosition(), 3, azimuth));
|
||||||
} else {
|
} else {
|
||||||
race.deleteObserver(this);
|
race.deleteObserver(this);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class SailsCommand extends ObserverCommand {
|
|||||||
|
|
||||||
public SailsCommand(MockRace race, MockBoat boat, boolean sailsOut) {
|
public SailsCommand(MockRace race, MockBoat boat, boolean sailsOut) {
|
||||||
super(race, boat);
|
super(race, boat);
|
||||||
race.addVelocityCommand(this);
|
race.addVelocityCommand(this, boat.getSourceID());
|
||||||
this.sailsOut = sailsOut;
|
this.sailsOut = sailsOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,15 +37,18 @@ public class SailsCommand extends ObserverCommand {
|
|||||||
public void update(Observable o, Object arg) {
|
public void update(Observable o, Object arg) {
|
||||||
double acceleration = 0.5;
|
double acceleration = 0.5;
|
||||||
|
|
||||||
if(sailsOut && boat.getCurrentSpeed() < goalVelocity) {
|
if (!boat.isColliding()) {
|
||||||
boat.setCurrentSpeed(Math.min(goalVelocity, boat.getCurrentSpeed() + acceleration));
|
boat.setVelocityDefault(false);
|
||||||
} else if (!sailsOut && boat.getCurrentSpeed() > goalVelocity) {
|
if (sailsOut && boat.getCurrentSpeed() < goalVelocity) {
|
||||||
// Apply deceleration to strictly 0 speed
|
boat.setCurrentSpeed(Math.min(goalVelocity, boat.getCurrentSpeed() + acceleration));
|
||||||
boat.setCurrentSpeed(Math.max(0, boat.getCurrentSpeed() - acceleration));
|
} else if (!sailsOut && boat.getCurrentSpeed() > goalVelocity) {
|
||||||
} else {
|
// Apply deceleration to strictly 0 speed
|
||||||
// Release boat from SailsCommand control
|
boat.setCurrentSpeed(Math.max(0, boat.getCurrentSpeed() - acceleration));
|
||||||
if(sailsOut) boat.setVelocityDefault(true);
|
} else {
|
||||||
race.deleteObserver(this);
|
// Release boat from SailsCommand control
|
||||||
|
if (sailsOut) boat.setVelocityDefault(true);
|
||||||
|
race.deleteObserver(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class TackGybeCommand extends ObserverCommand {
|
|||||||
*/
|
*/
|
||||||
public TackGybeCommand(MockRace race, MockBoat boat) {
|
public TackGybeCommand(MockRace race, MockBoat boat) {
|
||||||
super(race, boat);
|
super(race, boat);
|
||||||
race.addAngularCommand(this);
|
race.addAngularCommand(this, boat.getSourceID());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class VMGCommand extends ObserverCommand {
|
|||||||
*/
|
*/
|
||||||
public VMGCommand(MockRace race, MockBoat boat) {
|
public VMGCommand(MockRace race, MockBoat boat) {
|
||||||
super(race, boat);
|
super(race, boat);
|
||||||
race.addAngularCommand(this);
|
race.addAngularCommand(this, boat.getSourceID());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class WindCommand extends ObserverCommand {
|
|||||||
*/
|
*/
|
||||||
public WindCommand(MockRace race, MockBoat boat, boolean upwind) {
|
public WindCommand(MockRace race, MockBoat boat, boolean upwind) {
|
||||||
super(race, boat);
|
super(race, boat);
|
||||||
race.addAngularCommand(this);
|
race.addAngularCommand(this, boat.getSourceID());
|
||||||
this.direction = upwind? -1 : 1;
|
this.direction = upwind? -1 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import java.time.ZonedDateTime;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper Class for creating a Race XML
|
* Helper Class for creating a Race XML
|
||||||
@@ -56,13 +58,12 @@ public class RaceXMLCreator {
|
|||||||
* Rotates the race in a specified direction.
|
* Rotates the race in a specified direction.
|
||||||
* @param s xml file name or contents.
|
* @param s xml file name or contents.
|
||||||
* @param fileType Whether s is a file name or contents.
|
* @param fileType Whether s is a file name or contents.
|
||||||
* @param degrees degrees to rotate
|
* @param degrees degrees to rotate. -1 means don't rotate.
|
||||||
* @param tutorial Whether we wish to run the tutorial - this changes the race start time.
|
|
||||||
* @return the new xml file as a string
|
* @return the new xml file as a string
|
||||||
* @throws XMLReaderException if the xml is not readable
|
* @throws XMLReaderException if the xml is not readable
|
||||||
* @throws InvalidRaceDataException if the race is invalid
|
* @throws InvalidRaceDataException if the race is invalid
|
||||||
*/
|
*/
|
||||||
public static String alterRaceToWind(String s, XMLFileType fileType, double degrees, boolean tutorial) throws XMLReaderException, InvalidRaceDataException {
|
public static String alterRaceToWind(String s, XMLFileType fileType, double degrees) throws XMLReaderException, InvalidRaceDataException {
|
||||||
|
|
||||||
RaceXMLReader reader = new RaceXMLReader(s, fileType);
|
RaceXMLReader reader = new RaceXMLReader(s, fileType);
|
||||||
|
|
||||||
@@ -73,11 +74,6 @@ public class RaceXMLCreator {
|
|||||||
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
|
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
|
||||||
XMLRace.class);
|
XMLRace.class);
|
||||||
|
|
||||||
if(tutorial){
|
|
||||||
setRaceXMLAtCurrentTimeToNow(race, 1000l, 5000l);
|
|
||||||
} else {
|
|
||||||
setRaceXMLAtCurrentTimeToNow(race);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CompoundMark leewardGate = getLeewardGate(reader);
|
CompoundMark leewardGate = getLeewardGate(reader);
|
||||||
@@ -181,10 +177,10 @@ public class RaceXMLCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets the destance between two marks
|
* gets the destance between two marks, in nautical miles.
|
||||||
* @param a mark 1
|
* @param a mark 1
|
||||||
* @param b mark 2
|
* @param b mark 2
|
||||||
* @return
|
* @return Distance in nautical miles.
|
||||||
*/
|
*/
|
||||||
private static double getDistance(XMLMark a, XMLMark b){
|
private static double getDistance(XMLMark a, XMLMark b){
|
||||||
GPSCoordinate coorda = new GPSCoordinate(a.getTargetLat(), a.getTargetLng());
|
GPSCoordinate coorda = new GPSCoordinate(a.getTargetLat(), a.getTargetLng());
|
||||||
@@ -218,12 +214,16 @@ public class RaceXMLCreator {
|
|||||||
double averageSpeed = (bestDownWindSpeed + bestUpWindSpeed) / 2;
|
double averageSpeed = (bestDownWindSpeed + bestUpWindSpeed) / 2;
|
||||||
double raceApproximateTime = getRaceLength(race, averageSpeed);
|
double raceApproximateTime = getRaceLength(race, averageSpeed);
|
||||||
double scale = milliseconds / raceApproximateTime;
|
double scale = milliseconds / raceApproximateTime;
|
||||||
|
Map<XMLCompoundMark, Boolean> hasBeenScaled = new HashMap<>();
|
||||||
for (XMLCorner cm: race.getCompoundMarkSequence().getCorner()){
|
for (XMLCorner cm: race.getCompoundMarkSequence().getCorner()){
|
||||||
int index = cm.getCompoundMarkID() - 1;
|
int index = cm.getCompoundMarkID() - 1;
|
||||||
XMLCompoundMark mark = race.getCourse().getCompoundMark().get(index);
|
XMLCompoundMark mark = race.getCourse().getCompoundMark().get(index);
|
||||||
for (XMLMark m: mark.getMark()){
|
if (!hasBeenScaled.containsKey(mark)) {
|
||||||
scalePoint(m, center, scale);
|
for (XMLMark m : mark.getMark()) {
|
||||||
|
scalePoint(m, center, scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
hasBeenScaled.put(mark, true);
|
||||||
}
|
}
|
||||||
for (XMLLimit limit: race.getCourseLimit().getLimit()){
|
for (XMLLimit limit: race.getCourseLimit().getLimit()){
|
||||||
scalePoint(limit, center, scale);
|
scalePoint(limit, center, scale);
|
||||||
@@ -325,8 +325,13 @@ public class RaceXMLCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the current race time of the xml
|
||||||
|
* @param raceXML race xml to alter
|
||||||
|
* @param racePrestartTime prestart time
|
||||||
|
* @param racePreparatoryTime preparatory time
|
||||||
|
* @deprecated this should be used from the RaceXMLCreator not from this function
|
||||||
|
*/
|
||||||
public static void setRaceXMLAtCurrentTimeToNow(XMLRace raceXML, long racePrestartTime, long racePreparatoryTime){
|
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.
|
//The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute.
|
||||||
long millisecondsToAdd = racePrestartTime + racePreparatoryTime;
|
long millisecondsToAdd = racePrestartTime + racePreparatoryTime;
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package network.MessageDecoders;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.BoatState;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static network.Utils.ByteConverter.bytesToInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decoder for {@link BoatState} messages
|
||||||
|
*/
|
||||||
|
public class BoatStateDecoder implements MessageDecoder {
|
||||||
|
/**
|
||||||
|
* Decoded BoatState message
|
||||||
|
*/
|
||||||
|
private BoatState message;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||||
|
byte[] sourceID = Arrays.copyOfRange(encodedMessage, 0, 4);
|
||||||
|
byte boatHealth = encodedMessage[4];
|
||||||
|
|
||||||
|
// Unpack bytes into BoatState
|
||||||
|
this.message = new BoatState(
|
||||||
|
bytesToInt(sourceID),
|
||||||
|
boatHealth
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return BoatState
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoatState getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@ public class DecoderFactory {
|
|||||||
|
|
||||||
case BOATACTION: return new BoatActionDecoder();
|
case BOATACTION: return new BoatActionDecoder();
|
||||||
|
|
||||||
|
case BOATSTATE: return new BoatStateDecoder();
|
||||||
|
|
||||||
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@ public interface MessageDecoder {
|
|||||||
* @return The decoded message.
|
* @return The decoded message.
|
||||||
* @throws InvalidMessageException Thrown if the encoded message is invalid in some way, or cannot be decoded.
|
* @throws InvalidMessageException Thrown if the encoded message is invalid in some way, or cannot be decoded.
|
||||||
*/
|
*/
|
||||||
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException;
|
AC35Data decode(byte[] encodedMessage) throws InvalidMessageException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package network.MessageEncoders;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.BoatState;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static network.Utils.ByteConverter.intToBytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encoder for {@link BoatState} message
|
||||||
|
*/
|
||||||
|
public class BoatStateEncoder implements MessageEncoder {
|
||||||
|
@Override
|
||||||
|
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||||
|
// Downcast message
|
||||||
|
BoatState boatState = (BoatState)message;
|
||||||
|
|
||||||
|
//Serialise message
|
||||||
|
byte[] sourceID = intToBytes(boatState.getSourceID());
|
||||||
|
byte boatHealth = (byte)boatState.getBoatHealth();
|
||||||
|
|
||||||
|
// Pack bytes into string
|
||||||
|
ByteBuffer boatStateMessage = ByteBuffer.allocate(5);
|
||||||
|
boatStateMessage.put(sourceID);
|
||||||
|
boatStateMessage.put(boatHealth);
|
||||||
|
|
||||||
|
// Return byte string
|
||||||
|
return boatStateMessage.array();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@ public class EncoderFactory {
|
|||||||
|
|
||||||
case BOATACTION: return new BoatActionEncoder();
|
case BOATACTION: return new BoatActionEncoder();
|
||||||
|
|
||||||
|
case BOATSTATE: return new BoatStateEncoder();
|
||||||
|
|
||||||
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package network.Messages;
|
||||||
|
|
||||||
|
import network.Messages.Enums.MessageType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the information in a BoatState message according to protocol meeting
|
||||||
|
*/
|
||||||
|
public class BoatState extends AC35Data {
|
||||||
|
/**
|
||||||
|
* Source ID of boat described in message
|
||||||
|
*/
|
||||||
|
private int sourceID;
|
||||||
|
/**
|
||||||
|
* Health between 0-100 of boat with above source ID
|
||||||
|
*/
|
||||||
|
private int boatHealth;
|
||||||
|
|
||||||
|
public BoatState(int sourceID, int boatHealth) {
|
||||||
|
super(MessageType.BOATSTATE);
|
||||||
|
this.sourceID = sourceID;
|
||||||
|
this.boatHealth = boatHealth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSourceID() {
|
||||||
|
return sourceID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBoatHealth() {
|
||||||
|
return boatHealth;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,8 @@ public enum MessageType {
|
|||||||
|
|
||||||
HOSTED_GAMES_REQUEST(109),
|
HOSTED_GAMES_REQUEST(109),
|
||||||
|
|
||||||
|
BOATSTATE(103),
|
||||||
|
|
||||||
NOTAMESSAGE(0);
|
NOTAMESSAGE(0);
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +58,7 @@ public enum MessageType {
|
|||||||
* Creates a MessageType enum from a given primitive integer value, cast to a byte.
|
* Creates a MessageType enum from a given primitive integer value, cast to a byte.
|
||||||
* @param value Integer, which is cast to byte, to construct from.
|
* @param value Integer, which is cast to byte, to construct from.
|
||||||
*/
|
*/
|
||||||
private MessageType(int value) {
|
MessageType(int value) {
|
||||||
this.value = (byte)value;
|
this.value = (byte)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -151,4 +151,9 @@ public class EmptyRaceDataSource implements RaceDataSource {
|
|||||||
public void incrementSequenceNumber() {
|
public void incrementSequenceNumber() {
|
||||||
sequenceNumber++;
|
sequenceNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStartDateTime(ZonedDateTime time) {
|
||||||
|
raceStartTime = time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ public interface RaceDataSource {
|
|||||||
*/
|
*/
|
||||||
ZonedDateTime getStartDateTime();
|
ZonedDateTime getStartDateTime();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the start time/date of the race.
|
||||||
|
* @param time Time to start at.
|
||||||
|
*/
|
||||||
|
void setStartDateTime(ZonedDateTime time);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the creation time/date of the race xml file.
|
* Returns the creation time/date of the race xml file.
|
||||||
* @return The race xml file's creation time.
|
* @return The race xml file's creation time.
|
||||||
|
|||||||
@@ -507,4 +507,9 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
|
|||||||
public void incrementSequenceNumber() {
|
public void incrementSequenceNumber() {
|
||||||
sequenceNumber++;
|
sequenceNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStartDateTime(ZonedDateTime time) {
|
||||||
|
raceStartTime = time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ public class Boat extends Collider {
|
|||||||
*/
|
*/
|
||||||
private boolean isColliding = false;
|
private boolean isColliding = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount of health boat currently has, between 0 and 100
|
||||||
|
*/
|
||||||
|
private double health;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a boat object with a given sourceID, name, country/team abbreviation, and polars table.
|
* Constructs a boat object with a given sourceID, name, country/team abbreviation, and polars table.
|
||||||
*
|
*
|
||||||
@@ -113,6 +118,7 @@ public class Boat extends Collider {
|
|||||||
public Boat(int sourceID, String name, String country) {
|
public Boat(int sourceID, String name, String country) {
|
||||||
|
|
||||||
this.sourceID = sourceID;
|
this.sourceID = sourceID;
|
||||||
|
this.health = 100;
|
||||||
this.setName(name);
|
this.setName(name);
|
||||||
this.setCountry(country);
|
this.setCountry(country);
|
||||||
|
|
||||||
@@ -419,6 +425,8 @@ public class Boat extends Collider {
|
|||||||
@Override
|
@Override
|
||||||
public void onCollisionEnter(Collision e) {
|
public void onCollisionEnter(Collision e) {
|
||||||
if(e.getBearing().degrees() > 270 || e.getBearing().degrees() < 90) {
|
if(e.getBearing().degrees() > 270 || e.getBearing().degrees() < 90) {
|
||||||
|
// Deplete health
|
||||||
|
e.getBoat().updateHealth(-5);
|
||||||
// Notify observers of collision
|
// Notify observers of collision
|
||||||
this.setChanged();
|
this.setChanged();
|
||||||
notifyObservers(e);
|
notifyObservers(e);
|
||||||
@@ -432,4 +440,22 @@ public class Boat extends Collider {
|
|||||||
public void setColliding(boolean colliding) {
|
public void setColliding(boolean colliding) {
|
||||||
isColliding = colliding;
|
isColliding = colliding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getHealth() {
|
||||||
|
return health;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHealth(double health) {
|
||||||
|
this.health = health;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a given amount of HP to boat health
|
||||||
|
* @param delta amount of HP to add
|
||||||
|
*/
|
||||||
|
public void updateHealth(double delta) {
|
||||||
|
health += delta;
|
||||||
|
if(health < 0) health = 0;
|
||||||
|
else if(health > 100) health = 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class Constants {
|
|||||||
* Frame periods are multiplied by this to get the amount of time a single frame represents.
|
* Frame periods are multiplied by this to get the amount of time a single frame represents.
|
||||||
* E.g., frame period = 20ms, scale = 5, frame represents 20 * 5 = 100ms, and so boats are simulated for 100ms, even though only 20ms actually occurred.
|
* E.g., frame period = 20ms, scale = 5, frame represents 20 * 5 = 100ms, and so boats are simulated for 100ms, even though only 20ms actually occurred.
|
||||||
*/
|
*/
|
||||||
public static final int RaceTimeScale = 2;
|
public static final int RaceTimeScale = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The race pre-start time, in milliseconds. 30 seconds.
|
* The race pre-start time, in milliseconds. 30 seconds.
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ public class Mark extends Collider{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCollisionEnter(Collision e) {
|
public void onCollisionEnter(Collision e) {
|
||||||
|
e.getBoat().updateHealth(-10);
|
||||||
this.setChanged();
|
this.setChanged();
|
||||||
notifyObservers(e);
|
notifyObservers(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ public class MarkRoundingSequence {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* For each leg, mark rounding information.
|
* For each leg, mark rounding information.
|
||||||
|
* Maps between leg number and rounding data.
|
||||||
*/
|
*/
|
||||||
private Map<Leg, MarkRoundingData> roundingPoints;
|
private Map<Integer, MarkRoundingData> roundingPoints;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ public class MarkRoundingSequence {
|
|||||||
* @return Rounding points for leg.
|
* @return Rounding points for leg.
|
||||||
*/
|
*/
|
||||||
public MarkRoundingData getRoundingData(Leg leg) {
|
public MarkRoundingData getRoundingData(Leg leg) {
|
||||||
return roundingPoints.get(leg);
|
return roundingPoints.get(leg.getLegNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -139,7 +140,7 @@ public class MarkRoundingSequence {
|
|||||||
roundingData.setRoundCheck2Halfway(roundCheck2Halfway);
|
roundingData.setRoundCheck2Halfway(roundCheck2Halfway);
|
||||||
|
|
||||||
|
|
||||||
this.roundingPoints.put(currentLeg, roundingData);
|
this.roundingPoints.put(currentLeg.getLegNumber(), roundingData);
|
||||||
|
|
||||||
|
|
||||||
//Rounding points:
|
//Rounding points:
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ public class RaceClock {
|
|||||||
/**
|
/**
|
||||||
* Format string used for duration before it has started.
|
* Format string used for duration before it has started.
|
||||||
*/
|
*/
|
||||||
private String durationBeforeStartFormat = "Starting in: %02d:%02d:%02d";
|
private String durationBeforeStartFormat = "%02d:%02d:%02d";
|
||||||
/**
|
/**
|
||||||
* Format string used for duration once the race has started.
|
* Format string used for duration once the race has started.
|
||||||
*/
|
*/
|
||||||
private String durationAfterStartFormat = "Time: %02d:%02d:%02d";
|
private String durationAfterStartFormat = "%02d:%02d:%02d";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package visualiser.Commands.VisualiserRaceCommands;
|
||||||
|
|
||||||
|
import mock.model.commandFactory.Command;
|
||||||
|
import network.Messages.BoatState;
|
||||||
|
import shared.exceptions.BoatNotFoundException;
|
||||||
|
import visualiser.model.VisualiserBoat;
|
||||||
|
import visualiser.model.VisualiserRaceState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates boats on visualiser when their health changes
|
||||||
|
*/
|
||||||
|
public class BoatStateCommand implements Command {
|
||||||
|
private BoatState boatState;
|
||||||
|
private VisualiserRaceState visualiserRace;
|
||||||
|
|
||||||
|
public BoatStateCommand(BoatState boatState, VisualiserRaceState visualiserRace) {
|
||||||
|
this.boatState = boatState;
|
||||||
|
this.visualiserRace = visualiserRace;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
try {
|
||||||
|
VisualiserBoat boat = visualiserRace.getBoat(boatState.getSourceID());
|
||||||
|
boat.setHealth(boatState.getBoatHealth());
|
||||||
|
} catch (BoatNotFoundException e) {
|
||||||
|
// Fail silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -24,7 +24,6 @@ public class VisualiserRaceCommandFactory {
|
|||||||
switch (message.getType()) {
|
switch (message.getType()) {
|
||||||
|
|
||||||
case BOATLOCATION:
|
case BOATLOCATION:
|
||||||
//System.out.println("Boat location received");
|
|
||||||
return new BoatLocationCommand((BoatLocation) message, visualiserRace);
|
return new BoatLocationCommand((BoatLocation) message, visualiserRace);
|
||||||
|
|
||||||
case RACESTATUS: return new RaceStatusCommand((RaceStatus) message, visualiserRace);
|
case RACESTATUS: return new RaceStatusCommand((RaceStatus) message, visualiserRace);
|
||||||
@@ -36,7 +35,8 @@ public class VisualiserRaceCommandFactory {
|
|||||||
case YACHTEVENTCODE:
|
case YACHTEVENTCODE:
|
||||||
return new BoatCollisionCommand((YachtEvent) message, visualiserRace);
|
return new BoatCollisionCommand((YachtEvent) message, visualiserRace);
|
||||||
|
|
||||||
|
case BOATSTATE:
|
||||||
|
return new BoatStateCommand((BoatState) message, visualiserRace);
|
||||||
|
|
||||||
default: throw new CommandConstructionException("Could not create VisualiserRaceCommand. Unrecognised or unsupported MessageType: " + message.getType());
|
default: throw new CommandConstructionException("Could not create VisualiserRaceCommand. Unrecognised or unsupported MessageType: " + message.getType());
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import javafx.application.Platform;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Slider;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import mock.app.Event;
|
import mock.app.Event;
|
||||||
@@ -28,8 +30,12 @@ import java.util.logging.Logger;
|
|||||||
*/
|
*/
|
||||||
public class HostGameController extends Controller {
|
public class HostGameController extends Controller {
|
||||||
private @FXML ImageView mapImage;
|
private @FXML ImageView mapImage;
|
||||||
|
private @FXML Slider sliderLength;
|
||||||
|
private @FXML Label lblLength;
|
||||||
private ArrayList<Image> listOfMaps;
|
private ArrayList<Image> listOfMaps;
|
||||||
private int currentMapIndex = 0;
|
private int currentMapIndex = 0;
|
||||||
|
private int selectedRaceLength; // in minutes
|
||||||
|
private final int MAX_RACE_LENGTH = 30; // in minutes
|
||||||
private DatagramSocket udpSocket;
|
private DatagramSocket udpSocket;
|
||||||
private MatchBrowserInterface matchBrowserInterface;
|
private MatchBrowserInterface matchBrowserInterface;
|
||||||
|
|
||||||
@@ -37,8 +43,31 @@ public class HostGameController extends Controller {
|
|||||||
loadMaps();
|
loadMaps();
|
||||||
this.udpSocket = MatchBrowserSingleton.getInstance().getUdpSocket();
|
this.udpSocket = MatchBrowserSingleton.getInstance().getUdpSocket();
|
||||||
this.matchBrowserInterface = MatchBrowserSingleton.getInstance().getMatchBrowserInterface();
|
this.matchBrowserInterface = MatchBrowserSingleton.getInstance().getMatchBrowserInterface();
|
||||||
|
setRaceLengthSlider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the values and display for a slider object which allows a user
|
||||||
|
* to select how many minutes long they would like their race to be.
|
||||||
|
*/
|
||||||
|
private void setRaceLengthSlider(){
|
||||||
|
// set the listener to update the label
|
||||||
|
sliderLength.valueProperty().addListener((ov, old_val, new_val) -> {
|
||||||
|
selectedRaceLength = new_val.intValue();
|
||||||
|
if (selectedRaceLength == 1){
|
||||||
|
lblLength.setText(selectedRaceLength + " minute.");
|
||||||
|
} else {
|
||||||
|
lblLength.setText(selectedRaceLength + " minutes.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// set values and marks to be displayed
|
||||||
|
sliderLength.setMin(5);
|
||||||
|
sliderLength.setMax(MAX_RACE_LENGTH);
|
||||||
|
sliderLength.setShowTickLabels(true);
|
||||||
|
sliderLength.setMajorTickUnit(MAX_RACE_LENGTH-1);
|
||||||
|
sliderLength.setBlockIncrement(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,8 +83,8 @@ public class HostGameController extends Controller {
|
|||||||
listOfMaps = new ArrayList(Arrays.asList(ac35Map, oMap, iMap, mMap));
|
listOfMaps = new ArrayList(Arrays.asList(ac35Map, oMap, iMap, mMap));
|
||||||
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
mapImage.fitWidthProperty()
|
mapImage.fitHeightProperty()
|
||||||
.bind(mapImage.getScene().getWindow().widthProperty().multiply(0.6));
|
.bind(mapImage.getScene().getWindow().heightProperty().multiply(0.6));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +93,8 @@ public class HostGameController extends Controller {
|
|||||||
*/
|
*/
|
||||||
public void hostGamePressed() {
|
public void hostGamePressed() {
|
||||||
try {
|
try {
|
||||||
App.game = new Event(false, currentMapIndex);
|
App.game = new Event(false, currentMapIndex,
|
||||||
|
selectedRaceLength*60*1000);
|
||||||
App.gameType = currentMapIndex;
|
App.gameType = currentMapIndex;
|
||||||
|
|
||||||
HttpMatchBrowserHost matchBrowserHost = new HttpMatchBrowserHost();
|
HttpMatchBrowserHost matchBrowserHost = new HttpMatchBrowserHost();
|
||||||
|
|||||||
@@ -12,25 +12,31 @@ import javafx.scene.AmbientLight;
|
|||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.PointLight;
|
import javafx.scene.PointLight;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.effect.Light;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.paint.PhongMaterial;
|
import javafx.scene.paint.PhongMaterial;
|
||||||
import javafx.scene.shape.MeshView;
|
import javafx.scene.shape.MeshView;
|
||||||
|
import javafx.scene.shape.Shape3D;
|
||||||
import mock.app.Event;
|
import mock.app.Event;
|
||||||
import network.Messages.Enums.RaceStatusEnum;
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
import network.Messages.Enums.RequestToJoinEnum;
|
import network.Messages.Enums.RequestToJoinEnum;
|
||||||
|
import shared.model.Boat;
|
||||||
import visualiser.app.App;
|
import visualiser.app.App;
|
||||||
import visualiser.gameController.ControllerClient;
|
import visualiser.gameController.ControllerClient;
|
||||||
import visualiser.layout.*;
|
import visualiser.layout.*;
|
||||||
import visualiser.model.VisualiserBoat;
|
import visualiser.model.VisualiserBoat;
|
||||||
import visualiser.model.VisualiserRaceEvent;
|
import visualiser.model.VisualiserRaceEvent;
|
||||||
import visualiser.model.VisualiserRaceState;
|
import visualiser.model.VisualiserRaceState;
|
||||||
|
import visualiser.network.HttpMatchBrowserHost;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
@@ -68,11 +74,13 @@ public class InGameLobbyController extends Controller {
|
|||||||
private Label playerLabel6;
|
private Label playerLabel6;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label countdownLabel;
|
private Button startButton;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private GridPane animatedPane;
|
private GridPane animatedPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button quitButton;
|
||||||
|
|
||||||
private Event game;
|
private Event game;
|
||||||
|
|
||||||
@@ -197,34 +205,54 @@ public class InGameLobbyController extends Controller {
|
|||||||
|
|
||||||
for (VisualiserBoat boat : copy) {
|
for (VisualiserBoat boat : copy) {
|
||||||
View3D playerBoatToSet = new View3D();
|
View3D playerBoatToSet = new View3D();
|
||||||
|
|
||||||
playerBoatToSet.setItems(subjects);
|
playerBoatToSet.setItems(subjects);
|
||||||
|
|
||||||
playerContainer.add(playerBoatToSet, (count % 3) , row);
|
playerContainer.add(playerBoatToSet, (count % 3) , row);
|
||||||
playerContainer.setMargin(playerBoatToSet, new Insets(10, 10, 10, 10));
|
playerContainer.setMargin(playerBoatToSet, new Insets(10, 10, 10, 10));
|
||||||
|
|
||||||
SeaSurface sea = new SeaSurface(750, 200);
|
// SeaSurface sea = new SeaSurface(750, 200);
|
||||||
sea.setX(250);
|
// sea.setX(250);
|
||||||
sea.setZ(210);
|
// sea.setZ(210);
|
||||||
subjects.add(sea);
|
// subjects.add(sea);
|
||||||
|
//
|
||||||
|
NewSeaSurface sea = new NewSeaSurface();
|
||||||
|
Subject3D seaSubject = new Annotation3D(sea);
|
||||||
|
seaSubject.setX(50);
|
||||||
|
seaSubject.setZ(50);
|
||||||
|
seaSubject.setXRot(0);
|
||||||
|
subjects.add(seaSubject);
|
||||||
|
|
||||||
MeshView mesh = new MeshView(importer.getImport());
|
// MeshView mesh = new MeshView(importer.getImport());
|
||||||
|
// Subject3D subject = new Subject3D(mesh,0);
|
||||||
|
// subjects.add(subject);
|
||||||
|
Shape3D mesh = Assets3D.getBoat();
|
||||||
PhongMaterial boatColorMat = new PhongMaterial(boat.getColor());
|
PhongMaterial boatColorMat = new PhongMaterial(boat.getColor());
|
||||||
mesh.setMaterial(boatColorMat);
|
//mesh.setMaterial(boatColorMat);
|
||||||
Subject3D subject = new Subject3D(mesh,0);
|
Subject3D subject = new Subject3D(mesh, 0);
|
||||||
subjects.add(subject);
|
subjects.add(subject);
|
||||||
|
|
||||||
|
Sails3D sails3D = new Sails3D();
|
||||||
|
Subject3D sailsSubject = new Subject3D(sails3D, 0);
|
||||||
|
sails3D.setMaterial(boatColorMat);
|
||||||
|
subjects.add(sailsSubject);
|
||||||
|
sailsSubject.setXRot(0d);
|
||||||
|
//sails3D.startLuffing();
|
||||||
|
|
||||||
playerBoatToSet.setDistance(50);
|
playerBoatToSet.setDistance(50);
|
||||||
playerBoatToSet.setYaw(45);
|
playerBoatToSet.setYaw(45);
|
||||||
playerBoatToSet.setPitch(20);
|
playerBoatToSet.setPitch(20);
|
||||||
|
|
||||||
|
|
||||||
if (boat.isClientBoat()) {
|
if (boat.isClientBoat()) {
|
||||||
Shockwave boatHighlight = new Shockwave(10);
|
/*Shockwave boatHighlight = new Shockwave(10);
|
||||||
boatHighlight.getMesh().setMaterial(new PhongMaterial(new Color(1, 1, 0, 0.1)));
|
boatHighlight.getMesh().setMaterial(new PhongMaterial(new Color(1, 1, 0, 0.1)));*/
|
||||||
boatHighlight.setX(subject.getPosition().getX());
|
|
||||||
boatHighlight.setY(subject.getPosition().getY());
|
Assets3D.boatHighlight.setX(subject.getPosition().getX());
|
||||||
boatHighlight.setZ(subject.getPosition().getZ());
|
Assets3D.boatHighlight.setY(subject.getPosition().getY());
|
||||||
subjects.add(boatHighlight);
|
Assets3D.boatHighlight.setZ(subject.getPosition().getZ());
|
||||||
|
|
||||||
|
subjects.add(Assets3D.boatHighlight);
|
||||||
subject.getMesh().toFront();
|
subject.getMesh().toFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +260,9 @@ public class InGameLobbyController extends Controller {
|
|||||||
@Override
|
@Override
|
||||||
public void handle(long now) {
|
public void handle(long now) {
|
||||||
subject.setHeading(subject.getHeading().getAngle() + 0.1);
|
subject.setHeading(subject.getHeading().getAngle() + 0.1);
|
||||||
|
sailsSubject.setHeading(subject.getHeading().getAngle() + 0.1);
|
||||||
|
sailsSubject.setX(subject.getX());
|
||||||
|
sailsSubject.setZ(subject.getZ());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
rotate.start();
|
rotate.start();
|
||||||
@@ -258,37 +289,13 @@ public class InGameLobbyController extends Controller {
|
|||||||
* Starts the race.
|
* Starts the race.
|
||||||
*/
|
*/
|
||||||
private void startRace() {
|
private void startRace() {
|
||||||
//Initialises the race clock.
|
|
||||||
initialiseRaceClock(this.visualiserRaceEvent.getVisualiserRaceState());
|
|
||||||
|
|
||||||
//Starts the race countdown timer.
|
//Starts the race countdown timer.
|
||||||
countdownTimer();
|
countdownTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the race clock/timer labels for the start time, current time, and remaining time.
|
|
||||||
* @param visualiserRace The race to get data from.
|
|
||||||
*/
|
|
||||||
private void initialiseRaceClock(VisualiserRaceState visualiserRace) {
|
|
||||||
//Remaining time.
|
|
||||||
initialiseRaceClockDuration(visualiserRace);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the race duration label.
|
|
||||||
* @param visualiserRace The race to get data from.
|
|
||||||
*/
|
|
||||||
private void initialiseRaceClockDuration(VisualiserRaceState visualiserRace) {
|
|
||||||
|
|
||||||
visualiserRace.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
countdownLabel.setText(newValue);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Countdown timer until race starts.
|
* Countdown timer until race starts.
|
||||||
*/
|
*/
|
||||||
@@ -299,6 +306,7 @@ public class InGameLobbyController extends Controller {
|
|||||||
//Get the current race status.
|
//Get the current race status.
|
||||||
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
|
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
|
||||||
|
|
||||||
|
|
||||||
//If the race has reached the preparatory phase, or has started...
|
//If the race has reached the preparatory phase, or has started...
|
||||||
if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) {
|
if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) {
|
||||||
//Stop this timer.
|
//Stop this timer.
|
||||||
@@ -336,6 +344,8 @@ public class InGameLobbyController extends Controller {
|
|||||||
|
|
||||||
this.visualiserRaceEvent.getVisualiserRaceState().getBoats().addListener(this.lobbyUpdateListener);
|
this.visualiserRaceEvent.getVisualiserRaceState().getBoats().addListener(this.lobbyUpdateListener);
|
||||||
|
|
||||||
|
enableStartIfHost();
|
||||||
|
|
||||||
startRace();
|
startRace();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
//TODO should probably let this propagate, so that we only enter this scene if everything works
|
//TODO should probably let this propagate, so that we only enter this scene if everything works
|
||||||
@@ -343,6 +353,22 @@ public class InGameLobbyController extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables the start button if the client is the host of the game.
|
||||||
|
*/
|
||||||
|
private void enableStartIfHost() {
|
||||||
|
if (isHost) {
|
||||||
|
startButton.setVisible(true);
|
||||||
|
startButton.setDisable(false);
|
||||||
|
} else {
|
||||||
|
startButton.setVisible(false);
|
||||||
|
startButton.setDisable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Menu button pressed. Prompt alert then return to menu
|
* Menu button pressed. Prompt alert then return to menu
|
||||||
* @throws IOException socket erro
|
* @throws IOException socket erro
|
||||||
@@ -370,7 +396,12 @@ public class InGameLobbyController extends Controller {
|
|||||||
* Start button pressed. Currently only prints out start
|
* Start button pressed. Currently only prints out start
|
||||||
*/
|
*/
|
||||||
public void startBtnPressed(){
|
public void startBtnPressed(){
|
||||||
//System.out.println("Should start the race. This button is only visible for the host");
|
try {
|
||||||
|
HttpMatchBrowserHost.httpMatchBrowserHost.sendStarted();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
App.game.getRaceLogic().getRace().startRace(App.game.getRaceLogic().getRace().getRacePreparatoryTime(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void joinSpecPressed(){
|
public void joinSpecPressed(){
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class LobbyController extends Controller {
|
|||||||
private @FXML TextField addressFld;
|
private @FXML TextField addressFld;
|
||||||
private @FXML TextField portFld;
|
private @FXML TextField portFld;
|
||||||
|
|
||||||
private ObservableList<RaceConnection> connections;
|
private ObservableList<RaceConnection> allConnections;
|
||||||
private ObservableList<RaceConnection> customConnections;
|
private ObservableList<RaceConnection> customConnections;
|
||||||
|
|
||||||
private AudioClip sound;
|
private AudioClip sound;
|
||||||
@@ -51,12 +51,20 @@ public class LobbyController extends Controller {
|
|||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
httpMatchBrowserClient = new HttpMatchBrowserClient();
|
httpMatchBrowserClient = new HttpMatchBrowserClient();
|
||||||
|
httpMatchBrowserClient.connections.addListener(new ListChangeListener<RaceConnection>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(Change<? extends RaceConnection> c) {
|
||||||
|
refreshTable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
new Thread(httpMatchBrowserClient, "Match Client").start();
|
new Thread(httpMatchBrowserClient, "Match Client").start();
|
||||||
// set up the connection table
|
// set up the connection table
|
||||||
customConnections = FXCollections.observableArrayList();
|
customConnections = FXCollections.observableArrayList();
|
||||||
|
allConnections = FXCollections.observableArrayList();
|
||||||
//connections.add(new RaceConnection("localhost", 4942, "Local Game"));
|
//connections.add(new RaceConnection("localhost", 4942, "Local Game"));
|
||||||
|
|
||||||
lobbyTable.setItems(httpMatchBrowserClient.connections);
|
lobbyTable.setItems(allConnections);
|
||||||
gameNameColumn.setCellValueFactory(cellData -> cellData.getValue().gamenameProperty());
|
gameNameColumn.setCellValueFactory(cellData -> cellData.getValue().gamenameProperty());
|
||||||
hostNameColumn.setCellValueFactory(cellData -> cellData.getValue().hostnameProperty());
|
hostNameColumn.setCellValueFactory(cellData -> cellData.getValue().hostnameProperty());
|
||||||
statusColumn.setCellValueFactory(cellData -> cellData.getValue().statusProperty());
|
statusColumn.setCellValueFactory(cellData -> cellData.getValue().statusProperty());
|
||||||
@@ -82,8 +90,14 @@ public class LobbyController extends Controller {
|
|||||||
public void refreshBtnPressed(){
|
public void refreshBtnPressed(){
|
||||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||||
sound.play();
|
sound.play();
|
||||||
|
refreshTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshTable() {
|
||||||
|
allConnections.clear();
|
||||||
|
addCustomGames();
|
||||||
addServerGames();
|
addServerGames();
|
||||||
for(RaceConnection connection: connections) {
|
for(RaceConnection connection: allConnections) {
|
||||||
connection.check();
|
connection.check();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -144,9 +158,9 @@ public class LobbyController extends Controller {
|
|||||||
try {
|
try {
|
||||||
int port = Integer.parseInt(portString);
|
int port = Integer.parseInt(portString);
|
||||||
customConnections.add(new RaceConnection(hostName, port, "Boat Game"));
|
customConnections.add(new RaceConnection(hostName, port, "Boat Game"));
|
||||||
connections.addAll(customConnections);
|
|
||||||
addressFld.clear();
|
addressFld.clear();
|
||||||
portFld.clear();
|
portFld.clear();
|
||||||
|
refreshTable();
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
System.err.println("Port number entered is not a number");
|
System.err.println("Port number entered is not a number");
|
||||||
}
|
}
|
||||||
@@ -173,17 +187,14 @@ public class LobbyController extends Controller {
|
|||||||
* Adds the games received from the server
|
* Adds the games received from the server
|
||||||
*/
|
*/
|
||||||
private void addServerGames() {
|
private void addServerGames() {
|
||||||
httpMatchBrowserClient.connections.addAll(customConnections);
|
allConnections.addAll(httpMatchBrowserClient.connections);
|
||||||
httpMatchBrowserClient.connections.addListener(new ListChangeListener<RaceConnection>() {
|
|
||||||
@Override
|
|
||||||
public void onChanged(Change<? extends RaceConnection> c) {
|
|
||||||
refreshBtnPressed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
for (HostGame game : matchBrowserLobbyInterface.getGames()) {
|
for (HostGame game : matchBrowserLobbyInterface.getGames()) {
|
||||||
connections.add(new RaceConnection(game.getIp(), 4942, "Boat Game"));
|
connections.add(new RaceConnection(game.getIp(), 4942, "Boat Game"));
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addCustomGames() {
|
||||||
|
allConnections.addAll(customConnections);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
||||||
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Point3D;
|
||||||
|
import javafx.scene.AmbientLight;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.PhongMaterial;
|
||||||
|
import javafx.scene.shape.Cylinder;
|
||||||
|
import javafx.scene.shape.MeshView;
|
||||||
|
import javafx.scene.shape.Shape3D;
|
||||||
|
import javafx.scene.transform.Rotate;
|
||||||
|
import shared.model.Bearing;
|
||||||
|
import shared.model.CompoundMark;
|
||||||
|
import shared.model.GPSCoordinate;
|
||||||
|
import visualiser.layout.Annotation3D;
|
||||||
|
import visualiser.layout.Assets3D;
|
||||||
|
import visualiser.layout.Subject3D;
|
||||||
|
import visualiser.layout.View3D;
|
||||||
|
import visualiser.model.VisualiserBoat;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Observable;
|
||||||
|
import java.util.Observer;
|
||||||
|
|
||||||
|
public class NextMarkController {
|
||||||
|
private @FXML StackPane arrowStackPane2d;
|
||||||
|
private @FXML StackPane arrowStackPane3d;
|
||||||
|
private @FXML Pane pane2d;
|
||||||
|
private @FXML Pane pane3d;
|
||||||
|
|
||||||
|
private VisualiserBoat boat;
|
||||||
|
|
||||||
|
public void initialiseArrowView(VisualiserBoat boat) {
|
||||||
|
this.boat = boat;
|
||||||
|
pane2d.setVisible(true);
|
||||||
|
pane3d.setVisible(false);
|
||||||
|
initialise2dArrowView();
|
||||||
|
initialise3dArrowView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialise2dArrowView() {
|
||||||
|
AnimationTimer arrow2d = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
if (boat.getCurrentLeg().getEndCompoundMark() != null) {
|
||||||
|
CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
|
||||||
|
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
|
||||||
|
arrowStackPane2d.setRotate(headingToMark.degrees());
|
||||||
|
} else {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
arrow2d.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialise3dArrowView() {
|
||||||
|
ObservableList<Subject3D> viewSubjects = FXCollections.observableArrayList();
|
||||||
|
String arrowPath = "assets/mark_arrow.x3d";
|
||||||
|
|
||||||
|
Shape3D arrow = Assets3D.loadX3d(arrowPath);
|
||||||
|
|
||||||
|
arrow.setScaleX(15);
|
||||||
|
arrow.setScaleY(25);
|
||||||
|
arrow.setScaleZ(50);
|
||||||
|
arrow.setRotationAxis(new Point3D(1,0,0));
|
||||||
|
|
||||||
|
arrowStackPane3d.getChildren().add(arrow);
|
||||||
|
|
||||||
|
AnimationTimer arrow3d = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
if (boat.getCurrentLeg().getEndCompoundMark() != null) {
|
||||||
|
arrow.getTransforms().clear();
|
||||||
|
double zRotation = calculateZRotate();
|
||||||
|
arrow.setRotate(calculateXRotate(zRotation));
|
||||||
|
arrow.getTransforms().add(new Rotate(zRotation, new Point3D(0, 0, 1)));
|
||||||
|
} else {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
arrow3d.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show2d() {
|
||||||
|
pane3d.setVisible(false);
|
||||||
|
pane2d.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show3d() {
|
||||||
|
pane2d.setVisible(false);
|
||||||
|
pane3d.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double calculateZRotate() {
|
||||||
|
CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
|
||||||
|
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
|
||||||
|
return -headingToMark.degrees() + boat.getBearing().degrees() + 180;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double calculateXRotate(double zRotation) {
|
||||||
|
// if (zRotation > 360) {
|
||||||
|
// zRotation -=360;
|
||||||
|
// } else if (zRotation < 0) {
|
||||||
|
// zRotation += 360;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (zRotation > 180) {
|
||||||
|
// zRotation = 360 - zRotation;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return 70;
|
||||||
|
//return 90 - 20 * Math.cos(Math.toRadians(zRotation));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package visualiser.Controllers;
|
package visualiser.Controllers;
|
||||||
|
|
||||||
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
||||||
|
import eu.hansolo.medusa.*;
|
||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
@@ -12,20 +13,29 @@ import javafx.scene.AmbientLight;
|
|||||||
import javafx.scene.PointLight;
|
import javafx.scene.PointLight;
|
||||||
import javafx.scene.chart.LineChart;
|
import javafx.scene.chart.LineChart;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.media.AudioClip;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.paint.Material;
|
import javafx.scene.paint.Material;
|
||||||
import javafx.scene.paint.PhongMaterial;
|
import javafx.scene.paint.PhongMaterial;
|
||||||
|
import javafx.scene.shape.Cylinder;
|
||||||
|
import javafx.scene.paint.Stop;
|
||||||
import javafx.scene.shape.MeshView;
|
import javafx.scene.shape.MeshView;
|
||||||
import javafx.scene.shape.Shape3D;
|
import javafx.scene.shape.Shape3D;
|
||||||
|
import javafx.scene.shape.Sphere;
|
||||||
import javafx.scene.transform.Translate;
|
import javafx.scene.transform.Translate;
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
|
import network.Messages.Enums.BoatStatusEnum;
|
||||||
import network.Messages.Enums.RaceStatusEnum;
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
import shared.dataInput.RaceDataSource;
|
import shared.dataInput.RaceDataSource;
|
||||||
|
import shared.enums.RoundingType;
|
||||||
import shared.exceptions.BoatNotFoundException;
|
import shared.exceptions.BoatNotFoundException;
|
||||||
import shared.model.*;
|
import shared.model.*;
|
||||||
import visualiser.app.App;
|
import visualiser.app.App;
|
||||||
@@ -38,11 +48,9 @@ import visualiser.model.*;
|
|||||||
import visualiser.utils.GPSConverter;
|
import visualiser.utils.GPSConverter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -61,9 +69,17 @@ public class RaceViewController extends Controller {
|
|||||||
private boolean isTutorial = false;
|
private boolean isTutorial = false;
|
||||||
private String keyToPress;
|
private String keyToPress;
|
||||||
private View3D view3D;
|
private View3D view3D;
|
||||||
|
private WindCompass windCompass;
|
||||||
private ObservableList<Subject3D> viewSubjects;
|
private ObservableList<Subject3D> viewSubjects;
|
||||||
|
private Gauge gauge;
|
||||||
|
private FGauge fGauge;
|
||||||
|
private long positionDelay = 1000;
|
||||||
|
private long positionTime = 0;
|
||||||
private ResizableRaceCanvas raceCanvas;
|
private ResizableRaceCanvas raceCanvas;
|
||||||
private boolean mapToggle = true;
|
private boolean mapToggle = true;
|
||||||
|
private GPSConverter gpsConverter;
|
||||||
|
private ArrayList<HealthEffect> healthEffectList = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrow pointing to next mark in third person
|
* Arrow pointing to next mark in third person
|
||||||
@@ -74,12 +90,22 @@ public class RaceViewController extends Controller {
|
|||||||
*/
|
*/
|
||||||
private AnimationTimer pointToMark;
|
private AnimationTimer pointToMark;
|
||||||
|
|
||||||
|
//seagulls
|
||||||
|
private ObservableList<Subject3D> seagulls = FXCollections.observableArrayList();
|
||||||
|
//seagulls goto
|
||||||
|
private Map<Subject3D, List<Double>> seagullsGoToX = new HashMap<>();
|
||||||
|
private Map<Subject3D, List<Double>> seagullsGoToY = new HashMap<>();
|
||||||
|
private double seagullSpeed = 0.01;
|
||||||
|
|
||||||
// note: it says it's not used but it is! do not remove :)
|
// note: it says it's not used but it is! do not remove :)
|
||||||
private @FXML ArrowController arrowController;
|
private @FXML ArrowController arrowController;
|
||||||
|
private @FXML NextMarkController nextMarkController;
|
||||||
private @FXML GridPane canvasBase;
|
private @FXML GridPane canvasBase;
|
||||||
private @FXML GridPane canvasBase1;
|
private @FXML GridPane canvasBase1;
|
||||||
|
private @FXML GridPane canvasBase2;
|
||||||
private @FXML SplitPane racePane;
|
private @FXML SplitPane racePane;
|
||||||
private @FXML StackPane arrowPane;
|
private @FXML StackPane arrowPane;
|
||||||
|
private @FXML Pane nextMarkPane;
|
||||||
private @FXML Label timer;
|
private @FXML Label timer;
|
||||||
private @FXML Label FPS;
|
private @FXML Label FPS;
|
||||||
private @FXML Label timeZone;
|
private @FXML Label timeZone;
|
||||||
@@ -89,10 +115,17 @@ public class RaceViewController extends Controller {
|
|||||||
private @FXML TableColumn<VisualiserBoat, String> boatTeamColumn;
|
private @FXML TableColumn<VisualiserBoat, String> boatTeamColumn;
|
||||||
private @FXML TableColumn<VisualiserBoat, Leg> boatMarkColumn;
|
private @FXML TableColumn<VisualiserBoat, Leg> boatMarkColumn;
|
||||||
private @FXML TableColumn<VisualiserBoat, Number> boatSpeedColumn;
|
private @FXML TableColumn<VisualiserBoat, Number> boatSpeedColumn;
|
||||||
|
private @FXML TableColumn<VisualiserBoat, Number> boatHealthColumn;
|
||||||
private @FXML LineChart<Number, Number> sparklineChart;
|
private @FXML LineChart<Number, Number> sparklineChart;
|
||||||
private @FXML Label tutorialText;
|
private @FXML Label tutorialText;
|
||||||
private @FXML AnchorPane infoWrapper;
|
private @FXML AnchorPane infoWrapper;
|
||||||
private @FXML AnchorPane lineChartWrapper;
|
private @FXML AnchorPane lineChartWrapper;
|
||||||
|
private @FXML StackPane speedPane;
|
||||||
|
private @FXML AnchorPane raceAnchorPane;
|
||||||
|
private @FXML GridPane playerHealthContainer;
|
||||||
|
private @FXML ImageView imageView;
|
||||||
|
private @FXML AnchorPane deathTransPane;
|
||||||
|
private @FXML StackPane deathPane;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a specified race.
|
* Displays a specified race.
|
||||||
@@ -107,11 +140,13 @@ public class RaceViewController extends Controller {
|
|||||||
this.controllerClient = controllerClient;
|
this.controllerClient = controllerClient;
|
||||||
this.isHost = isHost;
|
this.isHost = isHost;
|
||||||
keyFactory.load();
|
keyFactory.load();
|
||||||
|
deathPane.setDisable(false);
|
||||||
|
deathPane.setVisible(false);
|
||||||
tutorialCheck();
|
tutorialCheck();
|
||||||
initKeypressHandler();
|
initKeypressHandler();
|
||||||
|
healthLoop();
|
||||||
initialiseRaceVisuals();
|
initialiseRaceVisuals();
|
||||||
initialiseRaceCanvas();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,7 +158,6 @@ public class RaceViewController extends Controller {
|
|||||||
isTutorial = true;
|
isTutorial = true;
|
||||||
tutorialText.setVisible(true);
|
tutorialText.setVisible(true);
|
||||||
tutorialStates = new ArrayList<>(Arrays.asList(TutorialState.values()));
|
tutorialStates = new ArrayList<>(Arrays.asList(TutorialState.values()));
|
||||||
|
|
||||||
currentState = tutorialStates.get(0);
|
currentState = tutorialStates.get(0);
|
||||||
tutorialStates.remove(0);
|
tutorialStates.remove(0);
|
||||||
searchMapForKey("Upwind");
|
searchMapForKey("Upwind");
|
||||||
@@ -204,37 +238,122 @@ public class RaceViewController extends Controller {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create speedometer
|
||||||
|
*/
|
||||||
|
private void initialiseSpeedometer() {
|
||||||
|
//Create the Medusa Gauge
|
||||||
|
gauge = GaugeBuilder.create()
|
||||||
|
.prefSize(200,200)
|
||||||
|
.foregroundBaseColor(Color.WHITE)
|
||||||
|
.title("Title")
|
||||||
|
.subTitle("Speed")
|
||||||
|
.unit("Knots")
|
||||||
|
.decimals(2)
|
||||||
|
.lcdVisible(true)
|
||||||
|
.lcdDesign(LcdDesign.STANDARD)
|
||||||
|
.lcdFont(LcdFont.DIGITAL_BOLD)
|
||||||
|
.scaleDirection(Gauge.ScaleDirection.CLOCKWISE)
|
||||||
|
.minValue(0)
|
||||||
|
.maxValue(50)
|
||||||
|
.startAngle(320)
|
||||||
|
.angleRange(280)
|
||||||
|
.tickLabelDecimals(0)
|
||||||
|
.tickLabelLocation(TickLabelLocation.INSIDE)
|
||||||
|
.tickLabelOrientation(TickLabelOrientation.ORTHOGONAL)
|
||||||
|
.onlyFirstAndLastTickLabelVisible(false)
|
||||||
|
.tickLabelSectionsVisible(false)
|
||||||
|
.tickLabelColor(Color.BLACK)
|
||||||
|
.tickMarkSectionsVisible(false)
|
||||||
|
.majorTickMarksVisible(true)
|
||||||
|
.majorTickMarkType(TickMarkType.TRAPEZOID)
|
||||||
|
.mediumTickMarksVisible(false)
|
||||||
|
.mediumTickMarkType(TickMarkType.LINE)
|
||||||
|
.minorTickMarksVisible(true)
|
||||||
|
.minorTickMarkType(TickMarkType.LINE)
|
||||||
|
.ledVisible(false)
|
||||||
|
.ledType(Gauge.LedType.STANDARD)
|
||||||
|
.ledColor(Color.rgb(255, 200, 0))
|
||||||
|
.ledBlinking(false)
|
||||||
|
.needleShape(Gauge.NeedleShape.ANGLED)
|
||||||
|
.needleSize(Gauge.NeedleSize.STANDARD)
|
||||||
|
.needleColor(Color.CRIMSON)
|
||||||
|
.startFromZero(false)
|
||||||
|
.returnToZero(false)
|
||||||
|
.knobType(Gauge.KnobType.METAL)
|
||||||
|
.knobColor(Color.LIGHTGRAY)
|
||||||
|
.interactive(false)
|
||||||
|
.thresholdVisible(true)
|
||||||
|
.threshold(50)
|
||||||
|
.thresholdColor(Color.RED)
|
||||||
|
.checkThreshold(true)
|
||||||
|
.gradientBarEnabled(true)
|
||||||
|
.gradientBarStops(new Stop(0.0, Color.BLUE),
|
||||||
|
new Stop(0.25, Color.CYAN),
|
||||||
|
new Stop(0.5, Color.LIME),
|
||||||
|
new Stop(0.75, Color.YELLOW),
|
||||||
|
new Stop(1.0, Color.RED))
|
||||||
|
.markersVisible(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
//Create a gauge with a frame and background that utilizes a Medusa gauge
|
||||||
|
fGauge = FGaugeBuilder
|
||||||
|
.create()
|
||||||
|
.prefSize(190, 190)
|
||||||
|
.gauge(gauge)
|
||||||
|
.gaugeDesign(GaugeDesign.METAL)
|
||||||
|
.gaugeBackground(GaugeDesign.GaugeBackground.CARBON)
|
||||||
|
.foregroundVisible(true)
|
||||||
|
.build();
|
||||||
|
speedPane.getChildren().add(fGauge);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises the various UI components to listen to the {@link #visualiserRace}.
|
* Initialises the various UI components to listen to the {@link #visualiserRace}.
|
||||||
*/
|
*/
|
||||||
private void initialiseRaceVisuals() {
|
private void initialiseRaceVisuals() {
|
||||||
|
|
||||||
// Import arrow mesh
|
|
||||||
URL asset = this.getClass().getClassLoader().getResource("assets/arrow V1.0.4.stl");
|
|
||||||
StlMeshImporter importer = new StlMeshImporter();
|
|
||||||
importer.read(asset);
|
|
||||||
|
|
||||||
MeshView arrow = new MeshView(importer.getImport());
|
|
||||||
PhongMaterial arrowMat = new PhongMaterial(Color.RED);
|
|
||||||
arrow.setMaterial(arrowMat);
|
|
||||||
|
|
||||||
this.nextMarkArrow = new Annotation3D(arrow);
|
|
||||||
this.nextMarkArrow.setScale(0.1);
|
|
||||||
|
|
||||||
// initialise displays
|
// initialise displays
|
||||||
initialiseFps();
|
initialiseFps();
|
||||||
initialiseInfoTable();
|
initialiseInfoTable();
|
||||||
initialiseView3D(this.visualiserRace);
|
initialiseView3D(this.visualiserRace);
|
||||||
|
initialiseHealthPane();
|
||||||
initialiseRaceClock();
|
initialiseRaceClock();
|
||||||
|
initialiseSpeedometer();
|
||||||
|
initialiseRaceCanvas();
|
||||||
raceTimer(); // start the timer
|
raceTimer(); // start the timer
|
||||||
|
//nextMarkPane.toFront();
|
||||||
|
speedometerLoop();
|
||||||
new Sparkline(this.raceState, this.sparklineChart);
|
new Sparkline(this.raceState, this.sparklineChart);
|
||||||
timeZone.setText(this.raceState.getRaceClock().getTimeZone());
|
}
|
||||||
arrowController.setWindProperty(this.raceState.windProperty());
|
|
||||||
|
|
||||||
|
|
||||||
|
private void initialiseHealthPane() {
|
||||||
|
InputStream tomato = this.getClass().getClassLoader().getResourceAsStream("visualiser/images/tomato.png");
|
||||||
|
HealthSlider healthSlider = new HealthSlider(new Image(tomato));
|
||||||
|
playerHealthContainer.add(healthSlider, 0, 0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
VisualiserBoat player = raceState.getBoat(raceState.getPlayerBoatID());
|
||||||
|
player.healthProperty().addListener((o, prev, curr) -> {
|
||||||
|
healthSlider.setCrop((double)curr/100.0);
|
||||||
|
});
|
||||||
|
} catch (BoatNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialiseView3D(VisualiserRaceEvent race) {
|
private void initialiseView3D(VisualiserRaceEvent race) {
|
||||||
viewSubjects = FXCollections.observableArrayList();
|
viewSubjects = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
try {
|
||||||
|
nextMarkController.initialiseArrowView(race.getVisualiserRaceState().getBoat(race.getVisualiserRaceState().getPlayerBoatID()));
|
||||||
|
} catch (BoatNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
AmbientLight ambientLight = new AmbientLight(Color.web("#CCCCFF"));
|
AmbientLight ambientLight = new AmbientLight(Color.web("#CCCCFF"));
|
||||||
ambientLight.setTranslateX(250);
|
ambientLight.setTranslateX(250);
|
||||||
ambientLight.setTranslateZ(210);
|
ambientLight.setTranslateZ(210);
|
||||||
@@ -268,24 +387,46 @@ public class RaceViewController extends Controller {
|
|||||||
view3D.addPointLight(pointLight);
|
view3D.addPointLight(pointLight);
|
||||||
canvasBase.add(view3D, 0, 0);
|
canvasBase.add(view3D, 0, 0);
|
||||||
|
|
||||||
|
windCompass = new WindCompass(view3D, this.raceState.windProperty());
|
||||||
|
arrowPane.getChildren().add(windCompass);
|
||||||
|
|
||||||
// Set up projection from GPS to view
|
// Set up projection from GPS to view
|
||||||
RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource();
|
RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource();
|
||||||
final GPSConverter gpsConverter = new GPSConverter(raceData, 450, 450);
|
gpsConverter = new GPSConverter(raceData, 450, 450);
|
||||||
|
|
||||||
SkyBox skyBox = new SkyBox(750, 200, 250, 0, 210);
|
SkyBox skyBox = Assets3D.skyBox;
|
||||||
viewSubjects.addAll(skyBox.getSkyBoxPlanes());
|
viewSubjects.addAll(skyBox.getSkyBoxPlanes());
|
||||||
|
|
||||||
// Set up sea surface
|
// Set up sea surface
|
||||||
SeaSurface sea = new SeaSurface(750, 200);
|
// SeaSurface sea = new SeaSurface(750, 200);
|
||||||
sea.setX(250);
|
// sea.setX(250);
|
||||||
sea.setZ(210);
|
// sea.setZ(210);
|
||||||
viewSubjects.add(sea);
|
// viewSubjects.add(sea);
|
||||||
|
|
||||||
// Set up sea surface overlay
|
// Set up sea surface overlay
|
||||||
SeaSurface seaOverlay = new SeaSurface(4000, 200);
|
// SeaSurface seaOverlay = new SeaSurface(4000, 200);
|
||||||
seaOverlay.setX(250);
|
// seaOverlay.setX(250);
|
||||||
seaOverlay.setZ(210);
|
// seaOverlay.setZ(210);
|
||||||
viewSubjects.add(seaOverlay);
|
// viewSubjects.add(seaOverlay);
|
||||||
|
// int seaX = 15;
|
||||||
|
// int seaY = 15;
|
||||||
|
int seaX = 15;
|
||||||
|
int seaY = 15;
|
||||||
|
for (int x = 0; x < seaX; x++) {
|
||||||
|
for (int y = 0; y < seaY; y++) {
|
||||||
|
NewSeaSurface seaSurface = new NewSeaSurface();
|
||||||
|
Subject3D seaSubject = new Annotation3D(seaSurface);
|
||||||
|
seaSubject.setXRot(0);
|
||||||
|
// seaSubject.setX(-75 + x * 50);
|
||||||
|
// seaSubject.setZ(-150 + y * 50);
|
||||||
|
// seaSubject.setX(-150 + x * 250);
|
||||||
|
// seaSubject.setZ(-80 + y * 250);
|
||||||
|
seaSubject.setX(-75 + x * 100);
|
||||||
|
seaSubject.setZ(-150 + y * 100);
|
||||||
|
seaSubject.setY(3);
|
||||||
|
viewSubjects.add(seaSubject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Boundary3D boundary3D = new Boundary3D(visualiserRace.getVisualiserRaceState().getRaceDataSource().getBoundary(), gpsConverter);
|
Boundary3D boundary3D = new Boundary3D(visualiserRace.getVisualiserRaceState().getRaceDataSource().getBoundary(), gpsConverter);
|
||||||
for (Subject3D subject3D: boundary3D.getBoundaryNodes()){
|
for (Subject3D subject3D: boundary3D.getBoundaryNodes()){
|
||||||
@@ -293,8 +434,9 @@ public class RaceViewController extends Controller {
|
|||||||
}
|
}
|
||||||
// Position and add each mark to view
|
// Position and add each mark to view
|
||||||
for(Mark mark: race.getVisualiserRaceState().getMarks()) {
|
for(Mark mark: race.getVisualiserRaceState().getMarks()) {
|
||||||
MeshView mesh = new MeshView(importerMark.getImport());
|
// MeshView mesh = new MeshView(importerMark.getImport());
|
||||||
Subject3D markModel = new Subject3D(mesh, mark.getSourceID());
|
// Subject3D markModel = new Subject3D(mesh, mark.getSourceID());
|
||||||
|
Subject3D markModel = new Subject3D(Assets3D.getMark(), mark.getSourceID());
|
||||||
|
|
||||||
markModel.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
|
markModel.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
|
||||||
markModel.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
|
markModel.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
|
||||||
@@ -302,35 +444,158 @@ public class RaceViewController extends Controller {
|
|||||||
viewSubjects.add(markModel);
|
viewSubjects.add(markModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
|
// Position and add each boat to view
|
||||||
|
for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
|
||||||
|
Shape3D mesh = Assets3D.getBoat();
|
||||||
|
|
||||||
|
PhongMaterial boatColorMat = new PhongMaterial(boat.getColor());
|
||||||
|
//mesh.setMaterial(boatColorMat);
|
||||||
|
Subject3D boatModel = new Subject3D(mesh, boat.getSourceID());
|
||||||
|
|
||||||
|
// Configure visualiser for client's boat
|
||||||
if (boat.isClientBoat()) {
|
if (boat.isClientBoat()) {
|
||||||
Shockwave boatHighlight = new Shockwave(10);
|
// Add player boat highlight
|
||||||
boatHighlight.getMesh().setMaterial(new PhongMaterial(new Color(1, 1, 0, 0.1)));
|
// Shockwave boatHighlight = new Shockwave(10);
|
||||||
viewSubjects.add(boatHighlight);
|
// boatHighlight.getMesh().setMaterial(new PhongMaterial(new Color(1, 1, 0, 0.1)));
|
||||||
|
// viewSubjects.add(boatHighlight);
|
||||||
|
|
||||||
|
viewSubjects.add(Assets3D.boatHighlight);
|
||||||
|
|
||||||
|
// Track player boat with camera
|
||||||
|
viewSubjects.add(boatModel);
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
view3D.trackSubject(boatModel, 0);
|
||||||
|
view3D.setThirdPerson();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Track player boat with highlight
|
||||||
AnimationTimer highlightTrack = new AnimationTimer() {
|
AnimationTimer highlightTrack = new AnimationTimer() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(long now) {
|
public void handle(long now) {
|
||||||
boatHighlight.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
Assets3D.boatHighlight.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
||||||
boatHighlight.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
Assets3D.boatHighlight.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
highlightTrack.start();
|
highlightTrack.start();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position and add each boat to view
|
// Highlight next mark only for player boat
|
||||||
for (VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
|
// Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
|
||||||
MeshView mesh;
|
// CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark();
|
||||||
if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) {
|
// view3D.getShape(nextMark.getMark1().getSourceID()).getMesh().setMaterial(markColor);
|
||||||
mesh = new MeshView(importer.getImport());
|
// if(nextMark.getMark2() != null) {
|
||||||
|
// view3D.getShape(nextMark.getMark2().getSourceID()).getMesh().setMaterial(markColor);
|
||||||
|
// }
|
||||||
|
// boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> swapColours(curr)));
|
||||||
|
|
||||||
|
//next mark indicator
|
||||||
|
changeNextMark(boat.getCurrentLeg());
|
||||||
|
viewSubjects.add(Assets3D.ccwNextArrow);
|
||||||
|
viewSubjects.add(Assets3D.cwNextArrow);
|
||||||
|
boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> changeNextMark(curr)));
|
||||||
} else {
|
} else {
|
||||||
mesh = new MeshView(importerBurgerBoat.getImport());
|
viewSubjects.add(boatModel);
|
||||||
|
boatModel.getMesh().toFront();
|
||||||
}
|
}
|
||||||
PhongMaterial boatColorMat = new PhongMaterial(boat.getColor());
|
|
||||||
mesh.setMaterial(boatColorMat);
|
|
||||||
Subject3D boatModel = new Subject3D(mesh, boat.getSourceID());
|
|
||||||
|
|
||||||
viewSubjects.add(boatModel);
|
//Create health effect
|
||||||
|
HealthEffect healthEffect = new HealthEffect(boat.getSourceID(), System.currentTimeMillis());
|
||||||
|
viewSubjects.add(healthEffect);
|
||||||
|
healthEffectList.add(healthEffect);
|
||||||
|
|
||||||
|
//add sail
|
||||||
|
Sails3D sails3D = new Sails3D();
|
||||||
|
Subject3D sailsSubject = new Subject3D(sails3D, 0);
|
||||||
|
sails3D.setMouseTransparent(true);
|
||||||
|
sails3D.setMaterial(boatColorMat);
|
||||||
|
sailsSubject.setXRot(0d);
|
||||||
|
sailsSubject.setHeading(visualiserRace.getVisualiserRaceState().getWindDirection().degrees());
|
||||||
|
viewSubjects.add(sailsSubject);
|
||||||
|
|
||||||
|
|
||||||
|
// SeagullFlock seagullFlock = new SeagullFlock(67, 43, 0);
|
||||||
|
//// seagullFlock.addToFlock();
|
||||||
|
//// seagullFlock.addToFlock();
|
||||||
|
// seagullFlock.setxBound(450);
|
||||||
|
// seagullFlock.setyBound(450);
|
||||||
|
// viewSubjects.addAll(seagullFlock.getSeagulls());
|
||||||
|
//
|
||||||
|
// Subject3D textSubject = new Annotation3D(new SeagullMesh());
|
||||||
|
// textSubject.setY(-10);
|
||||||
|
// viewSubjects.add(textSubject);
|
||||||
|
addToFlock();
|
||||||
|
seagullGoTo.start();
|
||||||
|
|
||||||
|
AnimationTimer rotateArrows = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
Assets3D.ccwNextArrow.setHeading(Assets3D.ccwNextArrow.getHeading().getAngle() - 3.0);
|
||||||
|
Assets3D.cwNextArrow.setHeading(Assets3D.cwNextArrow.getHeading().getAngle() + 3.0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rotateArrows.start();
|
||||||
|
|
||||||
|
AnimationTimer sailsFollowBoat = new AnimationTimer() {
|
||||||
|
double sailCurrent = visualiserRace.getVisualiserRaceState().getWindDirection().degrees();
|
||||||
|
boolean canLuff = true;
|
||||||
|
double turnRate = 5;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
double sailDir;
|
||||||
|
//if sails are out etc
|
||||||
|
if (boat.isSailsOut()) {
|
||||||
|
double windDir = visualiserRace.getVisualiserRaceState().getWindDirection().degrees();
|
||||||
|
double windOffset = (360 - windDir + boat.getBearing().degrees()) % 360;
|
||||||
|
sailDir = windOffset / 180 * 270 + windDir + 180;
|
||||||
|
boolean leftOfWind = windOffset >= 180;
|
||||||
|
if (leftOfWind){
|
||||||
|
sailDir = -sailDir;
|
||||||
|
} else {
|
||||||
|
sailDir = windDir - sailDir;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sailDir = visualiserRace.getVisualiserRaceState().getWindDirection().degrees();
|
||||||
|
}
|
||||||
|
|
||||||
|
//get new place to move towards
|
||||||
|
double compA = ((sailCurrent - sailDir) % 360 + 360) % 360;//degrees right
|
||||||
|
if (compA > 180) compA = 360 - compA;
|
||||||
|
double compB = ((sailDir - sailCurrent) % 360 + 360) % 360;//degrees left
|
||||||
|
if (compB > compA){
|
||||||
|
if (compA > turnRate){
|
||||||
|
sailCurrent = ((sailCurrent - turnRate) % 360 + 360) % 360;
|
||||||
|
canLuff = false;
|
||||||
|
} else {
|
||||||
|
sailCurrent = sailDir;
|
||||||
|
canLuff = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (compB > turnRate){
|
||||||
|
sailCurrent = ((sailCurrent + turnRate) % 360 + 360) % 360;
|
||||||
|
canLuff = false;
|
||||||
|
} else {
|
||||||
|
sailCurrent = sailDir;
|
||||||
|
canLuff = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sailsSubject.setHeading(sailCurrent);
|
||||||
|
if (canLuff) {
|
||||||
|
if (boat.isSailsOut()) {
|
||||||
|
if (sails3D.isLuffing()) {
|
||||||
|
sails3D.stopLuffing();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!sails3D.isLuffing()) {
|
||||||
|
sails3D.startLuffing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sailsSubject.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
||||||
|
sailsSubject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
||||||
|
sailsSubject.getMesh().toFront();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sailsFollowBoat.start();
|
||||||
|
|
||||||
// Track this boat's movement with the new subject
|
// Track this boat's movement with the new subject
|
||||||
AnimationTimer trackBoat = new AnimationTimer() {
|
AnimationTimer trackBoat = new AnimationTimer() {
|
||||||
@@ -339,21 +604,31 @@ public class RaceViewController extends Controller {
|
|||||||
boatModel.setHeading(boat.getBearing().degrees());
|
boatModel.setHeading(boat.getBearing().degrees());
|
||||||
boatModel.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
boatModel.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
||||||
boatModel.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
boatModel.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
||||||
|
boatModel.getMesh().toFront();
|
||||||
|
|
||||||
|
//Fire follows boat
|
||||||
|
healthEffect.setHeading(boat.getBearing().degrees());
|
||||||
|
healthEffect.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
||||||
|
healthEffect.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
||||||
|
healthEffect.setY(0);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
trackBoat.start();
|
trackBoat.start();
|
||||||
|
|
||||||
Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
|
//next mark indicator
|
||||||
CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark();
|
//Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
|
||||||
|
/*CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark();
|
||||||
|
|
||||||
view3D.getShape(nextMark.getMark1().getSourceID()).getMesh().setMaterial(markColor);
|
view3D.getShape(nextMark.getMark1().getSourceID()).getMesh().setMaterial(markColor);
|
||||||
if(nextMark.getMark2() != null) {
|
if(nextMark.getMark2() != null) {
|
||||||
view3D.getShape(nextMark.getMark2().getSourceID()).getMesh().setMaterial(markColor);
|
view3D.getShape(nextMark.getMark2().getSourceID()).getMesh().setMaterial(markColor);
|
||||||
}
|
}*/
|
||||||
Subject3D shockwave = new Shockwave(10);
|
|
||||||
|
|
||||||
|
Subject3D shockwave = new Shockwave(10);
|
||||||
viewSubjects.add(shockwave);
|
viewSubjects.add(shockwave);
|
||||||
|
|
||||||
boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> swapColours(curr)));
|
//boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> swapColours(curr)));
|
||||||
boat.hasCollidedProperty().addListener((o, prev, curr) -> Platform.runLater(() -> showCollision(boat, shockwave)));
|
boat.hasCollidedProperty().addListener((o, prev, curr) -> Platform.runLater(() -> showCollision(boat, shockwave)));
|
||||||
}
|
}
|
||||||
// Fix initial bird's-eye position
|
// Fix initial bird's-eye position
|
||||||
@@ -369,7 +644,12 @@ public class RaceViewController extends Controller {
|
|||||||
|
|
||||||
// Bind zooming to scrolling
|
// Bind zooming to scrolling
|
||||||
view3D.setOnScroll(e -> {
|
view3D.setOnScroll(e -> {
|
||||||
view3D.updateDistance(e.getDeltaY());
|
//view3D.updateDistance(e.getDeltaY());
|
||||||
|
if (e.getDeltaY() > 0) {
|
||||||
|
view3D.zoomIn();
|
||||||
|
} else {
|
||||||
|
view3D.zoomOut();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bind zooming to keypress (Z/X default)
|
// Bind zooming to keypress (Z/X default)
|
||||||
@@ -390,7 +670,7 @@ public class RaceViewController extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view3D.updateDistance(-10);
|
view3D.zoomIn();
|
||||||
break;
|
break;
|
||||||
case "Zoom Out":
|
case "Zoom Out":
|
||||||
//Check if race is a tutorial
|
//Check if race is a tutorial
|
||||||
@@ -405,7 +685,7 @@ public class RaceViewController extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view3D.updateDistance(10);
|
view3D.zoomOut();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,44 +718,99 @@ public class RaceViewController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addThirdPersonAnnotations(Subject3D subject3D) {
|
private void addThirdPersonAnnotations(Subject3D subject3D) {
|
||||||
viewSubjects.add(nextMarkArrow);
|
nextMarkController.show3d();
|
||||||
final VisualiserBoat boat;
|
// viewSubjects.add(nextMarkArrow);
|
||||||
try {
|
// final VisualiserBoat boat;
|
||||||
boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
|
// try {
|
||||||
} catch (BoatNotFoundException e) {
|
// boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
|
||||||
e.printStackTrace();
|
// } catch (BoatNotFoundException e) {
|
||||||
return;
|
// e.printStackTrace();
|
||||||
}
|
// return;
|
||||||
arrowToNextMark = new AnimationTimer() {
|
// }
|
||||||
@Override
|
// arrowToNextMark = new AnimationTimer() {
|
||||||
public void handle(long now) {
|
// @Override
|
||||||
CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
|
// public void handle(long now) {
|
||||||
if (target != null) {
|
// CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
|
||||||
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
|
// Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
|
||||||
|
//
|
||||||
nextMarkArrow.setX(view3D.getPivot().getX());
|
// nextMarkArrow.setX(view3D.getPivot().getX());
|
||||||
nextMarkArrow.setY(view3D.getPivot().getY());
|
// nextMarkArrow.setY(view3D.getPivot().getY());
|
||||||
nextMarkArrow.setZ(view3D.getPivot().getZ() + 15);
|
// nextMarkArrow.setZ(view3D.getPivot().getZ() + 15);
|
||||||
nextMarkArrow.setHeading(headingToMark.degrees());
|
// nextMarkArrow.setHeading(headingToMark.degrees());
|
||||||
}
|
// }
|
||||||
}
|
// };
|
||||||
};
|
// arrowToNextMark.start();
|
||||||
arrowToNextMark.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeThirdPersonAnnotations() {
|
private void removeThirdPersonAnnotations() {
|
||||||
viewSubjects.remove(nextMarkArrow);
|
nextMarkController.show2d();
|
||||||
if (arrowToNextMark != null) {
|
|
||||||
arrowToNextMark.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void setNextMarkArrowDirection(Mark mark1, Mark mark2, RoundingType roundingType, int direction){
|
||||||
* Swap the colour of the next mark to pass with the last mark passed
|
boolean port = roundingType == RoundingType.Port;
|
||||||
* @param leg boat has started on
|
Mark chosenMark = null;
|
||||||
*/
|
|
||||||
private void swapColours(Leg leg) {
|
if (mark2 == null){
|
||||||
|
chosenMark = mark1;
|
||||||
|
} else {
|
||||||
|
boolean rounding = port;
|
||||||
|
switch (direction) {
|
||||||
|
case 2:
|
||||||
|
rounding = !port;
|
||||||
|
case 0:
|
||||||
|
if (rounding){
|
||||||
|
if (mark1.getPosition().getLongitude() < mark2.getPosition().getLongitude()){
|
||||||
|
chosenMark = mark1;
|
||||||
|
} else {
|
||||||
|
chosenMark = mark2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mark1.getPosition().getLongitude() > mark2.getPosition().getLongitude()){
|
||||||
|
chosenMark = mark1;
|
||||||
|
} else {
|
||||||
|
chosenMark = mark2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
rounding = !port;
|
||||||
|
case 1:
|
||||||
|
if (rounding){
|
||||||
|
if (mark1.getPosition().getLatitude() > mark2.getPosition().getLatitude()){
|
||||||
|
chosenMark = mark1;
|
||||||
|
} else {
|
||||||
|
chosenMark = mark2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mark1.getPosition().getLatitude() < mark2.getPosition().getLatitude()){
|
||||||
|
chosenMark = mark1;
|
||||||
|
} else {
|
||||||
|
chosenMark = mark2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chosenMark == null){
|
||||||
|
System.err.println("Mark to pick is null.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port){
|
||||||
|
Assets3D.ccwNextArrow.setX(gpsConverter.convertGPS(chosenMark.getPosition()).getX());
|
||||||
|
Assets3D.ccwNextArrow.setZ(gpsConverter.convertGPS(chosenMark.getPosition()).getY());
|
||||||
|
Assets3D.ccwNextArrow.getMesh().setVisible(true);
|
||||||
|
Assets3D.cwNextArrow.getMesh().setVisible(false);
|
||||||
|
} else {
|
||||||
|
Assets3D.cwNextArrow.setX(gpsConverter.convertGPS(chosenMark.getPosition()).getX());
|
||||||
|
Assets3D.cwNextArrow.setZ(gpsConverter.convertGPS(chosenMark.getPosition()).getY());
|
||||||
|
Assets3D.cwNextArrow.getMesh().setVisible(true);
|
||||||
|
Assets3D.ccwNextArrow.getMesh().setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeNextMark(Leg leg){
|
||||||
CompoundMark start = leg.getStartCompoundMark();
|
CompoundMark start = leg.getStartCompoundMark();
|
||||||
CompoundMark end = leg.getEndCompoundMark();
|
CompoundMark end = leg.getEndCompoundMark();
|
||||||
|
|
||||||
@@ -484,23 +819,28 @@ public class RaceViewController extends Controller {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape3D start1 = view3D.getShape(start.getMark1().getSourceID()).getMesh();
|
|
||||||
Shape3D end1 = view3D.getShape(end.getMark1().getSourceID()).getMesh();
|
|
||||||
|
|
||||||
Material nextMark = start1.getMaterial();
|
//find direction coming
|
||||||
Material lastMark = end1.getMaterial();
|
double angle = gpsConverter.getAngle(gpsConverter.convertGPS(start.getMark1().getPosition()),
|
||||||
|
gpsConverter.convertGPS(end.getMark1Position()));
|
||||||
start1.setMaterial(lastMark);
|
angle = (Math.toDegrees(angle) % 360 + 360) % 360;
|
||||||
if(start.getMark2() != null) {
|
int dir = 0; //0 = top 1 = right 2 = down 3 = down
|
||||||
Shape3D start2 = view3D.getShape(start.getMark2().getSourceID()).getMesh();
|
if (angle < 315){
|
||||||
start2.setMaterial(lastMark);
|
if (angle > 45){
|
||||||
|
dir ++;
|
||||||
|
}
|
||||||
|
if (angle > 135){
|
||||||
|
dir ++;
|
||||||
|
}
|
||||||
|
if (angle > 225){
|
||||||
|
dir ++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end1.setMaterial(nextMark);
|
Mark startMark1 = end.getMark1();
|
||||||
if(end.getMark2() != null) {
|
Mark startMark2 = end.getMark2();
|
||||||
Shape3D end2 = view3D.getShape(end.getMark2().getSourceID()).getMesh();
|
|
||||||
end2.setMaterial(nextMark);
|
setNextMarkArrowDirection(startMark1, startMark2, end.getRoundingType(), dir);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -544,13 +884,17 @@ public class RaceViewController extends Controller {
|
|||||||
// set table data
|
// set table data
|
||||||
boatInfoTable.setItems(sortedBoats);
|
boatInfoTable.setItems(sortedBoats);
|
||||||
boatTeamColumn.setCellValueFactory(
|
boatTeamColumn.setCellValueFactory(
|
||||||
cellData -> cellData.getValue().nameProperty());
|
cellData -> cellData.getValue().nameProperty()
|
||||||
|
);
|
||||||
boatSpeedColumn.setCellValueFactory(
|
boatSpeedColumn.setCellValueFactory(
|
||||||
cellData -> cellData.getValue().currentSpeedProperty());
|
cellData -> cellData.getValue().currentSpeedProperty()
|
||||||
|
);
|
||||||
boatMarkColumn.setCellValueFactory(
|
boatMarkColumn.setCellValueFactory(
|
||||||
cellData -> cellData.getValue().legProperty());
|
cellData -> cellData.getValue().legProperty()
|
||||||
boatPlacingColumn.setCellValueFactory(
|
);
|
||||||
cellData -> cellData.getValue().placingProperty());
|
boatHealthColumn.setCellValueFactory(
|
||||||
|
cellData -> cellData.getValue().healthProperty()
|
||||||
|
);
|
||||||
|
|
||||||
//Kind of ugly, but allows for formatting an observed speed.
|
//Kind of ugly, but allows for formatting an observed speed.
|
||||||
boatSpeedColumn.setCellFactory(
|
boatSpeedColumn.setCellFactory(
|
||||||
@@ -650,6 +994,101 @@ public class RaceViewController extends Controller {
|
|||||||
}.start();
|
}.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation timer loop for the speedometer
|
||||||
|
*/
|
||||||
|
private void speedometerLoop(){
|
||||||
|
new AnimationTimer(){
|
||||||
|
@Override
|
||||||
|
public void handle(long arg0){
|
||||||
|
if (raceState.getRaceStatusEnum() == RaceStatusEnum.FINISHED) {
|
||||||
|
stop(); // stop the timer
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
//Set the current speed value of the boat
|
||||||
|
gauge.setValue(raceState.getBoat(raceState.getPlayerBoatID()).getCurrentSpeed());
|
||||||
|
fGauge.getGauge().setValue(raceState.getBoat(raceState.getPlayerBoatID()).getCurrentSpeed());
|
||||||
|
|
||||||
|
//Create list with sorted boat placements
|
||||||
|
List<VisualiserBoat> boatList = boatInfoTable.getItems();
|
||||||
|
for (VisualiserBoat boat : boatList){
|
||||||
|
if(raceState.getPlayerBoatID()==boat.getSourceID()){
|
||||||
|
//Set boat current placement value as title of speedometer
|
||||||
|
gauge.titleProperty().setValue("Position: " + (boatInfoTable.getItems().indexOf(boat)+1));
|
||||||
|
fGauge.getGauge().titleProperty().setValue("Position: " + (boatInfoTable.getItems().indexOf(boat)+1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (BoatNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation timer for health
|
||||||
|
*/
|
||||||
|
private void healthLoop(){
|
||||||
|
new AnimationTimer(){
|
||||||
|
@Override
|
||||||
|
public void handle(long arg0){
|
||||||
|
if (raceState.getRaceStatusEnum() == RaceStatusEnum.FINISHED) {
|
||||||
|
stop(); // stop the timer
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
//Check if boat is dead
|
||||||
|
if(raceState.getBoat(raceState.getPlayerBoatID()).getHealth()<=0){
|
||||||
|
if(!deathPane.isDisable()) {
|
||||||
|
deathPane.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (BoatNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(VisualiserBoat boat : raceState.getBoats()){
|
||||||
|
for (HealthEffect fp : healthEffectList){
|
||||||
|
|
||||||
|
if(fp.getSourceID()==boat.getSourceID()){
|
||||||
|
if(boat.getHealth()<=0){
|
||||||
|
//Boat is dead. Don't check it anymore for hp
|
||||||
|
fp.displayDeath(fp.getSourceID()==raceState.getPlayerBoatID());
|
||||||
|
Annotation3D sharks = new Annotation3D(Assets3D.getSharks());
|
||||||
|
sharks.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
||||||
|
sharks.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
||||||
|
viewSubjects.add(sharks);
|
||||||
|
new AnimationTimer(){
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
sharks.setHeading(sharks.getHeading().getAngle() - 0.5);
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
|
||||||
|
fp.setSourceID(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
//Speed up tick when <=10 hp
|
||||||
|
if(boat.getHealth()<=10){
|
||||||
|
fp.flash(System.currentTimeMillis(), 300, boat.getSourceID()==raceState.getPlayerBoatID());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
//Visual indication of low hp
|
||||||
|
if(boat.getHealth()<=20) {
|
||||||
|
//fp.setVisible(true);
|
||||||
|
fp.flash(System.currentTimeMillis(), 500, boat.getSourceID()==raceState.getPlayerBoatID());
|
||||||
|
} else {
|
||||||
|
fp.setVisible(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* toggles if the info table is shown
|
* toggles if the info table is shown
|
||||||
*/
|
*/
|
||||||
@@ -783,25 +1222,115 @@ public class RaceViewController extends Controller {
|
|||||||
|
|
||||||
private void bigMap(){
|
private void bigMap(){
|
||||||
if (mapToggle){
|
if (mapToggle){
|
||||||
raceCanvas.widthProperty().bind(canvasBase.widthProperty());
|
raceCanvas.widthProperty().bind(canvasBase2.widthProperty());
|
||||||
raceCanvas.heightProperty().bind(canvasBase.heightProperty());
|
raceCanvas.heightProperty().bind(canvasBase2.heightProperty());
|
||||||
|
|
||||||
raceCanvas.setFullScreen(true);
|
raceCanvas.setFullScreen(true);
|
||||||
raceCanvas.setOpacity(0.6);
|
raceCanvas.setOpacity(0.6);
|
||||||
|
|
||||||
canvasBase1.getChildren().remove(raceCanvas);
|
canvasBase1.getChildren().remove(raceCanvas);
|
||||||
canvasBase.getChildren().add(1, raceCanvas);
|
canvasBase2.getChildren().add(0, raceCanvas);
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
raceCanvas.widthProperty().bind(canvasBase1.widthProperty());
|
raceCanvas.widthProperty().bind(canvasBase1.widthProperty());
|
||||||
raceCanvas.heightProperty().bind(canvasBase1.heightProperty());
|
raceCanvas.heightProperty().bind(canvasBase1.heightProperty());
|
||||||
|
|
||||||
raceCanvas.setFullScreen(false);
|
raceCanvas.setFullScreen(false);
|
||||||
|
raceCanvas.setOpacity(1);
|
||||||
|
|
||||||
canvasBase.getChildren().remove(raceCanvas);
|
canvasBase2.getChildren().remove(raceCanvas);
|
||||||
canvasBase1.getChildren().add(0, raceCanvas);
|
canvasBase1.getChildren().add(0, raceCanvas);
|
||||||
}
|
}
|
||||||
mapToggle = !mapToggle;
|
mapToggle = !mapToggle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FXML method for death button
|
||||||
|
*/
|
||||||
|
public void deathOKPressed(){
|
||||||
|
deathPane.setDisable(true);
|
||||||
|
deathPane.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private AnimationTimer seagullGoTo = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
for (Subject3D seagull: seagulls) {
|
||||||
|
if (seagullsGoToX.get(seagull).size() > 0) {
|
||||||
|
//System.out.println(xPosition + " " + yPosition);
|
||||||
|
seagull.setHeading(GPSConverter.getAngle(new GraphCoordinate(seagull.getX(), seagull.getZ()),
|
||||||
|
new GraphCoordinate(seagullsGoToX.get(seagull).get(0), seagullsGoToY.get(seagull).get(0))));
|
||||||
|
double delx = seagullsGoToX.get(seagull).get(0) - seagull.getX();
|
||||||
|
double dely = seagullsGoToY.get(seagull).get(0) - seagull.getZ();
|
||||||
|
double scale = seagullSpeed / Math.sqrt(delx * delx + dely * dely);
|
||||||
|
if (scale < 1) {
|
||||||
|
seagull.setX(seagull.getX() + delx * scale);
|
||||||
|
seagull.setZ(seagull.getZ() + dely * scale);
|
||||||
|
} else {
|
||||||
|
seagullsGoToX.get(seagull).remove(0);
|
||||||
|
seagullsGoToY.get(seagull).remove(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
randSeagullNewAction(seagull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public void randSeagullNewAction(Subject3D seagull){
|
||||||
|
Random rand = new Random();
|
||||||
|
int nextAction = rand.nextInt(1);
|
||||||
|
switch(nextAction){
|
||||||
|
case 0:
|
||||||
|
//do a straight line
|
||||||
|
double nextX = rand.nextInt((int)gpsConverter.getLongitudeFactor());
|
||||||
|
/*if (nextX > this.xBound/2){
|
||||||
|
nextX = - nextX/2;
|
||||||
|
}*/
|
||||||
|
double nextY = rand.nextInt((int)gpsConverter.getLatitudeFactor());
|
||||||
|
/*if (nextY > this.yBound/2){
|
||||||
|
nextY = - nextY/2;
|
||||||
|
}*/
|
||||||
|
seagullsGoToX.get(seagull).add(nextX);
|
||||||
|
seagullsGoToY.get(seagull).add(nextY);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
//do a octogan circle
|
||||||
|
seagullsGoToX.get(seagull).add(seagull.getX() - 3);
|
||||||
|
seagullsGoToX.get(seagull).add(seagull.getX() - 3);
|
||||||
|
seagullsGoToX.get(seagull).add(seagull.getX());
|
||||||
|
seagullsGoToX.get(seagull).add(seagull.getX() + 3);
|
||||||
|
seagullsGoToX.get(seagull).add(seagull.getX() + 6);
|
||||||
|
seagullsGoToX.get(seagull).add(seagull.getX() + 6);
|
||||||
|
seagullsGoToX.get(seagull).add(seagull.getX() + 3);
|
||||||
|
seagullsGoToX.get(seagull).add(seagull.getX());
|
||||||
|
//y
|
||||||
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 3);
|
||||||
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 6);
|
||||||
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 9);
|
||||||
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 9);
|
||||||
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 6);
|
||||||
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 3);
|
||||||
|
seagullsGoToY.get(seagull).add(seagull.getZ());
|
||||||
|
seagullsGoToY.get(seagull).add(seagull.getZ());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addToFlock(){
|
||||||
|
Annotation3D newSeagull = new Annotation3D(new SeagullMesh());
|
||||||
|
newSeagull.setY(-15);
|
||||||
|
newSeagull.setX(67);
|
||||||
|
newSeagull.setZ(43);
|
||||||
|
newSeagull.setXRot(0);
|
||||||
|
seagulls.add(newSeagull);
|
||||||
|
seagullsGoToX.put(newSeagull, new ArrayList<>());
|
||||||
|
seagullsGoToY.put(newSeagull, new ArrayList<>());
|
||||||
|
viewSubjects.add(newSeagull);
|
||||||
|
randSeagullNewAction(newSeagull);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package visualiser.Controllers;
|
|||||||
|
|
||||||
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
@@ -9,8 +10,10 @@ import javafx.scene.AmbientLight;
|
|||||||
import javafx.scene.PointLight;
|
import javafx.scene.PointLight;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.RadioButton;
|
import javafx.scene.control.RadioButton;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.media.AudioClip;
|
import javafx.scene.media.AudioClip;
|
||||||
@@ -41,14 +44,18 @@ import java.util.logging.Logger;
|
|||||||
* the game.
|
* the game.
|
||||||
*/
|
*/
|
||||||
public class TitleController extends Controller {
|
public class TitleController extends Controller {
|
||||||
|
private @FXML AnchorPane titleWrapper;
|
||||||
private @FXML RadioButton dayModeRD;
|
private @FXML RadioButton dayModeRD;
|
||||||
private @FXML RadioButton nightModeRD;
|
private @FXML RadioButton nightModeRD;
|
||||||
private @FXML Button tutorialButton;
|
private @FXML Button tutorialButton;
|
||||||
private @FXML Pane menuPane;
|
|
||||||
private @FXML ImageView imgSun;
|
private @FXML ImageView imgSun;
|
||||||
private @FXML GridPane view3DContainer;
|
private @FXML GridPane view3DContainer;
|
||||||
|
private ToggleGroup toggleGroup = new ToggleGroup();
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
dayModeRD.setToggleGroup(toggleGroup);
|
||||||
|
nightModeRD.setToggleGroup(toggleGroup);
|
||||||
|
|
||||||
AmbientLight ambientLight = new AmbientLight(Color.web("#CCCCFF"));
|
AmbientLight ambientLight = new AmbientLight(Color.web("#CCCCFF"));
|
||||||
ambientLight.setTranslateX(250);
|
ambientLight.setTranslateX(250);
|
||||||
ambientLight.setTranslateZ(210);
|
ambientLight.setTranslateZ(210);
|
||||||
@@ -102,6 +109,8 @@ public class TitleController extends Controller {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
loop.start();
|
loop.start();
|
||||||
|
|
||||||
|
titleWrapper.getStylesheets().add("/css/dayMode.css");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,10 +137,7 @@ public class TitleController extends Controller {
|
|||||||
*/
|
*/
|
||||||
public void setDayMode(){
|
public void setDayMode(){
|
||||||
dayModeRD.getScene().getStylesheets().clear();
|
dayModeRD.getScene().getStylesheets().clear();
|
||||||
menuPane.getStylesheets().clear();
|
|
||||||
imgSun.setImage(new Image(getClass().getResource("/visualiser/images/sun.png").toExternalForm()));
|
|
||||||
dayModeRD.getScene().getStylesheets().add("/css/dayMode.css");
|
dayModeRD.getScene().getStylesheets().add("/css/dayMode.css");
|
||||||
menuPane.setStyle("-fx-background-color: #6be6ff;");
|
|
||||||
nightModeRD.setSelected(false);
|
nightModeRD.setSelected(false);
|
||||||
App.dayMode = true;
|
App.dayMode = true;
|
||||||
}
|
}
|
||||||
@@ -141,10 +147,7 @@ public class TitleController extends Controller {
|
|||||||
*/
|
*/
|
||||||
public void setNightMode(){
|
public void setNightMode(){
|
||||||
nightModeRD.getScene().getStylesheets().clear();
|
nightModeRD.getScene().getStylesheets().clear();
|
||||||
menuPane.getStylesheets().clear();
|
|
||||||
//imgSun.setImage(new Image(getClass().getResource("/visualiser/images/sunsleep.png").toExternalForm()));
|
|
||||||
nightModeRD.getScene().getStylesheets().add("/css/nightMode.css");
|
nightModeRD.getScene().getStylesheets().add("/css/nightMode.css");
|
||||||
menuPane.setStyle("-fx-background-color: #1f2c60;");
|
|
||||||
dayModeRD.setSelected(false);
|
dayModeRD.setSelected(false);
|
||||||
App.dayMode = false;
|
App.dayMode = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import javafx.stage.Stage;
|
|||||||
import javafx.stage.StageStyle;
|
import javafx.stage.StageStyle;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import mock.app.Event;
|
import mock.app.Event;
|
||||||
|
import visualiser.layout.Assets3D;
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
private static Stage stage;
|
private static Stage stage;
|
||||||
@@ -113,6 +114,7 @@ public class App extends Application {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
new Thread(boatTask).start();
|
new Thread(boatTask).start();
|
||||||
|
Assets3D.loadAssets();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import com.interactivemesh.jfx.importer.obj.ObjModelImporter;
|
||||||
|
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
||||||
|
import com.interactivemesh.jfx.importer.x3d.X3dModelImporter;
|
||||||
|
import javafx.geometry.Point3D;
|
||||||
|
import javafx.scene.Group;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.Material;
|
||||||
|
import javafx.scene.paint.PhongMaterial;
|
||||||
|
import javafx.scene.shape.MeshView;
|
||||||
|
import javafx.scene.shape.Shape3D;
|
||||||
|
import visualiser.Controllers.HostGameController;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gondr on 21/09/2017.
|
||||||
|
*/
|
||||||
|
public class Assets3D {
|
||||||
|
|
||||||
|
public static MeshView[] sails;
|
||||||
|
public static MeshView[] sea;
|
||||||
|
public static MeshView[] seagull;
|
||||||
|
public static Subject3D windArrow;
|
||||||
|
public static Subject3D compass;
|
||||||
|
public static Subject3D cwNextArrow;
|
||||||
|
public static Subject3D ccwNextArrow;
|
||||||
|
public static SkyBox skyBox;
|
||||||
|
public static Subject3D boatHighlight;
|
||||||
|
public static Subject3D sharks;
|
||||||
|
|
||||||
|
public static void loadAssets(){
|
||||||
|
loadSails();
|
||||||
|
loadWindArrow();
|
||||||
|
loadNextArrow();
|
||||||
|
loadSeaSurface();
|
||||||
|
loadSkybox();
|
||||||
|
loadSeagull();
|
||||||
|
loadBoatHightlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadNextArrow(){
|
||||||
|
StlMeshImporter objModelImporter = new StlMeshImporter();
|
||||||
|
String cwPath = "assets/Next Mark Arrow CW V1.0.stl";
|
||||||
|
URL asset = Assets3D.class.getClassLoader().getResource(cwPath);
|
||||||
|
objModelImporter.read(asset);
|
||||||
|
Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
|
||||||
|
|
||||||
|
MeshView cwMesh = new MeshView(objModelImporter.getImport());
|
||||||
|
cwMesh.setMaterial(markColor);
|
||||||
|
cwMesh.setMouseTransparent(true);
|
||||||
|
cwMesh.toBack();
|
||||||
|
cwMesh.setVisible(false);
|
||||||
|
cwNextArrow = new Annotation3D(cwMesh);
|
||||||
|
|
||||||
|
String ccwPath = "assets/Next Mark Arrow CCW V1.0.stl";
|
||||||
|
URL ccwAsset = Assets3D.class.getClassLoader().getResource(ccwPath);
|
||||||
|
objModelImporter.read(ccwAsset);
|
||||||
|
|
||||||
|
MeshView ccwMesh = new MeshView(objModelImporter.getImport());
|
||||||
|
ccwMesh.setMaterial(markColor);
|
||||||
|
ccwMesh.setMouseTransparent(true);
|
||||||
|
ccwMesh.toBack();
|
||||||
|
cwMesh.setVisible(false);
|
||||||
|
ccwNextArrow = new Annotation3D(ccwMesh);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadBoatHightlight(){
|
||||||
|
Material markColor = new PhongMaterial(new Color(0,1,0,0.5));
|
||||||
|
StlMeshImporter objModelImporter = new StlMeshImporter();
|
||||||
|
String path = "assets/V1.0 Boat Highlight.stl";
|
||||||
|
URL highlight = Assets3D.class.getClassLoader().getResource(path);
|
||||||
|
objModelImporter.read(highlight);
|
||||||
|
|
||||||
|
MeshView hMesh = new MeshView(objModelImporter.getImport());
|
||||||
|
hMesh.setMaterial(markColor);
|
||||||
|
hMesh.setMouseTransparent(true);
|
||||||
|
hMesh.toBack();
|
||||||
|
boatHighlight = new Subject3D(hMesh, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadSails(){
|
||||||
|
sails = new MeshView[40];
|
||||||
|
ObjModelImporter objModelImporter = new ObjModelImporter();
|
||||||
|
|
||||||
|
for (int i = 0; i < sails.length; i++){
|
||||||
|
String path = String.format("assets/Sails/V1.5 Sail_%06d.obj", i + 1);
|
||||||
|
URL asset = Assets3D.class.getClassLoader().getResource(path);
|
||||||
|
objModelImporter.read(asset);
|
||||||
|
if (objModelImporter.getImport().length > 0) {
|
||||||
|
sails[i] = objModelImporter.getImport()[0];
|
||||||
|
/*sails[i].setRotationAxis(new Point3D(1, 0, 0));
|
||||||
|
sails[i].setRotate(-90);*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadSeaSurface(){
|
||||||
|
sea = new MeshView[100];
|
||||||
|
ObjModelImporter objModelImporter = new ObjModelImporter();
|
||||||
|
|
||||||
|
for (int i = 0; i < sea.length; i++){
|
||||||
|
// String path = String.format("assets/Ocean V1.0 Small Animation/Ocean V1.0 Large Animation_%06d.obj", i + 1);
|
||||||
|
// String path = String.format("assets/Ocean V1.0 Animation/Ocean V1.0_%06d.obj", i + 1);
|
||||||
|
String path = String.format("assets/Ocean Animation/Ocean Animation_%06d.obj", i + 1);
|
||||||
|
URL asset = Assets3D.class.getClassLoader().getResource(path);
|
||||||
|
objModelImporter.read(asset);
|
||||||
|
if (objModelImporter.getImport().length > 0) {
|
||||||
|
sea[i] = objModelImporter.getImport()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadSeagull(){
|
||||||
|
seagull = new MeshView[30];
|
||||||
|
ObjModelImporter objModelImporter = new ObjModelImporter();
|
||||||
|
|
||||||
|
for (int i = 0; i < seagull.length; i++){
|
||||||
|
String path = String.format("assets/V1.1 Animated/Seagull V1.1_%06d.obj", i + 1);
|
||||||
|
URL asset = Assets3D.class.getClassLoader().getResource(path);
|
||||||
|
objModelImporter.read(asset);
|
||||||
|
if (objModelImporter.getImport().length > 0) {
|
||||||
|
seagull[i] = objModelImporter.getImport()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Shape3D getBoat(){
|
||||||
|
String path = "assets/V1.4 Boat.x3d";
|
||||||
|
return loadX3d(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Shape3D getMark(){
|
||||||
|
String path = "assets/Burger Bouy V1.1.x3d";
|
||||||
|
return loadX3d(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadWindArrow(){
|
||||||
|
String compassPath = "assets/wind_compass.x3d";
|
||||||
|
compass = new Annotation3D(loadX3d(compassPath));
|
||||||
|
String arrowPath = "assets/wind_arrow.x3d";
|
||||||
|
windArrow = new Annotation3D(loadX3d(arrowPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Shape3D getSharks(){
|
||||||
|
String path = "assets/V1.0 Sharks.x3d";
|
||||||
|
return loadX3d(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Shape3D loadX3d(String path){
|
||||||
|
X3dModelImporter x3dModelImporter = new X3dModelImporter();
|
||||||
|
URL asset = Assets3D.class.getClassLoader().getResource(path);
|
||||||
|
x3dModelImporter.read(asset);
|
||||||
|
if (x3dModelImporter.getImport().length > 0) {
|
||||||
|
//if (x3dModelImporter.getImport()[0] instanceof MeshView) {
|
||||||
|
Group g = (Group)((Group)((Group) x3dModelImporter.getImport()[0]).getChildren().get(0)).getChildren().get(0);
|
||||||
|
Shape3D shape = (Shape3D)g.getChildren().get(0);
|
||||||
|
return shape;
|
||||||
|
/*} else {
|
||||||
|
System.err.println("Boat is not a Mesh View 0.0");
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadSkybox(){
|
||||||
|
skyBox = new SkyBox(750, 200, 250, 0, 210);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.geometry.Point3D;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.media.AudioClip;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.PhongMaterial;
|
||||||
|
import javafx.scene.shape.*;
|
||||||
|
import javafx.scene.transform.Rotate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by zwu18 on 24/09/17.
|
||||||
|
*/
|
||||||
|
public class HealthEffect extends Subject3D {
|
||||||
|
|
||||||
|
private int sourceID;
|
||||||
|
private long currentTime;
|
||||||
|
private AudioClip warningSound = new AudioClip(this.getClass().getResource("/visualiser/sounds/warning.mp3").toExternalForm());
|
||||||
|
private AudioClip deadSound = new AudioClip(this.getClass().getResource("/visualiser/sounds/dead1.wav").toExternalForm());
|
||||||
|
|
||||||
|
public HealthEffect(int sourceID, long currentTime){
|
||||||
|
super(createEffect(), 0);
|
||||||
|
this.sourceID = sourceID;
|
||||||
|
this.currentTime = currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the mesh view with image
|
||||||
|
* @return Mesh view
|
||||||
|
*/
|
||||||
|
private static Shape3D createEffect(){
|
||||||
|
|
||||||
|
Image image = new Image(HealthEffect.class.getClassLoader().getResourceAsStream("images/warning.png"));
|
||||||
|
|
||||||
|
Plane3D plane = new Plane3D(20, 20, 10, 10);
|
||||||
|
|
||||||
|
PhongMaterial material = new PhongMaterial();
|
||||||
|
material.setDiffuseColor(Color.web("#FFFFFF"));
|
||||||
|
material.setSpecularColor(Color.web("#000000"));
|
||||||
|
material.setDiffuseMap(image);
|
||||||
|
|
||||||
|
MeshView imageSurface = new MeshView(plane);
|
||||||
|
|
||||||
|
|
||||||
|
imageSurface.setMaterial(material);
|
||||||
|
imageSurface.setMouseTransparent(true);
|
||||||
|
//imageSurface.toFront(); this.flashInterval = flashInterval;
|
||||||
|
|
||||||
|
return imageSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rotateView(Double angle, Double pivotX, Double pivotY, Double pivotZ, Point3D axis){
|
||||||
|
Rotate rotate = new Rotate(angle, axis);
|
||||||
|
rotate.setPivotX(pivotX);
|
||||||
|
rotate.setPivotY(pivotY);
|
||||||
|
rotate.setPivotZ(pivotZ);
|
||||||
|
this.getMesh().getTransforms().add(rotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVisible(boolean bool){
|
||||||
|
this.getMesh().setVisible(bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getSourceID(){
|
||||||
|
return sourceID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceID(int id){
|
||||||
|
this.sourceID = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display visual indication when boat dies
|
||||||
|
* @param player boolean if player is current user or not
|
||||||
|
*/
|
||||||
|
public void displayDeath(boolean player){
|
||||||
|
Image image = new Image(HealthEffect.class.getClassLoader().getResourceAsStream("images/warning2.png"));
|
||||||
|
PhongMaterial material = (PhongMaterial) this.getMesh().getMaterial();
|
||||||
|
material.setDiffuseColor(Color.web("#FFFFFF"));
|
||||||
|
material.setSpecularColor(Color.web("#000000"));
|
||||||
|
material.setDiffuseMap(image);
|
||||||
|
this.getMesh().setMaterial(material);
|
||||||
|
if(player) {
|
||||||
|
deadSound.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flash the mesh view at a certain interval
|
||||||
|
* @param checkTime The current time of flash
|
||||||
|
* @param flashInterval Desired flash interval
|
||||||
|
* @param playerBoat Whether or not this effect is for the player's boat.
|
||||||
|
*/
|
||||||
|
public void flash(long checkTime, long flashInterval, boolean playerBoat){
|
||||||
|
if(checkTime >= (currentTime+flashInterval)){
|
||||||
|
this.currentTime = checkTime;
|
||||||
|
if(this.getMesh().isVisible()){
|
||||||
|
this.setVisible(false);
|
||||||
|
} else {
|
||||||
|
if(playerBoat) {
|
||||||
|
warningSound.setVolume(0.1);
|
||||||
|
warningSound.play();
|
||||||
|
}
|
||||||
|
this.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.WritableImage;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
import javafx.scene.shape.Rectangle;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by connortaylorbrown on 21/09/17.
|
||||||
|
*/
|
||||||
|
public class HealthSlider extends Pane {
|
||||||
|
/**
|
||||||
|
* Image used to fill health slider
|
||||||
|
*/
|
||||||
|
private Image fillImage;
|
||||||
|
/**
|
||||||
|
* Size of background for image configuration
|
||||||
|
*/
|
||||||
|
private BackgroundSize backgroundSize;
|
||||||
|
/**
|
||||||
|
* Percentage of image cropped out from top
|
||||||
|
*/
|
||||||
|
private double crop;
|
||||||
|
|
||||||
|
public HealthSlider(Image fillImage) {
|
||||||
|
this.fillImage = fillImage;
|
||||||
|
this.crop = 1;
|
||||||
|
this.backgroundSize = new BackgroundSize(
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false);
|
||||||
|
drawSlider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCrop(double crop) {
|
||||||
|
this.crop = crop;
|
||||||
|
drawSlider();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawSlider() {
|
||||||
|
int top = Math.max(0,(int)(fillImage.getHeight() - crop * fillImage.getHeight()));
|
||||||
|
|
||||||
|
WritableImage croppedImage = new WritableImage(
|
||||||
|
fillImage.getPixelReader(),
|
||||||
|
0,
|
||||||
|
top,
|
||||||
|
(int)fillImage.getWidth(),
|
||||||
|
(int)fillImage.getHeight() - top
|
||||||
|
);
|
||||||
|
|
||||||
|
BackgroundImage backgroundImage = new BackgroundImage(
|
||||||
|
croppedImage,
|
||||||
|
BackgroundRepeat.NO_REPEAT,
|
||||||
|
BackgroundRepeat.NO_REPEAT,
|
||||||
|
BackgroundPosition.CENTER,
|
||||||
|
backgroundSize
|
||||||
|
);
|
||||||
|
|
||||||
|
this.setBackground(new Background(backgroundImage));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.Material;
|
||||||
|
import javafx.scene.paint.PhongMaterial;
|
||||||
|
import javafx.scene.shape.MeshView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gondr on 27/09/2017.
|
||||||
|
*/
|
||||||
|
public class NewSeaSurface extends MeshView{
|
||||||
|
private int seaIndex = 0;
|
||||||
|
|
||||||
|
private AnimationTimer seaRipple = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
seaIndex = (seaIndex + 1) % Assets3D.sea.length;
|
||||||
|
setMesh(Assets3D.sea[seaIndex].getMesh());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public NewSeaSurface(){
|
||||||
|
super(Assets3D.sea[0].getMesh());
|
||||||
|
Color seaBlue = new Color(0.284, 0.573, 1.00, 1);
|
||||||
|
Material seaMat = new PhongMaterial(seaBlue);
|
||||||
|
setMaterial(seaMat);
|
||||||
|
setMouseTransparent(true);
|
||||||
|
seaRipple.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.scene.shape.Mesh;
|
||||||
|
import javafx.scene.shape.MeshView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gondr on 21/09/2017.
|
||||||
|
*/
|
||||||
|
public class Sails3D extends MeshView {
|
||||||
|
private int index = 0;
|
||||||
|
private boolean isLuffing = false;
|
||||||
|
|
||||||
|
public Sails3D() {
|
||||||
|
setMesh(Assets3D.sails[0].getMesh());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimationTimer luff = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
setMesh(Assets3D.sails[index].getMesh());
|
||||||
|
index = (index + 1) % 40;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void startLuffing(){
|
||||||
|
luff.start();
|
||||||
|
isLuffing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopLuffing(){
|
||||||
|
luff.stop();
|
||||||
|
isLuffing = false;
|
||||||
|
setMesh(Assets3D.sails[0].getMesh());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLuffing() {
|
||||||
|
return isLuffing;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package visualiser.layout;
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.animation.AnimationTimer;
|
||||||
import javafx.geometry.Point3D;
|
import javafx.geometry.Point3D;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
@@ -16,7 +17,7 @@ import visualiser.utils.PerlinNoiseGenerator;
|
|||||||
/**
|
/**
|
||||||
* Creates a SeaSurface
|
* Creates a SeaSurface
|
||||||
*/
|
*/
|
||||||
public class SeaSurface extends Subject3D {
|
public class SeaSurface extends Annotation3D {
|
||||||
private static Image image;
|
private static Image image;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,7 +27,7 @@ public class SeaSurface extends Subject3D {
|
|||||||
* @param freq frequency the perlin noise is to be generated at
|
* @param freq frequency the perlin noise is to be generated at
|
||||||
*/
|
*/
|
||||||
public SeaSurface(int size, double freq) {
|
public SeaSurface(int size, double freq) {
|
||||||
super(createSurface(size, freq), 0);
|
super(createSurface(size, freq));
|
||||||
image = new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterDown2048.png"));
|
image = new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterDown2048.png"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.scene.transform.Translate;
|
||||||
|
import visualiser.model.GraphCoordinate;
|
||||||
|
import visualiser.utils.GPSConverter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gondr on 28/09/2017.
|
||||||
|
*/
|
||||||
|
public class SeagullFlock{
|
||||||
|
private ObservableList<Subject3D> seagulls = FXCollections.observableArrayList();
|
||||||
|
private double xPos = 0;
|
||||||
|
private double yPos = 0;
|
||||||
|
private double zPos = 20;
|
||||||
|
private double heading = 0;
|
||||||
|
private double[] xOffset = {0, -5, -5};
|
||||||
|
private double[] yOffset = {0, -5, 5};
|
||||||
|
private double xBound = Integer.MAX_VALUE;//-/+
|
||||||
|
private double yBound = Integer.MAX_VALUE;
|
||||||
|
private List<Double> goToX = new ArrayList<>();
|
||||||
|
private List<Double> goToY = new ArrayList<>();
|
||||||
|
|
||||||
|
private double speed = 0.01;
|
||||||
|
|
||||||
|
private AnimationTimer goTo = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
if (goToX.size() > 0) {
|
||||||
|
//System.out.println(xPosition + " " + yPosition);
|
||||||
|
heading = GPSConverter.getAngle(new GraphCoordinate(xPos, yPos),
|
||||||
|
new GraphCoordinate(goToX.get(0), goToY.get(0)));
|
||||||
|
double delx = goToX.get(0) - xPos;
|
||||||
|
double dely = goToY.get(0) - yPos;
|
||||||
|
double scale = speed / Math.sqrt(delx * delx + dely * dely);
|
||||||
|
if (scale < 1) {
|
||||||
|
xPos += delx * scale;
|
||||||
|
yPos += dely * scale;
|
||||||
|
} else {
|
||||||
|
goToX.remove(0);
|
||||||
|
goToY.remove(0);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
stop();
|
||||||
|
randNewAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private AnimationTimer update = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
for (int i = 0; i < seagulls.size(); i++){
|
||||||
|
Subject3D seagull = seagulls.get(i);
|
||||||
|
seagull.getMesh().setTranslateY(zPos);
|
||||||
|
|
||||||
|
seagull.setHeading(heading);
|
||||||
|
seagull.setX(xPos + xOffset[i]);
|
||||||
|
seagull.setZ(yPos + yOffset[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public SeagullFlock() {
|
||||||
|
this(0d, 0d, 0d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SeagullFlock(double x, double y, double z){
|
||||||
|
xPos = x;
|
||||||
|
yPos = y;
|
||||||
|
zPos = z;
|
||||||
|
addToFlock();
|
||||||
|
update.start();
|
||||||
|
randNewAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToFlock(){
|
||||||
|
if (seagulls.size() < 3) {
|
||||||
|
Annotation3D newSeagull = new Annotation3D(new SeagullMesh());
|
||||||
|
newSeagull.setXRot(0);
|
||||||
|
seagulls.add(newSeagull);
|
||||||
|
// seagulls.add(new Annotation3D(new Sphere(5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setxBound(double xBound) {
|
||||||
|
this.xBound = xBound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setyBound(double yBound) {
|
||||||
|
this.yBound = yBound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void randNewAction(){
|
||||||
|
Random rand = new Random();
|
||||||
|
int nextAction = rand.nextInt(1);
|
||||||
|
switch(nextAction){
|
||||||
|
case 0:
|
||||||
|
//do a straight line
|
||||||
|
double nextX = rand.nextInt((int)this.xBound);
|
||||||
|
/*if (nextX > this.xBound/2){
|
||||||
|
nextX = - nextX/2;
|
||||||
|
}*/
|
||||||
|
double nextY = rand.nextInt((int)this.yBound);
|
||||||
|
/*if (nextY > this.yBound/2){
|
||||||
|
nextY = - nextY/2;
|
||||||
|
}*/
|
||||||
|
goToX.add(nextX);
|
||||||
|
goToY.add(nextY);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
//do a octogan circle
|
||||||
|
goToX.add(xPos - 3);
|
||||||
|
goToX.add(xPos - 3);
|
||||||
|
goToX.add(xPos);
|
||||||
|
goToX.add(xPos + 3);
|
||||||
|
goToX.add(xPos + 6);
|
||||||
|
goToX.add(xPos + 6);
|
||||||
|
goToX.add(xPos + 3);
|
||||||
|
goToX.add(xPos);
|
||||||
|
//y
|
||||||
|
goToX.add(yPos - 3);
|
||||||
|
goToX.add(yPos - 6);
|
||||||
|
goToX.add(yPos - 9);
|
||||||
|
goToX.add(yPos - 9);
|
||||||
|
goToX.add(yPos - 6);
|
||||||
|
goToX.add(yPos - 3);
|
||||||
|
goToX.add(yPos);
|
||||||
|
goToX.add(yPos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
goTo.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Subject3D> getSeagulls() {
|
||||||
|
return seagulls;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.PhongMaterial;
|
||||||
|
import javafx.scene.shape.MeshView;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gondr on 28/09/2017.
|
||||||
|
*/
|
||||||
|
public class SeagullMesh extends MeshView{
|
||||||
|
private int index = 0;
|
||||||
|
private int isFlapping = 0;
|
||||||
|
private int flapPeriod;
|
||||||
|
private int flapStrength;
|
||||||
|
private int periodElapsed = 0;
|
||||||
|
|
||||||
|
public SeagullMesh() {
|
||||||
|
setMesh(Assets3D.seagull[0].getMesh());
|
||||||
|
PhongMaterial white = new PhongMaterial(Color.WHITE);
|
||||||
|
setMaterial(white);
|
||||||
|
Random rand = new Random();
|
||||||
|
flapPeriod = rand.nextInt(9);
|
||||||
|
flapPeriod = 60 * (11 + flapPeriod);
|
||||||
|
flapStrength = rand.nextInt(3) + 2;
|
||||||
|
scheduledFlap.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimationTimer flap = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
index = (index + 1) % Assets3D.seagull.length;
|
||||||
|
setMesh(Assets3D.seagull[index].getMesh());
|
||||||
|
if (index == 0){
|
||||||
|
isFlapping ++;
|
||||||
|
if (isFlapping >= flapStrength){
|
||||||
|
stop();
|
||||||
|
setMesh(Assets3D.seagull[0].getMesh());
|
||||||
|
isFlapping = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private AnimationTimer scheduledFlap = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
if (periodElapsed == 0 && isFlapping == 0){
|
||||||
|
startFlapping();
|
||||||
|
}
|
||||||
|
periodElapsed = (periodElapsed + 1) % flapPeriod;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void startFlapping(){
|
||||||
|
flap.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,8 +28,13 @@ public class Subject3D {
|
|||||||
*/
|
*/
|
||||||
private Rotate heading;
|
private Rotate heading;
|
||||||
|
|
||||||
|
private Rotate xRot;
|
||||||
|
|
||||||
|
private Rotate yRot;
|
||||||
|
|
||||||
private Scale scale;
|
private Scale scale;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for view subject wrapper
|
* Constructor for view subject wrapper
|
||||||
* @param mesh to be rendered
|
* @param mesh to be rendered
|
||||||
@@ -41,7 +46,9 @@ public class Subject3D {
|
|||||||
this.scale = new Scale();
|
this.scale = new Scale();
|
||||||
this.position = new Translate();
|
this.position = new Translate();
|
||||||
this.heading = new Rotate(0, Rotate.Y_AXIS);
|
this.heading = new Rotate(0, Rotate.Y_AXIS);
|
||||||
this.mesh.getTransforms().addAll(position, scale, heading, new Rotate(90, Rotate.X_AXIS), new Rotate(180, Rotate.Y_AXIS));
|
this.xRot = new Rotate(90, Rotate.X_AXIS);
|
||||||
|
this.yRot = new Rotate(180, Rotate.Y_AXIS);
|
||||||
|
this.mesh.getTransforms().addAll(position, scale, heading, xRot, yRot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Shape3D getMesh() {
|
public Shape3D getMesh() {
|
||||||
@@ -70,15 +77,35 @@ public class Subject3D {
|
|||||||
position.setX(x);
|
position.setX(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getX(){
|
||||||
|
return position.getX();
|
||||||
|
}
|
||||||
|
|
||||||
public void setY(double y) {
|
public void setY(double y) {
|
||||||
position.setY(y);
|
position.setY(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getY(){
|
||||||
|
return position.getY();
|
||||||
|
}
|
||||||
|
|
||||||
public void setZ(double z) {
|
public void setZ(double z) {
|
||||||
position.setZ(z);
|
position.setZ(z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getZ(){
|
||||||
|
return position.getZ();
|
||||||
|
}
|
||||||
|
|
||||||
public void setHeading(double angle) {
|
public void setHeading(double angle) {
|
||||||
heading.setAngle(angle);
|
heading.setAngle(angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setXRot(double angle){
|
||||||
|
xRot.setAngle(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setYRot(double angle){
|
||||||
|
yRot.setAngle(angle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package visualiser.layout;
|
|||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.value.ChangeListener;
|
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.scene.*;
|
import javafx.scene.*;
|
||||||
@@ -78,11 +77,17 @@ public class View3D extends Pane {
|
|||||||
/**
|
/**
|
||||||
* Distance to switch from third person to bird's eye
|
* Distance to switch from third person to bird's eye
|
||||||
*/
|
*/
|
||||||
private final double THIRD_PERSON_LIMIT = 100;
|
private final double THIRD_PERSON_LIMIT = 500;
|
||||||
/**
|
/**
|
||||||
* Distance to stop zoom
|
* Distance to stop zoom
|
||||||
*/
|
*/
|
||||||
private final double FIRST_PERSON_LIMIT = 2;
|
private final double ZOOM_IN_LIMIT = 15;
|
||||||
|
private final double ZOOM_OUT_LIMIT = 700;
|
||||||
|
private final double MAX_ZOOM_LIMIT = 1150;
|
||||||
|
private final double MAX_PITCH = 60; // birds eye view
|
||||||
|
private final double MIN_PITCH = 5; // third person view
|
||||||
|
private final double ZOOM_PER_KEYPRESS = 5; // distance changed per zoom
|
||||||
|
private double itemScale = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor for View3D. Sets up Scene and PerspectiveCamera.
|
* Default constructor for View3D. Sets up Scene and PerspectiveCamera.
|
||||||
@@ -183,12 +188,9 @@ public class View3D extends Pane {
|
|||||||
* Configures camera to third person view
|
* Configures camera to third person view
|
||||||
*/
|
*/
|
||||||
public void setThirdPerson() {
|
public void setThirdPerson() {
|
||||||
this.setDistance(THIRD_PERSON_LIMIT / 2);
|
this.setDistance(ZOOM_IN_LIMIT * 5);
|
||||||
this.setPitch(10);
|
adjustPitchForZoom();
|
||||||
|
adjustScaleForZoom();
|
||||||
for(Subject3D item: items) {
|
|
||||||
item.setScale(0.1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -196,17 +198,14 @@ public class View3D extends Pane {
|
|||||||
*/
|
*/
|
||||||
public void setBirdsEye() {
|
public void setBirdsEye() {
|
||||||
this.setYaw(0);
|
this.setYaw(0);
|
||||||
this.setPitch(60);
|
this.setPitch(MAX_PITCH);
|
||||||
|
adjustScaleForZoom();
|
||||||
for(Subject3D item: items) {
|
|
||||||
item.setScale(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop camera from following the last selected subject
|
* Stop camera from following the last selected subject
|
||||||
*/
|
*/
|
||||||
private void untrackSubject() {
|
public void untrackSubject() {
|
||||||
if(target.get() != null) {
|
if(target.get() != null) {
|
||||||
trackBoat.stop();
|
trackBoat.stop();
|
||||||
target.setValue(null);
|
target.setValue(null);
|
||||||
@@ -268,17 +267,81 @@ public class View3D extends Pane {
|
|||||||
* @param delta amount to change distance by
|
* @param delta amount to change distance by
|
||||||
*/
|
*/
|
||||||
public void updateDistance(double delta) {
|
public void updateDistance(double delta) {
|
||||||
double distance = -this.distance.getZ() + delta;
|
double newDistance = -this.distance.getZ() + delta;
|
||||||
|
if (target.get() == null){
|
||||||
if(distance <= FIRST_PERSON_LIMIT) {
|
if (newDistance > MAX_ZOOM_LIMIT) {
|
||||||
this.setDistance(FIRST_PERSON_LIMIT);
|
setDistance(MAX_ZOOM_LIMIT);
|
||||||
} else if(distance > THIRD_PERSON_LIMIT) {
|
} else if (newDistance <= ZOOM_IN_LIMIT) {
|
||||||
this.setDistance(distance);
|
setDistance(ZOOM_IN_LIMIT);
|
||||||
|
} else {
|
||||||
|
setDistance(newDistance);
|
||||||
|
}
|
||||||
|
} else if(newDistance <= ZOOM_IN_LIMIT) {
|
||||||
|
setDistance(ZOOM_IN_LIMIT);
|
||||||
|
} else if (newDistance > ZOOM_OUT_LIMIT){
|
||||||
untrackSubject();
|
untrackSubject();
|
||||||
setBirdsEye();
|
setDistance(1050);
|
||||||
|
updatePivot(new Translate(250, 0, 210));
|
||||||
|
setYaw(0);
|
||||||
} else {
|
} else {
|
||||||
this.setDistance(distance);
|
setDistance(newDistance);
|
||||||
}
|
}
|
||||||
|
adjustPitchForZoom();
|
||||||
|
adjustScaleForZoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the scale size of boats and markers as a user zooms in or out,
|
||||||
|
* to smooth the change between third person to birds eye view.
|
||||||
|
*/
|
||||||
|
private void adjustScaleForZoom(){
|
||||||
|
double itemScale = (((-distance.getZ() - (ZOOM_IN_LIMIT * 2)) /
|
||||||
|
((THIRD_PERSON_LIMIT - (ZOOM_IN_LIMIT * 2)) /
|
||||||
|
(1 - 0.1))) + 0.1);
|
||||||
|
// if zoomed right in
|
||||||
|
if (itemScale < 0.1){
|
||||||
|
itemScale = 0.1;
|
||||||
|
// if zoomed right out
|
||||||
|
} else if (itemScale > 1) {
|
||||||
|
itemScale = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update scale
|
||||||
|
for (Subject3D item : items) {
|
||||||
|
item.setScale(itemScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the pitch as a user zooms in or out, to smooth the change
|
||||||
|
* between third person to birds eye view.
|
||||||
|
*/
|
||||||
|
private void adjustPitchForZoom(){
|
||||||
|
double pitch = (((-distance.getZ() - (ZOOM_IN_LIMIT*2)) /
|
||||||
|
((THIRD_PERSON_LIMIT - (ZOOM_IN_LIMIT*2)) / (MAX_PITCH -
|
||||||
|
MIN_PITCH))) + MIN_PITCH);
|
||||||
|
|
||||||
|
// if pitch is out of bounds, set it to the min/max
|
||||||
|
if (pitch < MIN_PITCH) {
|
||||||
|
pitch = MIN_PITCH;
|
||||||
|
} else if (pitch > MAX_PITCH){
|
||||||
|
pitch = MAX_PITCH;
|
||||||
|
}
|
||||||
|
setPitch(pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to be called when the zoom in key is pressed.
|
||||||
|
*/
|
||||||
|
public void zoomIn(){
|
||||||
|
updateDistance(-ZOOM_PER_KEYPRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to be called when the zoom out key is pressed.
|
||||||
|
*/
|
||||||
|
public void zoomOut(){
|
||||||
|
updateDistance(ZOOM_PER_KEYPRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -289,6 +352,10 @@ public class View3D extends Pane {
|
|||||||
this.yaw.setAngle(yaw);
|
this.yaw.setAngle(yaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getYaw(){
|
||||||
|
return this.yaw.getAngle();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set elevation of camera
|
* Set elevation of camera
|
||||||
* @param pitch in degrees
|
* @param pitch in degrees
|
||||||
@@ -297,6 +364,10 @@ public class View3D extends Pane {
|
|||||||
this.pitch.setAngle(-pitch);
|
this.pitch.setAngle(-pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getPitch(){
|
||||||
|
return this.pitch.getAngle();
|
||||||
|
}
|
||||||
|
|
||||||
public void addAmbientLight(AmbientLight ambientLight) {
|
public void addAmbientLight(AmbientLight ambientLight) {
|
||||||
this.world.getChildren().add(ambientLight);
|
this.world.getChildren().add(ambientLight);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import shared.model.Wind;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gondr on 25/09/2017.
|
||||||
|
*/
|
||||||
|
public class WindCompass extends View3D {
|
||||||
|
Subject3D compass;
|
||||||
|
Subject3D windArrow;
|
||||||
|
View3D view3D;
|
||||||
|
Property<Wind> wind;
|
||||||
|
|
||||||
|
AnimationTimer followView3D = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
setYaw(180+ view3D.getYaw());
|
||||||
|
setPitch(30 - view3D.getPitch());
|
||||||
|
windArrow.setHeading(wind.getValue().getWindDirection().degrees());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public WindCompass(View3D view3D, Property<Wind> wind){
|
||||||
|
super(false);
|
||||||
|
this.wind = wind;
|
||||||
|
this.view3D = view3D;
|
||||||
|
this.followView3D.start();
|
||||||
|
ObservableList<Subject3D> subjects = FXCollections.observableArrayList();
|
||||||
|
this.setItems(subjects);
|
||||||
|
compass = Assets3D.compass;
|
||||||
|
windArrow = Assets3D.windArrow;
|
||||||
|
subjects.addAll(compass, windArrow);
|
||||||
|
this.setDistance(30);
|
||||||
|
this.setPitch(90);
|
||||||
|
this.setYaw(180);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeading(double heading){
|
||||||
|
windArrow.setHeading(heading);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompassPitch(double pitch){
|
||||||
|
setPitch(90d + pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -605,7 +605,7 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
|
|
||||||
//Prepare to draw.
|
//Prepare to draw.
|
||||||
gc.setLineWidth(1);
|
gc.setLineWidth(1);
|
||||||
gc.setFill(Color.DEEPSKYBLUE);
|
gc.setFill(Color.web("#f1f1d4"));
|
||||||
|
|
||||||
|
|
||||||
//Calculate the screen coordinates of the boundary.
|
//Calculate the screen coordinates of the boundary.
|
||||||
@@ -647,7 +647,7 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
|
|
||||||
//rounding lines
|
//rounding lines
|
||||||
if (isFullScreen){
|
if (isFullScreen){
|
||||||
drawRoundingLines();
|
// drawRoundingLines();
|
||||||
drawRaceLine();
|
drawRaceLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -794,18 +794,11 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Color getLineColor(Leg leg) {
|
private Color getLineColor(Leg leg) {
|
||||||
for (VisualiserBoat boat : raceState.getBoats()) {
|
if(ThisBoat.getInstance().getLegNumber() == leg.getLegNumber()){
|
||||||
if (boat.isClientBoat()) {
|
return Color.ORANGE;
|
||||||
if (boat.getCurrentLeg() == leg) {
|
}else{
|
||||||
return Color.ORANGE;
|
return Color.MEDIUMAQUAMARINE;
|
||||||
} else {
|
|
||||||
return Color.MEDIUMAQUAMARINE;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
return Color.MEDIUMAQUAMARINE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Color.MEDIUMAQUAMARINE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawArrowHead(GPSCoordinate start, GPSCoordinate end){
|
private void drawArrowHead(GPSCoordinate start, GPSCoordinate end){
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ public class ThisBoat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getLegNumber(){
|
||||||
|
if(this.boat != null){
|
||||||
|
return this.boat.getCurrentLeg().getLegNumber();
|
||||||
|
}else{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setBoat(VisualiserBoat boat) {
|
public void setBoat(VisualiserBoat boat) {
|
||||||
this.boat = boat;
|
this.boat = boat;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package visualiser.model;
|
package visualiser.model;
|
||||||
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import network.Messages.Enums.BoatStatusEnum;
|
import network.Messages.Enums.BoatStatusEnum;
|
||||||
import shared.model.*;
|
import shared.model.*;
|
||||||
@@ -65,6 +62,7 @@ public class VisualiserBoat extends Boat {
|
|||||||
private ObjectProperty<GPSCoordinate> positionProperty;
|
private ObjectProperty<GPSCoordinate> positionProperty;
|
||||||
private ObjectProperty<Bearing> bearingProperty;
|
private ObjectProperty<Bearing> bearingProperty;
|
||||||
private BooleanProperty hasCollided;
|
private BooleanProperty hasCollided;
|
||||||
|
private DoubleProperty healthProperty;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,6 +76,7 @@ public class VisualiserBoat extends Boat {
|
|||||||
|
|
||||||
this.color = color;
|
this.color = color;
|
||||||
this.hasCollided = new SimpleBooleanProperty(false);
|
this.hasCollided = new SimpleBooleanProperty(false);
|
||||||
|
this.healthProperty = new SimpleDoubleProperty(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -284,4 +283,18 @@ public class VisualiserBoat extends Boat {
|
|||||||
public void setHasCollided(boolean hasCollided) {
|
public void setHasCollided(boolean hasCollided) {
|
||||||
this.hasCollided.set(hasCollided);
|
this.hasCollided.set(hasCollided);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DoubleProperty healthProperty() {
|
||||||
|
return healthProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getHealth() {
|
||||||
|
return healthProperty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHealth(double healthProperty) {
|
||||||
|
this.healthProperty.set((int)healthProperty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,7 +248,6 @@ public class VisualiserRaceState extends RaceState {
|
|||||||
|
|
||||||
|
|
||||||
if ((boat.getStatus() == BoatStatusEnum.DNF) || (boat.getStatus() == BoatStatusEnum.PRESTART) || (boat.getCurrentLeg().getLegNumber() < 0)) {
|
if ((boat.getStatus() == BoatStatusEnum.DNF) || (boat.getStatus() == BoatStatusEnum.PRESTART) || (boat.getCurrentLeg().getLegNumber() < 0)) {
|
||||||
|
|
||||||
boat.setPlacing("-");
|
boat.setPlacing("-");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -96,6 +96,10 @@ public class HttpMatchBrowserHost extends Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendStarted() throws IOException {
|
||||||
|
sendHttp("http://api.umbrasheep.com/seng/match_started/");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* THe host starts sending out heartbeat messages every 2 seconds.
|
* THe host starts sending out heartbeat messages every 2 seconds.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ public class ServerConnection implements RunnableWithFramePeriod {
|
|||||||
this.messageRouter.addRoute(MessageType.MARKROUNDING, incomingMessages);
|
this.messageRouter.addRoute(MessageType.MARKROUNDING, incomingMessages);
|
||||||
this.messageRouter.addRoute(MessageType.XMLMESSAGE, incomingMessages);
|
this.messageRouter.addRoute(MessageType.XMLMESSAGE, incomingMessages);
|
||||||
this.messageRouter.addRoute(MessageType.ASSIGN_PLAYER_BOAT, incomingMessages);
|
this.messageRouter.addRoute(MessageType.ASSIGN_PLAYER_BOAT, incomingMessages);
|
||||||
|
this.messageRouter.addRoute(MessageType.BOATSTATE, incomingMessages);
|
||||||
this.messageRouter.removeDefaultRoute(); //We no longer want to keep un-routed messages.
|
this.messageRouter.removeDefaultRoute(); //We no longer want to keep un-routed messages.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -111,4 +111,11 @@ public class GPSConverter {
|
|||||||
return Math.atan2(coord2.getX() - coord1.getX(), coord2.getY() - coord1.getY());
|
return Math.atan2(coord2.getX() - coord1.getX(), coord2.getY() - coord1.getY());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getLongitudeFactor() {
|
||||||
|
return longitudeFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getLatitudeFactor() {
|
||||||
|
return latitudeFactor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31828
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31821
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31836
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31816
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31832
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31825
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31827
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31828
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31827
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31822
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31821
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31822
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31821
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31823
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31824
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31823
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31818
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31830
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31815
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
+31831
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# Blender MTL File: 'Ocean V1.0.blend'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Material
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user