Merge branch 'storyA_sounds' into storyD-3D
# Conflicts: # racevisionGame/src/main/java/visualiser/layout/SeaSurface.java # racevisionGame/src/main/java/visualiser/layout/SkyBox.java
This commit is contained in:
+39
@@ -0,0 +1,39 @@
|
||||
package visualiser.Commands.VisualiserRaceCommands;
|
||||
|
||||
import javafx.scene.media.AudioClip;
|
||||
import mock.model.commandFactory.Command;
|
||||
import network.Messages.YachtEvent;
|
||||
import visualiser.model.VisualiserRaceState;
|
||||
|
||||
/**
|
||||
* Created by zwu18 on 4/09/17.
|
||||
*/
|
||||
public class BoatCollisionCommand implements Command {
|
||||
|
||||
YachtEvent yachtEvent;
|
||||
|
||||
VisualiserRaceState visualiserRace;
|
||||
|
||||
public BoatCollisionCommand(YachtEvent yachtEvent, VisualiserRaceState visualiserRace){
|
||||
this.yachtEvent = yachtEvent;
|
||||
this.visualiserRace = visualiserRace;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
|
||||
if(visualiserRace.getPlayerBoatID()==yachtEvent.getSourceID()){
|
||||
System.out.println("I crashed!");
|
||||
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/collision.wav").toExternalForm());
|
||||
sound.play();
|
||||
|
||||
} else {
|
||||
System.out.println("Someone else crashed!");
|
||||
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/quietcollision.wav").toExternalForm());
|
||||
sound.play();
|
||||
}
|
||||
|
||||
//System.out.println("Collision command executed!");
|
||||
}
|
||||
}
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package visualiser.Commands.VisualiserRaceCommands;
|
||||
|
||||
import javafx.scene.media.AudioClip;
|
||||
import mock.model.commandFactory.Command;
|
||||
import network.Messages.BoatStatus;
|
||||
import network.Messages.Enums.BoatStatusEnum;
|
||||
@@ -176,6 +177,10 @@ public class RaceStatusCommand implements Command {
|
||||
//Record order in which boat finished leg.
|
||||
visualiserRace.getLegCompletionOrder().get(boat.getCurrentLeg()).add(boat);
|
||||
|
||||
//play sound
|
||||
AudioClip sound = new AudioClip(getClass().getResource("/visualiser/sounds/passmark.wav").toExternalForm());
|
||||
sound.play();
|
||||
|
||||
//Update boat.
|
||||
boat.setCurrentLeg(leg);
|
||||
boat.setTimeAtLastMark(visualiserRace.getRaceClock().getCurrentTime());
|
||||
|
||||
+8
-1
@@ -23,7 +23,9 @@ public class VisualiserRaceCommandFactory {
|
||||
|
||||
switch (message.getType()) {
|
||||
|
||||
case BOATLOCATION: return new BoatLocationCommand((BoatLocation) message, visualiserRace);
|
||||
case BOATLOCATION:
|
||||
//System.out.println("Boat location received");
|
||||
return new BoatLocationCommand((BoatLocation) message, visualiserRace);
|
||||
|
||||
case RACESTATUS: return new RaceStatusCommand((RaceStatus) message, visualiserRace);
|
||||
|
||||
@@ -31,6 +33,11 @@ public class VisualiserRaceCommandFactory {
|
||||
|
||||
case ASSIGN_PLAYER_BOAT: return new AssignPlayerBoatCommand((AssignPlayerBoat) message, visualiserRace);
|
||||
|
||||
case YACHTEVENTCODE:
|
||||
return new BoatCollisionCommand((YachtEvent) message, visualiserRace);
|
||||
|
||||
|
||||
|
||||
default: throw new CommandConstructionException("Could not create VisualiserRaceCommand. Unrecognised or unsupported MessageType: " + message.getType());
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
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.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.SplitPane;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.shape.MeshView;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.scene.media.AudioClip;
|
||||
import mock.app.Event;
|
||||
import org.xml.sax.SAXException;
|
||||
import mock.exceptions.EventConstructionException;
|
||||
import visualiser.layout.Plane3D;
|
||||
import visualiser.layout.SeaSurface;
|
||||
import visualiser.layout.Subject3D;
|
||||
import visualiser.layout.View3D;
|
||||
import shared.exceptions.InvalidBoatDataException;
|
||||
import shared.exceptions.InvalidRaceDataException;
|
||||
import shared.exceptions.InvalidRegattaDataException;
|
||||
import shared.exceptions.XMLReaderException;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -33,69 +34,48 @@ import java.util.logging.Logger;
|
||||
public class HostController extends Controller {
|
||||
|
||||
|
||||
// @FXML
|
||||
// TextField gameNameField;
|
||||
//
|
||||
// @FXML
|
||||
// TextField hostNameField;
|
||||
@FXML
|
||||
TextField gameNameField;
|
||||
|
||||
@FXML
|
||||
private ImageView imageView;
|
||||
TextField hostNameField;
|
||||
|
||||
@FXML
|
||||
AnchorPane hostWrapper;
|
||||
|
||||
@FXML
|
||||
AnchorPane imagePane;
|
||||
Button previousButton;
|
||||
|
||||
@FXML
|
||||
SplitPane splitPane;
|
||||
Button nextButton;
|
||||
|
||||
@FXML
|
||||
AnchorPane specPane;
|
||||
|
||||
@FXML
|
||||
GridPane playerContainer;
|
||||
ImageView mapImage;
|
||||
|
||||
private Event game;
|
||||
|
||||
private View3D view3D;
|
||||
private ArrayList<Image> listOfMaps;
|
||||
private int currentMapIndex = 0;
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
ObservableList<Subject3D> subjects = FXCollections.observableArrayList();
|
||||
public void initialize(URL location, ResourceBundle resources){
|
||||
Image ac35Map = new Image(getClass().getClassLoader().getResourceAsStream("images/AC35_Racecourse_MAP.png"));
|
||||
Image oMap = new Image(getClass().getClassLoader().getResourceAsStream("images/oMapLayout.png"));
|
||||
Image iMap = new Image(getClass().getClassLoader().getResourceAsStream("images/iMapLayout.png"));
|
||||
Image mMap = new Image(getClass().getClassLoader().getResourceAsStream("images/mMapLayout.png"));
|
||||
|
||||
view3D = new View3D();
|
||||
view3D.setItems(subjects);
|
||||
playerContainer.add(view3D, 0,0);
|
||||
|
||||
URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl");
|
||||
|
||||
StlMeshImporter importer = new StlMeshImporter();
|
||||
importer.read(asset);
|
||||
Subject3D subject = new Subject3D(new MeshView(importer.getImport()));
|
||||
|
||||
subjects.add(subject);
|
||||
|
||||
view3D.setDistance(50);
|
||||
view3D.setYaw(45);
|
||||
view3D.setPitch(20);
|
||||
|
||||
AnimationTimer rotate = new AnimationTimer() {
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
subject.setHeading(subject.getHeading().getAngle() + 0.1);
|
||||
}
|
||||
};
|
||||
rotate.start();
|
||||
listOfMaps = new ArrayList(Arrays.asList(ac35Map, oMap, iMap, mMap));
|
||||
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hosts a game
|
||||
* @throws IOException if socket cannot be connected to
|
||||
*/
|
||||
public void hostGamePressed() {
|
||||
public void hostGamePressed() throws IOException{
|
||||
try {
|
||||
this.game = new Event(false);
|
||||
this.game = new Event(false, currentMapIndex);
|
||||
connectSocket("localhost", 4942);
|
||||
} catch (EventConstructionException e) {
|
||||
Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e);
|
||||
@@ -128,10 +108,7 @@ public class HostController extends Controller {
|
||||
* Hosts a game.
|
||||
*/
|
||||
public void hostGame(){
|
||||
splitPane.setResizableWithParent(specPane, false);
|
||||
splitPane.lookupAll(".split-pane-divider").stream().forEach(div -> div.setMouseTransparent(true));
|
||||
imageView.fitWidthProperty().bind(imagePane.widthProperty());
|
||||
imageView.fitHeightProperty().bind(imagePane.heightProperty());
|
||||
mapImage.fitWidthProperty().bind(((Stage) mapImage.getScene().getWindow()).widthProperty().multiply(0.6));
|
||||
hostWrapper.setVisible(true);
|
||||
}
|
||||
|
||||
@@ -139,23 +116,33 @@ public class HostController extends Controller {
|
||||
* Menu button pressed. Prompt alert then return to menu
|
||||
*/
|
||||
public void menuBtnPressed(){
|
||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||
alert.setTitle("Quitting race");
|
||||
alert.setContentText("Do you wish to quit the race?");
|
||||
alert.setHeaderText("You are about to quit the race");
|
||||
Optional<ButtonType> result = alert.showAndWait();
|
||||
if(result.get() == ButtonType.OK){
|
||||
hostWrapper.setVisible(false);
|
||||
parent.enterTitle();
|
||||
}
|
||||
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||
sound.play();
|
||||
hostWrapper.setVisible(false);
|
||||
parent.enterTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start button pressed. Currently only prints out start
|
||||
*/
|
||||
public void startBtnPressed(){
|
||||
//System.out.println("Should start the race. This button is only visible for the host");
|
||||
hostGamePressed();
|
||||
public void nextImage(){
|
||||
increaseIndex();
|
||||
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
||||
}
|
||||
|
||||
public void previousImage(){
|
||||
decreaseIndex();
|
||||
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
||||
}
|
||||
private void increaseIndex(){
|
||||
currentMapIndex = (currentMapIndex + 1)%listOfMaps.size();
|
||||
}
|
||||
|
||||
private void decreaseIndex(){
|
||||
currentMapIndex = ((((currentMapIndex - 1)%listOfMaps.size())+listOfMaps.size())%listOfMaps.size());
|
||||
}
|
||||
|
||||
public void setGameType(int gameType){
|
||||
this.currentMapIndex = gameType;
|
||||
}
|
||||
|
||||
public int getGameType(){ return this.currentMapIndex; }
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import javafx.scene.control.TableColumn;
|
||||
import javafx.scene.control.TableView;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.media.AudioClip;
|
||||
import visualiser.model.RaceConnection;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -39,6 +40,8 @@ public class LobbyController extends Controller {
|
||||
|
||||
private ObservableList<RaceConnection> connections;
|
||||
|
||||
private AudioClip sound;
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
@@ -66,6 +69,8 @@ public class LobbyController extends Controller {
|
||||
* Refreshes the connections in the lobby
|
||||
*/
|
||||
public void refreshBtnPressed(){
|
||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||
sound.play();
|
||||
for(RaceConnection connection: connections) {
|
||||
connection.check();
|
||||
}
|
||||
@@ -93,6 +98,8 @@ public class LobbyController extends Controller {
|
||||
}
|
||||
|
||||
public void menuBtnPressed(){
|
||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||
sound.play();
|
||||
lobbyWrapper.setVisible(false);
|
||||
parent.enterTitle();
|
||||
}
|
||||
@@ -101,6 +108,8 @@ public class LobbyController extends Controller {
|
||||
* adds a new connection
|
||||
*/
|
||||
public void addConnectionPressed(){
|
||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||
sound.play();
|
||||
String hostName = addressFld.getText();
|
||||
String portString = portFld.getText();
|
||||
try{
|
||||
|
||||
@@ -3,6 +3,8 @@ package visualiser.Controllers;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.media.AudioClip;
|
||||
import visualiser.app.App;
|
||||
import visualiser.gameController.ControllerClient;
|
||||
import visualiser.model.VisualiserBoat;
|
||||
import visualiser.model.VisualiserRaceEvent;
|
||||
@@ -26,6 +28,8 @@ public class MainController extends Controller {
|
||||
@FXML private HostController hostController;
|
||||
@FXML private LobbyController lobbyController;
|
||||
|
||||
private AudioClip sound;
|
||||
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
@@ -53,6 +57,8 @@ public class MainController extends Controller {
|
||||
* @param isHost is connection a host
|
||||
*/
|
||||
public void enterLobby(Socket socket, Boolean isHost) {
|
||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||
sound.play();
|
||||
startController.enterLobby(socket, isHost);
|
||||
}
|
||||
|
||||
@@ -74,18 +80,38 @@ public class MainController extends Controller {
|
||||
/**
|
||||
* Transitions into lobby screen
|
||||
*/
|
||||
public void enterLobby(){ lobbyController.enterLobby(); }
|
||||
public void enterLobby(){
|
||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||
sound.play();
|
||||
lobbyController.enterLobby(); }
|
||||
|
||||
/**
|
||||
* Transitions into host game screen
|
||||
*/
|
||||
public void hostGame(){ hostController.hostGame(); }
|
||||
public void hostGame(){
|
||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||
sound.play();
|
||||
hostController.hostGame(); }
|
||||
|
||||
/**
|
||||
* Sets up the css for the start of the program
|
||||
*/
|
||||
public void startCss(){titleController.setDayMode();}
|
||||
|
||||
/**
|
||||
* host controller host a game
|
||||
* @throws IOException throws exception
|
||||
*/
|
||||
public void beginGame() throws IOException {
|
||||
hostController.hostGamePressed();
|
||||
}
|
||||
|
||||
public void setGameType(int gameType){
|
||||
hostController.setGameType(gameType);
|
||||
}
|
||||
|
||||
public int getGameType(){ return hostController.getGameType(); }
|
||||
|
||||
/**
|
||||
* Main Controller for the applications will house the menu and the displayed pane.
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@ package visualiser.Controllers;
|
||||
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -18,14 +19,20 @@ import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.Material;
|
||||
import javafx.scene.paint.PhongMaterial;
|
||||
import javafx.scene.shape.Box;
|
||||
import javafx.scene.shape.MeshView;
|
||||
import javafx.scene.shape.Shape3D;
|
||||
import javafx.scene.shape.Sphere;
|
||||
import javafx.scene.transform.Translate;
|
||||
import javafx.util.Callback;
|
||||
import network.Messages.Enums.RaceStatusEnum;
|
||||
import shared.dataInput.RaceDataSource;
|
||||
import shared.model.Leg;
|
||||
import shared.model.Mark;
|
||||
import shared.exceptions.BoatNotFoundException;
|
||||
import shared.model.*;
|
||||
import visualiser.app.App;
|
||||
import visualiser.enums.TutorialState;
|
||||
import visualiser.gameController.ControllerClient;
|
||||
import visualiser.gameController.Keys.ControlKey;
|
||||
import visualiser.layout.*;
|
||||
@@ -34,8 +41,7 @@ import visualiser.utils.GPSConverter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -58,6 +64,15 @@ public class RaceController extends Controller {
|
||||
|
||||
private boolean isHost;
|
||||
|
||||
private TutorialState currentState;
|
||||
|
||||
private ArrayList<TutorialState> tutorialStates;
|
||||
|
||||
private boolean isTutorial = false;
|
||||
|
||||
private String keyToPress;
|
||||
|
||||
|
||||
/**
|
||||
* state of the info table
|
||||
*/
|
||||
@@ -66,6 +81,9 @@ public class RaceController extends Controller {
|
||||
private View3D view3D;
|
||||
private ObservableList<Subject3D> viewSubjects;
|
||||
|
||||
private Subject3D nextMarkArrow;
|
||||
private ChangeListener<? super GPSCoordinate> pointToMark;
|
||||
|
||||
/**
|
||||
* The arrow controller.
|
||||
*/
|
||||
@@ -75,6 +93,9 @@ public class RaceController extends Controller {
|
||||
|
||||
@FXML private SplitPane racePane;
|
||||
|
||||
@FXML private Label tutorialText;
|
||||
|
||||
|
||||
/**
|
||||
* This is the pane we place the actual arrow control inside of.
|
||||
*/
|
||||
@@ -94,6 +115,7 @@ public class RaceController extends Controller {
|
||||
* Ctor.
|
||||
*/
|
||||
public RaceController() {
|
||||
this.nextMarkArrow = new Annotation3D(new Box(1,3,0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,13 +132,27 @@ public class RaceController extends Controller {
|
||||
if(controlKey != null) {
|
||||
try {
|
||||
controlKey.onAction(); // Change key state if applicable
|
||||
|
||||
//Check if current race is a tutorial
|
||||
if (isTutorial){
|
||||
//Check if current tutorial state has the same boat protocol code as key press
|
||||
if (controlKey.getProtocolCode().equals(currentState.getAction())){
|
||||
//Update tutorial
|
||||
checkTutorialState();
|
||||
}
|
||||
}
|
||||
|
||||
controllerClient.sendKey(controlKey);
|
||||
event.consume();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
Logger.getGlobal().log(Level.WARNING, "RaceController was interrupted on thread: " + Thread.currentThread() + "while sending: " + controlKey, e);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(event.getCode() == KeyCode.ESCAPE) {
|
||||
|
||||
try {
|
||||
@@ -237,7 +273,7 @@ public class RaceController extends Controller {
|
||||
// Position and add each mark to view
|
||||
for(Mark mark: race.getVisualiserRaceState().getMarks()) {
|
||||
MeshView mesh = new MeshView(importerMark.getImport());
|
||||
Subject3D markModel = new Subject3D(mesh);
|
||||
Subject3D markModel = new Subject3D(mesh, mark.getSourceID());
|
||||
|
||||
markModel.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
|
||||
markModel.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
|
||||
@@ -252,7 +288,7 @@ public class RaceController extends Controller {
|
||||
} else {
|
||||
mesh = new MeshView(importerBurgerBoat.getImport());
|
||||
}
|
||||
Subject3D boatModel = new Subject3D(mesh);
|
||||
Subject3D boatModel = new Subject3D(mesh, boat.getSourceID());
|
||||
|
||||
viewSubjects.add(boatModel);
|
||||
|
||||
@@ -266,10 +302,27 @@ public class RaceController extends Controller {
|
||||
}
|
||||
};
|
||||
trackBoat.start();
|
||||
|
||||
Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
|
||||
CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark();
|
||||
view3D.getShape(nextMark.getMark1().getSourceID()).setMaterial(markColor);
|
||||
if(nextMark.getMark2() != null) {
|
||||
view3D.getShape(nextMark.getMark2().getSourceID()).setMaterial(markColor);
|
||||
}
|
||||
|
||||
boat.legProperty().addListener((o, prev, curr) -> swapColours(curr));
|
||||
}
|
||||
// Fix initial bird's-eye position
|
||||
view3D.updatePivot(new Translate(250, 0, 210));
|
||||
|
||||
view3D.targetProperty().addListener((o, prev, curr)-> {
|
||||
if(curr != null && visualiserRace.getVisualiserRaceState().isVisualiserBoat(curr.getSourceID())) {
|
||||
addThirdPersonAnnotations(curr);
|
||||
} else {
|
||||
removeThirdPersonAnnotations(prev);
|
||||
}
|
||||
});
|
||||
|
||||
// Bind zooming to scrolling
|
||||
view3D.setOnScroll(e -> {
|
||||
view3D.updateDistance(e.getDeltaY());
|
||||
@@ -281,9 +334,33 @@ public class RaceController extends Controller {
|
||||
if(key != null) {
|
||||
switch (key.toString()) {
|
||||
case "Zoom In":
|
||||
//Check if race is a tutorial
|
||||
if (isTutorial) {
|
||||
//Check if the current tutorial state is zoom-in
|
||||
if (currentState.equals(TutorialState.ZOOMIN)) {
|
||||
try {
|
||||
//Update tutorial
|
||||
checkTutorialState();
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
view3D.updateDistance(-10);
|
||||
break;
|
||||
case "Zoom Out":
|
||||
//Check if race is a tutorial
|
||||
if(isTutorial) {
|
||||
//Check if current tutorial state is zoom-out
|
||||
if (currentState.equals(TutorialState.ZOOMOUT)) {
|
||||
try {
|
||||
//Update tutorial
|
||||
checkTutorialState();
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
view3D.updateDistance(10);
|
||||
break;
|
||||
}
|
||||
@@ -291,6 +368,64 @@ public class RaceController extends Controller {
|
||||
});
|
||||
}
|
||||
|
||||
private void addThirdPersonAnnotations(Subject3D subject3D) {
|
||||
viewSubjects.add(nextMarkArrow);
|
||||
|
||||
try {
|
||||
VisualiserBoat boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
|
||||
this.pointToMark = (o, prev, curr) -> {
|
||||
CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
|
||||
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
|
||||
|
||||
nextMarkArrow.setX(view3D.getPivot().getX());
|
||||
nextMarkArrow.setY(view3D.getPivot().getY());
|
||||
nextMarkArrow.setZ(view3D.getPivot().getZ() + 10);
|
||||
nextMarkArrow.setHeading(headingToMark.degrees());
|
||||
};
|
||||
|
||||
boat.positionProperty().addListener(pointToMark);
|
||||
} catch (BoatNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeThirdPersonAnnotations(Subject3D subject3D) {
|
||||
viewSubjects.remove(nextMarkArrow);
|
||||
|
||||
try {
|
||||
VisualiserBoat boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
|
||||
boat.positionProperty().removeListener(pointToMark);
|
||||
} catch (BoatNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the colour of the next mark to pass with the last mark passed
|
||||
* @param leg boat has started on
|
||||
*/
|
||||
private void swapColours(Leg leg) {
|
||||
CompoundMark start = leg.getStartCompoundMark();
|
||||
CompoundMark end = leg.getEndCompoundMark();
|
||||
|
||||
Shape3D start1 = view3D.getShape(start.getMark1().getSourceID());
|
||||
Shape3D end1 = view3D.getShape(end.getMark1().getSourceID());
|
||||
|
||||
Material nextMark = start1.getMaterial();
|
||||
Material lastMark = end1.getMaterial();
|
||||
|
||||
start1.setMaterial(lastMark);
|
||||
if(start.getMark2() != null) {
|
||||
Shape3D start2 = view3D.getShape(start.getMark2().getSourceID());
|
||||
start2.setMaterial(lastMark);
|
||||
}
|
||||
|
||||
end1.setMaterial(nextMark);
|
||||
if(end.getMark2() != null) {
|
||||
Shape3D end2 = view3D.getShape(end.getMark2().getSourceID());
|
||||
end2.setMaterial(nextMark);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the frame rate functionality. This allows for toggling the frame rate, and connect the fps label to the race's fps property.
|
||||
@@ -468,6 +603,25 @@ public class RaceController extends Controller {
|
||||
this.controllerClient = controllerClient;
|
||||
this.isHost = isHost;
|
||||
|
||||
|
||||
//Check if the game is a tutorial
|
||||
if (parent.getGameType()==4){
|
||||
isTutorial = true;
|
||||
tutorialText.setVisible(true);
|
||||
tutorialStates = new ArrayList<>(Arrays.asList(TutorialState.values()));
|
||||
|
||||
currentState = tutorialStates.get(0);
|
||||
tutorialStates.remove(0);
|
||||
|
||||
searchMapForKey("Upwind");
|
||||
|
||||
tutorialText.setText("Welcome to the tutorial! Exit at anytime with ESC. \nWe will first learn how to turn upwind. Press " + keyToPress + " to turn upwind.");
|
||||
|
||||
} else {
|
||||
isTutorial = false;
|
||||
tutorialText.setVisible(false);
|
||||
}
|
||||
|
||||
initialiseRace();
|
||||
|
||||
//Display this controller.
|
||||
@@ -565,4 +719,108 @@ public class RaceController extends Controller {
|
||||
infoTableShow = !infoTableShow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next tutorial state
|
||||
*/
|
||||
private void updateTutorialState(){
|
||||
//Next tutorial state is popped from list
|
||||
currentState = tutorialStates.get(0);
|
||||
tutorialStates.remove(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search key map for key given string of command
|
||||
* @param command the command of the key
|
||||
*/
|
||||
private void searchMapForKey(String command){
|
||||
//For loop through keyFactory
|
||||
for (Map.Entry<String, ControlKey> entry: keyFactory.getKeyState().entrySet()){
|
||||
if(entry.getValue().toString().equals(command)){
|
||||
|
||||
//Found next key required to press
|
||||
keyToPress = entry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates tutorial state and gui display for tutorial text
|
||||
* @throws Exception Exception thrown
|
||||
*/
|
||||
private void checkTutorialState() throws Exception {
|
||||
//Switch statement to check what the current tutorial state is
|
||||
switch (currentState){
|
||||
case UPWIND:
|
||||
//Set next key to press as the downwind key
|
||||
searchMapForKey("Downwind");
|
||||
//Update tutorial text
|
||||
tutorialText.setText("Nice! To turn downwind press " + keyToPress + ".");
|
||||
updateTutorialState();
|
||||
break;
|
||||
case DOWNWIND:
|
||||
//Set next key to press as the tack/gybe key
|
||||
searchMapForKey("Tack/Gybe");
|
||||
//Update tutorial text
|
||||
tutorialText.setText("Nice! To tack or gybe press " + keyToPress + ".");
|
||||
updateTutorialState();
|
||||
break;
|
||||
case TACKGYBE:
|
||||
//Set next key to press as the VMG key
|
||||
searchMapForKey("VMG");
|
||||
//Update tutorial text
|
||||
tutorialText.setText("Nice! To use VMG press " + keyToPress + ". This will turn the boat.");
|
||||
updateTutorialState();
|
||||
break;
|
||||
case VMG:
|
||||
//Set next key to press as the sails-in key
|
||||
searchMapForKey("Toggle Sails");
|
||||
//Update tutorial text
|
||||
tutorialText.setText("Nice! To sails in press " + keyToPress + ". This will stop the boat.");
|
||||
updateTutorialState();
|
||||
break;
|
||||
case SAILSIN:
|
||||
//Set next key to press as the sails-out key
|
||||
searchMapForKey("Toggle Sails");
|
||||
//Update tutorial text
|
||||
tutorialText.setText("Nice! To sails out press " + keyToPress + " again. The will start moving again.");
|
||||
updateTutorialState();
|
||||
break;
|
||||
case SAILSOUT:
|
||||
//Set next key to press as the zoom-in key
|
||||
searchMapForKey("Zoom In");
|
||||
//Update tutorial text
|
||||
tutorialText.setText("Nice! To zoom in press " + keyToPress + ".");
|
||||
updateTutorialState();
|
||||
break;
|
||||
case ZOOMIN:
|
||||
//Set next key to press as the zoom-out key
|
||||
searchMapForKey("Zoom Out");
|
||||
//Update tutorial text
|
||||
tutorialText.setText("Nice! You will also be able to zoom into boats and marks by clicking them. To zoom out press " + keyToPress + ".");
|
||||
updateTutorialState();
|
||||
break;
|
||||
case ZOOMOUT:
|
||||
//Finished tutorial. Display pop-up for exiting the tutorial
|
||||
tutorialText.setText("Congratulations! You're done!");
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle("Finished Tutorial");
|
||||
alert.setHeaderText("You have finished the tutorial.");
|
||||
alert.setContentText("Now you know the controls you are ready to race!");
|
||||
Optional<ButtonType> result = alert.showAndWait();
|
||||
if (result.get() == ButtonType.OK) {
|
||||
parent.endEvent();
|
||||
racePane.setVisible(false);
|
||||
App.app.showMainStage(App.getStage());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//State not found. Exit tutorial to title menu
|
||||
parent.endEvent();
|
||||
racePane.setVisible(false);
|
||||
App.app.showMainStage(App.getStage());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -86,9 +86,6 @@ public class StartController extends Controller {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*/
|
||||
@@ -218,29 +215,30 @@ public class StartController extends Controller {
|
||||
* Countdown timer until race starts.
|
||||
*/
|
||||
private void countdownTimer() {
|
||||
new AnimationTimer() {
|
||||
@Override
|
||||
public void handle(long arg0) {
|
||||
new AnimationTimer() {
|
||||
@Override
|
||||
public void handle(long arg0) {
|
||||
|
||||
//Get the current race status.
|
||||
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
|
||||
//Get the current race status.
|
||||
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
|
||||
|
||||
//If the race has reached the preparatory phase, or has started...
|
||||
if (raceStatus == RaceStatusEnum.WARNING
|
||||
|| raceStatus == RaceStatusEnum.PREPARATORY
|
||||
|| raceStatus == RaceStatusEnum.STARTED) {
|
||||
//Stop this timer.
|
||||
stop();
|
||||
//If the race has reached the preparatory phase, or has started...
|
||||
if (raceStatus == RaceStatusEnum.WARNING
|
||||
|| raceStatus == RaceStatusEnum.PREPARATORY
|
||||
|| raceStatus == RaceStatusEnum.STARTED) {
|
||||
//Stop this timer.
|
||||
stop();
|
||||
|
||||
//Hide this, and display the race controller.
|
||||
startWrapper.setVisible(false);
|
||||
//start.setVisible(false);//TODO is this needed?
|
||||
//Hide this, and display the race controller.
|
||||
startWrapper.setVisible(false);
|
||||
//start.setVisible(false);//TODO is this needed?
|
||||
|
||||
parent.beginRace(visualiserRaceEvent, controllerClient, isHost);
|
||||
parent.beginRace(visualiserRaceEvent, controllerClient, isHost);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}.start();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,16 +6,29 @@ import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.RadioButton;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.media.AudioClip;
|
||||
import javafx.scene.media.Media;
|
||||
import javafx.scene.media.MediaPlayer;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import mock.app.Event;
|
||||
import mock.exceptions.EventConstructionException;
|
||||
import javafx.stage.WindowEvent;
|
||||
import visualiser.app.App;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Controller for the opening title window.
|
||||
@@ -33,6 +46,12 @@ public class TitleController extends Controller {
|
||||
RadioButton dayModeRD;
|
||||
@FXML
|
||||
RadioButton nightModeRD;
|
||||
@FXML
|
||||
Label tutorialLabel;
|
||||
@FXML
|
||||
Pane menuPane;
|
||||
@FXML
|
||||
ImageView imgSun;
|
||||
|
||||
/**
|
||||
* Method called when the 'host a game' button is pressed.
|
||||
@@ -40,8 +59,9 @@ public class TitleController extends Controller {
|
||||
* Currently used to run the RaceVision mock.
|
||||
* @throws IOException if main has problems
|
||||
*/
|
||||
public void hostAGame() throws IOException {
|
||||
public void hostAGame() throws IOException, URISyntaxException {
|
||||
titleWrapper.setVisible(false);
|
||||
parent.setGameType(0);
|
||||
parent.hostGame();
|
||||
App.getStage().setResizable(true);
|
||||
}
|
||||
@@ -68,7 +88,10 @@ public class TitleController extends Controller {
|
||||
*/
|
||||
public void setDayMode(){
|
||||
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");
|
||||
menuPane.setStyle("-fx-background-color: #6be6ff;");
|
||||
nightModeRD.setSelected(false);
|
||||
}
|
||||
|
||||
@@ -77,12 +100,17 @@ public class TitleController extends Controller {
|
||||
*/
|
||||
public void setNightMode(){
|
||||
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");
|
||||
menuPane.setStyle("-fx-background-color: #1f2c60;");
|
||||
dayModeRD.setSelected(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
tutorialLabel.setWrapText(true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +118,8 @@ public class TitleController extends Controller {
|
||||
*/
|
||||
public void controlBtnPressed(){
|
||||
try {
|
||||
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||
sound.play();
|
||||
FXMLLoader loader = new FXMLLoader();
|
||||
loader.setLocation(getClass().getResource("/visualiser/scenes/keyBindings.fxml"));
|
||||
Parent layout = loader.load();
|
||||
@@ -114,4 +144,12 @@ public class TitleController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void tutorialStartPressed() throws IOException {
|
||||
titleWrapper.setVisible(false);
|
||||
parent.setGameType(4);
|
||||
parent.beginGame();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package visualiser.app;
|
||||
import javafx.animation.FadeTransition;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Task;
|
||||
@@ -14,6 +15,7 @@ import javafx.geometry.Rectangle2D;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.ProgressBar;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.image.Image;
|
||||
@@ -95,7 +97,7 @@ public class App extends Application {
|
||||
);
|
||||
|
||||
updateMessage("Preparing ingredients . . .");
|
||||
Thread.sleep(200);
|
||||
Thread.sleep(100);
|
||||
for (int i = 0; i < burgerFilling.size(); i++) {
|
||||
Thread.sleep(100);
|
||||
updateProgress(i + 1, burgerFilling.size());
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package visualiser.enums;
|
||||
|
||||
import javafx.scene.input.KeyCode;
|
||||
import network.Messages.BoatAction;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
import visualiser.gameController.Keys.ControlKey;
|
||||
import static visualiser.app.App.keyFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* State of which stage the tutorial is currently in
|
||||
*/
|
||||
public enum TutorialState {
|
||||
|
||||
/**
|
||||
* State for upwind in tutorial
|
||||
*/
|
||||
UPWIND(BoatActionEnum.UPWIND),
|
||||
|
||||
/**
|
||||
* State for downwind in tutorial
|
||||
*/
|
||||
DOWNWIND(BoatActionEnum.DOWNWIND),
|
||||
|
||||
/**
|
||||
* State for tacking/gybing in tutorial
|
||||
*/
|
||||
TACKGYBE(BoatActionEnum.TACK_GYBE),
|
||||
|
||||
/**
|
||||
* State for vmg in tutorial
|
||||
*/
|
||||
VMG(BoatActionEnum.AUTO_PILOT),
|
||||
|
||||
/**
|
||||
* State for sails-in in tutorial
|
||||
*/
|
||||
SAILSIN(BoatActionEnum.SAILS_IN),
|
||||
|
||||
/**
|
||||
* State for sails-out in tutorial
|
||||
*/
|
||||
SAILSOUT(BoatActionEnum.SAILS_OUT),
|
||||
|
||||
/**
|
||||
* State for zoom-in in tutorial
|
||||
*/
|
||||
ZOOMIN(null),
|
||||
|
||||
/**
|
||||
* State for zoom-out in tutorial
|
||||
*/
|
||||
ZOOMOUT(null);
|
||||
|
||||
private BoatActionEnum action;
|
||||
|
||||
TutorialState(BoatActionEnum action){
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public BoatActionEnum getAction(){
|
||||
return action;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package visualiser.layout;
|
||||
|
||||
import javafx.scene.shape.Shape3D;
|
||||
|
||||
/**
|
||||
* Created by connortaylorbrown on 13/09/17.
|
||||
*/
|
||||
public class Annotation3D extends Subject3D {
|
||||
/**
|
||||
* Constructor for view subject wrapper
|
||||
*
|
||||
* @param mesh to be rendered
|
||||
*/
|
||||
public Annotation3D(Shape3D mesh) {
|
||||
super(mesh, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent rescaling of this subject
|
||||
* @param scale ignored
|
||||
*/
|
||||
@Override
|
||||
public void setScale(double scale) {}
|
||||
}
|
||||
@@ -62,12 +62,12 @@ public class Boundary3D {
|
||||
double c = Math.sqrt(a * a + b * b);
|
||||
|
||||
|
||||
Subject3D bound1 = new Subject3D(new Sphere(thickness * 2));
|
||||
Subject3D bound1 = new Subject3D(new Sphere(thickness * 2),0);
|
||||
bound1.setX(graphCoord1.getX());
|
||||
bound1.setZ(graphCoord1.getY());
|
||||
boundaryNodes.add(bound1);
|
||||
|
||||
Subject3D connector = new Subject3D(new Box(c, thickness, thickness));
|
||||
Subject3D connector = new Subject3D(new Box(c, thickness, thickness),0);
|
||||
connector.setX(avgCoord.getX());
|
||||
connector.setZ(avgCoord.getY());
|
||||
double angle = 90 + Math.toDegrees(GPSConverter.getAngle(graphCoord2, graphCoord1));
|
||||
|
||||
@@ -24,7 +24,7 @@ public class SeaSurface extends Subject3D {
|
||||
* @param freq frequency the perlin noise is to be generated at
|
||||
*/
|
||||
public SeaSurface(int size, double freq){
|
||||
super(createSurface(size, freq));
|
||||
super(createSurface(size, freq),0);
|
||||
image = new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterDown2048.png"));
|
||||
}
|
||||
|
||||
|
||||
@@ -50,10 +50,10 @@ public class SkyBox {
|
||||
surface.setRotate(180);
|
||||
|
||||
surface.setTranslateX(x);
|
||||
surface.setTranslateY(y - size/2 + yshift + clipOverlap);
|
||||
surface.setTranslateY(y - size + 1);
|
||||
surface.setTranslateZ(z);
|
||||
|
||||
Subject3D top = new SkyBoxPlane(surface);
|
||||
Subject3D top = new Subject3D(surface,0);
|
||||
skyBoxPlanes.add(top);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class SkyBox {
|
||||
surface.setTranslateZ(z + size/2 - clipOverlap);
|
||||
|
||||
|
||||
Subject3D right = new SkyBoxPlane(surface);
|
||||
Subject3D right = new Subject3D(surface,0);
|
||||
skyBoxPlanes.add(right);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class SkyBox {
|
||||
surface.setTranslateZ(z - size/2 + clipOverlap);
|
||||
|
||||
|
||||
Subject3D left = new SkyBoxPlane(surface);
|
||||
Subject3D left = new Subject3D(surface,0);
|
||||
skyBoxPlanes.add(left);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class SkyBox {
|
||||
surface.setTranslateY(y + yshift);
|
||||
surface.setTranslateZ(z);
|
||||
|
||||
Subject3D back = new SkyBoxPlane(surface);
|
||||
Subject3D back = new Subject3D(surface,0);
|
||||
skyBoxPlanes.add(back);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ public class SkyBox {
|
||||
surface.setTranslateY(y + yshift);
|
||||
surface.setTranslateZ(z);
|
||||
|
||||
Subject3D front = new SkyBoxPlane(surface);
|
||||
Subject3D front = new Subject3D(surface,0);
|
||||
skyBoxPlanes.add(front);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ public class Subject3D {
|
||||
* Rendered mesh
|
||||
*/
|
||||
private Shape3D mesh;
|
||||
/**
|
||||
* Source ID of subject in game model
|
||||
*/
|
||||
private int sourceID;
|
||||
|
||||
/**
|
||||
* Position translation updated by state listeners
|
||||
@@ -30,8 +34,9 @@ public class Subject3D {
|
||||
* Constructor for view subject wrapper
|
||||
* @param mesh to be rendered
|
||||
*/
|
||||
public Subject3D(Shape3D mesh) {
|
||||
public Subject3D(Shape3D mesh, int sourceID) {
|
||||
this.mesh = mesh;
|
||||
this.sourceID = sourceID;
|
||||
this.scale = new Scale();
|
||||
this.position = new Translate();
|
||||
this.heading = new Rotate(0, Rotate.Y_AXIS);
|
||||
@@ -43,6 +48,10 @@ public class Subject3D {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public int getSourceID() {
|
||||
return sourceID;
|
||||
}
|
||||
|
||||
public Translate getPosition() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package visualiser.layout;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -31,11 +33,15 @@ public class View3D extends Pane {
|
||||
/**
|
||||
* Map for selecting Subject3D from Shape3D
|
||||
*/
|
||||
private Map<Shape3D, Subject3D> selectionMap;
|
||||
private Map<Shape3D, Subject3D> shapeMap;
|
||||
/**
|
||||
* Map for selecting Shape3D from source ID
|
||||
*/
|
||||
private Map<Integer, Shape3D> sourceMap;
|
||||
/**
|
||||
* Subject tracked by camera
|
||||
*/
|
||||
private Subject3D target;
|
||||
private ObjectProperty<Subject3D> target;
|
||||
/**
|
||||
* Rendering container for shapes
|
||||
*/
|
||||
@@ -95,8 +101,9 @@ public class View3D extends Pane {
|
||||
*/
|
||||
public View3D(boolean fill) {
|
||||
this.world = new Group();
|
||||
this.selectionMap = new HashMap<>();
|
||||
this.target = null;
|
||||
this.shapeMap = new HashMap<>();
|
||||
this.sourceMap = new HashMap<>();
|
||||
this.target = new SimpleObjectProperty<>(null);
|
||||
this.scene = new SubScene(world, 300, 300);
|
||||
|
||||
scene.widthProperty().bind(this.widthProperty());
|
||||
@@ -148,17 +155,23 @@ public class View3D extends Pane {
|
||||
if (c.wasRemoved() || c.wasAdded()) {
|
||||
for (Subject3D shape : c.getRemoved()) {
|
||||
world.getChildren().remove(shape.getMesh());
|
||||
selectionMap.remove(shape.getMesh());
|
||||
shapeMap.remove(shape.getMesh());
|
||||
sourceMap.remove(shape.getSourceID());
|
||||
}
|
||||
for (Subject3D shape : c.getAddedSubList()) {
|
||||
world.getChildren().add(shape.getMesh());
|
||||
selectionMap.put(shape.getMesh(), shape);
|
||||
shapeMap.put(shape.getMesh(), shape);
|
||||
sourceMap.put(shape.getSourceID(), shape.getMesh());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Shape3D getShape(int sourceID) {
|
||||
return sourceMap.get(sourceID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept mouse clicks on subjects in view. The applied listener cannot be removed.
|
||||
*/
|
||||
@@ -167,12 +180,16 @@ public class View3D extends Pane {
|
||||
PickResult result = e.getPickResult();
|
||||
if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) {
|
||||
untrackSubject();
|
||||
trackSubject(selectionMap.get(result.getIntersectedNode()));
|
||||
trackSubject(shapeMap.get(result.getIntersectedNode()));
|
||||
setThirdPerson();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ObjectProperty<Subject3D> targetProperty() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures camera to third person view
|
||||
*/
|
||||
@@ -201,11 +218,12 @@ public class View3D extends Pane {
|
||||
* Stop camera from following the last selected subject
|
||||
*/
|
||||
private void untrackSubject() {
|
||||
if(target != null) {
|
||||
target.getPosition().xProperty().removeListener(pivotX);
|
||||
target.getPosition().yProperty().removeListener(pivotY);
|
||||
target.getPosition().zProperty().removeListener(pivotZ);
|
||||
target.getHeading().angleProperty().removeListener(pivotHeading);
|
||||
if(target.get() != null) {
|
||||
target.get().getPosition().xProperty().removeListener(pivotX);
|
||||
target.get().getPosition().yProperty().removeListener(pivotY);
|
||||
target.get().getPosition().zProperty().removeListener(pivotZ);
|
||||
target.get().getHeading().angleProperty().removeListener(pivotHeading);
|
||||
target.setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,15 +232,15 @@ public class View3D extends Pane {
|
||||
* @param subject to track
|
||||
*/
|
||||
private void trackSubject(Subject3D subject) {
|
||||
target = subject;
|
||||
target.set(subject);
|
||||
|
||||
updatePivot(target.getPosition());
|
||||
setYaw(target.getHeading().getAngle());
|
||||
updatePivot(target.get().getPosition());
|
||||
setYaw(target.get().getHeading().getAngle());
|
||||
|
||||
target.getPosition().xProperty().addListener(pivotX);
|
||||
target.getPosition().yProperty().addListener(pivotY);
|
||||
target.getPosition().zProperty().addListener(pivotZ);
|
||||
target.getHeading().angleProperty().addListener(pivotHeading);
|
||||
target.get().getPosition().xProperty().addListener(pivotX);
|
||||
target.get().getPosition().yProperty().addListener(pivotY);
|
||||
target.get().getPosition().zProperty().addListener(pivotZ);
|
||||
target.get().getHeading().angleProperty().addListener(pivotHeading);
|
||||
}
|
||||
|
||||
public void setNearClip(double nearClip) {
|
||||
@@ -233,6 +251,10 @@ public class View3D extends Pane {
|
||||
this.farClip = farClip;
|
||||
}
|
||||
|
||||
public Translate getPivot() {
|
||||
return pivot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the coordinates of the camera pivot once.
|
||||
* @param pivot source of coordinates
|
||||
|
||||
@@ -65,7 +65,7 @@ public class VisualiserRaceController implements RunnableWithFramePeriod {
|
||||
compositeRaceCommand.addCommand(command);
|
||||
|
||||
} catch (CommandConstructionException e) {
|
||||
Logger.getGlobal().log(Level.WARNING, "VisualiserRaceController could not create a command for incoming message.");
|
||||
//Logger.getGlobal().log(Level.WARNING, "VisualiserRaceController could not create a command for incoming message.");
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Logger.getGlobal().log(Level.SEVERE, "VisualiserRaceController was interrupted on thread: " + Thread.currentThread() + " while waiting for messages.");
|
||||
|
||||
Reference in New Issue
Block a user