Merge branch 'master' into story1292_hp_punish
# Conflicts: # racevisionGame/src/main/java/visualiser/Controllers/RaceViewController.java # racevisionGame/src/main/resources/visualiser/scenes/newRaceView.fxml # racevisionGame/src/main/resources/visualiser/scenes/raceView.fxml
This commit is contained in:
@@ -10,6 +10,7 @@ import javafx.fxml.FXML;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.ImageView;
|
||||
@@ -18,21 +19,21 @@ import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.PhongMaterial;
|
||||
import javafx.scene.shape.MeshView;
|
||||
import javafx.scene.shape.Shape3D;
|
||||
import mock.app.Event;
|
||||
import network.Messages.Enums.RaceStatusEnum;
|
||||
import network.Messages.Enums.RequestToJoinEnum;
|
||||
import shared.model.Boat;
|
||||
import visualiser.app.App;
|
||||
import visualiser.gameController.ControllerClient;
|
||||
import visualiser.layout.SeaSurface;
|
||||
import visualiser.layout.Subject3D;
|
||||
import visualiser.layout.View3D;
|
||||
import visualiser.layout.*;
|
||||
import visualiser.model.VisualiserBoat;
|
||||
import visualiser.model.VisualiserRaceEvent;
|
||||
import visualiser.model.VisualiserRaceState;
|
||||
import visualiser.network.HttpMatchBrowserHost;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@@ -70,13 +71,10 @@ public class InGameLobbyController extends Controller {
|
||||
private Label playerLabel6;
|
||||
|
||||
@FXML
|
||||
private Label countdownLabel;
|
||||
private Button startButton;
|
||||
|
||||
@FXML
|
||||
private AnchorPane countdownTenPane;
|
||||
|
||||
@FXML
|
||||
private Label countdownTenText;
|
||||
private Button quitButton;
|
||||
|
||||
private Event game;
|
||||
|
||||
@@ -157,22 +155,44 @@ public class InGameLobbyController extends Controller {
|
||||
sea.setZ(210);
|
||||
subjects.add(sea);
|
||||
|
||||
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());
|
||||
mesh.setMaterial(boatColorMat);
|
||||
Subject3D subject = new Subject3D(mesh,0);
|
||||
//mesh.setMaterial(boatColorMat);
|
||||
Subject3D subject = new Subject3D(mesh, 0);
|
||||
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.setYaw(45);
|
||||
playerBoatToSet.setPitch(20);
|
||||
|
||||
|
||||
if (boat.isClientBoat()) {
|
||||
Shockwave boatHighlight = new Shockwave(10);
|
||||
boatHighlight.getMesh().setMaterial(new PhongMaterial(new Color(1, 1, 0, 0.1)));
|
||||
boatHighlight.setX(subject.getPosition().getX());
|
||||
boatHighlight.setY(subject.getPosition().getY());
|
||||
boatHighlight.setZ(subject.getPosition().getZ());
|
||||
subjects.add(boatHighlight);
|
||||
subject.getMesh().toFront();
|
||||
}
|
||||
|
||||
AnimationTimer rotate = new AnimationTimer() {
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
subject.setHeading(subject.getHeading().getAngle() + 0.1);
|
||||
sailsSubject.setHeading(subject.getHeading().getAngle() + 0.1);
|
||||
sailsSubject.setX(subject.getX());
|
||||
sailsSubject.setZ(subject.getZ());
|
||||
}
|
||||
};
|
||||
rotate.start();
|
||||
@@ -199,37 +219,13 @@ public class InGameLobbyController extends Controller {
|
||||
* Starts the race.
|
||||
*/
|
||||
private void startRace() {
|
||||
//Initialises the race clock.
|
||||
initialiseRaceClock(this.visualiserRaceEvent.getVisualiserRaceState());
|
||||
|
||||
//Starts the race countdown timer.
|
||||
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.
|
||||
*/
|
||||
@@ -239,19 +235,6 @@ public class InGameLobbyController extends Controller {
|
||||
public void handle(long arg0) {
|
||||
//Get the current race status.
|
||||
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
|
||||
|
||||
//Try catch for getting interval times
|
||||
try {
|
||||
long interval = ChronoUnit.MILLIS.between(visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getCurrentTime(), visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getStartingTime());
|
||||
if(interval<=10000){
|
||||
countdownLabel.setVisible(false);
|
||||
countdownTenPane.setVisible(true);
|
||||
countdownTenText.setVisible(true);
|
||||
}
|
||||
countdownText(interval);
|
||||
} catch (Exception e){
|
||||
|
||||
}
|
||||
|
||||
|
||||
//If the race has reached the preparatory phase, or has started...
|
||||
@@ -291,6 +274,8 @@ public class InGameLobbyController extends Controller {
|
||||
|
||||
this.visualiserRaceEvent.getVisualiserRaceState().getBoats().addListener(this.lobbyUpdateListener);
|
||||
|
||||
enableStartIfHost();
|
||||
|
||||
startRace();
|
||||
} catch (IOException e) {
|
||||
//TODO should probably let this propagate, so that we only enter this scene if everything works
|
||||
@@ -298,6 +283,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
|
||||
* @throws IOException socket erro
|
||||
@@ -325,7 +326,7 @@ public class InGameLobbyController extends Controller {
|
||||
* 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");
|
||||
App.game.getRaceLogic().getRace().startRace(App.game.getRaceLogic().getRace().getRacePreparatoryTime(), true);
|
||||
}
|
||||
|
||||
public void joinSpecPressed(){
|
||||
@@ -336,48 +337,5 @@ public class InGameLobbyController extends Controller {
|
||||
//System.out.println("Empty race user pane pressed. Joining racers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Countdown interval checker that updates the countdown text
|
||||
* @param interval Countdown interval to check
|
||||
*/
|
||||
private void countdownText(long interval){
|
||||
countdownTenPane.setVisible(false);
|
||||
//Do nothing if 5 seconds or less to go
|
||||
if (interval <=5000){
|
||||
countdownTenPane.setVisible(false);
|
||||
//countdownTenText.setText("5");
|
||||
return;
|
||||
}
|
||||
|
||||
//6 seconds left. Display 1 second for countdown
|
||||
if (interval <=6000){
|
||||
countdownTenText.setText("1");
|
||||
return;
|
||||
}
|
||||
|
||||
//7 seconds left. Display 2 seconds for countdown
|
||||
if (interval <=7000){
|
||||
countdownTenText.setText("2");
|
||||
return;
|
||||
}
|
||||
|
||||
//8 seconds left. Display 3 seconds for countdown
|
||||
if (interval <=8000){
|
||||
countdownTenText.setText("3");
|
||||
return;
|
||||
}
|
||||
|
||||
//9 seconds left. Display 4 seconds for countdown
|
||||
if (interval <=9000){
|
||||
countdownTenText.setText("4");
|
||||
return;
|
||||
}
|
||||
|
||||
//10 seconds left. Display 5 seconds for countdown
|
||||
if (interval <=10000){
|
||||
countdownTenText.setText("5");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package visualiser.Controllers;
|
||||
|
||||
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
||||
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
|
||||
import eu.hansolo.medusa.*;
|
||||
import eu.hansolo.medusa.events.UpdateEvent;
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
@@ -23,6 +26,7 @@ import javafx.scene.media.AudioClip;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.Material;
|
||||
import javafx.scene.paint.PhongMaterial;
|
||||
import javafx.scene.paint.Stop;
|
||||
import javafx.scene.shape.MeshView;
|
||||
import javafx.scene.shape.Shape3D;
|
||||
import javafx.scene.transform.Translate;
|
||||
@@ -44,10 +48,7 @@ import visualiser.utils.GPSConverter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -66,7 +67,12 @@ public class RaceViewController extends Controller {
|
||||
private boolean isTutorial = false;
|
||||
private String keyToPress;
|
||||
private View3D view3D;
|
||||
private WindCompass windCompass;
|
||||
private ObservableList<Subject3D> viewSubjects;
|
||||
private Gauge gauge;
|
||||
private FGauge fGauge;
|
||||
private long positionDelay = 1000;
|
||||
private long positionTime = 0;
|
||||
private ResizableRaceCanvas raceCanvas;
|
||||
private boolean mapToggle = true;
|
||||
private GPSConverter gpsConverter;
|
||||
@@ -93,6 +99,7 @@ public class RaceViewController extends Controller {
|
||||
private @FXML Label timeZone;
|
||||
private @FXML CheckBox showFPS;
|
||||
private @FXML TableView<VisualiserBoat> boatInfoTable;
|
||||
private @FXML TableColumn<VisualiserBoat, String> boatPlacingColumn;
|
||||
private @FXML TableColumn<VisualiserBoat, String> boatTeamColumn;
|
||||
private @FXML TableColumn<VisualiserBoat, Leg> boatMarkColumn;
|
||||
private @FXML TableColumn<VisualiserBoat, Number> boatSpeedColumn;
|
||||
@@ -101,6 +108,8 @@ public class RaceViewController extends Controller {
|
||||
private @FXML Label tutorialText;
|
||||
private @FXML AnchorPane infoWrapper;
|
||||
private @FXML AnchorPane lineChartWrapper;
|
||||
private @FXML StackPane speedPane;
|
||||
private @FXML AnchorPane raceAnchorPane;
|
||||
private @FXML GridPane playerHealthContainer;
|
||||
private @FXML ImageView imageView;
|
||||
private @FXML AnchorPane deathTransPane;
|
||||
@@ -217,6 +226,78 @@ 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(200, 200)
|
||||
.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}.
|
||||
*/
|
||||
@@ -234,14 +315,15 @@ public class RaceViewController extends Controller {
|
||||
this.nextMarkArrow = new Annotation3D(arrow);
|
||||
this.nextMarkArrow.setScale(0.1);
|
||||
|
||||
|
||||
// initialise displays
|
||||
initialiseFps();
|
||||
initialiseInfoTable();
|
||||
initialiseView3D(this.visualiserRace);
|
||||
initialiseHealthPane();
|
||||
initialiseRaceClock();
|
||||
initialiseSpeedometer();
|
||||
raceTimer(); // start the timer
|
||||
speedometerLoop();
|
||||
new Sparkline(this.raceState, this.sparklineChart);
|
||||
timeZone.setText(this.raceState.getRaceClock().getTimeZone());
|
||||
arrowController.setWindProperty(this.raceState.windProperty());
|
||||
@@ -300,6 +382,9 @@ public class RaceViewController extends Controller {
|
||||
view3D.addPointLight(pointLight);
|
||||
canvasBase.add(view3D, 0, 0);
|
||||
|
||||
windCompass = new WindCompass(view3D, this.raceState.windProperty());
|
||||
arrowPane.getChildren().add(windCompass);
|
||||
|
||||
// Set up projection from GPS to view
|
||||
RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource();
|
||||
gpsConverter = new GPSConverter(raceData, 450, 450);
|
||||
@@ -319,8 +404,6 @@ public class RaceViewController extends Controller {
|
||||
seaOverlay.setZ(210);
|
||||
viewSubjects.add(seaOverlay);
|
||||
|
||||
|
||||
|
||||
Boundary3D boundary3D = new Boundary3D(visualiserRace.getVisualiserRaceState().getRaceDataSource().getBoundary(), gpsConverter);
|
||||
for (Subject3D subject3D: boundary3D.getBoundaryNodes()){
|
||||
viewSubjects.add(subject3D);
|
||||
@@ -335,32 +418,117 @@ public class RaceViewController extends Controller {
|
||||
|
||||
viewSubjects.add(markModel);
|
||||
}
|
||||
|
||||
for (VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
|
||||
if (boat.isClientBoat()) {
|
||||
Shockwave boatHighlight = new Shockwave(10);
|
||||
boatHighlight.getMesh().setMaterial(new PhongMaterial(new Color(1, 1, 0, 0.1)));
|
||||
viewSubjects.add(boatHighlight);
|
||||
AnimationTimer highlightTrack = new AnimationTimer() {
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
boatHighlight.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
||||
boatHighlight.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
||||
}
|
||||
};
|
||||
highlightTrack.start();
|
||||
}
|
||||
}
|
||||
|
||||
// Position and add each boat to view
|
||||
for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
|
||||
MeshView mesh;
|
||||
if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) {
|
||||
mesh = new MeshView(importer.getImport());
|
||||
} else {
|
||||
mesh = new MeshView(importerBurgerBoat.getImport());
|
||||
}
|
||||
// MeshView mesh;
|
||||
// if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) {
|
||||
// mesh = new MeshView(importer.getImport());
|
||||
// } else {
|
||||
// mesh = new MeshView(importerBurgerBoat.getImport());
|
||||
// }
|
||||
Shape3D mesh = Assets3D.getBoat();
|
||||
|
||||
PhongMaterial boatColorMat = new PhongMaterial(boat.getColor());
|
||||
mesh.setMaterial(boatColorMat);
|
||||
//mesh.setMaterial(boatColorMat);
|
||||
Subject3D boatModel = new Subject3D(mesh, boat.getSourceID());
|
||||
|
||||
viewSubjects.add(boatModel);
|
||||
|
||||
|
||||
//Create first fire
|
||||
HealthEffect fire = new HealthEffect(boat.getSourceID(), System.currentTimeMillis());
|
||||
viewSubjects.add(fire);
|
||||
//fire.setVisible(false);
|
||||
healthEffectList.add(fire);
|
||||
|
||||
//add sail
|
||||
Sails3D sails3D = new Sails3D();
|
||||
Subject3D sailsSubject = new Subject3D(sails3D, 0);
|
||||
sails3D.setMaterial(boatColorMat);
|
||||
sailsSubject.setXRot(0d);
|
||||
sailsSubject.setHeading(visualiserRace.getVisualiserRaceState().getWindDirection().degrees());
|
||||
|
||||
//fire.rotateView((double) 90, fire.getPosition().getX(), fire.getPosition().getY(), fire.getPosition().getZ(), Rotate.X_AXIS);
|
||||
viewSubjects.add(sailsSubject);
|
||||
|
||||
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());
|
||||
|
||||
}
|
||||
};
|
||||
sailsFollowBoat.start();
|
||||
|
||||
|
||||
// Track this boat's movement with the new subject
|
||||
AnimationTimer trackBoat = new AnimationTimer() {
|
||||
@Override
|
||||
@@ -427,7 +595,7 @@ public class RaceViewController extends Controller {
|
||||
}
|
||||
}
|
||||
}
|
||||
view3D.updateDistance(-10);
|
||||
view3D.zoomIn();
|
||||
break;
|
||||
case "Zoom Out":
|
||||
//Check if race is a tutorial
|
||||
@@ -442,7 +610,7 @@ public class RaceViewController extends Controller {
|
||||
}
|
||||
}
|
||||
}
|
||||
view3D.updateDistance(10);
|
||||
view3D.zoomOut();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -691,6 +859,39 @@ public class RaceViewController extends Controller {
|
||||
}.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();
|
||||
}
|
||||
|
||||
|
||||
private void healthLoop(){
|
||||
new AnimationTimer(){
|
||||
@Override
|
||||
|
||||
@@ -25,6 +25,7 @@ import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import javafx.util.Duration;
|
||||
import mock.app.Event;
|
||||
import visualiser.layout.Assets3D;
|
||||
|
||||
public class App extends Application {
|
||||
private static Stage stage;
|
||||
@@ -113,6 +114,7 @@ public class App extends Application {
|
||||
}
|
||||
);
|
||||
new Thread(boatTask).start();
|
||||
Assets3D.loadAssets();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package visualiser.layout;
|
||||
|
||||
import com.interactivemesh.jfx.importer.obj.ObjModelImporter;
|
||||
import com.interactivemesh.jfx.importer.x3d.X3dModelImporter;
|
||||
import javafx.geometry.Point3D;
|
||||
import javafx.scene.Group;
|
||||
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 Subject3D windArrow;
|
||||
public static Subject3D compass;
|
||||
|
||||
public static void loadAssets(){
|
||||
loadSails();
|
||||
loadWindArrow();
|
||||
}
|
||||
|
||||
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);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Shape3D getBoat(){
|
||||
String path = "assets/V1.4 Boat.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));
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -13,5 +13,6 @@ public class Shockwave extends Subject3D {
|
||||
super(new Cylinder(radius,0),0);
|
||||
getMesh().getTransforms().add(new Rotate(-90, Rotate.X_AXIS));
|
||||
getMesh().setMaterial(new PhongMaterial(new Color(0,0,0,0)));
|
||||
getMesh().setMouseTransparent(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,13 @@ public class Subject3D {
|
||||
*/
|
||||
private Rotate heading;
|
||||
|
||||
private Rotate xRot;
|
||||
|
||||
private Rotate yRot;
|
||||
|
||||
private Scale scale;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for view subject wrapper
|
||||
* @param mesh to be rendered
|
||||
@@ -41,7 +46,9 @@ public class Subject3D {
|
||||
this.scale = new Scale();
|
||||
this.position = new Translate();
|
||||
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() {
|
||||
@@ -70,15 +77,35 @@ public class Subject3D {
|
||||
position.setX(x);
|
||||
}
|
||||
|
||||
public double getX(){
|
||||
return position.getX();
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
position.setY(y);
|
||||
}
|
||||
|
||||
public double getY(){
|
||||
return position.getY();
|
||||
}
|
||||
|
||||
public void setZ(double z) {
|
||||
position.setZ(z);
|
||||
}
|
||||
|
||||
public double getZ(){
|
||||
return position.getZ();
|
||||
}
|
||||
|
||||
public void setHeading(double 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.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.*;
|
||||
@@ -78,11 +77,17 @@ public class View3D extends Pane {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private final double FIRST_PERSON_LIMIT = 2;
|
||||
private final double ZOOM_IN_LIMIT = 30;
|
||||
private final double ZOOM_OUT_LIMIT = 700;
|
||||
private final double MAX_ZOOM_LIMIT = 1500;
|
||||
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.
|
||||
@@ -183,12 +188,9 @@ public class View3D extends Pane {
|
||||
* Configures camera to third person view
|
||||
*/
|
||||
public void setThirdPerson() {
|
||||
this.setDistance(THIRD_PERSON_LIMIT / 2);
|
||||
this.setPitch(10);
|
||||
|
||||
for(Subject3D item: items) {
|
||||
item.setScale(0.1);
|
||||
}
|
||||
this.setDistance(ZOOM_IN_LIMIT * 2);
|
||||
adjustPitchForZoom();
|
||||
adjustScaleForZoom();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,17 +198,14 @@ public class View3D extends Pane {
|
||||
*/
|
||||
public void setBirdsEye() {
|
||||
this.setYaw(0);
|
||||
this.setPitch(60);
|
||||
|
||||
for(Subject3D item: items) {
|
||||
item.setScale(1);
|
||||
}
|
||||
this.setPitch(MAX_PITCH);
|
||||
adjustScaleForZoom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop camera from following the last selected subject
|
||||
*/
|
||||
private void untrackSubject() {
|
||||
public void untrackSubject() {
|
||||
if(target.get() != null) {
|
||||
trackBoat.stop();
|
||||
target.setValue(null);
|
||||
@@ -217,7 +216,7 @@ public class View3D extends Pane {
|
||||
* Set camera to follow the selected subject
|
||||
* @param subject to track
|
||||
*/
|
||||
private void trackSubject(Subject3D subject) {
|
||||
public void trackSubject(Subject3D subject) {
|
||||
target.set(subject);
|
||||
|
||||
this.trackBoat = new AnimationTimer() {
|
||||
@@ -267,17 +266,79 @@ public class View3D extends Pane {
|
||||
* @param delta amount to change distance by
|
||||
*/
|
||||
public void updateDistance(double delta) {
|
||||
double distance = -this.distance.getZ() + delta;
|
||||
|
||||
if(distance <= FIRST_PERSON_LIMIT) {
|
||||
this.setDistance(FIRST_PERSON_LIMIT);
|
||||
} else if(distance > THIRD_PERSON_LIMIT) {
|
||||
this.setDistance(distance);
|
||||
double newDistance = -this.distance.getZ() + delta;
|
||||
if (target.get() == null){
|
||||
if (newDistance > MAX_ZOOM_LIMIT){
|
||||
setDistance(MAX_ZOOM_LIMIT);
|
||||
} else {
|
||||
setDistance(newDistance);
|
||||
}
|
||||
} else if(newDistance <= ZOOM_IN_LIMIT) {
|
||||
setDistance(ZOOM_IN_LIMIT);
|
||||
} else if (newDistance > ZOOM_OUT_LIMIT){
|
||||
untrackSubject();
|
||||
setBirdsEye();
|
||||
setDistance(1050);
|
||||
updatePivot(new Translate(250, 0, 210));
|
||||
setYaw(0);
|
||||
} 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,6 +349,10 @@ public class View3D extends Pane {
|
||||
this.yaw.setAngle(yaw);
|
||||
}
|
||||
|
||||
public double getYaw(){
|
||||
return this.yaw.getAngle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set elevation of camera
|
||||
* @param pitch in degrees
|
||||
@@ -296,6 +361,10 @@ public class View3D extends Pane {
|
||||
this.pitch.setAngle(-pitch);
|
||||
}
|
||||
|
||||
public double getPitch(){
|
||||
return this.pitch.getAngle();
|
||||
}
|
||||
|
||||
public void addAmbientLight(AmbientLight 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();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user