16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 15:09:19 +02:00

refactoring: application mode code split

This commit is contained in:
tmj-fstate
2018-08-02 20:39:19 +02:00
parent 460bf6d382
commit ff6bed67a9
74 changed files with 5375 additions and 2921 deletions

View File

@@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/.
#include "MdlMngr.h"
#include "simulation.h"
#include "simulationtime.h"
#include "event.h"
#include "Globals.h"
#include "Timer.h"
#include "Logs.h"
@@ -610,7 +611,7 @@ void TAnimModel::RaPrepare()
if (LightsOff[i])
LightsOff[i]->iVisible = !state;
}
TSubModel::iInstance = (size_t)this; //żeby nie robić cudzych animacji
TSubModel::iInstance = reinterpret_cast<std::uintptr_t>( this ); //żeby nie robić cudzych animacji
TSubModel::pasText = &asText; // przekazanie tekstu do wyświetlacza (!!!! do przemyślenia)
if (pAdvanced) // jeśli jest zaawansowana animacja
Advanced(); // wykonać co tam trzeba

View File

@@ -125,7 +125,7 @@ class TAnimAdvanced
class TAnimModel : public scene::basic_node {
friend opengl_renderer;
friend ui_layer;
friend editor_ui;
public:
// constructors

View File

@@ -18,16 +18,16 @@ http://mozilla.org/MPL/2.0/.
//---------------------------------------------------------------------------
void TCamera::Init( Math3D::vector3 NPos, Math3D::vector3 NAngle) {
void TCamera::Init( Math3D::vector3 const &NPos, Math3D::vector3 const &NAngle, TCameraType const NType ) {
vUp = Math3D::vector3(0, 1, 0);
Velocity = Math3D::vector3(0, 0, 0);
vUp = { 0, 1, 0 };
Velocity = { 0, 0, 0 };
Pitch = NAngle.x;
Yaw = NAngle.y;
Roll = NAngle.z;
Pos = NPos;
Type = (Global.bFreeFly ? TCameraType::tp_Free : TCameraType::tp_Follow);
Type = NType;
};
void TCamera::Reset() {

View File

@@ -23,7 +23,7 @@ enum class TCameraType
class TCamera {
public: // McZapkie: potrzebuje do kiwania na boki
void Init( Math3D::vector3 NPos, Math3D::vector3 NAngle);
void Init( Math3D::vector3 const &Location, Math3D::vector3 const &Angle, TCameraType const Type );
void Reset();
void OnCursorMove(double const x, double const y);
bool OnCommand( command_data const &Command );

View File

@@ -28,11 +28,17 @@ class sound_source;
class TEventLauncher;
class TTraction; // drut
class TTractionPowerSource; // zasilanie drutów
class TWorld;
class TCamera;
class scenario_time;
class TMoverParameters;
class ui_layer;
class editor_ui;
class event_manager;
class memory_table;
class powergridsource_table;
class instance_table;
class vehicle_table;
struct light_array;
namespace scene {
struct node_data;

View File

@@ -89,8 +89,7 @@ Console::Console()
Console::~Console()
{
delete PoKeys55[0];
delete PoKeys55[1];
Console::Off();
};
void Console::ModeSet(int m, int h)

View File

@@ -18,11 +18,12 @@ http://mozilla.org/MPL/2.0/.
#include <direct.h>
#include "Globals.h"
#include "Logs.h"
#include "train.h"
#include "mtable.h"
#include "DynObj.h"
#include "Event.h"
#include "MemCell.h"
#include "world.h"
#include "simulation.h"
#include "simulationtime.h"
#include "track.h"
#include "station.h"
@@ -1725,7 +1726,11 @@ void TController::Activation()
}
if (pVehicle != old)
{ // jeśli zmieniony został pojazd prowadzony
Global.pWorld->CabChange(old, pVehicle); // ewentualna zmiana kabiny użytkownikowi
if( ( simulation::Train )
&& ( simulation::Train->Dynamic() == old ) ) {
// ewentualna zmiana kabiny użytkownikowi
Global.changeDynObj = pVehicle; // uruchomienie protezy
}
ControllingSet(); // utworzenie połączenia do sterowanego pojazdu (może się zmienić) -
// silnikowy dla EZT
}

View File

@@ -16,15 +16,17 @@ http://mozilla.org/MPL/2.0/.
#include "DynObj.h"
#include "simulation.h"
#include "world.h"
#include "train.h"
#include "driver.h"
#include "Globals.h"
#include "Timer.h"
#include "logs.h"
#include "Console.h"
#include "MdlMngr.h"
#include "model3d.h"
#include "renderer.h"
#include "uitranscripts.h"
#include "messaging.h"
// Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny
#define vWorldFront Math3D::vector3(0, 0, 1)
@@ -4305,7 +4307,7 @@ void TDynamicObject::RenderSounds() {
FreeFlyModeFlag ? true : // in external view all vehicles emit outer noise
// Global.pWorld->train() == nullptr ? true : // (can skip this check, with no player train the external view is a given)
ctOwner == nullptr ? true : // standalone vehicle, can't be part of user-driven train
ctOwner != Global.pWorld->train()->Dynamic()->ctOwner ? true : // confirmed isn't a part of the user-driven train
ctOwner != simulation::Train->Dynamic()->ctOwner ? true : // confirmed isn't a part of the user-driven train
Global.CabWindowOpen ? true : // sticking head out we get to hear outer noise
false ) ) {
@@ -6994,8 +6996,11 @@ vehicle_table::erase_disabled() {
&& ( true == vehicle->MyTrack->RemoveDynamicObject( vehicle ) ) ) {
vehicle->MyTrack = nullptr;
}
if( simulation::Train->Dynamic() == vehicle ) {
// clear potential train binding
Global.pWorld->TrainDelete( vehicle );
// TBD, TODO: manually eject the driver first ?
SafeDelete( simulation::Train );
}
// remove potential entries in the light array
simulation::Lights.remove( vehicle );
/*

View File

@@ -17,8 +17,16 @@ http://mozilla.org/MPL/2.0/.
#include "event.h"
#include "simulation.h"
#include "world.h"
#include "messaging.h"
#include "globals.h"
#include "memcell.h"
#include "track.h"
#include "traction.h"
#include "tractionpower.h"
#include "sound.h"
#include "animmodel.h"
#include "dynobj.h"
#include "driver.h"
#include "timer.h"
#include "logs.h"
@@ -1207,7 +1215,7 @@ event_manager::CheckQuery() {
}
case 1: {
if( m_workevent->Params[ 1 ].asdouble > 0.0 ) {
Global.pWorld->radio_message(
simulation::radio_message(
m_workevent->Params[ 9 ].tsTextSound,
static_cast<int>( m_workevent->Params[ 1 ].asdouble ) );
}

View File

@@ -14,8 +14,9 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "globals.h"
#include "world.h"
#include "simulation.h"
#include "simulationenvironment.h"
#include "driver.h"
#include "logs.h"
#include "Console.h"
#include "PyInt.h"
@@ -94,7 +95,7 @@ global_settings::ConfigParse(cParser &Parser) {
{ // Mczapkie-130302
Parser.getTokens();
Parser >> bFreeFly;
Parser >> FreeFlyModeFlag;
Parser.getTokens(3, false);
Parser >>
FreeCameraInit[0].x,
@@ -298,7 +299,7 @@ global_settings::ConfigParse(cParser &Parser) {
std::tm *localtime = std::localtime(&timenow);
fMoveLight = localtime->tm_yday + 1; // numer bieżącego dnia w roku
}
pWorld->compute_season( fMoveLight );
simulation::Environment.compute_season( fMoveLight );
}
else if( token == "dynamiclights" ) {
// number of dynamic lights in the scene

View File

@@ -25,8 +25,8 @@ struct global_settings {
bool altState{ false };
std::mt19937 random_engine{ std::mt19937( static_cast<unsigned int>( std::time( NULL ) ) ) };
TDynamicObject *changeDynObj{ nullptr };// info o zmianie pojazdu
TWorld *pWorld{ nullptr }; // wskaźnik na świat do usuwania pojazdów
TCamera *pCamera{ nullptr }; // parametry kamery
TCamera *pDebugCamera{ nullptr };
Math3D::vector3 pCameraPosition; // pozycja kamery w świecie
Math3D::vector3 DebugCameraPosition; // pozycja kamery w świecie
std::vector<Math3D::vector3> FreeCameraInit; // pozycje kamery
@@ -64,7 +64,6 @@ struct global_settings {
unsigned int DisabledLogTypes{ 0 };
// simulation
bool RealisticControlMode{ false }; // controls ability to steer the vehicle from outside views
bool bFreeFly{ false };
bool bEnableTraction{ true };
float fFriction{ 1.f }; // mnożnik tarcia - KURS90
bool bLiveTraction{ true };

View File

@@ -1294,7 +1294,7 @@ public:
void ComputeConstans(void);//ABu: wczesniejsze wyznaczenie stalych dla liczenia sil
double ComputeMass(void);
void ComputeTotalForce(double dt, double dt1, bool FullVer);
double Adhesive(double staticfriction);
double Adhesive(double staticfriction) const;
double TractionForce(double dt);
double FrictionForce(double R, int TDamage);
double BrakeForceR(double ratio, double velocity);
@@ -1327,8 +1327,8 @@ public:
bool FuseOn(void); //bezpiecznik nadamiary
bool FuseFlagCheck(void); // sprawdzanie flagi nadmiarowego
void FuseOff(void); // wylaczenie nadmiarowego
double ShowCurrent( int AmpN ); //pokazuje bezwgl. wartosc pradu na wybranym amperomierzu
double ShowCurrentP(int AmpN); //pokazuje bezwgl. wartosc pradu w wybranym pojezdzie //Q 20160722
double ShowCurrent( int AmpN ) const; //pokazuje bezwgl. wartosc pradu na wybranym amperomierzu
double ShowCurrentP(int AmpN) const; //pokazuje bezwgl. wartosc pradu w wybranym pojezdzie //Q 20160722
/*!o pokazuje bezwgl. wartosc obrotow na obrotomierzu jednego z 3 pojazdow*/
/*function ShowEngineRotation(VehN:int): integer; //Ra 2014-06: przeniesione do C++*/
@@ -1374,7 +1374,7 @@ public:
/*funkcje ladujace pliki opisujace pojazd*/
bool LoadFIZ(std::string chkpath); //Q 20160717 bool LoadChkFile(std::string chkpath);
bool CheckLocomotiveParameters( bool ReadyFlag, int Dir );
std::string EngineDescription( int what );
std::string EngineDescription( int what ) const;
private:
void LoadFIZ_Param( std::string const &line );
void LoadFIZ_Load( std::string const &line );

View File

@@ -1629,7 +1629,7 @@ void TMoverParameters::OilPumpCheck( double const Timestep ) {
}
double TMoverParameters::ShowCurrent(int AmpN)
double TMoverParameters::ShowCurrent(int AmpN) const
{ // Odczyt poboru prądu na podanym amperomierzu
switch (EngineType)
{
@@ -4124,7 +4124,7 @@ double TMoverParameters::FrictionForce(double R, int TDamage)
// Q: 20160713
// Oblicza przyczepność
// *************************************************************************************************
double TMoverParameters::Adhesive(double staticfriction)
double TMoverParameters::Adhesive(double staticfriction) const
{
double adhesion = 0.0;
const double adh_factor = 0.25; //współczynnik określający, jak bardzo spada tarcie przy poślizgu
@@ -6763,7 +6763,7 @@ bool TMoverParameters::ChangeOffsetH(double DeltaOffset)
// Q: 20160713
// Testuje zmienną (narazie tylko 0) i na podstawie uszkodzenia zwraca informację tekstową
// *************************************************************************************************
std::string TMoverParameters::EngineDescription(int what)
std::string TMoverParameters::EngineDescription(int what) const
{
std::string outstr { "OK" };
switch (what) {
@@ -9599,7 +9599,7 @@ bool TMoverParameters::RunInternalCommand()
// Q: 20160714
// Zwraca wartość natężenia prądu na wybranym amperomierzu. Podfunkcja do ShowCurrent.
// *************************************************************************************************
double TMoverParameters::ShowCurrentP(int AmpN)
double TMoverParameters::ShowCurrentP(int AmpN) const
{
int b, Bn;
bool Grupowy;

View File

@@ -17,6 +17,8 @@ http://mozilla.org/MPL/2.0/.
#include "memcell.h"
#include "simulation.h"
#include "driver.h"
#include "event.h"
#include "logs.h"
//---------------------------------------------------------------------------

View File

@@ -20,6 +20,7 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
#include "utilities.h"
#include "renderer.h"
#include "Timer.h"
#include "simulation.h"
#include "simulationtime.h"
#include "mtable.h"
#include "sn_utils.h"
@@ -28,7 +29,7 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
using namespace Mtable;
float TSubModel::fSquareDist = 0.f;
size_t TSubModel::iInstance; // numer renderowanego egzemplarza obiektu
std::uintptr_t TSubModel::iInstance; // numer renderowanego egzemplarza obiektu
texture_handle const *TSubModel::ReplacableSkinId = NULL;
int TSubModel::iAlpha = 0x30300030; // maska do testowania flag tekstur wymiennych
TModel3d *TSubModel::pRoot; // Ra: tymczasowo wskaźnik na model widoczny z submodelu
@@ -1191,7 +1192,7 @@ TSubModel::offset( float const Geometrytestoffsetthreshold ) const {
if( true == TestFlag( iFlags, 0x0200 ) ) {
// flip coordinates for t3d file which wasn't yet initialized
if( ( false == Global.pWorld->InitPerformed() )
if( ( false == simulation::is_ready )
|| ( false == Vertices.empty() ) ) {
// NOTE, HACK: results require flipping if the model wasn't yet initialized, so we're using crude method to detect possible cases
// TODO: sort out this mess, either unify offset lookups to take place before (or after) initialization,

View File

@@ -135,7 +135,7 @@ public: // chwilowo
float3 v_TransVector { 0.0f, 0.0f, 0.0f };
gfx::vertex_array Vertices;
float m_boundingradius { 0 };
size_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację
std::uintptr_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację
TAnimType b_aAnim{ TAnimType::at_None }; // kody animacji oddzielnie, bo zerowane
float4x4 *mAnimMatrix{ nullptr }; // macierz do animacji kwaternionowych (należy do AnimContainer)
TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D)

View File

@@ -203,16 +203,15 @@ double TSegment::GetTFromS(double const s) const
// initial guess for Newton's method
double fTolerance = 0.001;
double fRatio = s / RombergIntegral(0, 1);
double fOmRatio = 1.0 - fRatio;
double fTime = fOmRatio * 0 + fRatio * 1;
int iteration = 0;
double fTime = interpolate( 0.0, 1.0, fRatio );
int iteration = 0;
double fDifference {}; // exposed for debug down the road
do {
double fDifference = RombergIntegral(0, fTime) - s;
fDifference = RombergIntegral(0, fTime) - s;
if( std::abs( fDifference ) < fTolerance ) {
return fTime;
}
fTime -= fDifference / GetFirstDerivative(fTime).Length();
++iteration;
}
@@ -323,7 +322,7 @@ Math3D::vector3 TSegment::FastGetDirection(double fDistance, double fOffset)
return (Point2 - CPointIn); // wektor na końcu jest stały
return (FastGetPoint(t2) - FastGetPoint(t1));
}
/*
Math3D::vector3 TSegment::GetPoint(double const fDistance) const
{ // wyliczenie współrzędnych XYZ na torze w odległości (fDistance) od Point1
if (bCurve)
@@ -332,13 +331,17 @@ Math3D::vector3 TSegment::GetPoint(double const fDistance) const
// return Interpolate(t,Point1,CPointOut,CPointIn,Point2);
return RaInterpolate(t);
}
else
{ // wyliczenie dla odcinka prostego jest prostsze
double t = fDistance / fLength; // zerowych torów nie ma
return ((1.0 - t) * Point1 + (t)*Point2);
else {
// wyliczenie dla odcinka prostego jest prostsze
return
interpolate(
Point1, Point2,
clamp(
fDistance / fLength,
0.0, 1.0 ) );
}
};
*/
// ustalenie pozycji osi na torze, przechyłki, pochylenia i kierunku jazdy
void TSegment::RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const {

View File

@@ -88,8 +88,10 @@ public:
return CPointOut; };
Math3D::vector3
FastGetDirection(double const fDistance, double const fOffset);
/*
Math3D::vector3
GetPoint(double const fDistance) const;
*/
void
RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const;
Math3D::vector3

View File

@@ -17,6 +17,11 @@ http://mozilla.org/MPL/2.0/.
#include "simulation.h"
#include "globals.h"
#include "event.h"
#include "messaging.h"
#include "dynobj.h"
#include "animmodel.h"
#include "track.h"
#include "timer.h"
#include "logs.h"
#include "renderer.h"

View File

@@ -128,7 +128,7 @@ class TTrack : public scene::basic_node {
friend opengl_renderer;
// NOTE: temporary arrangement
friend ui_layer;
friend editor_ui;
private:
TIsolated * pIsolated = nullptr; // obwód izolowany obsługujący zajęcia/zwolnienia grupy torów

View File

@@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/.
#include "simulation.h"
#include "Globals.h"
#include "tractionpower.h"
#include "logs.h"
#include "renderer.h"

View File

@@ -21,8 +21,10 @@ http://mozilla.org/MPL/2.0/.
#include "camera.h"
#include "Logs.h"
#include "MdlMngr.h"
#include "model3d.h"
#include "Timer.h"
#include "Driver.h"
#include "dynobj.h"
#include "mtable.h"
#include "Console.h"

View File

@@ -18,6 +18,8 @@ http://mozilla.org/MPL/2.0/.
#include "simulation.h"
#include "Globals.h"
#include "dynobj.h"
#include "driver.h"
#include "Logs.h"
TTrackFollower::~TTrackFollower()

View File

@@ -9,14 +9,13 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "application.h"
#include "scenarioloadermode.h"
#include "drivermode.h"
#include "editormode.h"
#include "globals.h"
#include "keyboardinput.h"
#include "mouseinput.h"
#include "gamepadinput.h"
#include "console.h"
#include "simulation.h"
#include "world.h"
#include "train.h"
#include "pyint.h"
#include "sceneeditor.h"
#include "renderer.h"
@@ -45,17 +44,7 @@ http://mozilla.org/MPL/2.0/.
eu07_application Application;
namespace input {
gamepad_input Gamepad;
mouse_input Mouse;
glm::dvec2 mouse_pickmodepos; // stores last mouse position in control picking mode
keyboard_input Keyboard;
Console console;
std::unique_ptr<uart_input> uart;
user_command command; // currently issued control command, if any
}
ui_layer uilayerstaticinitializer;
#ifdef _WIN32
extern "C"
@@ -71,6 +60,14 @@ extern WNDPROC BaseWindowProc;
// user input callbacks
void focus_callback( GLFWwindow *window, int focus ) {
if( Global.bInactivePause ) // jeśli ma być pauzowanie okna w tle
if( focus )
Global.iPause &= ~4; // odpauzowanie, gdy jest na pierwszym planie
else
Global.iPause |= 4; // włączenie pauzy, gdy nieaktywy
}
void window_resize_callback( GLFWwindow *window, int w, int h ) {
// NOTE: we have two variables which basically do the same thing as we don't have dynamic fullscreen toggle
// TBD, TODO: merge them?
@@ -81,101 +78,23 @@ void window_resize_callback( GLFWwindow *window, int w, int h ) {
}
void cursor_pos_callback( GLFWwindow *window, double x, double y ) {
if( false == Global.ControlPicking ) {
glfwSetCursorPos( window, 0, 0 );
}
// give the potential event recipient a shot at it, in the virtual z order
if( true == scene::Editor.on_mouse_move( x, y ) ) { return; }
input::Mouse.move( x, y );
Application.on_cursor_pos( x, y );
}
void mouse_button_callback( GLFWwindow* window, int button, int action, int mods ) {
if( ( button != GLFW_MOUSE_BUTTON_LEFT )
&& ( button != GLFW_MOUSE_BUTTON_RIGHT ) ) {
// we don't care about other mouse buttons at the moment
return;
}
// give the potential event recipient a shot at it, in the virtual z order
if( true == scene::Editor.on_mouse_button( button, action ) ) { return; }
input::Mouse.button( button, action );
}
void key_callback( GLFWwindow *window, int key, int scancode, int action, int mods ) {
Global.shiftState = ( mods & GLFW_MOD_SHIFT ) ? true : false;
Global.ctrlState = ( mods & GLFW_MOD_CONTROL ) ? true : false;
Global.altState = ( mods & GLFW_MOD_ALT ) ? true : false;
// give the ui first shot at the input processing...
if( true == UILayer.on_key( key, action ) ) { return; }
if( true == scene::Editor.on_key( key, action ) ) { return; }
// ...if the input is left untouched, pass it on
input::Keyboard.key( key, action );
if( ( true == Global.InputMouse )
&& ( ( key == GLFW_KEY_LEFT_ALT )
|| ( key == GLFW_KEY_RIGHT_ALT ) ) ) {
// if the alt key was pressed toggle control picking mode and set matching cursor behaviour
if( action == GLFW_RELEASE ) {
if( Global.ControlPicking ) {
// switch off
Application.get_cursor_pos( input::mouse_pickmodepos.x, input::mouse_pickmodepos.y );
Application.set_cursor( GLFW_CURSOR_DISABLED );
Application.set_cursor_pos( 0, 0 );
}
else {
// enter picking mode
Application.set_cursor_pos( input::mouse_pickmodepos.x, input::mouse_pickmodepos.y );
Application.set_cursor( GLFW_CURSOR_NORMAL );
}
// actually toggle the mode
Global.ControlPicking = !Global.ControlPicking;
}
}
if( ( key == GLFW_KEY_LEFT_SHIFT )
|| ( key == GLFW_KEY_LEFT_CONTROL )
|| ( key == GLFW_KEY_LEFT_ALT )
|| ( key == GLFW_KEY_RIGHT_SHIFT )
|| ( key == GLFW_KEY_RIGHT_CONTROL )
|| ( key == GLFW_KEY_RIGHT_ALT ) ) {
// don't bother passing these
return;
}
if( action == GLFW_PRESS || action == GLFW_REPEAT ) {
World.OnKeyDown( key );
#ifdef CAN_I_HAS_LIBPNG
switch( key ) {
case GLFW_KEY_PRINT_SCREEN: {
make_screenshot();
break;
}
default: { break; }
}
#endif
}
}
void focus_callback( GLFWwindow *window, int focus ) {
if( Global.bInactivePause ) // jeśli ma być pauzowanie okna w tle
if( focus )
Global.iPause &= ~4; // odpauzowanie, gdy jest na pierwszym planie
else
Global.iPause |= 4; // włączenie pauzy, gdy nieaktywy
Application.on_mouse_button( button, action, mods );
}
void scroll_callback( GLFWwindow* window, double xoffset, double yoffset ) {
if( Global.ctrlState ) {
// ctrl + scroll wheel adjusts fov in debug mode
Global.FieldOfView = clamp( static_cast<float>( Global.FieldOfView - yoffset * 20.0 / Global.fFpsAverage ), 15.0f, 75.0f );
Application.on_scroll( xoffset, yoffset );
}
void key_callback( GLFWwindow *window, int key, int scancode, int action, int mods ) {
Application.on_key( key, scancode, action, mods );
}
// public:
@@ -190,6 +109,12 @@ eu07_application::init( int Argc, char *Argv[] ) {
if( ( result = init_settings( Argc, Argv ) ) != 0 ) {
return result;
}
WriteLog( "Starting MaSzyna rail vehicle simulator (release: " + Global.asVersion + ")" );
WriteLog( "For online documentation and additional files refer to: http://eu07.pl" );
WriteLog( "Authors: Marcin_EU, McZapkie, ABu, Winger, Tolaris, nbmx, OLO_EU, Bart, Quark-t, "
"ShaXbee, Oli_EU, youBy, KURS90, Ra, hunter, szociu, Stele, Q, firleju and others\n" );
if( ( result = init_glfw() ) != 0 ) {
return result;
}
@@ -200,6 +125,9 @@ eu07_application::init( int Argc, char *Argv[] ) {
if( ( result = init_audio() ) != 0 ) {
return result;
}
if( ( result = init_modes() ) != 0 ) {
return result;
}
return result;
}
@@ -207,56 +135,12 @@ eu07_application::init( int Argc, char *Argv[] ) {
int
eu07_application::run() {
// HACK: prevent mouse capture before simulation starts
Global.ControlPicking = true;
// TODO: move input sources and their initializations to the application mode member
input::Keyboard.init();
input::Mouse.init();
input::Gamepad.init();
if( true == Global.uart_conf.enable ) {
input::uart = std::make_unique<uart_input>();
input::uart->init();
}
#ifdef _WIN32
Console::On(); // włączenie konsoli
#endif
Global.pWorld = &World; // Ra: wskaźnik potrzebny do usuwania pojazdów
if( false == World.Init( m_window ) ) {
ErrorLog( "Bad init: simulation setup failed" );
return -1;
}
if( Global.iConvertModels < 0 ) {
// generate binary files for all 3d models
Global.iConvertModels = -Global.iConvertModels;
World.CreateE3D( szModelPath ); // rekurencyjne przeglądanie katalogów
World.CreateE3D( szDynamicPath, true );
// auto-close when you're done
WriteLog( "Binary 3d model generation completed" );
return 0;
}
set_cursor( GLFW_CURSOR_DISABLED );
set_cursor_pos( 0, 0 );
Global.ControlPicking = false;
// main application loop
// TODO: split into parts and delegate these to application mode member
while( ( false == glfwWindowShouldClose( m_window ) )
&& ( true == World.Update() )
&& ( false == m_modestack.empty() )
&& ( true == m_modes[ m_modestack.top() ]->update() )
&& ( true == GfxRenderer.Render() ) ) {
glfwPollEvents();
input::Keyboard.poll();
if( true == Global.InputMouse ) { input::Mouse.poll(); }
if( true == Global.InputGamepad ) { input::Gamepad.poll(); }
if( input::uart != nullptr ) { input::uart->poll(); }
// TODO: wrap current command in object, include other input sources
input::command = (
input::Mouse.command() != user_command::none ?
input::Mouse.command() :
input::Keyboard.command() );
}
return 0;
@@ -265,9 +149,7 @@ eu07_application::run() {
void
eu07_application::exit() {
#ifdef _WIN32
Console::Off(); // wyłączenie konsoli (komunikacji zwrotnej)
#endif
SafeDelete( simulation::Train );
SafeDelete( simulation::Region );
glfwDestroyWindow( m_window );
@@ -276,28 +158,103 @@ eu07_application::exit() {
TPythonInterpreter::killInstance();
}
void
eu07_application::render_ui() {
if( m_modestack.empty() ) { return; }
m_modes[ m_modestack.top() ]->render_ui();
}
bool
eu07_application::pop_mode() {
if( m_modestack.empty() ) { return false; }
m_modes[ m_modestack.top() ]->exit();
m_modestack.pop();
return true;
}
bool
eu07_application::push_mode( eu07_application::mode const Mode ) {
if( Mode >= mode::count_ ) { return false; }
m_modes[ Mode ]->enter();
m_modestack.push( Mode );
return true;
}
void
eu07_application::set_title( std::string const &Title ) {
glfwSetWindowTitle( m_window, Title.c_str() );
}
void
eu07_application::set_progress( float const Progress, float const Subtaskprogress ) {
if( m_modestack.empty() ) { return; }
m_modes[ m_modestack.top() ]->set_progress( Progress, Subtaskprogress );
}
void
eu07_application::set_cursor( int const Mode ) {
UILayer.set_cursor( Mode );
ui_layer::set_cursor( Mode );
}
void
eu07_application::set_cursor_pos( double const X, double const Y ) {
eu07_application::set_cursor_pos( double const Horizontal, double const Vertical ) {
if( m_window != nullptr ) {
glfwSetCursorPos( m_window, X, Y );
glfwSetCursorPos( m_window, Horizontal, Vertical );
}
}
void
eu07_application::get_cursor_pos( double &X, double &Y ) const {
eu07_application::get_cursor_pos( double &Horizontal, double &Vertical ) const {
if( m_window != nullptr ) {
glfwGetCursorPos( m_window, &X, &Y );
glfwGetCursorPos( m_window, &Horizontal, &Vertical );
}
}
void
eu07_application::on_key( int const Key, int const Scancode, int const Action, int const Mods ) {
if( m_modestack.empty() ) { return; }
m_modes[ m_modestack.top() ]->on_key( Key, Scancode, Action, Mods );
}
void
eu07_application::on_cursor_pos( double const Horizontal, double const Vertical ) {
if( m_modestack.empty() ) { return; }
m_modes[ m_modestack.top() ]->on_cursor_pos( Horizontal, Vertical );
}
void
eu07_application::on_mouse_button( int const Button, int const Action, int const Mods ) {
if( m_modestack.empty() ) { return; }
m_modes[ m_modestack.top() ]->on_mouse_button( Button, Action, Mods );
}
void
eu07_application::on_scroll( double const Xoffset, double const Yoffset ) {
if( m_modestack.empty() ) { return; }
m_modes[ m_modestack.top() ]->on_scroll( Xoffset, Yoffset );
}
// private:
void
@@ -385,13 +342,7 @@ eu07_application::init_settings( int Argc, char *Argv[] ) {
std::string token { Argv[ i ] };
if( token == "-e3d" ) {
Global.iConvertModels = (
Global.iConvertModels > 0 ?
-Global.iConvertModels :
-7 ); // z optymalizacją, bananami i prawidłowym Opacity
}
else if( token == "-s" ) {
if( token == "-s" ) {
if( i + 1 < Argc ) {
Global.SceneryFile = ToLower( Argv[ ++i ] );
}
@@ -406,7 +357,6 @@ eu07_application::init_settings( int Argc, char *Argv[] ) {
<< "usage: " << std::string( Argv[ 0 ] )
<< " [-s sceneryfilepath]"
<< " [-v vehiclename]"
<< " [-e3d]"
<< std::endl;
return -1;
}
@@ -468,7 +418,6 @@ eu07_application::init_glfw() {
// switch off the topmost flag
::SetWindowPos( Hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
#endif
// TBD, TODO: move the global pointer to a more appropriate place
m_window = window;
return 0;
@@ -499,7 +448,7 @@ eu07_application::init_gfx() {
}
if( ( false == GfxRenderer.Init( m_window ) )
|| ( false == UILayer.init( m_window ) ) ) {
|| ( false == ui_layer::init( m_window ) ) ) {
return -1;
}
@@ -515,3 +464,25 @@ eu07_application::init_audio() {
// NOTE: lack of audio isn't deemed a failure serious enough to throw in the towel
return 0;
}
int
eu07_application::init_modes() {
// NOTE: we could delay creation/initialization until transition to specific mode is requested,
// but doing it in one go at the start saves us some error checking headache down the road
// create all application behaviour modes
m_modes[ mode::scenarioloader ] = std::make_shared<scenarioloader_mode>();
m_modes[ mode::driver ] = std::make_shared<driver_mode>();
m_modes[ mode::editor ] = std::make_shared<editor_mode>();
// initialize the mode objects
for( auto &mode : m_modes ) {
if( false == mode->init() ) {
return -1;
}
}
// activate the default mode
push_mode( mode::scenarioloader );
return 0;
}

View File

@@ -7,9 +7,21 @@ obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "applicationmode.h"
class eu07_application {
public:
// types
enum mode {
// launcher = 0,
scenarioloader,
driver,
editor,
count_
};
// constructors
eu07_application() = default;
// methods
@@ -19,18 +31,41 @@ public:
run();
void
exit();
void
render_ui();
// switches application to specified mode
bool
pop_mode();
bool
push_mode( eu07_application::mode const Mode );
void
set_title( std::string const &Title );
void
set_progress( float const Progress = 0.f, float const Subtaskprogress = 0.f );
void
set_cursor( int const Mode );
void
set_cursor_pos( double const X, double const Y );
set_cursor_pos( double const Horizontal, double const Vertical );
void
get_cursor_pos( double &X, double &Y ) const;
get_cursor_pos( double &Horizontal, double &Vertical ) const;
// input handlers
void
on_key( int const Key, int const Scancode, int const Action, int const Mods );
void
on_cursor_pos( double const Horizontal, double const Vertical );
void
on_mouse_button( int const Button, int const Action, int const Mods );
void
on_scroll( double const Xoffset, double const Yoffset );
inline
GLFWwindow *
window() {
return m_window; }
private:
// types
using modeptr_array = std::array<std::shared_ptr<application_mode>, static_cast<std::size_t>( mode::count_ )>;
using mode_stack = std::stack<mode>;
// methods
void init_debug();
void init_files();
@@ -39,8 +74,11 @@ private:
void init_callbacks();
int init_gfx();
int init_audio();
int init_modes();
// members
GLFWwindow * m_window { nullptr };
modeptr_array m_modes { nullptr }; // collection of available application behaviour modes
mode_stack m_modestack; // current behaviour mode
};
extern eu07_application Application;

67
applicationmode.h Normal file
View File

@@ -0,0 +1,67 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "uilayer.h"
// component implementing specific mode of application behaviour
// base interface
class application_mode {
public:
// destructor
virtual
~application_mode() = default;
// methods;
// initializes internal data structures of the mode. returns: true on success, false otherwise
virtual
bool
init() = 0;
// mode-specific update of simulation data. returns: false on error, true otherwise
virtual
bool
update() = 0;
// draws node-specific user interface
inline
void
render_ui() {
if( m_userinterface != nullptr ) {
m_userinterface->render(); } }
inline
void
set_progress( float const Progress = 0.f, float const Subtaskprogress = 0.f ) {
if( m_userinterface != nullptr ) {
m_userinterface->set_progress( Progress, Subtaskprogress ); } }
// maintenance method, called when the mode is activated
virtual
void
enter() = 0;
// maintenance method, called when the mode is deactivated
virtual
void
exit() = 0;
// input handlers
virtual
void
on_key( int const Key, int const Scancode, int const Action, int const Mods ) = 0;
virtual
void
on_cursor_pos( double const X, double const Y ) = 0;
virtual
void
on_mouse_button( int const Button, int const Action, int const Mods ) = 0;
virtual
void
on_scroll( double const Xoffset, double const Yoffset ) = 0;
protected:
// members
std::shared_ptr<ui_layer> m_userinterface;
};

13
combustionengine.cpp Normal file
View File

@@ -0,0 +1,13 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "combustionengine.h"
//---------------------------------------------------------------------------

20
combustionengine.h Normal file
View File

@@ -0,0 +1,20 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
// basic approximation of a fuel pump
// TODO: fuel consumption, optional automatic engine start after activation
struct fuel_pump {
bool is_enabled { false }; // device is allowed/requested to operate
bool is_active { false }; // device is working
};
//---------------------------------------------------------------------------

215
driverkeyboardinput.cpp Normal file
View File

@@ -0,0 +1,215 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "driverkeyboardinput.h"
bool
driverkeyboard_input::init() {
default_bindings();
return recall_bindings();
}
void
driverkeyboard_input::default_bindings() {
m_bindingsetups = {
{ user_command::aidriverenable, GLFW_KEY_Q | keymodifier::shift },
{ user_command::aidriverdisable, GLFW_KEY_Q },
{ user_command::mastercontrollerincrease, GLFW_KEY_KP_ADD },
{ user_command::mastercontrollerincreasefast, GLFW_KEY_KP_ADD | keymodifier::shift },
{ user_command::mastercontrollerdecrease, GLFW_KEY_KP_SUBTRACT },
{ user_command::mastercontrollerdecreasefast, GLFW_KEY_KP_SUBTRACT | keymodifier::shift },
// mastercontrollerset,
{ user_command::secondcontrollerincrease, GLFW_KEY_KP_DIVIDE },
{ user_command::secondcontrollerincreasefast, GLFW_KEY_KP_DIVIDE | keymodifier::shift },
{ user_command::secondcontrollerdecrease, GLFW_KEY_KP_MULTIPLY },
{ user_command::secondcontrollerdecreasefast, GLFW_KEY_KP_MULTIPLY | keymodifier::shift },
// secondcontrollerset,
{ user_command::mucurrentindicatorothersourceactivate, GLFW_KEY_Z | keymodifier::shift },
{ user_command::independentbrakeincrease, GLFW_KEY_KP_1 },
{ user_command::independentbrakeincreasefast, GLFW_KEY_KP_1 | keymodifier::shift },
{ user_command::independentbrakedecrease, GLFW_KEY_KP_7 },
{ user_command::independentbrakedecreasefast, GLFW_KEY_KP_7 | keymodifier::shift },
// independentbrakeset,
{ user_command::independentbrakebailoff, GLFW_KEY_KP_4 },
{ user_command::trainbrakeincrease, GLFW_KEY_KP_3 },
{ user_command::trainbrakedecrease, GLFW_KEY_KP_9 },
// trainbrakeset,
{ user_command::trainbrakecharging, GLFW_KEY_KP_DECIMAL },
{ user_command::trainbrakerelease, GLFW_KEY_KP_6 },
{ user_command::trainbrakefirstservice, GLFW_KEY_KP_8 },
{ user_command::trainbrakeservice, GLFW_KEY_KP_5 },
{ user_command::trainbrakefullservice, GLFW_KEY_KP_2 },
{ user_command::trainbrakehandleoff, GLFW_KEY_KP_5 | keymodifier::control },
{ user_command::trainbrakeemergency, GLFW_KEY_KP_0 },
{ user_command::trainbrakebasepressureincrease, GLFW_KEY_KP_3 | keymodifier::control },
{ user_command::trainbrakebasepressuredecrease, GLFW_KEY_KP_9 | keymodifier::control },
{ user_command::trainbrakebasepressurereset, GLFW_KEY_KP_6 | keymodifier::control },
{ user_command::trainbrakeoperationtoggle, GLFW_KEY_KP_4 | keymodifier::control },
{ user_command::manualbrakeincrease, GLFW_KEY_KP_1 | keymodifier::control },
{ user_command::manualbrakedecrease, GLFW_KEY_KP_7 | keymodifier::control },
{ user_command::alarmchaintoggle, GLFW_KEY_B | keymodifier::shift | keymodifier::control },
{ user_command::wheelspinbrakeactivate, GLFW_KEY_KP_ENTER },
{ user_command::sandboxactivate, GLFW_KEY_S },
{ user_command::reverserincrease, GLFW_KEY_D },
{ user_command::reverserdecrease, GLFW_KEY_R },
// reverserforwardhigh,
// reverserforward,
// reverserneutral,
// reverserbackward,
{ user_command::waterpumpbreakertoggle, GLFW_KEY_W | keymodifier::control },
// waterpumpbreakerclose,
// waterpumpbreakeropen,
{ user_command::waterpumptoggle, GLFW_KEY_W },
// waterpumpenable,
// waterpumpdisable,
{ user_command::waterheaterbreakertoggle, GLFW_KEY_W | keymodifier::control | keymodifier::shift },
// waterheaterbreakerclose,
// waterheaterbreakeropen,
{ user_command::waterheatertoggle, GLFW_KEY_W | keymodifier::shift },
// waterheaterenable,
// waterheaterdisable,
{ user_command::watercircuitslinktoggle, GLFW_KEY_H | keymodifier::shift },
// watercircuitslinkenable,
// watercircuitslinkdisable,
{ user_command::fuelpumptoggle, GLFW_KEY_F },
// fuelpumpenable,
// fuelpumpdisable,
{ user_command::oilpumptoggle, GLFW_KEY_F | keymodifier::shift },
// oilpumpenable,
// oilpumpdisable,
{ user_command::linebreakertoggle, GLFW_KEY_M },
// linebreakeropen,
// linebreakerclose,
{ user_command::convertertoggle, GLFW_KEY_X },
// converterenable,
// converterdisable,
{ user_command::convertertogglelocal, GLFW_KEY_X | keymodifier::shift },
{ user_command::converteroverloadrelayreset, GLFW_KEY_N | keymodifier::control },
{ user_command::compressortoggle, GLFW_KEY_C },
// compressorenable,
// compressordisable,
{ user_command::compressortogglelocal, GLFW_KEY_C | keymodifier::shift },
{ user_command::motoroverloadrelaythresholdtoggle, GLFW_KEY_F },
// motoroverloadrelaythresholdsetlow,
// motoroverloadrelaythresholdsethigh,
{ user_command::motoroverloadrelayreset, GLFW_KEY_N },
{ user_command::notchingrelaytoggle, GLFW_KEY_G },
{ user_command::epbrakecontroltoggle, GLFW_KEY_Z | keymodifier::control },
{ user_command::trainbrakeoperationmodeincrease, GLFW_KEY_KP_2 | keymodifier::control },
{ user_command::trainbrakeoperationmodedecrease, GLFW_KEY_KP_8 | keymodifier::control },
{ user_command::brakeactingspeedincrease, GLFW_KEY_B | keymodifier::shift },
{ user_command::brakeactingspeeddecrease, GLFW_KEY_B },
// brakeactingspeedsetcargo,
// brakeactingspeedsetpassenger,
// brakeactingspeedsetrapid,
{ user_command::brakeloadcompensationincrease, GLFW_KEY_H | keymodifier::shift | keymodifier::control },
{ user_command::brakeloadcompensationdecrease, GLFW_KEY_H | keymodifier::control },
{ user_command::mubrakingindicatortoggle, GLFW_KEY_L | keymodifier::shift },
{ user_command::alerteracknowledge, GLFW_KEY_SPACE },
{ user_command::hornlowactivate, GLFW_KEY_A },
{ user_command::hornhighactivate, GLFW_KEY_S },
{ user_command::whistleactivate, GLFW_KEY_Z },
{ user_command::radiotoggle, GLFW_KEY_R | keymodifier::control },
{ user_command::radiochannelincrease, GLFW_KEY_R | keymodifier::shift },
{ user_command::radiochanneldecrease, GLFW_KEY_R },
{ user_command::radiostopsend, GLFW_KEY_PAUSE | keymodifier::shift | keymodifier::control },
{ user_command::radiostoptest, GLFW_KEY_R | keymodifier::shift | keymodifier::control },
{ user_command::cabchangeforward, GLFW_KEY_HOME },
{ user_command::cabchangebackward, GLFW_KEY_END },
// viewturn,
// movehorizontal,
// movehorizontalfast,
// movevertical,
// moveverticalfast,
{ user_command::moveleft, GLFW_KEY_LEFT },
{ user_command::moveright, GLFW_KEY_RIGHT },
{ user_command::moveforward, GLFW_KEY_UP },
{ user_command::moveback, GLFW_KEY_DOWN },
{ user_command::moveup, GLFW_KEY_PAGE_UP },
{ user_command::movedown, GLFW_KEY_PAGE_DOWN },
{ user_command::carcouplingincrease, GLFW_KEY_INSERT },
{ user_command::carcouplingdisconnect, GLFW_KEY_DELETE },
{ user_command::doortoggleleft, GLFW_KEY_COMMA },
{ user_command::doortoggleright, GLFW_KEY_PERIOD },
{ user_command::departureannounce, GLFW_KEY_SLASH },
{ user_command::doorlocktoggle, GLFW_KEY_S | keymodifier::shift },
{ user_command::pantographcompressorvalvetoggle, GLFW_KEY_V | keymodifier::control },
{ user_command::pantographcompressoractivate, GLFW_KEY_V | keymodifier::shift },
{ user_command::pantographtogglefront, GLFW_KEY_P },
{ user_command::pantographtogglerear, GLFW_KEY_O },
// pantographraisefront,
// pantographraiserear,
// pantographlowerfront,
// pantographlowerrear,
{ user_command::pantographlowerall, GLFW_KEY_P | keymodifier::control },
{ user_command::heatingtoggle, GLFW_KEY_H },
// heatingenable,
// heatingdisable,
{ user_command::lightspresetactivatenext, GLFW_KEY_T | keymodifier::shift },
{ user_command::lightspresetactivateprevious, GLFW_KEY_T },
{ user_command::headlighttoggleleft, GLFW_KEY_Y },
// headlightenableleft,
// headlightdisableleft,
{ user_command::headlighttoggleright, GLFW_KEY_I },
// headlightenableright,
// headlightdisableright,
{ user_command::headlighttoggleupper, GLFW_KEY_U },
// headlightenableupper,
// headlightdisableupper,
{ user_command::redmarkertoggleleft, GLFW_KEY_Y | keymodifier::shift },
// redmarkerenableleft,
// redmarkerdisableleft,
{ user_command::redmarkertoggleright, GLFW_KEY_I | keymodifier::shift },
// redmarkerenableright,
// redmarkerdisableright,
{ user_command::headlighttogglerearleft, GLFW_KEY_Y | keymodifier::control },
{ user_command::headlighttogglerearright, GLFW_KEY_I | keymodifier::control },
{ user_command::headlighttogglerearupper, GLFW_KEY_U | keymodifier::control },
{ user_command::redmarkertogglerearleft, GLFW_KEY_Y | keymodifier::control | keymodifier::shift },
{ user_command::redmarkertogglerearright, GLFW_KEY_I | keymodifier::control | keymodifier::shift },
{ user_command::redmarkerstoggle, GLFW_KEY_E | keymodifier::shift },
{ user_command::endsignalstoggle, GLFW_KEY_E },
{ user_command::headlightsdimtoggle, GLFW_KEY_L | keymodifier::control },
// headlightsdimenable,
// headlightsdimdisable,
{ user_command::motorconnectorsopen, GLFW_KEY_L },
// motorconnectorsclose,
{ user_command::motordisconnect, GLFW_KEY_E | keymodifier::control },
{ user_command::interiorlighttoggle, GLFW_KEY_APOSTROPHE },
// interiorlightenable,
// interiorlightdisable,
{ user_command::interiorlightdimtoggle, GLFW_KEY_APOSTROPHE | keymodifier::control },
// interiorlightdimenable,
// interiorlightdimdisable,
{ user_command::instrumentlighttoggle, GLFW_KEY_SEMICOLON },
// instrumentlightenable,
// instrumentlightdisable,
{ user_command::generictoggle0, GLFW_KEY_0 },
{ user_command::generictoggle1, GLFW_KEY_1 },
{ user_command::generictoggle2, GLFW_KEY_2 },
{ user_command::generictoggle3, GLFW_KEY_3 },
{ user_command::generictoggle4, GLFW_KEY_4 },
{ user_command::generictoggle5, GLFW_KEY_5 },
{ user_command::generictoggle6, GLFW_KEY_6 },
{ user_command::generictoggle7, GLFW_KEY_7 },
{ user_command::generictoggle8, GLFW_KEY_8 },
{ user_command::generictoggle9, GLFW_KEY_9 },
{ user_command::batterytoggle, GLFW_KEY_J },
// batteryenable,
// batterydisable,
};
bind();
}
//---------------------------------------------------------------------------

27
driverkeyboardinput.h Normal file
View File

@@ -0,0 +1,27 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "keyboardinput.h"
class driverkeyboard_input : public keyboard_input {
public:
// methods
bool
init() override;
protected:
// methods
void
default_bindings();
};
//---------------------------------------------------------------------------

1007
drivermode.cpp Normal file

File diff suppressed because it is too large Load Diff

87
drivermode.h Normal file
View File

@@ -0,0 +1,87 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "applicationmode.h"
#include "driverkeyboardinput.h"
#include "drivermouseinput.h"
#include "gamepadinput.h"
#include "uart.h"
#include "console.h"
#include "camera.h"
#include "classes.h"
class driver_mode : public application_mode {
public:
// constructors
driver_mode();
// methods
// initializes internal data structures of the mode. returns: true on success, false otherwise
bool
init() override;
// mode-specific update of simulation data. returns: false on error, true otherwise
bool
update() override;
// maintenance method, called when the mode is activated
void
enter() override;
// maintenance method, called when the mode is deactivated
void
exit() override;
// input handlers
void
on_key( int const Key, int const Scancode, int const Action, int const Mods ) override;
void
on_cursor_pos( double const Horizontal, double const Vertical ) override;
void
on_mouse_button( int const Button, int const Action, int const Mods ) override;
void
on_scroll( double const Xoffset, double const Yoffset ) override;
private:
// types
struct drivermode_input {
gamepad_input gamepad;
mouse_input mouse;
glm::dvec2 mouse_pickmodepos; // stores last mouse position in control picking mode
driverkeyboard_input keyboard;
Console console;
std::unique_ptr<uart_input> uart;
bool init();
void poll();
};
// methods
void update_camera( const double Deltatime );
// handles vehicle change flag
void OnKeyDown( int cKey );
void ChangeDynamic();
void InOutKey( bool const Near = true );
void FollowView( bool wycisz = true );
void DistantView( bool const Near = false );
// members
drivermode_input m_input;
std::array<TEvent *, 10> KeyEvents { nullptr }; // eventy wyzwalane z klawiaury
TCamera Camera;
TCamera DebugCamera;
TDynamicObject *pDynamicNearest { nullptr }; // vehicle nearest to the active camera. TODO: move to camera
double fTime50Hz { 0.0 }; // bufor czasu dla komunikacji z PoKeys
double const m_primaryupdaterate { 1.0 / 100.0 };
double const m_secondaryupdaterate { 1.0 / 50.0 };
double m_primaryupdateaccumulator { m_secondaryupdaterate }; // keeps track of elapsed simulation time, for core fixed step routines
double m_secondaryupdateaccumulator { m_secondaryupdaterate }; // keeps track of elapsed simulation time, for less important fixed step routines
int iPause { 0 }; // wykrywanie zmian w zapauzowaniu
};

View File

@@ -8,15 +8,15 @@ http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "mouseinput.h"
#include "drivermouseinput.h"
#include "application.h"
#include "utilities.h"
#include "globals.h"
#include "timer.h"
#include "simulation.h"
#include "world.h"
#include "train.h"
#include "animmodel.h"
#include "renderer.h"
#include "uilayer.h"
@@ -25,7 +25,7 @@ mouse_slider::bind( user_command const &Command ) {
m_command = Command;
auto const *train { World.train() };
auto const *train { simulation::Train };
TMoverParameters const *vehicle { nullptr };
switch( m_command ) {
case user_command::mastercontrollerset:
@@ -184,6 +184,11 @@ mouse_input::move( double Mousex, double Mousey ) {
void
mouse_input::button( int const Button, int const Action ) {
// store key state
if( Button >= 0 ) {
m_buttons[ Button ] = Action;
}
if( false == Global.ControlPicking ) { return; }
if( true == FreeFlyModeFlag ) {
@@ -236,7 +241,7 @@ mouse_input::button( int const Button, int const Action ) {
}
else {
// if not release then it's press
auto const lookup = m_mousecommands.find( World.train()->GetLabel( GfxRenderer.Update_Pick_Control() ) );
auto const lookup = m_mousecommands.find( simulation::Train->GetLabel( GfxRenderer.Update_Pick_Control() ) );
if( lookup != m_mousecommands.end() ) {
// if the recognized element under the cursor has a command associated with the pressed button, notify the recipient
mousecommand = (
@@ -301,6 +306,12 @@ mouse_input::button( int const Button, int const Action ) {
}
}
int
mouse_input::button( int const Button ) const {
return m_buttons[ Button ];
}
void
mouse_input::poll() {

View File

@@ -53,6 +53,8 @@ public:
init();
void
button( int const Button, int const Action );
int
button( int const Button ) const;
void
move( double const Mousex, double const Mousey );
void
@@ -87,6 +89,7 @@ private:
glm::dvec2 m_cursorposition; // stored last cursor position, used for panning
bool m_varyingpollrate { false }; // indicates rate of command repeats is affected by the cursor position
glm::dvec2 m_varyingpollrateorigin; // helper, cursor position when the command was initiated
std::array<int, GLFW_MOUSE_BUTTON_LAST> m_buttons;
};
//---------------------------------------------------------------------------

904
driveruilayer.cpp Normal file
View File

@@ -0,0 +1,904 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "driveruilayer.h"
#include "globals.h"
#include "translation.h"
#include "simulation.h"
#include "simulationtime.h"
#include "event.h"
#include "camera.h"
#include "mtable.h"
#include "train.h"
#include "driver.h"
#include "animmodel.h"
#include "dynobj.h"
#include "model3d.h"
#include "renderer.h"
#include "utilities.h"
#include "logs.h"
driver_ui::driver_ui() {
clear_texts();
/*
UIHeader = std::make_shared<ui_panel>( 20, 20 ); // header ui panel
UITable = std::make_shared<ui_panel>( 20, 100 ); // schedule or scan table
UITranscripts = std::make_shared<ui_panel>( 85, 600 ); // voice transcripts
*/
// make 4 empty lines for the ui header, to cut down on work down the road
UIHeader.text_lines.emplace_back( "", Global.UITextColor );
UIHeader.text_lines.emplace_back( "", Global.UITextColor );
UIHeader.text_lines.emplace_back( "", Global.UITextColor );
UIHeader.text_lines.emplace_back( "", Global.UITextColor );
// bind the panels with ui object. maybe not the best place for this but, eh
push_back( &UIHeader );
push_back( &UITable );
push_back( &UITranscripts );
}
// potentially processes provided input key. returns: true if key was processed, false otherwise
bool
driver_ui::on_key( int const Key, int const Action ) {
// TODO: pass the input first through an active ui element if there's any
// if the ui element shows no interest or we don't have one, try to interpret the input yourself:
// shared conditions
switch( Key ) {
case GLFW_KEY_F1:
case GLFW_KEY_F2:
case GLFW_KEY_F3:
case GLFW_KEY_F8:
case GLFW_KEY_F9:
case GLFW_KEY_F10:
case GLFW_KEY_F12: { // ui mode selectors
if( ( true == Global.ctrlState )
|| ( true == Global.shiftState ) ) {
// only react to keys without modifiers
return false;
}
if( Action == GLFW_RELEASE ) { return true; } // recognized, but ignored
/*
EditorModeFlag = ( Key == GLFW_KEY_F11 );
if( ( true == EditorModeFlag )
&& ( false == Global.ControlPicking ) ) {
set_cursor( GLFW_CURSOR_NORMAL );
Global.ControlPicking = true;
}
*/
}
default: { // everything else
break;
}
}
switch (Key) {
case GLFW_KEY_F1: {
// basic consist info
if( Global.iTextMode == Key ) { ++Global.iScreenMode[ Key - GLFW_KEY_F1 ]; }
if( Global.iScreenMode[ Key - GLFW_KEY_F1 ] > 1 ) {
// wyłączenie napisów
Global.iTextMode = 0;
Global.iScreenMode[ Key - GLFW_KEY_F1 ] = 0;
}
else {
Global.iTextMode = Key;
}
return true;
}
case GLFW_KEY_F2: {
// parametry pojazdu
if( Global.iTextMode == Key ) { ++Global.iScreenMode[ Key - GLFW_KEY_F1 ]; }
if( Global.iScreenMode[ Key - GLFW_KEY_F1 ] > 1 ) {
// wyłączenie napisów
Global.iTextMode = 0;
Global.iScreenMode[ Key - GLFW_KEY_F1 ] = 0;
}
else {
Global.iTextMode = Key;
}
return true;
}
case GLFW_KEY_F3: {
// timetable
if( Global.iTextMode == Key ) { ++Global.iScreenMode[ Key - GLFW_KEY_F1 ]; }
if( Global.iScreenMode[ Key - GLFW_KEY_F1 ] > 1 ) {
// wyłączenie napisów
Global.iTextMode = 0;
Global.iScreenMode[ Key - GLFW_KEY_F1 ] = 0;
}
else {
Global.iTextMode = Key;
}
return true;
}
case GLFW_KEY_F8: {
// renderer debug data
Global.iTextMode = Key;
return true;
}
case GLFW_KEY_F9: {
// wersja
Global.iTextMode = Key;
return true;
}
case GLFW_KEY_F10: {
// quit
if( Global.iTextMode == Key ) {
Global.iTextMode =
( Global.iPause && ( Key != GLFW_KEY_F1 ) ?
GLFW_KEY_F1 :
0 ); // wyłączenie napisów, chyba że pauza
}
else {
Global.iTextMode = Key;
}
return true;
}
/*
case GLFW_KEY_F11: {
// scenario inspector
Global.iTextMode = Key;
return true;
}
*/
case GLFW_KEY_F12: {
// coś tam jeszcze
Global.iTextMode = Key;
return true;
}
case GLFW_KEY_Y: {
// potentially quit
if( Global.iTextMode != GLFW_KEY_F10 ) { return false; } // not in quit mode
if( Action == GLFW_RELEASE ) { return true; } // recognized, but ignored
glfwSetWindowShouldClose( m_window, 1 );
return true;
}
default: {
break;
}
}
return false;
}
// updates state of UI elements
void
driver_ui::update() {
UITable.text_lines.clear();
std::string uitextline1, uitextline2, uitextline3, uitextline4;
set_tooltip( "" );
auto const *train { simulation::Train };
auto const *controlled { ( train ? train->Dynamic() : nullptr ) };
auto const *camera { Global.pCamera };
if( ( train != nullptr ) && ( false == FreeFlyModeFlag ) ) {
if( false == DebugModeFlag ) {
// in regular mode show control functions, for defined controls
set_tooltip( locale::label_cab_control( train->GetLabel( GfxRenderer.Pick_Control() ) ) );
}
else {
// in debug mode show names of submodels, to help with cab setup and/or debugging
auto const cabcontrol = GfxRenderer.Pick_Control();
set_tooltip( ( cabcontrol ? cabcontrol->pName : "" ) );
}
}
if( ( true == Global.ControlPicking ) && ( true == FreeFlyModeFlag ) && ( true == DebugModeFlag ) ) {
auto const scenerynode = GfxRenderer.Pick_Node();
set_tooltip(
( scenerynode ?
scenerynode->name() :
"" ) );
}
switch( Global.iTextMode ) {
case( GLFW_KEY_F1 ) : {
// f1, default mode: current time and timetable excerpt
auto const &time = simulation::Time.data();
uitextline1 =
"Time: "
+ to_string( time.wHour ) + ":"
+ ( time.wMinute < 10 ? "0" : "" ) + to_string( time.wMinute ) + ":"
+ ( time.wSecond < 10 ? "0" : "" ) + to_string( time.wSecond );
if( Global.iPause ) {
uitextline1 += " (paused)";
}
if( ( controlled != nullptr )
&& ( controlled->Mechanik != nullptr ) ) {
auto const *mover = controlled->MoverParameters;
auto const *driver = controlled->Mechanik;
uitextline2 = "Throttle: " + to_string( driver->Controlling()->MainCtrlPos, 0, 2 ) + "+" + std::to_string( driver->Controlling()->ScndCtrlPos );
if( mover->ActiveDir > 0 ) { uitextline2 += " D"; }
else if( mover->ActiveDir < 0 ) { uitextline2 += " R"; }
else { uitextline2 += " N"; }
uitextline3 = "Brakes:" + to_string( mover->fBrakeCtrlPos, 1, 5 ) + "+" + to_string( mover->LocalBrakePosA * LocalBrakePosNo, 0 ) + ( mover->SlippingWheels ? " !" : " " );
uitextline4 = (
true == TestFlag( mover->SecuritySystem.Status, s_aware ) ?
"!ALERTER! " :
" " );
uitextline4 += (
true == TestFlag( mover->SecuritySystem.Status, s_active ) ?
"!SHP! " :
" " );
if( Global.iScreenMode[ Global.iTextMode - GLFW_KEY_F1 ] == 1 ) {
// detail mode on second key press
auto const speedlimit { static_cast<int>( std::floor( driver->VelDesired ) ) };
uitextline2 +=
" Speed: " + std::to_string( static_cast<int>( std::floor( mover->Vel ) ) ) + " km/h"
+ " (limit: " + std::to_string( speedlimit ) + " km/h";
auto const nextspeedlimit { static_cast<int>( std::floor( driver->VelNext ) ) };
if( nextspeedlimit != speedlimit ) {
uitextline2 +=
", new limit: " + std::to_string( nextspeedlimit ) + " km/h"
+ " in " + to_string( driver->ActualProximityDist * 0.001, 1 ) + " km";
}
uitextline2 += ")";
auto const reverser { ( mover->ActiveDir > 0 ? 1 : -1 ) };
auto const grade { controlled->VectorFront().y * 100 * ( controlled->DirectionGet() == reverser ? 1 : -1 ) * reverser };
if( std::abs( grade ) >= 0.25 ) {
uitextline2 += " Grade: " + to_string( grade, 1 ) + "%";
}
uitextline3 +=
" Pressure: " + to_string( mover->BrakePress * 100.0, 2 ) + " kPa"
+ " (train pipe: " + to_string( mover->PipePress * 100.0, 2 ) + " kPa)";
auto const stoptime { static_cast<int>( -1.0 * controlled->Mechanik->fStopTime ) };
if( stoptime > 0 ) {
uitextline4 += " Loading/unloading in progress (" + to_string( stoptime ) + ( stoptime > 1 ? " seconds" : " second" ) + " left)";
}
else {
auto const trackblockdistance{ std::abs( controlled->Mechanik->TrackBlock() ) };
if( trackblockdistance <= 75.0 ) {
uitextline4 += " Another vehicle ahead (distance: " + to_string( trackblockdistance, 1 ) + " m)";
}
}
}
}
break;
}
case( GLFW_KEY_F2 ) : {
// timetable
auto *vehicle {
( FreeFlyModeFlag ?
std::get<TDynamicObject *>( simulation::Region->find_vehicle( camera->Pos, 20, false, false ) ) :
controlled ) }; // w trybie latania lokalizujemy wg mapy
if( vehicle == nullptr ) { break; }
// if the nearest located vehicle doesn't have a direct driver, try to query its owner
auto const owner = (
( ( vehicle->Mechanik != nullptr ) && ( vehicle->Mechanik->Primary() ) ) ?
vehicle->Mechanik :
vehicle->ctOwner );
if( owner == nullptr ){ break; }
auto const *table = owner->TrainTimetable();
if( table == nullptr ) { break; }
auto const &time = simulation::Time.data();
uitextline1 =
"Time: "
+ to_string( time.wHour ) + ":"
+ ( time.wMinute < 10 ? "0" : "" ) + to_string( time.wMinute ) + ":"
+ ( time.wSecond < 10 ? "0" : "" ) + to_string( time.wSecond );
if( Global.iPause ) {
uitextline1 += " (paused)";
}
uitextline2 = Bezogonkow( owner->Relation(), true ) + " (" + Bezogonkow( owner->TrainName(), true ) + ")";
auto const nextstation = Bezogonkow( owner->NextStop(), true );
if( !nextstation.empty() ) {
// jeśli jest podana relacja, to dodajemy punkt następnego zatrzymania
uitextline3 = " -> " + nextstation;
}
if( Global.iScreenMode[ Global.iTextMode - GLFW_KEY_F1 ] == 1 ) {
if( 0 == table->StationCount ) {
// only bother if there's stations to list
UITable.text_lines.emplace_back( "(no timetable)", Global.UITextColor );
}
else {
// header
UITable.text_lines.emplace_back( "+-----+------------------------------------+-------+-----+", Global.UITextColor );
TMTableLine const *tableline;
for( int i = owner->iStationStart; i <= std::min( owner->iStationStart + 10, table->StationCount ); ++i ) {
// wyświetlenie pozycji z rozkładu
tableline = table->TimeTable + i; // linijka rozkładu
std::string vmax =
" "
+ to_string( tableline->vmax, 0 );
vmax = vmax.substr( vmax.size() - 3, 3 ); // z wyrównaniem do prawej
std::string const station = (
Bezogonkow( tableline->StationName, true )
+ " " )
.substr( 0, 34 );
std::string const location = (
( tableline->km > 0.0 ?
to_string( tableline->km, 2 ) :
"" )
+ " " )
.substr( 0, 34 - tableline->StationWare.size() );
std::string const arrival = (
tableline->Ah >= 0 ?
to_string( int( 100 + tableline->Ah ) ).substr( 1, 2 ) + ":" + to_string( int( 100 + tableline->Am ) ).substr( 1, 2 ) :
" | " );
std::string const departure = (
tableline->Dh >= 0 ?
to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_string( int( 100 + tableline->Dm ) ).substr( 1, 2 ) :
" | " );
auto const candeparture = (
( owner->iStationStart < table->StationIndex )
&& ( i < table->StationIndex )
&& ( ( time.wHour * 60 + time.wMinute ) >= ( tableline->Dh * 60 + tableline->Dm ) ) );
auto traveltime =
" "
+ ( i < 2 ? "" :
tableline->Ah >= 0 ? to_string( CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Ah, tableline->Am ), 0 ) :
to_string( std::max( 0.0, CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Dh, tableline->Dm ) - 0.5 ), 0 ) );
traveltime = traveltime.substr( traveltime.size() - 3, 3 ); // z wyrównaniem do prawej
UITable.text_lines.emplace_back(
( "| " + vmax + " | " + station + " | " + arrival + " | " + traveltime + " |" ),
( candeparture ?
glm::vec4( 0.0f, 1.0f, 0.0f, 1.0f ) :// czas minął i odjazd był, to nazwa stacji będzie na zielono
Global.UITextColor ) );
UITable.text_lines.emplace_back(
( "| | " + location + tableline->StationWare + " | " + departure + " | |" ),
( candeparture ?
glm::vec4( 0.0f, 1.0f, 0.0f, 1.0f ) :// czas minął i odjazd był, to nazwa stacji będzie na zielono
Global.UITextColor ) );
// divider/footer
UITable.text_lines.emplace_back( "+-----+------------------------------------+-------+-----+", Global.UITextColor );
}
if( owner->iStationStart + 10 < table->StationCount ) {
// if we can't display entire timetable, add a scrolling indicator at the bottom
UITable.text_lines.emplace_back( " ... ", Global.UITextColor );
}
}
}
break;
}
case( GLFW_KEY_F3 ) : {
auto const *vehicle {
( FreeFlyModeFlag ?
std::get<TDynamicObject *>( simulation::Region->find_vehicle( camera->Pos, 20, false, false ) ) :
controlled ) }; // w trybie latania lokalizujemy wg mapy
if( vehicle != nullptr ) {
// jeśli domyślny ekran po pierwszym naciśnięciu
auto const *mover { vehicle->MoverParameters };
uitextline1 = "Vehicle name: " + mover->Name;
if( ( vehicle->Mechanik == nullptr ) && ( vehicle->ctOwner ) ) {
// for cars other than leading unit indicate the leader
uitextline1 += ", owned by " + vehicle->ctOwner->OwnerName();
}
uitextline1 += "; Status: " + mover->EngineDescription( 0 );
// informacja o sprzęgach
uitextline1 +=
"; C0:" +
( vehicle->PrevConnected ?
vehicle->PrevConnected->name() + ":" + to_string( mover->Couplers[ 0 ].CouplingFlag ) + (
mover->Couplers[ 0 ].CouplingFlag == 0 ?
" (" + to_string( mover->Couplers[ 0 ].CoupleDist, 1 ) + " m)" :
"" ) :
"none" );
uitextline1 +=
" C1:" +
( vehicle->NextConnected ?
vehicle->NextConnected->name() + ":" + to_string( mover->Couplers[ 1 ].CouplingFlag ) + (
mover->Couplers[ 1 ].CouplingFlag == 0 ?
" (" + to_string( mover->Couplers[ 1 ].CoupleDist, 1 ) + " m)" :
"" ) :
"none" );
// equipment flags
uitextline2 = ( mover->Battery ? "B" : "." );
uitextline2 += ( mover->Mains ? "M" : "." );
uitextline2 += ( mover->PantRearUp ? ( mover->PantRearVolt > 0.0 ? "O" : "o" ) : "." );
uitextline2 += ( mover->PantFrontUp ? ( mover->PantFrontVolt > 0.0 ? "P" : "p" ) : "." );
uitextline2 += ( mover->PantPressLockActive ? "!" : ( mover->PantPressSwitchActive ? "*" : "." ) );
uitextline2 += ( mover->WaterPump.is_active ? "W" : ( false == mover->WaterPump.breaker ? "-" : ( mover->WaterPump.is_enabled ? "w" : "." ) ) );
uitextline2 += ( true == mover->WaterHeater.is_damaged ? "!" : ( mover->WaterHeater.is_active ? "H" : ( false == mover->WaterHeater.breaker ? "-" : ( mover->WaterHeater.is_enabled ? "h" : "." ) ) ) );
uitextline2 += ( mover->FuelPump.is_active ? "F" : ( mover->FuelPump.is_enabled ? "f" : "." ) );
uitextline2 += ( mover->OilPump.is_active ? "O" : ( mover->OilPump.is_enabled ? "o" : "." ) );
uitextline2 += ( false == mover->ConverterAllowLocal ? "-" : ( mover->ConverterAllow ? ( mover->ConverterFlag ? "X" : "x" ) : "." ) );
uitextline2 += ( mover->ConvOvldFlag ? "!" : "." );
uitextline2 += ( mover->CompressorFlag ? "C" : ( false == mover->CompressorAllowLocal ? "-" : ( ( mover->CompressorAllow || mover->CompressorStart == start_t::automatic ) ? "c" : "." ) ) );
uitextline2 += ( mover->CompressorGovernorLock ? "!" : "." );
auto const *train { simulation::Train };
if( ( train != nullptr ) && ( train->Dynamic() == vehicle ) ) {
uitextline2 += ( mover->Radio ? " R: " : " r: " ) + std::to_string( train->RadioChannel() );
}
uitextline2 += " Bdelay: ";
if( ( mover->BrakeDelayFlag & bdelay_G ) == bdelay_G )
uitextline2 += "G";
if( ( mover->BrakeDelayFlag & bdelay_P ) == bdelay_P )
uitextline2 += "P";
if( ( mover->BrakeDelayFlag & bdelay_R ) == bdelay_R )
uitextline2 += "R";
if( ( mover->BrakeDelayFlag & bdelay_M ) == bdelay_M )
uitextline2 += "+Mg";
uitextline2 += ", Load: " + to_string( mover->Load, 0 ) + " (" + to_string( mover->LoadFlag, 0 ) + ")";
uitextline2 +=
"; Pant: "
+ to_string( mover->PantPress, 2 )
+ ( mover->bPantKurek3 ? "-ZG" : "|ZG" );
uitextline2 +=
"; Ft: " + to_string(
mover->Ft * 0.001f * (
mover->ActiveCab ? mover->ActiveCab :
vehicle->ctOwner ? vehicle->ctOwner->Controlling()->ActiveCab :
1 ), 1 )
+ ", Fb: " + to_string( mover->Fb * 0.001f, 1 )
+ ", Fr: " + to_string( mover->Adhesive( mover->RunningTrack.friction ), 2 )
+ ( mover->SlippingWheels ? " (!)" : "" );
if( vehicle->Mechanik ) {
uitextline2 += "; Ag: " + to_string( vehicle->Mechanik->fAccGravity, 2 ) + " (" + ( vehicle->Mechanik->fAccGravity > 0.01 ? "\\" : ( vehicle->Mechanik->fAccGravity < -0.01 ? "/" : "-" ) ) + ")";
}
uitextline2 +=
"; TC:"
+ to_string( mover->TotalCurrent, 0 );
auto const frontcouplerhighvoltage =
to_string( mover->Couplers[ side::front ].power_high.voltage, 0 )
+ "@"
+ to_string( mover->Couplers[ side::front ].power_high.current, 0 );
auto const rearcouplerhighvoltage =
to_string( mover->Couplers[ side::rear ].power_high.voltage, 0 )
+ "@"
+ to_string( mover->Couplers[ side::rear ].power_high.current, 0 );
uitextline2 += ", HV: ";
if( mover->Couplers[ side::front ].power_high.local == false ) {
uitextline2 +=
"(" + frontcouplerhighvoltage + ")-"
+ ":F" + ( vehicle->DirectionGet() ? "<<" : ">>" ) + "R:"
+ "-(" + rearcouplerhighvoltage + ")";
}
else {
uitextline2 +=
frontcouplerhighvoltage
+ ":F" + ( vehicle->DirectionGet() ? "<<" : ">>" ) + "R:"
+ rearcouplerhighvoltage;
}
uitextline3 +=
"TrB: " + to_string( mover->BrakePress, 2 )
+ ", " + to_hex_str( mover->Hamulec->GetBrakeStatus(), 2 );
uitextline3 +=
"; LcB: " + to_string( mover->LocBrakePress, 2 )
+ "; hat: " + to_string( mover->BrakeCtrlPos2, 2 )
+ "; pipes: " + to_string( mover->PipePress, 2 )
+ "/" + to_string( mover->ScndPipePress, 2 )
+ "/" + to_string( mover->EqvtPipePress, 2 )
+ ", MT: " + to_string( mover->CompressedVolume, 3 )
+ ", BT: " + to_string( mover->Volume, 3 )
+ ", CtlP: " + to_string( mover->CntrlPipePress, 3 )
+ ", CtlT: " + to_string( mover->Hamulec->GetCRP(), 3 );
if( mover->ManualBrakePos > 0 ) {
uitextline3 += "; manual brake on";
}
if( vehicle->Mechanik ) {
// o ile jest ktoś w środku
std::string flags = "cpapcplhhndoiefgvdpseil "; // flagi AI (definicja w Driver.h)
for( int i = 0, j = 1; i < 23; ++i, j <<= 1 )
if( false == ( vehicle->Mechanik->DrivigFlags() & j ) ) // jak bit ustawiony
flags[ i ] = '.';// std::toupper( flags[ i ] ); // ^= 0x20; // to zmiana na wielką literę
uitextline4 = flags;
uitextline4 +=
"Driver: Vd=" + to_string( vehicle->Mechanik->VelDesired, 0 )
+ " Ad=" + to_string( vehicle->Mechanik->AccDesired, 2 )
+ " Ah=" + to_string( vehicle->Mechanik->fAccThreshold, 2 )
+ "@" + to_string( vehicle->Mechanik->fBrake_a0[ 0 ], 2 )
+ "+" + to_string( vehicle->Mechanik->fBrake_a1[ 0 ], 2 )
+ " Bd=" + to_string( vehicle->Mechanik->fBrakeDist, 0 )
+ " Pd=" + to_string( vehicle->Mechanik->ActualProximityDist, 0 )
+ " Vn=" + to_string( vehicle->Mechanik->VelNext, 0 )
+ " VSl=" + to_string( vehicle->Mechanik->VelSignalLast, 0 )
+ " VLl=" + to_string( vehicle->Mechanik->VelLimitLast, 0 )
+ " VRd=" + to_string( vehicle->Mechanik->VelRoad, 0 )
+ " VRst=" + to_string( vehicle->Mechanik->VelRestricted, 0 );
if( ( vehicle->Mechanik->VelNext == 0.0 )
&& ( vehicle->Mechanik->eSignNext ) ) {
// jeśli ma zapamiętany event semafora, nazwa eventu semafora
uitextline4 += " (" + Bezogonkow( vehicle->Mechanik->eSignNext->asName ) + ")";
}
// biezaca komenda dla AI
uitextline4 += ", command: " + vehicle->Mechanik->OrderCurrent();
}
if( Global.iScreenMode[ Global.iTextMode - GLFW_KEY_F1 ] == 1 ) {
// f2 screen, track scan mode
if( vehicle->Mechanik == nullptr ) {
//żeby była tabelka, musi być AI
break;
}
std::size_t i = 0; std::size_t const speedtablesize = clamp( static_cast<int>( vehicle->Mechanik->TableSize() ) - 1, 0, 30 );
do {
std::string scanline = vehicle->Mechanik->TableText( i );
if( scanline.empty() ) { break; }
UITable.text_lines.emplace_back( Bezogonkow( scanline ), Global.UITextColor );
++i;
} while( i < speedtablesize ); // TController:iSpeedTableSize TODO: change when the table gets recoded
}
}
else {
// wyświetlenie współrzędnych w scenerii oraz kąta kamery, gdy nie mamy wskaźnika
uitextline1 =
"Camera position: "
+ to_string( camera->Pos.x, 2 ) + " "
+ to_string( camera->Pos.y, 2 ) + " "
+ to_string( camera->Pos.z, 2 )
+ ", azimuth: "
+ to_string( 180.0 - glm::degrees( camera->Yaw ), 0 ) // ma być azymut, czyli 0 na północy i rośnie na wschód
+ " "
+ std::string( "S SEE NEN NWW SW" )
.substr( 0 + 2 * floor( fmod( 8 + ( camera->Yaw + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 );
// current luminance level
uitextline2 = "Light level: " + to_string( Global.fLuminance, 3 );
if( Global.FakeLight ) { uitextline2 += "(*)"; }
}
break;
}
case( GLFW_KEY_F8 ) : {
// gfx renderer data
uitextline1 =
"FoV: " + to_string( Global.FieldOfView / Global.ZoomFactor, 1 )
+ ", Draw range x " + to_string( Global.fDistanceFactor, 1 )
// + "; sectors: " + std::to_string( GfxRenderer.m_drawcount )
// + ", FPS: " + to_string( Timer::GetFPS(), 2 );
+ ", FPS: " + std::to_string( static_cast<int>(std::round(GfxRenderer.Framerate())) );
if( Global.iSlowMotion ) {
uitextline1 += " (slowmotion " + to_string( Global.iSlowMotion ) + ")";
}
uitextline2 =
std::string( "Rendering mode: " )
+ ( Global.bUseVBO ?
"VBO" :
"Display Lists" )
+ " ";
if( false == Global.LastGLError.empty() ) {
uitextline2 +=
"Last openGL error: "
+ Global.LastGLError;
}
// renderer stats
uitextline3 = GfxRenderer.info_times();
uitextline4 = GfxRenderer.info_stats();
break;
}
case( GLFW_KEY_F9 ) : {
// informacja o wersji
uitextline1 = "MaSzyna " + Global.asVersion; // informacja o wersji
if( Global.iMultiplayer ) {
uitextline1 += " (multiplayer mode is active)";
}
uitextline3 =
"vehicles: " + to_string( Timer::subsystem.sim_dynamics.average(), 2 ) + " msec"
+ " update total: " + to_string( Timer::subsystem.sim_total.average(), 2 ) + " msec";
// current event queue
auto const time { Timer::GetTime() };
auto const *event { simulation::Events.begin() };
auto eventtableindex{ 0 };
while( ( event != nullptr )
&& ( eventtableindex < 30 ) ) {
if( ( false == event->m_ignored )
&& ( true == event->bEnabled ) ) {
auto const delay { " " + to_string( std::max( 0.0, event->fStartTime - time ), 1 ) };
auto const eventline =
"Delay: " + delay.substr( delay.length() - 6 )
+ ", Event: " + event->asName
+ ( event->Activator ? " (by: " + event->Activator->asName + ")" : "" )
+ ( event->evJoined ? " (joint event)" : "" );
UITable.text_lines.emplace_back( eventline, Global.UITextColor );
++eventtableindex;
}
event = event->evNext;
}
break;
}
case( GLFW_KEY_F10 ) : {
uitextline1 = "Press [Y] key to quit / Aby zakonczyc program, przycisnij klawisz [Y].";
break;
}
case( GLFW_KEY_F12 ) : {
// opcje włączenia i wyłączenia logowania
uitextline1 = "[0] Debugmode " + std::string( DebugModeFlag ? "(on)" : "(off)" );
uitextline2 = "[1] log.txt " + std::string( ( Global.iWriteLogEnabled & 1 ) ? "(on)" : "(off)" );
uitextline3 = "[2] Console " + std::string( ( Global.iWriteLogEnabled & 2 ) ? "(on)" : "(off)" );
break;
}
default: {
// uncovered cases, nothing to do here...
// ... unless we're in debug mode
if( DebugModeFlag ) {
auto const *vehicle {
( FreeFlyModeFlag ?
std::get<TDynamicObject *>( simulation::Region->find_vehicle( camera->Pos, 20, false, false ) ) :
controlled ) }; // w trybie latania lokalizujemy wg mapy
if( vehicle == nullptr ) {
break;
}
auto const *mover { vehicle->MoverParameters };
uitextline1 =
"vel: " + to_string( vehicle->GetVelocity(), 2 ) + "/" + to_string( mover->nrot* M_PI * mover->WheelDiameter * 3.6, 2 )
+ " km/h;" + ( mover->SlippingWheels ? " (!)" : " " )
+ " dist: " + to_string( mover->DistCounter, 2 ) + " km"
+ "; pos: [" + to_string( vehicle->GetPosition().x, 2 ) + ", " + to_string( vehicle->GetPosition().y, 2 ) + ", " + to_string( vehicle->GetPosition().z, 2 ) + "]"
+ ", PM=" + to_string( mover->WheelFlat, 1 )
+ " mm; enpwr=" + to_string( mover->EnginePower, 1 )
+ "; enrot=" + to_string( mover->enrot * 60, 0 )
+ " tmrot=" + to_string( std::abs( mover->nrot ) * mover->Transmision.Ratio * 60, 0 )
+ "; ventrot=" + to_string( mover->RventRot * 60, 1 )
+ "; fanrot=" + to_string( mover->dizel_heat.rpmw, 1 ) + ", " + to_string( mover->dizel_heat.rpmw2, 1 );
uitextline2 =
"HamZ=" + to_string( mover->fBrakeCtrlPos, 2 )
+ "; HamP=" + to_string( mover->LocalBrakePosA, 2 )
+ "; NasJ=" + std::to_string( mover->MainCtrlPos ) + "(" + std::to_string( mover->MainCtrlActualPos ) + ")"
+ ( ( mover->ShuntMode && mover->EngineType == TEngineType::DieselElectric ) ?
"; NasB=" + to_string( mover->AnPos, 2 ) :
"; NasB=" + std::to_string( mover->ScndCtrlPos ) + "(" + std::to_string( mover->ScndCtrlActualPos ) + ")" )
+ "; I=" +
( mover->TrainType == dt_EZT ?
std::to_string( int( mover->ShowCurrent( 0 ) ) ) :
std::to_string( int( mover->Im ) ) )
+ "; U=" + to_string( int( mover->RunningTraction.TractionVoltage + 0.5 ) )
+ "; R=" +
( std::abs( mover->RunningShape.R ) > 10000.0 ?
"~0.0" :
to_string( mover->RunningShape.R, 1 ) )
+ " An=" + to_string( mover->AccN, 2 ); // przyspieszenie poprzeczne
if( tprev != simulation::Time.data().wSecond ) {
tprev = simulation::Time.data().wSecond;
Acc = ( mover->Vel - VelPrev ) / 3.6;
VelPrev = mover->Vel;
}
uitextline2 += "; As=" + to_string( Acc, 2 ); // przyspieszenie wzdłużne
// uitextline2 += " eAngle=" + to_string( std::cos( mover->eAngle ), 2 );
uitextline2 += "; oilP=" + to_string( mover->OilPump.pressure_present, 3 );
uitextline2 += " oilT=" + to_string( mover->dizel_heat.To, 2 );
uitextline2 += "; waterT=" + to_string( mover->dizel_heat.temperatura1, 2 );
uitextline2 += ( mover->WaterCircuitsLink ? "-" : "|" );
uitextline2 += to_string( mover->dizel_heat.temperatura2, 2 );
uitextline2 += "; engineT=" + to_string( mover->dizel_heat.Ts, 2 );
uitextline3 =
"cyl.ham. " + to_string( mover->BrakePress, 2 )
+ "; prz.gl. " + to_string( mover->PipePress, 2 )
+ "; zb.gl. " + to_string( mover->CompressedVolume, 2 )
// youBy - drugi wezyk
+ "; p.zas. " + to_string( mover->ScndPipePress, 2 );
// McZapkie: warto wiedziec w jakim stanie sa przelaczniki
if( mover->ConvOvldFlag )
uitextline3 += " C! ";
else if( mover->FuseFlag )
uitextline3 += " F! ";
else if( !mover->Mains )
uitextline3 += " () ";
else {
switch(
mover->ActiveDir *
( mover->Imin == mover->IminLo ?
1 :
2 ) ) {
case 2: { uitextline3 += " >> "; break; }
case 1: { uitextline3 += " -> "; break; }
case 0: { uitextline3 += " -- "; break; }
case -1: { uitextline3 += " <- "; break; }
case -2: { uitextline3 += " << "; break; }
}
}
// McZapkie: predkosc szlakowa
if( mover->RunningTrack.Velmax == -1 ) {
uitextline3 += " Vtrack=Vmax";
}
else {
uitextline3 += " Vtrack " + to_string( mover->RunningTrack.Velmax, 2 );
}
if( ( mover->EnginePowerSource.SourceType == TPowerSource::CurrentCollector )
|| ( mover->TrainType == dt_EZT ) ) {
uitextline3 +=
"; pant. " + to_string( mover->PantPress, 2 )
+ ( mover->bPantKurek3 ? "=" : "^" ) + "ZG";
}
// McZapkie: komenda i jej parametry
if( mover->CommandIn.Command != ( "" ) ) {
uitextline4 =
"C:" + mover->CommandIn.Command
+ " V1=" + to_string( mover->CommandIn.Value1, 0 )
+ " V2=" + to_string( mover->CommandIn.Value2, 0 );
}
if( ( vehicle->Mechanik )
&& ( vehicle->Mechanik->AIControllFlag == AIdriver ) ) {
uitextline4 +=
"AI: Vd=" + to_string( vehicle->Mechanik->VelDesired, 0 )
+ " ad=" + to_string(vehicle->Mechanik->AccDesired, 2)
+ "/" + to_string(vehicle->Mechanik->AccDesired*vehicle->Mechanik->BrakeAccFactor(), 2)
+ " atrain=" + to_string(vehicle->Mechanik->fBrake_a0[0], 2)
+ "+" + to_string(vehicle->Mechanik->fBrake_a1[0], 2)
+ " aS=" + to_string(vehicle->Mechanik->AbsAccS_pub, 2)
+ " Pd=" + to_string( vehicle->Mechanik->ActualProximityDist, 0 )
+ " Vn=" + to_string( vehicle->Mechanik->VelNext, 0 );
}
// induction motor data
if( mover->EngineType == TEngineType::ElectricInductionMotor ) {
UITable.text_lines.emplace_back( " eimc: eimv: press:", Global.UITextColor );
for( int i = 0; i <= 20; ++i ) {
std::string parameters =
mover->eimc_labels[ i ] + to_string( mover->eimc[ i ], 2, 9 )
+ " | "
+ mover->eimv_labels[ i ] + to_string( mover->eimv[ i ], 2, 9 );
if( i < 10 ) {
parameters += " | " + train->fPress_labels[i] + to_string( train->fPress[ i ][ 0 ], 2, 9 );
}
else if( i == 12 ) {
parameters += " med:";
}
else if( i >= 13 ) {
parameters += " | " + vehicle->MED_labels[ i - 13 ] + to_string( vehicle->MED[ 0 ][ i - 13 ], 2, 9 );
}
UITable.text_lines.emplace_back( parameters, Global.UITextColor );
}
}
if (mover->EngineType == TEngineType::DieselEngine) {
std::string parameters = "param value";
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "efill: " + to_string(mover->dizel_fill, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "etorq: " + to_string(mover->dizel_Torque, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "creal: " + to_string(mover->dizel_engage, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "cdesi: " + to_string(mover->dizel_engagestate, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "cdelt: " + to_string(mover->dizel_engagedeltaomega, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "gears: " + to_string(mover->dizel_automaticgearstatus, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "hydro value";
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "hTCnI: " + to_string(mover->hydro_TC_nIn, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "hTCnO: " + to_string(mover->hydro_TC_nOut, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "hTCTM: " + to_string(mover->hydro_TC_TMRatio, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "hTCTI: " + to_string(mover->hydro_TC_TorqueIn, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "hTCTO: " + to_string(mover->hydro_TC_TorqueOut, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "hTCfl: " + to_string(mover->hydro_TC_Fill, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
parameters = "hTCLR: " + to_string(mover->hydro_TC_LockupRate, 2, 9);
UITable.text_lines.emplace_back(parameters, Global.UITextColor);
//parameters = "hTCXX: " + to_string(mover->hydro_TC_nIn, 2, 9);
//UITable.text_lines.emplace_back(parameters, Global.UITextColor);
}
} // if( DebugModeFlag && Controlled )
break;
}
}
#ifdef EU07_USE_OLD_UI_CODE
if( Controlled && DebugModeFlag && !Global.iTextMode ) {
uitextline1 +=
( "; d_omega " ) + to_string( Controlled->MoverParameters->dizel_engagedeltaomega, 3 );
if( Controlled->MoverParameters->EngineType == ElectricInductionMotor ) {
for( int i = 0; i <= 8; i++ ) {
for( int j = 0; j <= 9; j++ ) {
glRasterPos2f( 0.05f + 0.03f * i, 0.16f - 0.01f * j );
uitextline4 = to_string( Train->fEIMParams[ i ][ j ], 2 );
}
}
}
}
#endif
// update the ui header texts
auto &headerdata = UIHeader.text_lines;
headerdata[ 0 ].data = uitextline1;
headerdata[ 1 ].data = uitextline2;
headerdata[ 2 ].data = uitextline3;
headerdata[ 3 ].data = uitextline4;
// stenogramy dźwięków (ukryć, gdy tabelka skanowania lub rozkład?)
auto &transcripts = UITranscripts.text_lines;
transcripts.clear();
for( auto const &transcript : ui::Transcripts.aLines ) {
if( Global.fTimeAngleDeg >= transcript.fShow ) {
cParser parser( transcript.asText );
while( true == parser.getTokens( 1, false, "|" ) ) {
std::string transcriptline; parser >> transcriptline;
transcripts.emplace_back( transcriptline, glm::vec4( 1.0f, 1.0f, 0.0f, 1.0f ) );
}
}
}
}

36
driveruilayer.h Normal file
View File

@@ -0,0 +1,36 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "uilayer.h"
class driver_ui : public ui_layer {
public:
// constructors
driver_ui();
// methods
// potentially processes provided input key. returns: true if the input was processed, false otherwise
bool
on_key( int const Key, int const Action ) override;
// updates state of UI elements
void
update() override;
private:
// members
ui_panel UIHeader { 20, 20 }; // header ui panel
ui_panel UITable { 20, 100 };// schedule or scan table
ui_panel UITranscripts { 85, 600 }; // voice transcripts
int tprev { 0 }; // poprzedni czas
double VelPrev { 0.0 }; // poprzednia prędkość
double Acc { 0.0 }; // przyspieszenie styczne
};

View File

@@ -68,12 +68,13 @@ class vector3
vector3( scalar_t X, scalar_t Y, scalar_t Z ) :
x( X ), y( Y ), z( Z )
{}
vector3( glm::dvec3 const &Vector ) :
template <typename Type_, glm::precision Precision_>
vector3( glm::tvec3<Type_, Precision_> const &Vector ) :
x( Vector.x ), y( Vector.y ), z( Vector.z )
{}
template <glm::precision Precision_>
operator glm::tvec3<double, Precision_>() const {
return glm::tvec3<double, Precision_>{ x, y, z }; }
template <typename Type_, glm::precision Precision_>
operator glm::tvec3<Type_, Precision_>() const {
return glm::tvec3<Type_, Precision_>{ x, y, z }; }
// The int parameter is the number of elements to copy from initArray (3 or 4)
// explicit vector3(scalar_t* initArray, int arraySize = 3)
// { for (int i = 0;i<arraySize;++i) e[i] = initArray[i]; }

37
editorkeyboardinput.cpp Normal file
View File

@@ -0,0 +1,37 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "editorkeyboardinput.h"
bool
editorkeyboard_input::init() {
default_bindings();
// TODO: re-enable after mode-specific binding import is in place
// return recall_bindings();
return true;
}
void
editorkeyboard_input::default_bindings() {
m_bindingsetups = {
{ user_command::moveleft, GLFW_KEY_LEFT },
{ user_command::moveright, GLFW_KEY_RIGHT },
{ user_command::moveforward, GLFW_KEY_UP },
{ user_command::moveback, GLFW_KEY_DOWN },
{ user_command::moveup, GLFW_KEY_PAGE_UP },
{ user_command::movedown, GLFW_KEY_PAGE_DOWN },
};
bind();
}
//---------------------------------------------------------------------------

27
editorkeyboardinput.h Normal file
View File

@@ -0,0 +1,27 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "keyboardinput.h"
class editorkeyboard_input : public keyboard_input {
public:
// methods
bool
init() override;
protected:
// methods
void
default_bindings() override;
};
//---------------------------------------------------------------------------

278
editormode.cpp Normal file
View File

@@ -0,0 +1,278 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "editormode.h"
#include "editoruilayer.h"
#include "application.h"
#include "globals.h"
#include "simulation.h"
#include "simulationtime.h"
#include "simulationenvironment.h"
#include "timer.h"
#include "console.h"
#include "renderer.h"
bool
editor_mode::editormode_input::init() {
return (
mouse.init()
&& keyboard.init() );
}
void
editor_mode::editormode_input::poll() {
keyboard.poll();
}
editor_mode::editor_mode() {
m_userinterface = std::make_shared<editor_ui>();
}
// initializes internal data structures of the mode. returns: true on success, false otherwise
bool
editor_mode::init() {
Camera.Init( {}, {}, TCameraType::tp_Free );
return m_input.init();
}
// mode-specific update of simulation data. returns: false on error, true otherwise
bool
editor_mode::update() {
Timer::UpdateTimers( true );
simulation::State.update_clocks();
simulation::Environment.update();
// render time routines follow:
auto const deltarealtime = Timer::GetDeltaRenderTime(); // nie uwzględnia pauzowania ani mnożenia czasu
// fixed step render time routines:
fTime50Hz += deltarealtime; // w pauzie też trzeba zliczać czas, bo przy dużym FPS będzie problem z odczytem ramek
while( fTime50Hz >= 1.0 / 50.0 ) {
Console::Update(); // to i tak trzeba wywoływać
m_userinterface->update();
// decelerate camera
Camera.Velocity *= 0.65;
if( std::abs( Camera.Velocity.x ) < 0.01 ) { Camera.Velocity.x = 0.0; }
if( std::abs( Camera.Velocity.y ) < 0.01 ) { Camera.Velocity.y = 0.0; }
if( std::abs( Camera.Velocity.z ) < 0.01 ) { Camera.Velocity.z = 0.0; }
fTime50Hz -= 1.0 / 50.0;
}
// variable step render time routines:
update_camera( deltarealtime );
simulation::Region->update_sounds();
audio::renderer.update( deltarealtime );
GfxRenderer.Update( deltarealtime );
simulation::is_ready = true;
m_input.poll();
return true;
}
void
editor_mode::update_camera( double const Deltatime ) {
// uwzględnienie ruchu wywołanego klawiszami
Camera.Update();
// reset window state, it'll be set again if applicable in a check below
Global.CabWindowOpen = false;
// all done, update camera position to the new value
Global.pCameraPosition = Camera.Pos;
}
// maintenance method, called when the mode is activated
void
editor_mode::enter() {
m_statebackup = { Global.pCamera, FreeFlyModeFlag, Global.ControlPicking };
Global.pCamera = &Camera;
FreeFlyModeFlag = true;
Global.ControlPicking = true;
EditorModeFlag = true;
Application.set_cursor( GLFW_CURSOR_NORMAL );
}
// maintenance method, called when the mode is deactivated
void
editor_mode::exit() {
EditorModeFlag = false;
Global.ControlPicking = m_statebackup.picking;
FreeFlyModeFlag = m_statebackup.freefly;
Global.pCamera = m_statebackup.camera;
Application.set_cursor(
( Global.ControlPicking ?
GLFW_CURSOR_NORMAL :
GLFW_CURSOR_DISABLED ) );
}
void
editor_mode::on_key( int const Key, int const Scancode, int const Action, int const Mods ) {
Global.shiftState = ( Mods & GLFW_MOD_SHIFT ) ? true : false;
Global.ctrlState = ( Mods & GLFW_MOD_CONTROL ) ? true : false;
Global.altState = ( Mods & GLFW_MOD_ALT ) ? true : false;
// give the ui first shot at the input processing...
if( true == m_userinterface->on_key( Key, Action ) ) { return; }
// ...if the input is left untouched, pass it on
if( true == m_input.keyboard.key( Key, Action ) ) { return; }
if( Action == GLFW_RELEASE ) { return; }
if( ( Key == GLFW_KEY_LEFT_SHIFT )
|| ( Key == GLFW_KEY_LEFT_CONTROL )
|| ( Key == GLFW_KEY_LEFT_ALT )
|| ( Key == GLFW_KEY_RIGHT_SHIFT )
|| ( Key == GLFW_KEY_RIGHT_CONTROL )
|| ( Key == GLFW_KEY_RIGHT_ALT ) ) {
// don't bother passing these
return;
}
// legacy hardcoded keyboard commands
// TODO: replace with current command system, move to input object(s)
switch( Key ) {
case GLFW_KEY_F11: {
if( Action != GLFW_PRESS ) { break; }
// mode switch
// TODO: unsaved changes warning
if( ( false == Global.ctrlState )
&& ( false == Global.shiftState ) ) {
Application.pop_mode();
}
// scenery export
if( Global.ctrlState
&& Global.shiftState ) {
simulation::State.export_as_text( Global.SceneryFile );
}
break;
}
case GLFW_KEY_F12: {
// quick debug mode toggle
if( Global.ctrlState
&& Global.shiftState ) {
DebugModeFlag = !DebugModeFlag;
}
break;
}
default: {
break;
}
}
}
void
editor_mode::on_cursor_pos( double const Horizontal, double const Vertical ) {
auto const mousemove { glm::dvec2{ Horizontal, Vertical } - m_input.mouse.position() };
m_input.mouse.position( Horizontal, Vertical );
if( m_input.mouse.button( GLFW_MOUSE_BUTTON_LEFT ) == GLFW_RELEASE ) { return; }
if( m_node == nullptr ) { return; }
if( m_takesnapshot ) {
// take a snapshot of selected node(s)
// TODO: implement this
m_takesnapshot = false;
}
if( mode_translation() ) {
// move selected node
if( mode_translation_vertical() ) {
auto const translation { mousemove.y * -0.01f };
m_editor.translate( m_node, translation );
}
else {
auto const mouseworldposition { Camera.Pos + GfxRenderer.Mouse_Position() };
m_editor.translate( m_node, mouseworldposition, mode_snap() );
}
}
else {
// rotate selected node
auto const rotation { glm::vec3 { mousemove.y, mousemove.x, 0 } * 0.25f };
auto const quantization { (
mode_snap() ?
15.f : // TODO: put quantization value in a variable
0.f ) };
m_editor.rotate( m_node, rotation, quantization );
}
}
void
editor_mode::on_mouse_button( int const Button, int const Action, int const Mods ) {
if( Button == GLFW_MOUSE_BUTTON_LEFT ) {
if( Action == GLFW_PRESS ) {
// left button press
m_node = GfxRenderer.Update_Pick_Node();
if( m_node ) {
Application.set_cursor( GLFW_CURSOR_DISABLED );
}
dynamic_cast<editor_ui*>( m_userinterface.get() )->set_node( m_node );
}
else {
// left button release
if( m_node ) {
Application.set_cursor( GLFW_CURSOR_NORMAL );
}
// prime history stack for another snapshot
m_takesnapshot = true;
}
}
m_input.mouse.button( Button, Action );
}
bool
editor_mode::mode_translation() const {
return ( false == Global.altState );
}
bool
editor_mode::mode_translation_vertical() const {
return ( true == Global.shiftState );
}
bool
editor_mode::mode_rotation() const {
return ( true == Global.altState );
}
bool
editor_mode::mode_snap() const {
return ( true == Global.ctrlState );
}

84
editormode.h Normal file
View File

@@ -0,0 +1,84 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "applicationmode.h"
#include "editormouseinput.h"
#include "editorkeyboardinput.h"
#include "camera.h"
#include "sceneeditor.h"
#include "scenenode.h"
class editor_mode : public application_mode {
public:
// constructors
editor_mode();
// methods
// initializes internal data structures of the mode. returns: true on success, false otherwise
bool
init() override;
// mode-specific update of simulation data. returns: false on error, true otherwise
bool
update() override;
// maintenance method, called when the mode is activated
void
enter() override;
// maintenance method, called when the mode is deactivated
void
exit() override;
// input handlers
void
on_key( int const Key, int const Scancode, int const Action, int const Mods ) override;
void
on_cursor_pos( double const Horizontal, double const Vertical ) override;
void
on_mouse_button( int const Button, int const Action, int const Mods ) override;
void
on_scroll( double const Xoffset, double const Yoffset ) override { ; }
private:
// types
struct editormode_input {
editormouse_input mouse;
editorkeyboard_input keyboard;
bool init();
void poll();
};
struct state_backup {
TCamera *camera;
bool freefly;
bool picking;
};
// methods
void
update_camera( double const Deltatime );
bool
mode_translation() const;
bool
mode_translation_vertical() const;
bool
mode_rotation() const;
bool
mode_snap() const;
// members
editormode_input m_input;
TCamera Camera;
double fTime50Hz { 0.0 }; // bufor czasu dla komunikacji z PoKeys
scene::basic_editor m_editor;
scene::basic_node *m_node; // currently selected scene node
bool m_takesnapshot { true }; // helper, hints whether snapshot of selected node(s) should be taken before modification
state_backup m_statebackup; // helper, cached variables to be restored on mode exit
};

55
editormouseinput.cpp Normal file
View File

@@ -0,0 +1,55 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "editormouseinput.h"
bool
editormouse_input::init() {
return true;
}
void
editormouse_input::position( double Horizontal, double Vertical ) {
if( false == m_pickmodepanning ) {
// even if the view panning isn't active we capture the cursor position in case it does get activated
m_cursorposition.x = Horizontal;
m_cursorposition.y = Vertical;
return;
}
glm::dvec2 cursorposition { Horizontal, Vertical };
auto const viewoffset = cursorposition - m_cursorposition;
m_relay.post(
user_command::viewturn,
viewoffset.x,
viewoffset.y,
GLFW_PRESS,
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
0 );
m_cursorposition = cursorposition;
}
void
editormouse_input::button( int const Button, int const Action ) {
// store key state
if( Button >= 0 ) {
m_buttons[ Button ] = Action;
}
// right button controls panning
if( Button == GLFW_MOUSE_BUTTON_RIGHT ) {
m_pickmodepanning = ( Action == GLFW_PRESS );
}
}
//---------------------------------------------------------------------------

44
editormouseinput.h Normal file
View File

@@ -0,0 +1,44 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "command.h"
class editormouse_input {
public:
// constructors
editormouse_input() = default;
// methods
bool
init();
void
position( double const Horizontal, double const Vertical );
inline
glm::dvec2
position() const {
return m_cursorposition; }
void
button( int const Button, int const Action );
inline
int
button( int const Button ) const {
return m_buttons[ Button ]; }
private:
// members
command_relay m_relay;
bool m_pickmodepanning { false }; // indicates mouse is in view panning mode
glm::dvec2 m_cursorposition { 0.0 }; // stored last cursor position, used for panning
std::array<int, GLFW_MOUSE_BUTTON_LAST> m_buttons { GLFW_RELEASE };
};
//---------------------------------------------------------------------------

205
editoruilayer.cpp Normal file
View File

@@ -0,0 +1,205 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "editoruilayer.h"
#include "globals.h"
#include "camera.h"
#include "animmodel.h"
#include "track.h"
#include "event.h"
#include "renderer.h"
#include "utilities.h"
#include "logs.h"
editor_ui::editor_ui() {
clear_texts();
/*
UIHeader = std::make_shared<ui_panel>( 20, 20 ); // header ui panel
*/
// make 4 empty lines for the ui header, to cut down on work down the road
UIHeader.text_lines.emplace_back( "", Global.UITextColor );
UIHeader.text_lines.emplace_back( "", Global.UITextColor );
UIHeader.text_lines.emplace_back( "", Global.UITextColor );
UIHeader.text_lines.emplace_back( "", Global.UITextColor );
// bind the panels with ui object. maybe not the best place for this but, eh
push_back( &UIHeader );
}
// potentially processes provided input key. returns: true if key was processed, false otherwise
bool
editor_ui::on_key( int const Key, int const Action ) {
return false;
}
// updates state of UI elements
void
editor_ui::update() {
std::string uitextline1, uitextline2, uitextline3, uitextline4;
set_tooltip( "" );
auto const *camera { Global.pCamera };
if( ( true == Global.ControlPicking )
&& ( true == DebugModeFlag ) ) {
auto const scenerynode = GfxRenderer.Pick_Node();
set_tooltip(
( scenerynode ?
scenerynode->name() :
"" ) );
}
// scenario inspector
auto const *node { m_node };
if( node == nullptr ) {
auto const mouseposition { Global.pCamera->Pos + GfxRenderer.Mouse_Position() };
uitextline1 = "mouse location: [" + to_string( mouseposition.x, 2 ) + ", " + to_string( mouseposition.y, 2 ) + ", " + to_string( mouseposition.z, 2 ) + "]";
goto update;
}
uitextline1 =
"node name: " + node->name()
+ "; location: [" + to_string( node->location().x, 2 ) + ", " + to_string( node->location().y, 2 ) + ", " + to_string( node->location().z, 2 ) + "]"
+ " (distance: " + to_string( glm::length( glm::dvec3{ node->location().x, 0.0, node->location().z } -glm::dvec3{ Global.pCameraPosition.x, 0.0, Global.pCameraPosition.z } ), 1 ) + " m)";
// subclass-specific data
// TBD, TODO: specialized data dump method in each node subclass, or data imports in the panel for provided subclass pointer?
if( typeid( *node ) == typeid( TAnimModel ) ) {
auto const *subnode = static_cast<TAnimModel const *>( node );
uitextline2 = "angle: " + to_string( clamp_circular( subnode->vAngle.y, 360.f ), 2 ) + " deg";
uitextline2 += "; lights: ";
if( subnode->iNumLights > 0 ) {
uitextline2 += '[';
for( int lightidx = 0; lightidx < subnode->iNumLights; ++lightidx ) {
uitextline2 += to_string( subnode->lsLights[ lightidx ] );
if( lightidx < subnode->iNumLights - 1 ) {
uitextline2 += ", ";
}
}
uitextline2 += ']';
}
else {
uitextline2 += "none";
}
// 3d shape
auto modelfile { (
( subnode->pModel != nullptr ) ?
subnode->pModel->NameGet() :
"none" ) };
if( modelfile.find( szModelPath ) == 0 ) {
// don't include 'models/' in the path
modelfile.erase( 0, std::string{ szModelPath }.size() );
}
// texture
auto texturefile { (
( subnode->Material()->replacable_skins[ 1 ] != null_handle ) ?
GfxRenderer.Material( subnode->Material()->replacable_skins[ 1 ] ).name :
"none" ) };
if( texturefile.find( szTexturePath ) == 0 ) {
// don't include 'textures/' in the path
texturefile.erase( 0, std::string{ szTexturePath }.size() );
}
uitextline3 = "mesh: " + modelfile;
uitextline4 = "skin: " + texturefile;
}
else if( typeid( *node ) == typeid( TTrack ) ) {
auto const *subnode = static_cast<TTrack const *>( node );
// basic attributes
uitextline2 =
"isolated: " + ( ( subnode->pIsolated != nullptr ) ? subnode->pIsolated->asName : "none" )
+ "; velocity: " + to_string( subnode->SwitchExtension ? subnode->SwitchExtension->fVelocity : subnode->fVelocity )
+ "; width: " + to_string( subnode->fTrackWidth ) + " m"
+ "; friction: " + to_string( subnode->fFriction, 2 )
+ "; quality: " + to_string( subnode->iQualityFlag );
// textures
auto texturefile { (
( subnode->m_material1 != null_handle ) ?
GfxRenderer.Material( subnode->m_material1 ).name :
"none" ) };
if( texturefile.find( szTexturePath ) == 0 ) {
texturefile.erase( 0, std::string{ szTexturePath }.size() );
}
auto texturefile2{ (
( subnode->m_material2 != null_handle ) ?
GfxRenderer.Material( subnode->m_material2 ).name :
"none" ) };
if( texturefile2.find( szTexturePath ) == 0 ) {
texturefile2.erase( 0, std::string{ szTexturePath }.size() );
}
uitextline2 += "; skins: [" + texturefile + ", " + texturefile2 + "]";
// paths
uitextline3 = "paths: ";
for( auto const &path : subnode->m_paths ) {
uitextline3 +=
"["
+ to_string( path.points[ segment_data::point::start ].x, 3 ) + ", "
+ to_string( path.points[ segment_data::point::start ].y, 3 ) + ", "
+ to_string( path.points[ segment_data::point::start ].z, 3 ) + "]->"
+ "["
+ to_string( path.points[ segment_data::point::end ].x, 3 ) + ", "
+ to_string( path.points[ segment_data::point::end ].y, 3 ) + ", "
+ to_string( path.points[ segment_data::point::end ].z, 3 ) + "] ";
}
// events
std::vector< std::pair< std::string, TTrack::event_sequence const * > > const eventsequences {
{ "ev0", &subnode->m_events0 }, { "ev0all", &subnode->m_events0all },
{ "ev1", &subnode->m_events1 }, { "ev1all", &subnode->m_events1all },
{ "ev2", &subnode->m_events2 }, { "ev2all", &subnode->m_events2all } };
for( auto const &eventsequence : eventsequences ) {
if( eventsequence.second->empty() ) { continue; }
uitextline4 += eventsequence.first + ": [";
for( auto const &event : *( eventsequence.second ) ) {
if( uitextline4.back() != '[' ) {
uitextline4 += ", ";
}
if( event.second ) {
uitextline4 += event.second->asName;
}
}
uitextline4 += "] ";
}
}
else if( typeid( *node ) == typeid( TMemCell ) ) {
auto const *subnode = static_cast<TMemCell const *>( node );
uitextline2 =
"data: [" + subnode->Text() + "]"
+ " [" + to_string( subnode->Value1(), 2 ) + "]"
+ " [" + to_string( subnode->Value2(), 2 ) + "]";
uitextline3 = "track: " + ( subnode->asTrackName.empty() ? "none" : subnode->asTrackName );
}
update:
// update the ui header texts
auto &headerdata = UIHeader.text_lines;
headerdata[ 0 ].data = uitextline1;
headerdata[ 1 ].data = uitextline2;
headerdata[ 2 ].data = uitextline3;
headerdata[ 3 ].data = uitextline4;
}
void
editor_ui::set_node( scene::basic_node * Node ) {
m_node = Node;
}

39
editoruilayer.h Normal file
View File

@@ -0,0 +1,39 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "uilayer.h"
namespace scene {
class basic_node;
}
class editor_ui : public ui_layer {
public:
// constructors
editor_ui();
// methods
// potentially processes provided input key. returns: true if the input was processed, false otherwise
bool
on_key( int const Key, int const Action ) override;
// updates state of UI elements
void
update() override;
void
set_node( scene::basic_node * Node );
private:
// members
ui_panel UIHeader { 20, 20 }; // header ui panel
scene::basic_node * m_node { nullptr }; // currently bound scene node, if any
};

View File

@@ -13,6 +13,15 @@ http://mozilla.org/MPL/2.0/.
#include "logs.h"
#include "parser.h"
namespace input {
std::array<char, GLFW_KEY_LAST + 1> keys { GLFW_RELEASE };
bool key_alt;
bool key_ctrl;
bool key_shift;
}
bool
keyboard_input::recall_bindings() {
@@ -94,7 +103,7 @@ keyboard_input::recall_bindings() {
}
if( ( binding & 0xffff ) != 0 ) {
m_commands.at( static_cast<std::size_t>( lookup->second ) ).binding = binding;
m_bindingsetups.emplace_back( binding_setup{ lookup->second, binding } );
}
}
}
@@ -113,7 +122,7 @@ keyboard_input::key( int const Key, int const Action ) {
if( ( Key == GLFW_KEY_LEFT_SHIFT ) || ( Key == GLFW_KEY_RIGHT_SHIFT ) ) {
// update internal state, but don't bother passing these
m_shift =
input::key_shift =
( Action == GLFW_RELEASE ?
false :
true );
@@ -122,20 +131,23 @@ keyboard_input::key( int const Key, int const Action ) {
}
if( ( Key == GLFW_KEY_LEFT_CONTROL ) || ( Key == GLFW_KEY_RIGHT_CONTROL ) ) {
// update internal state, but don't bother passing these
m_ctrl =
input::key_ctrl =
( Action == GLFW_RELEASE ?
false :
true );
modifier = true;
}
if( ( Key == GLFW_KEY_LEFT_ALT ) || ( Key == GLFW_KEY_RIGHT_ALT ) ) {
// currently we have no interest in these whatsoever
return false;
// update internal state, but don't bother passing these
input::key_alt =
( Action == GLFW_RELEASE ?
false :
true );
}
// store key state
if( Key != -1 ) {
m_keys[ Key ] = Action;
input::keys[ Key ] = Action;
}
if( true == is_movement_key( Key ) ) {
@@ -146,8 +158,8 @@ keyboard_input::key( int const Key, int const Action ) {
// include active modifiers for currently pressed key, except if the key is a modifier itself
auto const key =
Key
| ( modifier ? 0 : ( m_shift ? keymodifier::shift : 0 ) )
| ( modifier ? 0 : ( m_ctrl ? keymodifier::control : 0 ) );
| ( modifier ? 0 : ( input::key_shift ? keymodifier::shift : 0 ) )
| ( modifier ? 0 : ( input::key_ctrl ? keymodifier::control : 0 ) );
auto const lookup = m_bindings.find( key );
if( lookup == m_bindings.end() ) {
@@ -166,384 +178,10 @@ keyboard_input::key( int const Key, int const Action ) {
return true;
}
void
keyboard_input::default_bindings() {
m_commands = {
// aidriverenable
{ GLFW_KEY_Q | keymodifier::shift },
// aidriverdisable
{ GLFW_KEY_Q },
// mastercontrollerincrease
{ GLFW_KEY_KP_ADD },
// mastercontrollerincreasefast
{ GLFW_KEY_KP_ADD | keymodifier::shift },
// mastercontrollerdecrease
{ GLFW_KEY_KP_SUBTRACT },
// mastercontrollerdecreasefast
{ GLFW_KEY_KP_SUBTRACT | keymodifier::shift },
// mastercontrollerset
{ -1 },
// secondcontrollerincrease
{ GLFW_KEY_KP_DIVIDE },
// secondcontrollerincreasefast
{ GLFW_KEY_KP_DIVIDE | keymodifier::shift },
// secondcontrollerdecrease
{ GLFW_KEY_KP_MULTIPLY },
// secondcontrollerdecreasefast
{ GLFW_KEY_KP_MULTIPLY | keymodifier::shift },
// secondcontrollerset
{ -1 },
// mucurrentindicatorothersourceactivate
{ GLFW_KEY_Z | keymodifier::shift },
// independentbrakeincrease
{ GLFW_KEY_KP_1 },
// independentbrakeincreasefast
{ GLFW_KEY_KP_1 | keymodifier::shift },
// independentbrakedecrease
{ GLFW_KEY_KP_7 },
// independentbrakedecreasefast
{ GLFW_KEY_KP_7 | keymodifier::shift },
// independentbrakeset
{ -1 },
// independentbrakebailoff
{ GLFW_KEY_KP_4 },
// trainbrakeincrease
{ GLFW_KEY_KP_3 },
// trainbrakedecrease
{ GLFW_KEY_KP_9 },
// trainbrakeset
{ -1 },
// trainbrakecharging
{ GLFW_KEY_KP_DECIMAL },
// trainbrakerelease
{ GLFW_KEY_KP_6 },
// trainbrakefirstservice
{ GLFW_KEY_KP_8 },
// trainbrakeservice
{ GLFW_KEY_KP_5 },
// trainbrakefullservice
{ GLFW_KEY_KP_2 },
// trainbrakehandleoff
{ GLFW_KEY_KP_5 | keymodifier::control },
// trainbrakeemergency
{ GLFW_KEY_KP_0 },
// trainbrakebasepressureincrease
{ GLFW_KEY_KP_3 | keymodifier::control },
// trainbrakebasepressuredecrease
{ GLFW_KEY_KP_9 | keymodifier::control },
// trainbrakebasepressurereset
{ GLFW_KEY_KP_6 | keymodifier::control },
// trainbrakeoperationtoggle
{ GLFW_KEY_KP_4 | keymodifier::control },
// manualbrakeincrease
{ GLFW_KEY_KP_1 | keymodifier::control },
// manualbrakedecrease
{ GLFW_KEY_KP_7 | keymodifier::control },
// alarm chain toggle
{ GLFW_KEY_B | keymodifier::shift | keymodifier::control },
// wheelspinbrakeactivate
{ GLFW_KEY_KP_ENTER },
// sandboxactivate
{ GLFW_KEY_S },
// reverserincrease
{ GLFW_KEY_D },
// reverserdecrease
{ GLFW_KEY_R },
// reverserforwardhigh
{ -1 },
// reverserforward
{ -1 },
// reverserneutral
{ -1 },
// reverserbackward
{ -1 },
// waterpumpbreakertoggle
{ GLFW_KEY_W | keymodifier::control },
// waterpumpbreakerclose
{ -1 },
// waterpumpbreakeropen
{ -1 },
// waterpumptoggle
{ GLFW_KEY_W },
// waterpumpenable
{ -1 },
// waterpumpdisable
{ -1 },
// waterheaterbreakertoggle
{ GLFW_KEY_W | keymodifier::control | keymodifier::shift },
// waterheaterbreakerclose
{ -1 },
// waterheaterbreakeropen
{ -1 },
// waterheatertoggle
{ GLFW_KEY_W | keymodifier::shift },
// waterheaterenable
{ -1 },
// waterheaterdisable
{ -1 },
// watercircuitslinktoggle
{ GLFW_KEY_H | keymodifier::shift },
// watercircuitslinkenable
{ -1 },
// watercircuitslinkdisable
{ -1 },
// fuelpumptoggle
{ GLFW_KEY_F },
// fuelpumpenable,
{ -1 },
// fuelpumpdisable,
{ -1 },
// oilpumptoggle
{ GLFW_KEY_F | keymodifier::shift },
// oilpumpenable,
{ -1 },
// oilpumpdisable,
{ -1 },
// linebreakertoggle
{ GLFW_KEY_M },
// linebreakeropen
{ -1 },
// linebreakerclose
{ -1 },
// convertertoggle
{ GLFW_KEY_X },
// converterenable,
{ -1 },
// converterdisable,
{ -1 },
// convertertogglelocal
{ GLFW_KEY_X | keymodifier::shift },
// converteroverloadrelayreset
{ GLFW_KEY_N | keymodifier::control },
// compressortoggle
{ GLFW_KEY_C },
// compressorenable
{ -1 },
// compressordisable
{ -1 },
// compressortoggleloal
{ GLFW_KEY_C | keymodifier::shift },
// motoroverloadrelaythresholdtoggle
{ GLFW_KEY_F },
// motoroverloadrelaythresholdsetlow
{ -1 },
// motoroverloadrelaythresholdsethigh
{ -1 },
// motoroverloadrelayreset
{ GLFW_KEY_N },
// notchingrelaytoggle
{ GLFW_KEY_G },
// epbrakecontroltoggle
{ GLFW_KEY_Z | keymodifier::control },
// trainbrakeoperationmodeincrease
{ GLFW_KEY_KP_2 | keymodifier::control },
// trainbrakeoperationmodedecrease
{ GLFW_KEY_KP_8 | keymodifier::control },
// brakeactingspeedincrease
{ GLFW_KEY_B | keymodifier::shift },
// brakeactingspeeddecrease
{ GLFW_KEY_B },
// brakeactingspeedsetcargo
{ -1 },
// brakeactingspeedsetpassenger
{ -1 },
// brakeactingspeedsetrapid
{ -1 },
// brakeloadcompensationincrease
{ GLFW_KEY_H | keymodifier::shift | keymodifier::control },
// brakeloadcompensationdecrease
{ GLFW_KEY_H | keymodifier::control },
// mubrakingindicatortoggle
{ GLFW_KEY_L | keymodifier::shift },
// alerteracknowledge
{ GLFW_KEY_SPACE },
// hornlowactivate
{ GLFW_KEY_A },
// hornhighactivate
{ GLFW_KEY_S },
// whistleactivate
{ GLFW_KEY_Z },
// radiotoggle
{ GLFW_KEY_R | keymodifier::control },
// radiochannelincrease
{ GLFW_KEY_R | keymodifier::shift },
// radiochanneldecrease
{ GLFW_KEY_R },
// radiostopsend
{ GLFW_KEY_PAUSE | keymodifier::shift | keymodifier::control },
// radiostoptest
{ GLFW_KEY_R | keymodifier::shift | keymodifier::control },
// cabchangeforward
{ GLFW_KEY_HOME },
// cabchangebackward
{ GLFW_KEY_END },
// viewturn
{ -1 },
// movehorizontal
{ -1 },
// movehorizontalfast
{ -1 },
// movevertical
{ -1 },
// moveverticalfast
{ -1 },
// moveleft
{ GLFW_KEY_LEFT },
// moveright
{ GLFW_KEY_RIGHT },
// moveforward
{ GLFW_KEY_UP },
// moveback
{ GLFW_KEY_DOWN },
// moveup
{ GLFW_KEY_PAGE_UP },
// movedown
{ GLFW_KEY_PAGE_DOWN },
// carcouplingincrease
{ GLFW_KEY_INSERT },
// carcouplingdisconnect
{ GLFW_KEY_DELETE },
// doortoggleleft
{ GLFW_KEY_COMMA },
// doortoggleright
{ GLFW_KEY_PERIOD },
// departureannounce
{ GLFW_KEY_SLASH },
// doorlocktoggle
{ GLFW_KEY_S | keymodifier::shift },
// pantographcompressorvalvetoggle
{ GLFW_KEY_V | keymodifier::control },
// pantographcompressoractivate
{ GLFW_KEY_V | keymodifier::shift },
// pantographtogglefront
{ GLFW_KEY_P },
// pantographtogglerear
{ GLFW_KEY_O },
// pantographraisefront
{ -1 },
// pantographraiserear
{ -1 },
// pantographlowerfront
{ -1 },
// pantographlowerrear
{ -1 },
// pantographlowerall
{ GLFW_KEY_P | keymodifier::control },
// heatingtoggle
{ GLFW_KEY_H },
// heatingenable
{ -1 },
// heatingdisable
{ -1 },
// lightspresetactivatenext
{ GLFW_KEY_T | keymodifier::shift },
// lightspresetactivateprevious
{ GLFW_KEY_T },
// headlighttoggleleft
{ GLFW_KEY_Y },
// headlightenableleft
{ -1 },
// headlightdisableleft
{ -1 },
// headlighttoggleright
{ GLFW_KEY_I },
// headlightenableright
{ -1 },
// headlightdisableright
{ -1 },
// headlighttoggleupper
{ GLFW_KEY_U },
// headlightenableupper
{ -1 },
// headlightdisableupper
{ -1 },
// redmarkertoggleleft
{ GLFW_KEY_Y | keymodifier::shift },
// redmarkerenableleft
{ -1 },
// redmarkerdisableleft
{ -1 },
// redmarkertoggleright
{ GLFW_KEY_I | keymodifier::shift },
// redmarkerenableright
{ -1 },
// redmarkerdisableright
{ -1 },
// headlighttogglerearleft
{ GLFW_KEY_Y | keymodifier::control },
// headlighttogglerearright
{ GLFW_KEY_I | keymodifier::control },
// headlighttogglerearupper
{ GLFW_KEY_U | keymodifier::control },
// redmarkertogglerearleft
{ GLFW_KEY_Y | keymodifier::control | keymodifier::shift },
// redmarkertogglerearright
{ GLFW_KEY_I | keymodifier::control | keymodifier::shift },
// redmarkerstoggle
{ GLFW_KEY_E | keymodifier::shift },
// endsignalstoggle
{ GLFW_KEY_E },
// headlightsdimtoggle
{ GLFW_KEY_L | keymodifier::control },
// headlightsdimenable
{ -1 },
// headlightsdimdisable
{ -1 },
// motorconnectorsopen
{ GLFW_KEY_L },
// motorconnectorsclose
{ -1 },
// motordisconnect
{ GLFW_KEY_E | keymodifier::control },
// interiorlighttoggle
{ GLFW_KEY_APOSTROPHE },
// interiorlightenable
{ -1 },
// interiorlightdisable
{ -1 },
// interiorlightdimtoggle
{ GLFW_KEY_APOSTROPHE | keymodifier::control },
// interiorlightdimenable
{ -1 },
// interiorlightdimdisable
{ -1 },
// instrumentlighttoggle
{ GLFW_KEY_SEMICOLON },
// instrumentlightenable
{ -1 },
// instrumentlightdisable,
{ -1 },
// "generictoggle0"
{ GLFW_KEY_0 },
// "generictoggle1"
{ GLFW_KEY_1 },
// "generictoggle2"
{ GLFW_KEY_2 },
// "generictoggle3"
{ GLFW_KEY_3 },
// "generictoggle4"
{ GLFW_KEY_4 },
// "generictoggle5"
{ GLFW_KEY_5 },
// "generictoggle6"
{ GLFW_KEY_6 },
// "generictoggle7"
{ GLFW_KEY_7 },
// "generictoggle8"
{ GLFW_KEY_8 },
// "generictoggle9"
{ GLFW_KEY_9 },
// "batterytoggle"
{ GLFW_KEY_J },
// batteryenable
{ -1 },
// batterydisable
{ -1 },
};
bind();
int
keyboard_input::key( int const Key ) const {
return input::keys[ Key ];
}
void
@@ -551,23 +189,32 @@ keyboard_input::bind() {
m_bindings.clear();
int commandcode{ 0 };
for( auto const &command : m_commands ) {
for( auto const &bindingsetup : m_bindingsetups ) {
if( command.binding != -1 ) {
if( bindingsetup.binding != -1 ) {
m_bindings.emplace(
command.binding,
static_cast<user_command>( commandcode ) );
bindingsetup.binding,
bindingsetup.command );
}
++commandcode;
}
// cache movement key bindings
m_bindingscache.forward = m_commands[ static_cast<std::size_t>( user_command::moveforward ) ].binding;
m_bindingscache.back = m_commands[ static_cast<std::size_t>( user_command::moveback ) ].binding;
m_bindingscache.left = m_commands[ static_cast<std::size_t>( user_command::moveleft ) ].binding;
m_bindingscache.right = m_commands[ static_cast<std::size_t>( user_command::moveright ) ].binding;
m_bindingscache.up = m_commands[ static_cast<std::size_t>( user_command::moveup ) ].binding;
m_bindingscache.down = m_commands[ static_cast<std::size_t>( user_command::movedown ) ].binding;
m_bindingscache.forward = binding( user_command::moveforward );
m_bindingscache.back = binding( user_command::moveback );
m_bindingscache.left = binding( user_command::moveleft );
m_bindingscache.right = binding( user_command::moveright );
m_bindingscache.up = binding( user_command::moveup );
m_bindingscache.down = binding( user_command::movedown );
}
int
keyboard_input::binding( user_command const Command ) const {
for( auto const &binding : m_bindings ) {
if( binding.second == Command ) {
return binding.first;
}
}
return -1;
}
bool
@@ -590,13 +237,13 @@ keyboard_input::poll() {
glm::vec2 const movementhorizontal {
// x-axis
( Global.shiftState ? 1.f : 0.5f ) *
( m_keys[ m_bindingscache.left ] != GLFW_RELEASE ? -1.f :
m_keys[ m_bindingscache.right ] != GLFW_RELEASE ? 1.f :
( input::keys[ m_bindingscache.left ] != GLFW_RELEASE ? -1.f :
input::keys[ m_bindingscache.right ] != GLFW_RELEASE ? 1.f :
0.f ),
// z-axis
( Global.shiftState ? 1.f : 0.5f ) *
( m_keys[ m_bindingscache.forward ] != GLFW_RELEASE ? 1.f :
m_keys[ m_bindingscache.back ] != GLFW_RELEASE ? -1.f :
( input::keys[ m_bindingscache.forward ] != GLFW_RELEASE ? 1.f :
input::keys[ m_bindingscache.back ] != GLFW_RELEASE ? -1.f :
0.f ) };
if( ( movementhorizontal.x != 0.f || movementhorizontal.y != 0.f )
@@ -616,8 +263,8 @@ keyboard_input::poll() {
float const movementvertical {
// y-axis
( Global.shiftState ? 1.f : 0.5f ) *
( m_keys[ m_bindingscache.up ] != GLFW_RELEASE ? 1.f :
m_keys[ m_bindingscache.down ] != GLFW_RELEASE ? -1.f :
( input::keys[ m_bindingscache.up ] != GLFW_RELEASE ? 1.f :
input::keys[ m_bindingscache.down ] != GLFW_RELEASE ? -1.f :
0.f ) };
if( ( movementvertical != 0.f )

View File

@@ -13,19 +13,32 @@ http://mozilla.org/MPL/2.0/.
#include <array>
#include "command.h"
namespace input {
extern std::array<char, GLFW_KEY_LAST + 1> keys;
extern bool key_alt;
extern bool key_ctrl;
extern bool key_shift;
}
class keyboard_input {
public:
// constructors
keyboard_input() { default_bindings(); }
keyboard_input() = default;
// destructor
virtual ~keyboard_input() = default;
// methods
virtual
bool
init() { return recall_bindings(); }
bool
recall_bindings();
init() { return true; }
bool
key( int const Key, int const Action );
int
key( int const Key ) const;
void
poll();
inline
@@ -33,7 +46,7 @@ public:
command() const {
return m_command; }
private:
protected:
// types
enum keymodifier : int {
@@ -41,13 +54,29 @@ private:
control = 0x20000
};
struct command_setup {
struct binding_setup {
user_command command;
int binding;
};
typedef std::vector<command_setup> commandsetup_sequence;
typedef std::unordered_map<int, user_command> usercommand_map;
using bindingsetup_sequence = std::vector<binding_setup>;
// methods
virtual
void
default_bindings() = 0;
bool
recall_bindings();
void
bind();
// members
bindingsetup_sequence m_bindingsetups;
private:
// types
using usercommand_map = std::unordered_map<int, user_command>;
struct bindings_cache {
@@ -60,24 +89,18 @@ private:
};
// methods
void
default_bindings();
void
bind();
int
binding( user_command const Command ) const;
bool
is_movement_key( int const Key ) const;
// members
commandsetup_sequence m_commands;
user_command m_command { user_command::none }; // last, if any, issued command
usercommand_map m_bindings;
command_relay m_relay;
bool m_shift { false };
bool m_ctrl { false };
bindings_cache m_bindingscache;
glm::vec2 m_movementhorizontal { 0.f };
float m_movementvertical { 0.f };
std::array<char, GLFW_KEY_LAST + 1> m_keys;
};
//---------------------------------------------------------------------------

View File

@@ -19,11 +19,35 @@
<Filter Include="Header Files\mczapkie">
<UniqueIdentifier>{36684428-8a48-435f-bca4-a24d9bfe2587}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\input">
<UniqueIdentifier>{cdf75bec-91f7-413c-8b57-9e32cba49148}</UniqueIdentifier>
<Filter Include="Header Files\application">
<UniqueIdentifier>{035056ae-b8d6-4338-9d1f-514a0ce5f1e0}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\input">
<UniqueIdentifier>{2d73d7b2-5252-499c-963a-88fa3cb1af53}</UniqueIdentifier>
<Filter Include="Source Files\application">
<UniqueIdentifier>{395be8b7-094b-4e4d-918e-02369cd6013b}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\application\mode_driver">
<UniqueIdentifier>{1ea7a1f1-5728-478c-8593-b6f4eecefdbf}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\application\mode_editor">
<UniqueIdentifier>{89843d2c-930c-4135-8575-714040f117ac}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\application\mode_scenarioloader">
<UniqueIdentifier>{ab9c6f13-eb8f-497b-b21d-66f8c0220cf8}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\application\mode_driver">
<UniqueIdentifier>{492181c1-fa1a-4087-be99-5d88edf66c69}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\application\mode_editor">
<UniqueIdentifier>{e8c50720-4d83-4722-8ac9-16ea6af834b5}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\application\mode_scenarioloader">
<UniqueIdentifier>{ecadb5ae-48aa-4811-909d-56e8a01badb2}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\application\input">
<UniqueIdentifier>{ba04e497-9bfa-4cfd-883f-7f018ac76301}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\application\input">
<UniqueIdentifier>{0630616b-1afe-4fb6-96d1-3755834ba8aa}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
@@ -120,27 +144,18 @@
<ClCompile Include="TrkFoll.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="World.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mczapkie\friction.cpp">
<Filter>Source Files\mczapkie</Filter>
</ClCompile>
<ClCompile Include="mczapkie\hamulce.cpp">
<Filter>Source Files\mczapkie</Filter>
</ClCompile>
<ClCompile Include="console\LPT.cpp">
<Filter>Source Files\input</Filter>
</ClCompile>
<ClCompile Include="mczapkie\Mover.cpp">
<Filter>Source Files\mczapkie</Filter>
</ClCompile>
<ClCompile Include="mczapkie\Oerlikon_ESt.cpp">
<Filter>Source Files\mczapkie</Filter>
</ClCompile>
<ClCompile Include="console\PoKeys55.cpp">
<Filter>Source Files\input</Filter>
</ClCompile>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -180,21 +195,12 @@
<ClCompile Include="moon.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="gamepadinput.cpp">
<Filter>Source Files\input</Filter>
</ClCompile>
<ClCompile Include="keyboardinput.cpp">
<Filter>Source Files\input</Filter>
</ClCompile>
<ClCompile Include="command.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="openglgeometrybank.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mouseinput.cpp">
<Filter>Source Files\input</Filter>
</ClCompile>
<ClCompile Include="translation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -231,27 +237,72 @@
<ClCompile Include="utilities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="uart.cpp">
<Filter>Source Files\input</Filter>
</ClCompile>
<ClCompile Include="station.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stb_vorbis.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="sceneeditor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simulationtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="application.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="eu07.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="application.cpp">
<Filter>Source Files\application</Filter>
</ClCompile>
<ClCompile Include="simulationstateserializer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simulationenvironment.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="sceneeditor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="drivermode.cpp">
<Filter>Source Files\application\mode_driver</Filter>
</ClCompile>
<ClCompile Include="driveruilayer.cpp">
<Filter>Source Files\application\mode_driver</Filter>
</ClCompile>
<ClCompile Include="editoruilayer.cpp">
<Filter>Source Files\application\mode_editor</Filter>
</ClCompile>
<ClCompile Include="editormode.cpp">
<Filter>Source Files\application\mode_editor</Filter>
</ClCompile>
<ClCompile Include="scenarioloadermode.cpp">
<Filter>Source Files\application\mode_scenarioloader</Filter>
</ClCompile>
<ClCompile Include="driverkeyboardinput.cpp">
<Filter>Source Files\application\mode_driver</Filter>
</ClCompile>
<ClCompile Include="drivermouseinput.cpp">
<Filter>Source Files\application\mode_driver</Filter>
</ClCompile>
<ClCompile Include="editormouseinput.cpp">
<Filter>Source Files\application\mode_editor</Filter>
</ClCompile>
<ClCompile Include="gamepadinput.cpp">
<Filter>Source Files\application\mode_driver</Filter>
</ClCompile>
<ClCompile Include="console\LPT.cpp">
<Filter>Source Files\application\mode_driver</Filter>
</ClCompile>
<ClCompile Include="console\PoKeys55.cpp">
<Filter>Source Files\application\mode_driver</Filter>
</ClCompile>
<ClCompile Include="uart.cpp">
<Filter>Source Files\application\mode_driver</Filter>
</ClCompile>
<ClCompile Include="editorkeyboardinput.cpp">
<Filter>Source Files\application\mode_editor</Filter>
</ClCompile>
<ClCompile Include="keyboardinput.cpp">
<Filter>Source Files\application\input</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Globals.h">
@@ -266,9 +317,6 @@
<ClInclude Include="McZapkie\mover.h">
<Filter>Header Files\mczapkie</Filter>
</ClInclude>
<ClInclude Include="World.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="dumb3d.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -317,12 +365,6 @@
<ClInclude Include="McZapkie\friction.h">
<Filter>Header Files\mczapkie</Filter>
</ClInclude>
<ClInclude Include="Console\PoKeys55.h">
<Filter>Header Files\input</Filter>
</ClInclude>
<ClInclude Include="Console\LPT.h">
<Filter>Header Files\input</Filter>
</ClInclude>
<ClInclude Include="Segment.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -419,21 +461,12 @@
<ClInclude Include="command.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="keyboardinput.h">
<Filter>Header Files\input</Filter>
</ClInclude>
<ClInclude Include="gamepadinput.h">
<Filter>Header Files\input</Filter>
</ClInclude>
<ClInclude Include="version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="openglgeometrybank.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="mouseinput.h">
<Filter>Header Files\input</Filter>
</ClInclude>
<ClInclude Include="translation.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -473,21 +506,72 @@
<ClInclude Include="utilities.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="uart.h">
<Filter>Header Files\input</Filter>
</ClInclude>
<ClInclude Include="station.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="sceneeditor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simulationtime.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="applicationmode.h">
<Filter>Header Files\application</Filter>
</ClInclude>
<ClInclude Include="application.h">
<Filter>Header Files\application</Filter>
</ClInclude>
<ClInclude Include="simulationstateserializer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simulationenvironment.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="sceneeditor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="drivermode.h">
<Filter>Header Files\application\mode_driver</Filter>
</ClInclude>
<ClInclude Include="driveruilayer.h">
<Filter>Header Files\application\mode_driver</Filter>
</ClInclude>
<ClInclude Include="driverkeyboardinput.h">
<Filter>Header Files\application\mode_driver</Filter>
</ClInclude>
<ClInclude Include="drivermouseinput.h">
<Filter>Header Files\application\mode_driver</Filter>
</ClInclude>
<ClInclude Include="gamepadinput.h">
<Filter>Header Files\application\mode_driver</Filter>
</ClInclude>
<ClInclude Include="Console\LPT.h">
<Filter>Header Files\application\mode_driver</Filter>
</ClInclude>
<ClInclude Include="Console\PoKeys55.h">
<Filter>Header Files\application\mode_driver</Filter>
</ClInclude>
<ClInclude Include="uart.h">
<Filter>Header Files\application\mode_driver</Filter>
</ClInclude>
<ClInclude Include="editoruilayer.h">
<Filter>Header Files\application\mode_editor</Filter>
</ClInclude>
<ClInclude Include="editormouseinput.h">
<Filter>Header Files\application\mode_editor</Filter>
</ClInclude>
<ClInclude Include="editormode.h">
<Filter>Header Files\application\mode_editor</Filter>
</ClInclude>
<ClInclude Include="scenarioloadermode.h">
<Filter>Header Files\application\mode_scenarioloader</Filter>
</ClInclude>
<ClInclude Include="scenarioloaderuilayer.h">
<Filter>Header Files\application\mode_scenarioloader</Filter>
</ClInclude>
<ClInclude Include="editorkeyboardinput.h">
<Filter>Header Files\application\mode_editor</Filter>
</ClInclude>
<ClInclude Include="keyboardinput.h">
<Filter>Header Files\application\input</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="maszyna.rc">

View File

@@ -13,6 +13,10 @@ http://mozilla.org/MPL/2.0/.
#include "globals.h"
#include "application.h"
#include "simulation.h"
#include "simulationtime.h"
#include "event.h"
#include "dynobj.h"
#include "driver.h"
#include "mtable.h"
#include "logs.h"
@@ -36,6 +40,165 @@ Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam) {
#endif
}
void
OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
{ // odebranie komunikatu z serwera
if (pRozkaz->iSygn == MAKE_ID4('E','U','0','7') )
switch (pRozkaz->iComm)
{
case 0: // odesłanie identyfikatora wersji
CommLog( Now() + " " + std::to_string(pRozkaz->iComm) + " version" + " rcvd");
WyslijString(Global.asVersion, 0); // przedsatwienie się
break;
case 1: // odesłanie identyfikatora wersji
CommLog( Now() + " " + std::to_string(pRozkaz->iComm) + " scenery" + " rcvd");
WyslijString(Global.SceneryFile, 1); // nazwa scenerii
break;
case 2: {
// event
CommLog( Now() + " " + std::to_string( pRozkaz->iComm ) + " " +
std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) + " rcvd" );
if( Global.iMultiplayer ) {
auto *event = simulation::Events.FindEvent( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) );
if( event != nullptr ) {
if( ( event->Type == tp_Multiple )
|| ( event->Type == tp_Lights )
|| ( event->evJoined != 0 ) ) {
// tylko jawne albo niejawne Multiple
simulation::Events.AddToQuery( event, nullptr ); // drugi parametr to dynamic wywołujący - tu brak
}
}
}
break;
}
case 3: // rozkaz dla AI
if (Global.iMultiplayer)
{
int i = int(pRozkaz->cString[8]); // długość pierwszego łańcucha (z przodu dwa floaty)
CommLog(
Now() + " " + to_string(pRozkaz->iComm) + " " +
std::string(pRozkaz->cString + 11 + i, (unsigned)(pRozkaz->cString[10 + i])) +
" rcvd");
// nazwa pojazdu jest druga
auto *vehicle = simulation::Vehicles.find( { pRozkaz->cString + 11 + i, (unsigned)pRozkaz->cString[ 10 + i ] } );
if( ( vehicle != nullptr )
&& ( vehicle->Mechanik != nullptr ) ) {
vehicle->Mechanik->PutCommand(
{ pRozkaz->cString + 9, static_cast<std::size_t>(i) },
pRozkaz->fPar[0], pRozkaz->fPar[1],
nullptr,
stopExt ); // floaty są z przodu
WriteLog("AI command: " + std::string(pRozkaz->cString + 9, i));
}
}
break;
case 4: // badanie zajętości toru
{
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " +
std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd");
auto *track = simulation::Paths.find( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) );
if( ( track != nullptr )
&& ( track->IsEmpty() ) ) {
WyslijWolny( track->name() );
}
}
break;
case 5: // ustawienie parametrów
{
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " params " + to_string(*pRozkaz->iPar) + " rcvd");
if (*pRozkaz->iPar == 0) // sprawdzenie czasu
if (*pRozkaz->iPar & 1) // ustawienie czasu
{
double t = pRozkaz->fPar[1];
simulation::Time.data().wDay = std::floor(t); // niby nie powinno być dnia, ale...
if (Global.fMoveLight >= 0)
Global.fMoveLight = t; // trzeba by deklinację Słońca przeliczyć
simulation::Time.data().wHour = std::floor(24 * t) - 24.0 * simulation::Time.data().wDay;
simulation::Time.data().wMinute = std::floor(60 * 24 * t) - 60.0 * (24.0 * simulation::Time.data().wDay + simulation::Time.data().wHour);
simulation::Time.data().wSecond = std::floor( 60 * 60 * 24 * t ) - 60.0 * ( 60.0 * ( 24.0 * simulation::Time.data().wDay + simulation::Time.data().wHour ) + simulation::Time.data().wMinute );
}
if (*pRozkaz->iPar & 2)
{ // ustawienie flag zapauzowania
Global.iPause = pRozkaz->fPar[2]; // zakładamy, że wysyłający wie, co robi
}
}
break;
case 6: // pobranie parametrów ruchu pojazdu
if (Global.iMultiplayer) {
// Ra 2014-12: to ma działać również dla pojazdów bez obsady
CommLog(
Now() + " "
+ to_string( pRozkaz->iComm ) + " "
+ std::string{ pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) }
+ " rcvd" );
if (pRozkaz->cString[0]) {
// jeśli długość nazwy jest niezerowa szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #7
auto *vehicle = (
pRozkaz->cString[ 1 ] == '*' ?
simulation::Vehicles.find( Global.asHumanCtrlVehicle ) :
simulation::Vehicles.find( std::string{ pRozkaz->cString + 1, (unsigned)pRozkaz->cString[ 0 ] } ) );
if( vehicle != nullptr ) {
WyslijNamiary( vehicle ); // wysłanie informacji o pojeździe
}
}
else {
// dla pustego wysyłamy ramki 6 z nazwami pojazdów AI (jeśli potrzebne wszystkie, to rozpoznać np. "*")
simulation::Vehicles.DynamicList();
}
}
break;
case 8: // ponowne wysłanie informacji o zajętych odcinkach toru
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy track" + " rcvd");
simulation::Paths.TrackBusyList();
break;
case 9: // ponowne wysłanie informacji o zajętych odcinkach izolowanych
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy isolated" + " rcvd");
simulation::Paths.IsolatedBusyList();
break;
case 10: // badanie zajętości jednego odcinka izolowanego
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " +
std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd");
simulation::Paths.IsolatedBusy( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) );
break;
case 11: // ustawienie parametrów ruchu pojazdu
// Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0])));
break;
case 12: // skrocona ramka parametrow pojazdow AI (wszystkich!!)
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " obsadzone" + " rcvd");
WyslijObsadzone();
// Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0])));
break;
case 13: // ramka uszkodzenia i innych stanow pojazdu, np. wylaczenie CA, wlaczenie recznego itd.
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " +
std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) +
" rcvd");
if( pRozkaz->cString[ 1 ] ) // jeśli długość nazwy jest niezerowa
{ // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13
auto *lookup = (
pRozkaz->cString[ 2 ] == '*' ?
simulation::Vehicles.find( Global.asHumanCtrlVehicle ) : // nazwa pojazdu użytkownika
simulation::Vehicles.find( std::string( pRozkaz->cString + 2, (unsigned)pRozkaz->cString[ 1 ] ) ) ); // nazwa pojazdu
if( lookup == nullptr ) { break; } // nothing found, nothing to do
auto *d { lookup };
while( d != nullptr ) {
d->Damage( pRozkaz->cString[ 0 ] );
d = d->Next(); // pozostałe też
}
d = lookup->Prev();
while( d != nullptr ) {
d->Damage( pRozkaz->cString[ 0 ] );
d = d->Prev(); // w drugą stronę też
}
WyslijUszkodzenia( lookup->asName, lookup->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe
}
break;
default:
break;
}
}
void
WyslijEvent(const std::string &e, const std::string &d)
{ // Ra: jeszcze do wyczyszczenia

View File

@@ -37,6 +37,8 @@ struct DaneRozkaz2 { // struktura komunikacji z EU07.EXE
void Navigate( std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam );
void OnCommandGet( multiplayer::DaneRozkaz *pRozkaz );
void WyslijEvent( const std::string &e, const std::string &d );
void WyslijString( const std::string &t, int n );
void WyslijWolny( const std::string &t );

View File

@@ -12,15 +12,15 @@ http://mozilla.org/MPL/2.0/.
#include "renderer.h"
#include "color.h"
#include "globals.h"
#include "camera.h"
#include "timer.h"
#include "simulation.h"
#include "simulationtime.h"
#include "world.h"
#include "train.h"
#include "dynobj.h"
#include "animmodel.h"
#include "traction.h"
#include "uilayer.h"
#include "application.h"
#include "logs.h"
#include "utilities.h"
@@ -128,7 +128,7 @@ opengl_renderer::Init( GLFWwindow *Window ) {
std::vector<GLint>{ m_diffusetextureunit } :
std::vector<GLint>{ m_normaltextureunit, m_diffusetextureunit } );
m_textures.assign_units( m_helpertextureunit, m_shadowtextureunit, m_normaltextureunit, m_diffusetextureunit ); // TODO: add reflections unit
UILayer.set_unit( m_diffusetextureunit );
ui_layer::set_unit( m_diffusetextureunit );
select_unit( m_diffusetextureunit );
::glDepthFunc( GL_LEQUAL );
@@ -397,7 +397,7 @@ opengl_renderer::Render() {
Timer::subsystem.gfx_total.start(); // note: gfx_total is actually frame total, clean this up
Timer::subsystem.gfx_color.start();
// fetch simulation data
if( World.InitPerformed() ) {
if( simulation::is_ready ) {
m_sunlight = Global.DayLight;
// quantize sun angle to reduce shadow crawl
auto const quantizationstep { 0.004f };
@@ -453,7 +453,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
if( ( true == Global.RenderShadows )
&& ( false == Global.bWireFrame )
&& ( true == World.InitPerformed() )
&& ( true == simulation::is_ready )
&& ( m_shadowcolor != colors::white ) ) {
// run shadowmaps pass before color
@@ -492,7 +492,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
}
if( ( true == m_environmentcubetexturesupport )
&& ( true == World.InitPerformed() ) ) {
&& ( true == simulation::is_ready ) ) {
// potentially update environmental cube map
if( true == Render_reflections() ) {
setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack
@@ -501,8 +501,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
::glViewport( 0, 0, Global.iWindowWidth, Global.iWindowHeight );
if( World.InitPerformed() ) {
auto const skydomecolour = World.Environment.m_skydome.GetAverageColor();
if( simulation::is_ready ) {
auto const skydomecolour = simulation::Environment.m_skydome.GetAverageColor();
::glClearColor( skydomecolour.x, skydomecolour.y, skydomecolour.z, 0.f ); // kolor nieba
}
else {
@@ -510,13 +510,13 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
}
::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
if( World.InitPerformed() ) {
if( simulation::is_ready ) {
// setup
setup_matrices();
// render
setup_drawing( true );
setup_units( true, false, false );
Render( &World.Environment );
Render( &simulation::Environment );
// opaque parts...
setup_drawing( false );
setup_units( true, true, true );
@@ -544,7 +544,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix );
// cache shadow colour in case we need to account for cab light
auto const shadowcolor { m_shadowcolor };
auto const *vehicle{ World.Train->Dynamic() };
auto const *vehicle{ simulation::Train->Dynamic() };
if( vehicle->InteriorLightLevel > 0.f ) {
setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) );
}
@@ -565,7 +565,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix );
// cache shadow colour in case we need to account for cab light
auto const shadowcolor{ m_shadowcolor };
auto const *vehicle{ World.Train->Dynamic() };
auto const *vehicle{ simulation::Train->Dynamic() };
if( vehicle->InteriorLightLevel > 0.f ) {
setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) );
}
@@ -584,13 +584,13 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
::glMatrixMode( GL_MODELVIEW );
}
}
UILayer.render();
Application.render_ui();
break;
}
case rendermode::shadows: {
if( World.InitPerformed() ) {
if( simulation::is_ready ) {
// setup
::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting
::glPolygonOffset( 1.f, 1.f );
@@ -631,7 +631,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
case rendermode::cabshadows: {
if( ( World.InitPerformed() )
if( ( simulation::is_ready )
&& ( false == FreeFlyModeFlag ) ) {
// setup
::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting
@@ -659,8 +659,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
#else
setup_units( false, false, false );
#endif
Render_cab( World.Train->Dynamic(), false );
Render_cab( World.Train->Dynamic(), true );
Render_cab( simulation::Train->Dynamic(), false );
Render_cab( simulation::Train->Dynamic(), true );
m_cabshadowpass = m_renderpass;
// post-render restore
@@ -678,7 +678,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
::glClearColor( 0.f, 0.f, 0.f, 1.f );
::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
if( World.InitPerformed() ) {
if( simulation::is_ready ) {
::glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE );
// setup
@@ -686,7 +686,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
// render
setup_drawing( true );
setup_units( true, false, false );
Render( &World.Environment );
Render( &simulation::Environment );
// opaque parts...
setup_drawing( false );
setup_units( true, true, true );
@@ -708,7 +708,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
}
case rendermode::pickcontrols: {
if( World.InitPerformed() ) {
if( simulation::is_ready ) {
// setup
#ifdef EU07_USE_PICKING_FRAMEBUFFER
::glViewport( 0, 0, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE );
@@ -723,14 +723,14 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
setup_drawing( false );
setup_units( false, false, false );
// cab render skips translucent parts, so we can do it here
if( World.Train != nullptr ) { Render_cab( World.Train->Dynamic() ); }
if( simulation::Train != nullptr ) { Render_cab( simulation::Train->Dynamic() ); }
// post-render cleanup
}
break;
}
case rendermode::pickscenery: {
if( World.InitPerformed() ) {
if( simulation::is_ready ) {
// setup
m_picksceneryitems.clear();
#ifdef EU07_USE_PICKING_FRAMEBUFFER
@@ -789,12 +789,12 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f
Config.draw_mode = Mode;
if( false == World.InitPerformed() ) { return; }
if( false == simulation::is_ready ) { return; }
// setup draw range
switch( Mode ) {
case rendermode::color: { Config.draw_range = Global.BaseDrawRange; break; }
case rendermode::shadows: { Config.draw_range = Global.BaseDrawRange * 0.5f; break; }
case rendermode::cabshadows: { Config.draw_range = ( Global.pWorld->train()->Dynamic()->MoverParameters->ActiveCab != 0 ? 10.f : 20.f ); break; }
case rendermode::cabshadows: { Config.draw_range = ( simulation::Train->Occupied()->ActiveCab != 0 ? 10.f : 20.f ); break; }
case rendermode::reflections: { Config.draw_range = Global.BaseDrawRange; break; }
case rendermode::pickcontrols: { Config.draw_range = 50.f; break; }
case rendermode::pickscenery: { Config.draw_range = Global.BaseDrawRange * 0.5f; break; }
@@ -811,11 +811,11 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f
// modelview
if( ( false == DebugCameraFlag ) || ( true == Ignoredebug ) ) {
camera.position() = Global.pCameraPosition;
World.Camera.SetMatrix( viewmatrix );
Global.pCamera->SetMatrix( viewmatrix );
}
else {
camera.position() = Global.DebugCameraPosition;
World.DebugCamera.SetMatrix( viewmatrix );
Global.pDebugCamera->SetMatrix( viewmatrix );
}
// projection
auto const zfar = Config.draw_range * Global.fDistanceFactor * Zfar;
@@ -972,7 +972,7 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f
// TODO: scissor test for pick modes
// modelview
camera.position() = Global.pCameraPosition;
World.Camera.SetMatrix( viewmatrix );
Global.pCamera->SetMatrix( viewmatrix );
// projection
camera.projection() *=
glm::perspective(
@@ -1670,7 +1670,7 @@ opengl_renderer::Render( scene::basic_region *Region ) {
Update_Lights( simulation::Lights );
Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) );
if( EditorModeFlag && FreeFlyModeFlag ) {
if( EditorModeFlag ) {
// when editor mode is active calculate world position of the cursor
// at this stage the z-buffer is filled with only ground geometry
Update_Mouse_Position();
@@ -2099,7 +2099,7 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) {
++m_debugstats.dynamics;
// setup
TSubModel::iInstance = ( size_t )Dynamic; //żeby nie robić cudzych animacji
TSubModel::iInstance = reinterpret_cast<std::uintptr_t>( Dynamic ); //żeby nie robić cudzych animacji
glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position();
// lod visibility ranges are defined for base (x 1.0) viewing distance. for render we adjust them for actual range multiplier and zoom
float squaredistance;
@@ -2201,7 +2201,7 @@ opengl_renderer::Render_cab( TDynamicObject const *Dynamic, bool const Alpha ) {
return false;
}
TSubModel::iInstance = reinterpret_cast<std::size_t>( Dynamic );
TSubModel::iInstance = reinterpret_cast<std::uintptr_t>( Dynamic );
if( ( true == FreeFlyModeFlag )
|| ( false == Dynamic->bDisplayCab )
@@ -3487,7 +3487,8 @@ glm::dvec3
opengl_renderer::Update_Mouse_Position() {
glm::dvec2 mousepos;
glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y );
Application.get_cursor_pos( mousepos.x, mousepos.y );
// glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y );
mousepos.x = clamp<int>( mousepos.x, 0, Global.iWindowWidth - 1 );
mousepos.y = clamp<int>( Global.iWindowHeight - clamp<int>( mousepos.y, 0, Global.iWindowHeight ), 0, Global.iWindowHeight - 1 ) ;
GLfloat pointdepth;
@@ -3550,7 +3551,7 @@ opengl_renderer::Update( double const Deltatime ) {
}
if( ( true == Global.ResourceSweep )
&& ( true == World.InitPerformed() ) ) {
&& ( true == simulation::is_ready ) ) {
// garbage collection
m_geometry.update();
m_textures.update();

View File

@@ -17,7 +17,7 @@ http://mozilla.org/MPL/2.0/.
#include "dumb3d.h"
#include "frustum.h"
#include "scene.h"
#include "world.h"
#include "simulationenvironment.h"
#include "memcell.h"
#define EU07_USE_PICKING_FRAMEBUFFER

79
scenarioloadermode.cpp Normal file
View File

@@ -0,0 +1,79 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "scenarioloadermode.h"
#include "globals.h"
#include "simulation.h"
#include "simulationtime.h"
#include "timer.h"
#include "application.h"
#include "scenarioloaderuilayer.h"
#include "renderer.h"
#include "logs.h"
scenarioloader_mode::scenarioloader_mode() {
m_userinterface = std::make_shared<scenarioloader_ui>();
}
// initializes internal data structures of the mode. returns: true on success, false otherwise
bool
scenarioloader_mode::init() {
// nothing to do here
return true;
}
// mode-specific update of simulation data. returns: false on error, true otherwise
bool
scenarioloader_mode::update() {
WriteLog( "\nLoading scenario..." );
auto timestart = std::chrono::system_clock::now();
if( true == simulation::State.deserialize( Global.SceneryFile ) ) {
WriteLog( "Scenario loading time: " + std::to_string( std::chrono::duration_cast<std::chrono::seconds>( ( std::chrono::system_clock::now() - timestart ) ).count() ) + " seconds" );
// TODO: implement and use next mode cue
Application.pop_mode();
Application.push_mode( eu07_application::mode::driver );
}
else {
ErrorLog( "Bad init: scenario loading failed" );
Application.pop_mode();
}
return true;
}
// maintenance method, called when the mode is activated
void
scenarioloader_mode::enter() {
// TBD: hide cursor in fullscreen mode?
Application.set_cursor( GLFW_CURSOR_NORMAL );
simulation::is_ready = false;
m_userinterface->set_background( "logo" );
Application.set_title( Global.AppName + " (" + Global.SceneryFile + ")" );
m_userinterface->set_progress();
m_userinterface->set_progress( "Loading scenery / Wczytywanie scenerii" );
GfxRenderer.Render();
}
// maintenance method, called when the mode is deactivated
void
scenarioloader_mode::exit() {
simulation::Time.init();
simulation::Environment.init();
}

41
scenarioloadermode.h Normal file
View File

@@ -0,0 +1,41 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "applicationmode.h"
class scenarioloader_mode : public application_mode {
public:
// constructors
scenarioloader_mode();
// methods
// initializes internal data structures of the mode. returns: true on success, false otherwise
bool
init() override;
// mode-specific update of simulation data. returns: false on error, true otherwise
bool
update() override;
// maintenance method, called when the mode is activated
void
enter() override;
// maintenance method, called when the mode is deactivated
void
exit() override;
// input handlers
void
on_key( int const Key, int const Scancode, int const Action, int const Mods ) override { ; }
void
on_cursor_pos( double const Horizontal, double const Vertical ) override { ; }
void
on_mouse_button( int const Button, int const Action, int const Mods ) override { ; }
void
on_scroll( double const Xoffset, double const Yoffset ) override { ; }
};

17
scenarioloaderuilayer.h Normal file
View File

@@ -0,0 +1,17 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "uilayer.h"
class scenarioloader_ui : public ui_layer {
// TODO: implement mode-specific elements
};

View File

@@ -12,6 +12,9 @@ http://mozilla.org/MPL/2.0/.
#include "simulation.h"
#include "globals.h"
#include "animmodel.h"
#include "event.h"
#include "evlaunch.h"
#include "timer.h"
#include "logs.h"
#include "sn_utils.h"

View File

@@ -13,97 +13,19 @@ http://mozilla.org/MPL/2.0/.
#include "globals.h"
#include "application.h"
#include "simulation.h"
#include "camera.h"
#include "animmodel.h"
#include "renderer.h"
namespace scene {
basic_editor Editor;
bool
basic_editor::on_key( int const Key, int const Action ) {
if( false == EditorModeFlag ) { return false; }
if( ( Key == GLFW_KEY_LEFT_ALT )
|| ( Key == GLFW_KEY_RIGHT_ALT ) ) {
// intercept these while in editor mode
return true;
}
return false;
}
bool
basic_editor::on_mouse_button( int const Button, int const Action ) {
if( false == EditorModeFlag ) { return false; }
// TBD: automatically activate and enforce freefly mode when editor is active?
if( false == FreeFlyModeFlag ) { return false; }
if( Button == GLFW_MOUSE_BUTTON_LEFT ) {
if( Action == GLFW_PRESS ) {
m_node = GfxRenderer.Update_Pick_Node();
m_nodesnapshot = { m_node };
if( m_node ) {
Application.set_cursor( GLFW_CURSOR_DISABLED );
}
}
else {
// left button release
// TODO: record the current undo step on the undo stack
m_nodesnapshot = { m_node };
if( m_node ) {
Application.set_cursor( GLFW_CURSOR_NORMAL );
}
}
m_mouseleftbuttondown = ( Action == GLFW_PRESS );
return ( m_node != nullptr );
}
return false;
}
bool
basic_editor::on_mouse_move( double const Mousex, double const Mousey ) {
auto const mousemove { glm::dvec2{ Mousex, Mousey } - m_mouseposition };
m_mouseposition = { Mousex, Mousey };
if( false == EditorModeFlag ) { return false; }
if( false == m_mouseleftbuttondown ) { return false; }
if( m_node == nullptr ) { return false; }
if( mode_translation() ) {
// move selected node
if( mode_translation_vertical() ) {
auto const translation { mousemove.y * -0.01f };
translate( translation );
}
else {
auto const mouseworldposition{ Global.pCamera->Pos + GfxRenderer.Mouse_Position() };
translate( mouseworldposition );
}
}
else {
// rotate selected node
auto const rotation { glm::vec3 { mousemove.y, mousemove.x, 0 } * 0.25f };
rotate( rotation );
}
return true;
}
void
basic_editor::translate( glm::dvec3 const &Location ) {
basic_editor::translate( scene::basic_node *Node, glm::dvec3 const &Location, bool const Snaptoground ) {
auto *node { m_node }; // placeholder for operations on multiple nodes
auto *node { Node }; // placeholder for operations on multiple nodes
auto location { Location };
if( false == mode_snap() ) {
if( false == Snaptoground ) {
location.y = node->location().y;
}
@@ -117,14 +39,14 @@ basic_editor::translate( glm::dvec3 const &Location ) {
}
void
basic_editor::translate( float const Offset ) {
basic_editor::translate( scene::basic_node *Node, float const Offset ) {
// NOTE: offset scaling is calculated early so the same multiplier can be applied to potential whole group
auto location { m_node->location() };
auto location { Node->location() };
auto const distance { glm::length( location - glm::dvec3{ Global.pCamera->Pos } ) };
auto const offset { Offset * std::max( 1.0, distance * 0.01 ) };
auto *node { m_node }; // placeholder for operations on multiple nodes
auto *node { Node }; // placeholder for operations on multiple nodes
if( typeid( *node ) == typeid( TAnimModel ) ) {
translate_instance( static_cast<TAnimModel *>( node ), offset );
@@ -167,47 +89,29 @@ basic_editor::translate_memorycell( TMemCell *Memorycell, float const Offset ) {
}
void
basic_editor::rotate( glm::vec3 const &Angle ) {
basic_editor::rotate( scene::basic_node *Node, glm::vec3 const &Angle, float const Quantization ) {
auto *node { m_node }; // placeholder for operations on multiple nodes
auto *node { Node }; // placeholder for operations on multiple nodes
if( typeid( *node ) == typeid( TAnimModel ) ) {
rotate_instance( static_cast<TAnimModel *>( node ), Angle );
rotate_instance( static_cast<TAnimModel *>( node ), Angle, Quantization );
}
}
void
basic_editor::rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle ) {
basic_editor::rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle, float const Quantization ) {
// adjust node data
glm::vec3 angle = glm::dvec3 { Instance->Angles() };
angle.y = clamp_circular( angle.y + Angle.y, 360.f );
if( mode_snap() ) {
if( Quantization > 0.f ) {
// TBD, TODO: adjustable quantization step
angle.y = quantize( angle.y, 15.f );
angle.y = quantize( angle.y, Quantization );
}
Instance->Angles( angle );
// update scene
}
bool
basic_editor::mode_translation() const {
return ( false == Global.altState );
}
bool
basic_editor::mode_translation_vertical() const {
return ( true == Global.shiftState );
}
bool
basic_editor::mode_snap() const {
return ( true == Global.ctrlState );
}
} // scene
//---------------------------------------------------------------------------

View File

@@ -13,21 +13,7 @@ http://mozilla.org/MPL/2.0/.
namespace scene {
class basic_editor {
public:
// methods
bool
on_key( int const Key, int const Action );
bool
on_mouse_button( int const Button, int const Action );
bool
on_mouse_move( double const Mousex, double const Mousey );
scene::basic_node const *
node() const {
return m_node; }
private:
// types
// TODO: move the snapshot to history stack
struct node_snapshot {
scene::basic_node *node;
@@ -38,19 +24,18 @@ private:
if( Node != nullptr ) {
Node->export_as_text( data ); } };
};
friend bool operator==( basic_editor::node_snapshot const &Left, basic_editor::node_snapshot const &Right );
friend bool operator!=( basic_editor::node_snapshot const &Left, basic_editor::node_snapshot const &Right );
inline bool operator==( node_snapshot const &Left, node_snapshot const &Right ) { return ( ( Left.node == Right.node ) && ( Left.data == Right.data ) ); }
inline bool operator!=( node_snapshot const &Left, node_snapshot const &Right ) { return ( !( Left == Right ) ); }
class basic_editor {
public:
// methods
bool
mode_translation() const;
bool
mode_translation_vertical() const;
bool
mode_snap() const;
void
translate( glm::dvec3 const &Location );
translate( scene::basic_node *Node, glm::dvec3 const &Location, bool const Snaptoground );
void
translate( float const Offset );
translate( scene::basic_node *Node, float const Offset );
void
translate_instance( TAnimModel *Instance, glm::dvec3 const &Location );
void
@@ -60,22 +45,11 @@ private:
void
translate_memorycell( TMemCell *Memorycell, float const Offset );
void
rotate( glm::vec3 const &Angle );
rotate( scene::basic_node *Node, glm::vec3 const &Angle, float const Quantization );
void
rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle );
// members
scene::basic_node *m_node; // temporary helper, currently selected scene node
node_snapshot m_nodesnapshot { nullptr }; // currently selected scene node in its pre-modified state
glm::dvec2 m_mouseposition { 0.0 };
bool m_mouseleftbuttondown { false };
rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle, float const Quantization );
};
inline bool operator==( basic_editor::node_snapshot const &Left, basic_editor::node_snapshot const &Right ) { return ( ( Left.node == Right.node ) && ( Left.data == Right.data ) ); }
inline bool operator!=( basic_editor::node_snapshot const &Left, basic_editor::node_snapshot const &Right ) { return ( !( Left == Right ) ); }
extern basic_editor Editor;
} // scene
//---------------------------------------------------------------------------

View File

@@ -9,13 +9,20 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "simulation.h"
#include "simulationtime.h"
#include "globals.h"
#include "simulationtime.h"
#include "uilayer.h"
#include "renderer.h"
#include "logs.h"
#include "event.h"
#include "memcell.h"
#include "track.h"
#include "traction.h"
#include "tractionpower.h"
#include "sound.h"
#include "animmodel.h"
#include "dynobj.h"
#include "lightarray.h"
#include "scene.h"
#include "train.h"
namespace simulation {
@@ -31,8 +38,9 @@ vehicle_table Vehicles;
light_array Lights;
scene::basic_region *Region { nullptr };
TTrain *Train { nullptr };
bool is_ready { false };
bool
state_manager::deserialize( std::string const &Scenariofile ) {
@@ -63,909 +71,27 @@ state_manager::update( double const Deltatime, int Iterationcount ) {
simulation::Vehicles.update( Deltatime, Iterationcount );
}
bool
state_serializer::deserialize( std::string const &Scenariofile ) {
// TODO: move initialization to separate routine so we can reuse it
SafeDelete( Region );
Region = new scene::basic_region();
// TODO: check first for presence of serialized binary files
// if this fails, fall back on the legacy text format
scene::scratch_data importscratchpad;
if( Scenariofile != "$.scn" ) {
// compilation to binary file isn't supported for rainsted-created overrides
importscratchpad.binary.terrain = Region->deserialize( Scenariofile );
}
// NOTE: for the time being import from text format is a given, since we don't have full binary serialization
cParser scenarioparser( Scenariofile, cParser::buffer_FILE, Global.asCurrentSceneryPath, Global.bLoadTraction );
if( false == scenarioparser.ok() ) { return false; }
deserialize( scenarioparser, importscratchpad );
if( ( false == importscratchpad.binary.terrain )
&& ( Scenariofile != "$.scn" ) ) {
// if we didn't find usable binary version of the scenario files, create them now for future use
// as long as the scenario file wasn't rainsted-created base file override
Region->serialize( Scenariofile );
}
return true;
}
// restores class data from provided stream
void
state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) {
state_manager::update_clocks() {
// prepare deserialization function table
// since all methods use the same objects, we can have simple, hard-coded binds or lambdas for the task
using deserializefunction = void( state_serializer::*)(cParser &, scene::scratch_data &);
std::vector<
std::pair<
std::string,
deserializefunction> > functionlist = {
{ "atmo", &state_serializer::deserialize_atmo },
{ "camera", &state_serializer::deserialize_camera },
{ "config", &state_serializer::deserialize_config },
{ "description", &state_serializer::deserialize_description },
{ "event", &state_serializer::deserialize_event },
{ "firstinit", &state_serializer::deserialize_firstinit },
{ "light", &state_serializer::deserialize_light },
{ "node", &state_serializer::deserialize_node },
{ "origin", &state_serializer::deserialize_origin },
{ "endorigin", &state_serializer::deserialize_endorigin },
{ "rotate", &state_serializer::deserialize_rotate },
{ "sky", &state_serializer::deserialize_sky },
{ "test", &state_serializer::deserialize_test },
{ "time", &state_serializer::deserialize_time },
{ "trainset", &state_serializer::deserialize_trainset },
{ "endtrainset", &state_serializer::deserialize_endtrainset } };
using deserializefunctionbind = std::function<void()>;
std::unordered_map<
std::string,
deserializefunctionbind> functionmap;
for( auto &function : functionlist ) {
functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( Scratchpad ) ) );
}
// deserialize content from the provided input
auto
timelast { std::chrono::steady_clock::now() },
timenow { timelast };
std::string token { Input.getToken<std::string>() };
while( false == token.empty() ) {
auto lookup = functionmap.find( token );
if( lookup != functionmap.end() ) {
lookup->second();
}
else {
ErrorLog( "Bad scenario: unexpected token \"" + token + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
}
timenow = std::chrono::steady_clock::now();
if( std::chrono::duration_cast<std::chrono::milliseconds>( timenow - timelast ).count() >= 200 ) {
timelast = timenow;
glfwPollEvents();
UILayer.set_progress( Input.getProgress(), Input.getFullProgress() );
GfxRenderer.Render();
}
token = Input.getToken<std::string>();
}
if( false == Scratchpad.initialized ) {
// manually perform scenario initialization
deserialize_firstinit( Input, Scratchpad );
}
// Ra 2014-07: przeliczenie kąta czasu (do animacji zależnych od czasu)
auto const &time = simulation::Time.data();
Global.fTimeAngleDeg = time.wHour * 15.0 + time.wMinute * 0.25 + ( ( time.wSecond + 0.001 * time.wMilliseconds ) / 240.0 );
Global.fClockAngleDeg[ 0 ] = 36.0 * ( time.wSecond % 10 ); // jednostki sekund
Global.fClockAngleDeg[ 1 ] = 36.0 * ( time.wSecond / 10 ); // dziesiątki sekund
Global.fClockAngleDeg[ 2 ] = 36.0 * ( time.wMinute % 10 ); // jednostki minut
Global.fClockAngleDeg[ 3 ] = 36.0 * ( time.wMinute / 10 ); // dziesiątki minut
Global.fClockAngleDeg[ 4 ] = 36.0 * ( time.wHour % 10 ); // jednostki godzin
Global.fClockAngleDeg[ 5 ] = 36.0 * ( time.wHour / 10 ); // dziesiątki godzin
}
// passes specified sound to all vehicles within range as a radio message broadcasted on specified channel
void
state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ) {
radio_message( sound_source *Message, int const Channel ) {
// NOTE: parameter system needs some decent replacement, but not worth the effort if we're moving to built-in editor
// atmosphere color; legacy parameter, no longer used
Input.getTokens( 3 );
// fog range
Input.getTokens( 2 );
Input
>> Global.fFogStart
>> Global.fFogEnd;
if( Global.fFogEnd > 0.0 ) {
// fog colour; optional legacy parameter, no longer used
Input.getTokens( 3 );
}
std::string token { Input.getToken<std::string>() };
if( token != "endatmo" ) {
// optional overcast parameter
Global.Overcast = clamp( std::stof( token ), 0.f, 2.f );
// overcast drives weather so do a calculation here
// NOTE: ugly, clean it up when we're done with world refactoring
Global.pWorld->compute_weather();
}
while( ( false == token.empty() )
&& ( token != "endatmo" ) ) {
// anything else left in the section has no defined meaning
token = Input.getToken<std::string>();
}
}
void
state_serializer::deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad ) {
glm::dvec3 xyz, abc;
int i = -1, into = -1; // do której definicji kamery wstawić
std::string token;
do { // opcjonalna siódma liczba określa numer kamery, a kiedyś były tylko 3
Input.getTokens();
Input >> token;
switch( ++i ) { // kiedyś camera miało tylko 3 współrzędne
case 0: { xyz.x = atof( token.c_str() ); break; }
case 1: { xyz.y = atof( token.c_str() ); break; }
case 2: { xyz.z = atof( token.c_str() ); break; }
case 3: { abc.x = atof( token.c_str() ); break; }
case 4: { abc.y = atof( token.c_str() ); break; }
case 5: { abc.z = atof( token.c_str() ); break; }
case 6: { into = atoi( token.c_str() ); break; } // takie sobie, bo można wpisać -1
default: { break; }
}
} while( token.compare( "endcamera" ) != 0 );
if( into < 0 )
into = ++Global.iCameraLast;
if( into < 10 ) { // przepisanie do odpowiedniego miejsca w tabelce
Global.FreeCameraInit[ into ] = xyz;
Global.FreeCameraInitAngle[ into ] =
Math3D::vector3(
glm::radians( abc.x ),
glm::radians( abc.y ),
glm::radians( abc.z ) );
Global.iCameraLast = into; // numer ostatniej
}
/*
// cleaned up version of the above.
// NOTE: no longer supports legacy mode where some parameters were optional
Input.getTokens( 7 );
glm::vec3
position,
rotation;
int index;
Input
>> position.x
>> position.y
>> position.z
>> rotation.x
>> rotation.y
>> rotation.z
>> index;
skip_until( Input, "endcamera" );
// TODO: finish this
*/
}
void
state_serializer::deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ) {
// config parameters (re)definition
Global.ConfigParse( Input );
}
void
state_serializer::deserialize_description( cParser &Input, scene::scratch_data &Scratchpad ) {
// legacy section, never really used;
skip_until( Input, "enddescription" );
}
void
state_serializer::deserialize_event( cParser &Input, scene::scratch_data &Scratchpad ) {
// TODO: refactor event class and its de/serialization. do offset and rotation after deserialization is done
auto *event = new TEvent();
Math3D::vector3 offset = (
Scratchpad.location.offset.empty() ?
Math3D::vector3() :
Math3D::vector3(
Scratchpad.location.offset.top().x,
Scratchpad.location.offset.top().y,
Scratchpad.location.offset.top().z ) );
event->Load( &Input, offset );
if( false == simulation::Events.insert( event ) ) {
delete event;
}
}
void
state_serializer::deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) {
if( true == Scratchpad.initialized ) { return; }
simulation::Paths.InitTracks();
simulation::Traction.InitTraction();
simulation::Events.InitEvents();
simulation::Events.InitLaunchers();
simulation::Memory.InitCells();
Scratchpad.initialized = true;
}
void
state_serializer::deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ) {
// legacy section, no longer used nor supported;
skip_until( Input, "endlight" );
}
void
state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ) {
auto const inputline = Input.Line(); // cache in case we need to report error
scene::node_data nodedata;
// common data and node type indicator
Input.getTokens( 4 );
Input
>> nodedata.range_max
>> nodedata.range_min
>> nodedata.name
>> nodedata.type;
// type-based deserialization. not elegant but it'll do
if( nodedata.type == "dynamic" ) {
auto *vehicle { deserialize_dynamic( Input, Scratchpad, nodedata ) };
// vehicle import can potentially fail
if( vehicle == nullptr ) { return; }
if( false == simulation::Vehicles.insert( vehicle ) ) {
ErrorLog( "Bad scenario: vehicle with duplicate name \"" + vehicle->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
if( ( vehicle->MoverParameters->CategoryFlag == 1 ) // trains only
&& ( ( ( vehicle->LightList( side::front ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 )
|| ( ( vehicle->LightList( side::rear ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 ) ) ) {
simulation::Lights.insert( vehicle );
}
}
else if( nodedata.type == "track" ) {
auto *path { deserialize_path( Input, Scratchpad, nodedata ) };
// duplicates of named tracks are currently experimentally allowed
if( false == simulation::Paths.insert( path ) ) {
ErrorLog( "Bad scenario: track with duplicate name \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
/*
delete path;
delete pathnode;
*/
}
simulation::Region->insert_and_register( path );
}
else if( nodedata.type == "traction" ) {
auto *traction { deserialize_traction( Input, Scratchpad, nodedata ) };
// traction loading is optional
if( traction == nullptr ) { return; }
if( false == simulation::Traction.insert( traction ) ) {
ErrorLog( "Bad scenario: traction piece with duplicate name \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert_and_register( traction );
}
else if( nodedata.type == "tractionpowersource" ) {
auto *powersource { deserialize_tractionpowersource( Input, Scratchpad, nodedata ) };
// traction loading is optional
if( powersource == nullptr ) { return; }
if( false == simulation::Powergrid.insert( powersource ) ) {
ErrorLog( "Bad scenario: power grid source with duplicate name \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
/*
// TODO: implement this
simulation::Region.insert_powersource( powersource, Scratchpad );
*/
}
else if( nodedata.type == "model" ) {
if( nodedata.range_min < 0.0 ) {
// 3d terrain
if( false == Scratchpad.binary.terrain ) {
// if we're loading data from text .scn file convert and import
auto *instance { deserialize_model( Input, Scratchpad, nodedata ) };
// model import can potentially fail
if( instance == nullptr ) { return; }
// go through submodels, and import them as shapes
auto const cellcount = instance->TerrainCount() + 1; // zliczenie submodeli
for( auto i = 1; i < cellcount; ++i ) {
auto *submodel = instance->TerrainSquare( i - 1 );
simulation::Region->insert(
scene::shape_node().convert( submodel ),
Scratchpad,
false );
// if there's more than one group of triangles in the cell they're held as children of the primary submodel
submodel = submodel->ChildGet();
while( submodel != nullptr ) {
simulation::Region->insert(
scene::shape_node().convert( submodel ),
Scratchpad,
false );
submodel = submodel->NextGet();
}
}
// with the import done we can get rid of the source model
delete instance;
}
else {
// if binary terrain file was present, we already have this data
skip_until( Input, "endmodel" );
}
}
else {
// regular instance of 3d mesh
auto *instance { deserialize_model( Input, Scratchpad, nodedata ) };
// model import can potentially fail
if( instance == nullptr ) { return; }
if( false == simulation::Instances.insert( instance ) ) {
ErrorLog( "Bad scenario: 3d model instance with duplicate name \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert( instance );
}
}
else if( ( nodedata.type == "triangles" )
|| ( nodedata.type == "triangle_strip" )
|| ( nodedata.type == "triangle_fan" ) ) {
if( false == Scratchpad.binary.terrain ) {
simulation::Region->insert(
scene::shape_node().import(
Input, nodedata ),
Scratchpad,
true );
}
else {
// all shapes were already loaded from the binary version of the file
skip_until( Input, "endtri" );
}
}
else if( ( nodedata.type == "lines" )
|| ( nodedata.type == "line_strip" )
|| ( nodedata.type == "line_loop" ) ) {
if( false == Scratchpad.binary.terrain ) {
simulation::Region->insert(
scene::lines_node().import(
Input, nodedata ),
Scratchpad );
}
else {
// all lines were already loaded from the binary version of the file
skip_until( Input, "endline" );
}
}
else if( nodedata.type == "memcell" ) {
auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) };
if( false == simulation::Memory.insert( memorycell ) ) {
ErrorLog( "Bad scenario: memory memorycell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert( memorycell );
}
else if( nodedata.type == "eventlauncher" ) {
auto *eventlauncher { deserialize_eventlauncher( Input, Scratchpad, nodedata ) };
if( false == simulation::Events.insert( eventlauncher ) ) {
ErrorLog( "Bad scenario: event launcher with duplicate name \"" + eventlauncher->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
// event launchers can be either global, or local with limited range of activation
// each gets assigned different caretaker
if( true == eventlauncher->IsGlobal() ) {
simulation::Events.queue( eventlauncher );
}
else {
simulation::Region->insert( eventlauncher );
}
}
else if( nodedata.type == "sound" ) {
auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) };
if( false == simulation::Sounds.insert( sound ) ) {
ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert( sound );
}
}
void
state_serializer::deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ) {
glm::dvec3 offset;
Input.getTokens( 3 );
Input
>> offset.x
>> offset.y
>> offset.z;
// sumowanie całkowitego przesunięcia
Scratchpad.location.offset.emplace(
offset + (
Scratchpad.location.offset.empty() ?
glm::dvec3() :
Scratchpad.location.offset.top() ) );
}
void
state_serializer::deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ) {
if( false == Scratchpad.location.offset.empty() ) {
Scratchpad.location.offset.pop();
}
else {
ErrorLog( "Bad origin: endorigin instruction with empty origin stack in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
}
}
void
state_serializer::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ) {
Input.getTokens( 3 );
Input
>> Scratchpad.location.rotation.x
>> Scratchpad.location.rotation.y
>> Scratchpad.location.rotation.z;
}
void
state_serializer::deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ) {
// sky model
Input.getTokens( 1 );
Input
>> Global.asSky;
// anything else left in the section has no defined meaning
skip_until( Input, "endsky" );
}
void
state_serializer::deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ) {
// legacy section, no longer supported;
skip_until( Input, "endtest" );
}
void
state_serializer::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ) {
// current scenario time
cParser timeparser( Input.getToken<std::string>() );
timeparser.getTokens( 2, false, ":" );
auto &time = simulation::Time.data();
timeparser
>> time.wHour
>> time.wMinute;
if( true == Global.ScenarioTimeCurrent ) {
// calculate time shift required to match scenario time with local clock
auto timenow = std::time( 0 );
auto const *localtime = std::localtime( &timenow );
Global.ScenarioTimeOffset = ( ( localtime->tm_hour * 60 + localtime->tm_min ) - ( time.wHour * 60 + time.wMinute ) ) / 60.f;
if( Train != nullptr ) {
Train->radio_message( Message, Channel );
}
// remaining sunrise and sunset parameters are no longer used, as they're now calculated dynamically
// anything else left in the section has no defined meaning
skip_until( Input, "endtime" );
}
void
state_serializer::deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ) {
if( true == Scratchpad.trainset.is_open ) {
// shouldn't happen but if it does wrap up currently open trainset and report an error
deserialize_endtrainset( Input, Scratchpad );
ErrorLog( "Bad scenario: encountered nested trainset definitions in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() ) + ")" );
}
Scratchpad.trainset = scene::scratch_data::trainset_data();
Scratchpad.trainset.is_open = true;
Input.getTokens( 4 );
Input
>> Scratchpad.trainset.name
>> Scratchpad.trainset.track
>> Scratchpad.trainset.offset
>> Scratchpad.trainset.velocity;
}
void
state_serializer::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) {
if( ( false == Scratchpad.trainset.is_open )
|| ( true == Scratchpad.trainset.vehicles.empty() ) ) {
// not bloody likely but we better check for it just the same
ErrorLog( "Bad trainset: empty trainset defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
Scratchpad.trainset.is_open = false;
return;
}
std::size_t vehicleindex { 0 };
for( auto *vehicle : Scratchpad.trainset.vehicles ) {
// go through list of vehicles in the trainset, coupling them together and checking for potential driver
if( ( vehicle->Mechanik != nullptr )
&& ( vehicle->Mechanik->Primary() ) ) {
// primary driver will receive the timetable for this trainset
Scratchpad.trainset.driver = vehicle;
}
if( vehicleindex > 0 ) {
// from second vehicle on couple it with the previous one
Scratchpad.trainset.vehicles[ vehicleindex - 1 ]->AttachPrev(
vehicle,
Scratchpad.trainset.couplings[ vehicleindex - 1 ] );
}
++vehicleindex;
}
if( Scratchpad.trainset.driver != nullptr ) {
// if present, send timetable to the driver
// wysłanie komendy "Timetable" ustawia odpowiedni tryb jazdy
auto *controller = Scratchpad.trainset.driver->Mechanik;
controller->DirectionInitial();
controller->PutCommand(
"Timetable:" + Scratchpad.trainset.name,
Scratchpad.trainset.velocity,
0,
nullptr );
}
if( Scratchpad.trainset.couplings.back() == coupling::faux ) {
// jeśli ostatni pojazd ma sprzęg 0 to założymy mu końcówki blaszane (jak AI się odpali, to sobie poprawi)
Scratchpad.trainset.vehicles.back()->RaLightsSet( -1, light::rearendsignals );
}
// all done
Scratchpad.trainset.is_open = false;
}
// creates path and its wrapper, restoring class data from provided stream
TTrack *
state_serializer::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
// TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done
auto *track = new TTrack( Nodedata );
auto const offset { (
Scratchpad.location.offset.empty() ?
glm::dvec3 { 0.0 } :
glm::dvec3 {
Scratchpad.location.offset.top().x,
Scratchpad.location.offset.top().y,
Scratchpad.location.offset.top().z } ) };
track->Load( &Input, offset );
return track;
}
TTraction *
state_serializer::deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
if( false == Global.bLoadTraction ) {
skip_until( Input, "endtraction" );
return nullptr;
}
// TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done
auto *traction = new TTraction( Nodedata );
auto offset = (
Scratchpad.location.offset.empty() ?
glm::dvec3() :
Scratchpad.location.offset.top() );
traction->Load( &Input, offset );
return traction;
}
TTractionPowerSource *
state_serializer::deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
if( false == Global.bLoadTraction ) {
skip_until( Input, "end" );
return nullptr;
}
auto *powersource = new TTractionPowerSource( Nodedata );
powersource->Load( &Input );
// adjust location
powersource->location( transform( powersource->location(), Scratchpad ) );
return powersource;
}
TMemCell *
state_serializer::deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
auto *memorycell = new TMemCell( Nodedata );
memorycell->Load( &Input );
// adjust location
memorycell->location( transform( memorycell->location(), Scratchpad ) );
return memorycell;
}
TEventLauncher *
state_serializer::deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
glm::dvec3 location;
Input.getTokens( 3 );
Input
>> location.x
>> location.y
>> location.z;
auto *eventlauncher = new TEventLauncher( Nodedata );
eventlauncher->Load( &Input );
eventlauncher->location( transform( location, Scratchpad ) );
return eventlauncher;
}
TAnimModel *
state_serializer::deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
glm::dvec3 location;
glm::vec3 rotation;
Input.getTokens( 4 );
Input
>> location.x
>> location.y
>> location.z
>> rotation.y;
auto *instance = new TAnimModel( Nodedata );
instance->Angles( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii
if( instance->Load( &Input, false ) ) {
instance->location( transform( location, Scratchpad ) );
}
else {
// model nie wczytał się - ignorowanie node
SafeDelete( instance );
}
return instance;
}
TDynamicObject *
state_serializer::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
if( false == Scratchpad.trainset.is_open ) {
// part of trainset data is used when loading standalone vehicles, so clear it just in case
Scratchpad.trainset = scene::scratch_data::trainset_data();
}
auto const inputline { Input.Line() }; // cache in case of errors
// basic attributes
auto const datafolder { Input.getToken<std::string>() };
auto const skinfile { Input.getToken<std::string>() };
auto const mmdfile { Input.getToken<std::string>() };
auto const pathname = (
Scratchpad.trainset.is_open ?
Scratchpad.trainset.track :
Input.getToken<std::string>() );
auto const offset { Input.getToken<double>( false ) };
auto const drivertype { Input.getToken<std::string>() };
auto const couplingdata = (
Scratchpad.trainset.is_open ?
Input.getToken<std::string>() :
"3" );
auto const velocity = (
Scratchpad.trainset.is_open ?
Scratchpad.trainset.velocity :
Input.getToken<float>( false ) );
// extract coupling type and optional parameters
auto const couplingdatawithparams = couplingdata.find( '.' );
auto coupling = (
couplingdatawithparams != std::string::npos ?
std::atoi( couplingdata.substr( 0, couplingdatawithparams ).c_str() ) :
std::atoi( couplingdata.c_str() ) );
if( coupling < 0 ) {
// sprzęg zablokowany (pojazdy nierozłączalne przy manewrach)
coupling = ( -coupling ) | coupling::permanent;
}
if( ( offset != -1.0 )
&& ( std::abs( offset ) > 0.5 ) ) { // maksymalna odległość między sprzęgami - do przemyślenia
// likwidacja sprzęgu, jeśli odległość zbyt duża - to powinno być uwzględniane w fizyce sprzęgów...
coupling = coupling::faux;
}
auto const params = (
couplingdatawithparams != std::string::npos ?
couplingdata.substr( couplingdatawithparams + 1 ) :
"" );
// load amount and type
auto loadcount { Input.getToken<int>( false ) };
auto loadtype = (
loadcount ?
Input.getToken<std::string>() :
"" );
if( loadtype == "enddynamic" ) {
// idiotoodporność: ładunek bez podanego typu nie liczy się jako ładunek
loadcount = 0;
loadtype = "";
}
auto *path = simulation::Paths.find( pathname );
if( path == nullptr ) {
ErrorLog( "Bad scenario: vehicle \"" + Nodedata.name + "\" placed on nonexistent path \"" + pathname + "\" in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
skip_until( Input, "enddynamic" );
return nullptr;
}
if( ( true == Scratchpad.trainset.vehicles.empty() ) // jeśli pierwszy pojazd,
&& ( false == path->m_events0.empty() ) // tor ma Event0
&& ( std::abs( velocity ) <= 1.f ) // a skład stoi
&& ( Scratchpad.trainset.offset >= 0.0 ) // ale może nie sięgać na owy tor
&& ( Scratchpad.trainset.offset < 8.0 ) ) { // i raczej nie sięga
// przesuwamy około pół EU07 dla wstecznej zgodności
Scratchpad.trainset.offset = 8.0;
}
auto *vehicle = new TDynamicObject();
auto const length =
vehicle->Init(
Nodedata.name,
datafolder, skinfile, mmdfile,
path,
( offset == -1.0 ?
Scratchpad.trainset.offset :
Scratchpad.trainset.offset - offset ),
drivertype,
velocity,
Scratchpad.trainset.name,
loadcount, loadtype,
( offset == -1.0 ),
params );
if( length != 0.0 ) { // zero oznacza błąd
// przesunięcie dla kolejnego, minus bo idziemy w stronę punktu 1
Scratchpad.trainset.offset -= length;
// automatically establish permanent connections for couplers which specify them in their definitions
if( ( coupling != 0 )
&& ( vehicle->MoverParameters->Couplers[ ( offset == -1.0 ? side::front : side::rear ) ].AllowedFlag & coupling::permanent ) ) {
coupling |= coupling::permanent;
}
if( true == Scratchpad.trainset.is_open ) {
Scratchpad.trainset.vehicles.emplace_back( vehicle );
Scratchpad.trainset.couplings.emplace_back( coupling );
}
}
else {
if( vehicle->MyTrack != nullptr ) {
// rare failure case where vehicle with length of 0 is added to the track,
// treated as error code and consequently deleted, but still remains on the track
vehicle->MyTrack->RemoveDynamicObject( vehicle );
}
delete vehicle;
skip_until( Input, "enddynamic" );
return nullptr;
}
auto const destination { Input.getToken<std::string>() };
if( destination != "enddynamic" ) {
// optional vehicle destination parameter
vehicle->asDestination = Input.getToken<std::string>();
skip_until( Input, "enddynamic" );
}
return vehicle;
}
sound_source *
state_serializer::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
glm::dvec3 location;
Input.getTokens( 3 );
Input
>> location.x
>> location.y
>> location.z;
// adjust location
location = transform( location, Scratchpad );
auto *sound = new sound_source( sound_placement::external, Nodedata.range_max );
sound->offset( location );
sound->name( Nodedata.name );
sound->deserialize( Input, sound_type::single );
skip_until( Input, "endsound" );
return sound;
}
// skips content of stream until specified token
void
state_serializer::skip_until( cParser &Input, std::string const &Token ) {
std::string token { Input.getToken<std::string>() };
while( ( false == token.empty() )
&& ( token != Token ) ) {
token = Input.getToken<std::string>();
}
}
// transforms provided location by specifed rotation and offset
glm::dvec3
state_serializer::transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ) {
if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) {
auto const rotation = glm::radians( Scratchpad.location.rotation );
Location = glm::rotateY<double>( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji
}
if( false == Scratchpad.location.offset.empty() ) {
Location += Scratchpad.location.offset.top();
}
return Location;
}
// stores class data in specified file, in legacy (text) format
void
state_serializer::export_as_text( std::string const &Scenariofile ) const {
if( Scenariofile == "$.scn" ) {
ErrorLog( "Bad file: scenery export not supported for file \"$.scn\"" );
}
else {
WriteLog( "Scenery data export in progress..." );
}
auto filename { Scenariofile };
while( filename[ 0 ] == '$' ) {
// trim leading $ char rainsted utility may add to the base name for modified .scn files
filename.erase( 0, 1 );
}
erase_extension( filename );
filename = Global.asCurrentSceneryPath + filename + "_export";
std::ofstream scmfile { filename + ".scm" };
// tracks
scmfile << "// paths\n";
for( auto const *path : Paths.sequence() ) {
path->export_as_text( scmfile );
}
// traction
scmfile << "// traction\n";
for( auto const *traction : Traction.sequence() ) {
traction->export_as_text( scmfile );
}
// power grid
scmfile << "// traction power sources\n";
for( auto const *powersource : Powergrid.sequence() ) {
powersource->export_as_text( scmfile );
}
// models
scmfile << "// instanced models\n";
for( auto const *instance : Instances.sequence() ) {
instance->export_as_text( scmfile );
}
// sounds
scmfile << "// sounds\n";
Region->export_as_text( scmfile );
std::ofstream ctrfile { filename + ".ctr" };
// mem cells
ctrfile << "// memory cells\n";
for( auto const *memorycell : Memory.sequence() ) {
if( true == memorycell->is_exportable ) {
memorycell->export_as_text( ctrfile );
}
}
// events
Events.export_as_text( ctrfile );
WriteLog( "Scenery data export done." );
}
} // simulation

View File

@@ -9,67 +9,11 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include "parser.h"
#include "scene.h"
#include "event.h"
#include "memcell.h"
#include "evlaunch.h"
#include "track.h"
#include "traction.h"
#include "tractionpower.h"
#include "sound.h"
#include "animmodel.h"
#include "dynobj.h"
#include "driver.h"
#include "lightarray.h"
#include "simulationstateserializer.h"
#include "classes.h"
namespace simulation {
class state_serializer {
public:
// methods
// restores simulation data from specified file. returns: true on success, false otherwise
bool
deserialize( std::string const &Scenariofile );
// stores class data in specified file, in legacy (text) format
void
export_as_text( std::string const &Scenariofile ) const;
private:
// methods
// restores class data from provided stream
void deserialize( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_config( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_description( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_event( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_light( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_test( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_time( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad );
TTrack * deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TTraction * deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TTractionPowerSource * deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TMemCell * deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TEventLauncher * deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TDynamicObject * deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
sound_source * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
// skips content of stream until specified token
void skip_until( cParser &Input, std::string const &Token );
// transforms provided location by specifed rotation and offset
glm::dvec3 transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad );
};
class state_manager {
public:
@@ -77,6 +21,8 @@ public:
// legacy method, calculates changes in simulation state over specified time
void
update( double Deltatime, int Iterationcount );
void
update_clocks();
// restores simulation data from specified file. returns: true on success, false otherwise
bool
deserialize( std::string const &Scenariofile );
@@ -89,6 +35,9 @@ private:
state_serializer m_serializer;
};
// passes specified sound to all vehicles within range as a radio message broadcasted on specified channel
void radio_message( sound_source *Message, int const Channel );
extern state_manager State;
extern event_manager Events;
extern memory_table Memory;
@@ -101,6 +50,9 @@ extern vehicle_table Vehicles;
extern light_array Lights;
extern scene::basic_region *Region;
extern TTrain *Train;
extern bool is_ready;
} // simulation

158
simulationenvironment.cpp Normal file
View File

@@ -0,0 +1,158 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "simulationenvironment.h"
#include "globals.h"
namespace simulation {
world_environment Environment;
} // simulation
void
world_environment::toggle_daylight() {
Global.FakeLight = !Global.FakeLight;
if( Global.FakeLight ) {
// for fake daylight enter fixed hour
time( 10, 30, 0 );
}
else {
// local clock based calculation
time();
}
}
// calculates current season of the year based on set simulation date
void
world_environment::compute_season( int const Yearday ) const {
using dayseasonpair = std::pair<int, std::string>;
std::vector<dayseasonpair> seasonsequence {
{ 65, "winter:" },
{ 158, "spring:" },
{ 252, "summer:" },
{ 341, "autumn:" },
{ 366, "winter:" } };
auto const lookup =
std::lower_bound(
std::begin( seasonsequence ), std::end( seasonsequence ),
clamp( Yearday, 1, seasonsequence.back().first ),
[]( dayseasonpair const &Left, const int Right ) {
return Left.first < Right; } );
Global.Season = lookup->second;
// season can affect the weather so if it changes, re-calculate weather as well
compute_weather();
}
// calculates current weather
void
world_environment::compute_weather() const {
Global.Weather = (
Global.Overcast < 0.25 ? "clear:" :
Global.Overcast < 1.0 ? "cloudy:" :
( Global.Season != "winter:" ?
"rain:" :
"snow:" ) );
}
void
world_environment::init() {
m_sun.init();
m_moon.init();
m_stars.init();
m_clouds.Init();
}
void
world_environment::update() {
// move celestial bodies...
m_sun.update();
m_moon.update();
// ...determine source of key light and adjust global state accordingly...
// diffuse (sun) intensity goes down after twilight, and reaches minimum 18 degrees below horizon
float twilightfactor = clamp( -m_sun.getAngle(), 0.0f, 18.0f ) / 18.0f;
// NOTE: sun light receives extra padding to prevent moon from kicking in too soon
auto const sunlightlevel = m_sun.getIntensity() + 0.05f * ( 1.f - twilightfactor );
auto const moonlightlevel = m_moon.getIntensity() * 0.65f; // scaled down by arbitrary factor, it's pretty bright otherwise
float keylightintensity;
glm::vec3 keylightcolor;
if( moonlightlevel > sunlightlevel ) {
// rare situations when the moon is brighter than the sun, typically at night
Global.SunAngle = m_moon.getAngle();
Global.DayLight.position = m_moon.getDirection();
Global.DayLight.direction = -1.0f * m_moon.getDirection();
keylightintensity = moonlightlevel;
// if the moon is up, it overrides the twilight
twilightfactor = 0.0f;
keylightcolor = glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 202.0f / 255.0f );
}
else {
// regular situation with sun as the key light
Global.SunAngle = m_sun.getAngle();
Global.DayLight.position = m_sun.getDirection();
Global.DayLight.direction = -1.0f * m_sun.getDirection();
keylightintensity = sunlightlevel;
// include 'golden hour' effect in twilight lighting
float const duskfactor = 1.0f - clamp( Global.SunAngle, 0.0f, 18.0f ) / 18.0f;
keylightcolor = interpolate(
glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ),
glm::vec3( 235.0f / 255.0f, 140.0f / 255.0f, 36.0f / 255.0f ),
duskfactor );
}
// ...update skydome to match the current sun position as well...
m_skydome.SetOvercastFactor( Global.Overcast );
m_skydome.Update( m_sun.getDirection() );
// ...retrieve current sky colour and brightness...
auto const skydomecolour = m_skydome.GetAverageColor();
auto const skydomehsv = colors::RGBtoHSV( skydomecolour );
// sun strength is reduced by overcast level
keylightintensity *= ( 1.0f - std::min( 1.f, Global.Overcast ) * 0.65f );
// intensity combines intensity of the sun and the light reflected by the sky dome
// it'd be more technically correct to have just the intensity of the sun here,
// but whether it'd _look_ better is something to be tested
auto const intensity = std::min( 1.15f * ( 0.05f + keylightintensity + skydomehsv.z ), 1.25f );
// the impact of sun component is reduced proportionally to overcast level, as overcast increases role of ambient light
auto const diffuselevel = interpolate( keylightintensity, intensity * ( 1.0f - twilightfactor ), 1.0f - std::min( 1.f, Global.Overcast ) * 0.75f );
// ...update light colours and intensity.
keylightcolor = keylightcolor * diffuselevel;
Global.DayLight.diffuse = glm::vec4( keylightcolor, Global.DayLight.diffuse.a );
Global.DayLight.specular = glm::vec4( keylightcolor * 0.85f, diffuselevel );
// tonal impact of skydome color is inversely proportional to how high the sun is above the horizon
// (this is pure conjecture, aimed more to 'look right' than be accurate)
float const ambienttone = clamp( 1.0f - ( Global.SunAngle / 90.0f ), 0.0f, 1.0f );
Global.DayLight.ambient[ 0 ] = interpolate( skydomehsv.z, skydomecolour.r, ambienttone );
Global.DayLight.ambient[ 1 ] = interpolate( skydomehsv.z, skydomecolour.g, ambienttone );
Global.DayLight.ambient[ 2 ] = interpolate( skydomehsv.z, skydomecolour.b, ambienttone );
Global.fLuminance = intensity;
// update the fog. setting it to match the average colour of the sky dome is cheap
// but quite effective way to make the distant items blend with background better
// NOTE: base brightness calculation provides scaled up value, so we bring it back to 'real' one here
Global.FogColor = m_skydome.GetAverageHorizonColor();
}
void
world_environment::time( int const Hour, int const Minute, int const Second ) {
m_sun.setTime( Hour, Minute, Second );
}
//---------------------------------------------------------------------------

52
simulationenvironment.h Normal file
View File

@@ -0,0 +1,52 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "sky.h"
#include "sun.h"
#include "moon.h"
#include "stars.h"
#include "skydome.h"
class opengl_renderer;
// wrapper for environment elements -- sky, sun, stars, clouds etc
class world_environment {
friend opengl_renderer;
public:
// methods
void init();
void update();
void time( int const Hour = -1, int const Minute = -1, int const Second = -1 );
// switches between static and dynamic daylight calculation
void toggle_daylight();
// calculates current season of the year based on set simulation date
void compute_season( int const Yearday ) const;
// calculates current weather
void compute_weather() const;
private:
// members
CSkyDome m_skydome;
cStars m_stars;
cSun m_sun;
cMoon m_moon;
TSky m_clouds;
};
namespace simulation {
extern world_environment Environment;
} // simulation
//---------------------------------------------------------------------------

View File

@@ -0,0 +1,934 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "stdafx.h"
#include "simulationstateserializer.h"
#include "globals.h"
#include "simulation.h"
#include "simulationtime.h"
#include "event.h"
#include "driver.h"
#include "dynobj.h"
#include "animmodel.h"
#include "tractionpower.h"
#include "application.h"
#include "renderer.h"
#include "logs.h"
namespace simulation {
bool
state_serializer::deserialize( std::string const &Scenariofile ) {
// TODO: move initialization to separate routine so we can reuse it
SafeDelete( Region );
Region = new scene::basic_region();
// TODO: check first for presence of serialized binary files
// if this fails, fall back on the legacy text format
scene::scratch_data importscratchpad;
if( Scenariofile != "$.scn" ) {
// compilation to binary file isn't supported for rainsted-created overrides
importscratchpad.binary.terrain = Region->deserialize( Scenariofile );
}
// NOTE: for the time being import from text format is a given, since we don't have full binary serialization
cParser scenarioparser( Scenariofile, cParser::buffer_FILE, Global.asCurrentSceneryPath, Global.bLoadTraction );
if( false == scenarioparser.ok() ) { return false; }
deserialize( scenarioparser, importscratchpad );
if( ( false == importscratchpad.binary.terrain )
&& ( Scenariofile != "$.scn" ) ) {
// if we didn't find usable binary version of the scenario files, create them now for future use
// as long as the scenario file wasn't rainsted-created base file override
Region->serialize( Scenariofile );
}
return true;
}
// restores class data from provided stream
void
state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) {
// prepare deserialization function table
// since all methods use the same objects, we can have simple, hard-coded binds or lambdas for the task
using deserializefunction = void( state_serializer::*)(cParser &, scene::scratch_data &);
std::vector<
std::pair<
std::string,
deserializefunction> > functionlist = {
{ "atmo", &state_serializer::deserialize_atmo },
{ "camera", &state_serializer::deserialize_camera },
{ "config", &state_serializer::deserialize_config },
{ "description", &state_serializer::deserialize_description },
{ "event", &state_serializer::deserialize_event },
{ "firstinit", &state_serializer::deserialize_firstinit },
{ "light", &state_serializer::deserialize_light },
{ "node", &state_serializer::deserialize_node },
{ "origin", &state_serializer::deserialize_origin },
{ "endorigin", &state_serializer::deserialize_endorigin },
{ "rotate", &state_serializer::deserialize_rotate },
{ "sky", &state_serializer::deserialize_sky },
{ "test", &state_serializer::deserialize_test },
{ "time", &state_serializer::deserialize_time },
{ "trainset", &state_serializer::deserialize_trainset },
{ "endtrainset", &state_serializer::deserialize_endtrainset } };
using deserializefunctionbind = std::function<void()>;
std::unordered_map<
std::string,
deserializefunctionbind> functionmap;
for( auto &function : functionlist ) {
functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( Scratchpad ) ) );
}
// deserialize content from the provided input
auto
timelast { std::chrono::steady_clock::now() },
timenow { timelast };
std::string token { Input.getToken<std::string>() };
while( false == token.empty() ) {
auto lookup = functionmap.find( token );
if( lookup != functionmap.end() ) {
lookup->second();
}
else {
ErrorLog( "Bad scenario: unexpected token \"" + token + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
}
timenow = std::chrono::steady_clock::now();
if( std::chrono::duration_cast<std::chrono::milliseconds>( timenow - timelast ).count() >= 200 ) {
timelast = timenow;
glfwPollEvents();
Application.set_progress( Input.getProgress(), Input.getFullProgress() );
GfxRenderer.Render();
}
token = Input.getToken<std::string>();
}
if( false == Scratchpad.initialized ) {
// manually perform scenario initialization
deserialize_firstinit( Input, Scratchpad );
}
}
void
state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ) {
// NOTE: parameter system needs some decent replacement, but not worth the effort if we're moving to built-in editor
// atmosphere color; legacy parameter, no longer used
Input.getTokens( 3 );
// fog range
Input.getTokens( 2 );
Input
>> Global.fFogStart
>> Global.fFogEnd;
if( Global.fFogEnd > 0.0 ) {
// fog colour; optional legacy parameter, no longer used
Input.getTokens( 3 );
}
std::string token { Input.getToken<std::string>() };
if( token != "endatmo" ) {
// optional overcast parameter
Global.Overcast = clamp( std::stof( token ), 0.f, 2.f );
// overcast drives weather so do a calculation here
// NOTE: ugly, clean it up when we're done with world refactoring
simulation::Environment.compute_weather();
}
while( ( false == token.empty() )
&& ( token != "endatmo" ) ) {
// anything else left in the section has no defined meaning
token = Input.getToken<std::string>();
}
}
void
state_serializer::deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad ) {
glm::dvec3 xyz, abc;
int i = -1, into = -1; // do której definicji kamery wstawić
std::string token;
do { // opcjonalna siódma liczba określa numer kamery, a kiedyś były tylko 3
Input.getTokens();
Input >> token;
switch( ++i ) { // kiedyś camera miało tylko 3 współrzędne
case 0: { xyz.x = atof( token.c_str() ); break; }
case 1: { xyz.y = atof( token.c_str() ); break; }
case 2: { xyz.z = atof( token.c_str() ); break; }
case 3: { abc.x = atof( token.c_str() ); break; }
case 4: { abc.y = atof( token.c_str() ); break; }
case 5: { abc.z = atof( token.c_str() ); break; }
case 6: { into = atoi( token.c_str() ); break; } // takie sobie, bo można wpisać -1
default: { break; }
}
} while( token.compare( "endcamera" ) != 0 );
if( into < 0 )
into = ++Global.iCameraLast;
if( into < 10 ) { // przepisanie do odpowiedniego miejsca w tabelce
Global.FreeCameraInit[ into ] = xyz;
Global.FreeCameraInitAngle[ into ] =
Math3D::vector3(
glm::radians( abc.x ),
glm::radians( abc.y ),
glm::radians( abc.z ) );
Global.iCameraLast = into; // numer ostatniej
}
/*
// cleaned up version of the above.
// NOTE: no longer supports legacy mode where some parameters were optional
Input.getTokens( 7 );
glm::vec3
position,
rotation;
int index;
Input
>> position.x
>> position.y
>> position.z
>> rotation.x
>> rotation.y
>> rotation.z
>> index;
skip_until( Input, "endcamera" );
// TODO: finish this
*/
}
void
state_serializer::deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ) {
// config parameters (re)definition
Global.ConfigParse( Input );
}
void
state_serializer::deserialize_description( cParser &Input, scene::scratch_data &Scratchpad ) {
// legacy section, never really used;
skip_until( Input, "enddescription" );
}
void
state_serializer::deserialize_event( cParser &Input, scene::scratch_data &Scratchpad ) {
// TODO: refactor event class and its de/serialization. do offset and rotation after deserialization is done
auto *event = new TEvent();
Math3D::vector3 offset = (
Scratchpad.location.offset.empty() ?
Math3D::vector3() :
Math3D::vector3(
Scratchpad.location.offset.top().x,
Scratchpad.location.offset.top().y,
Scratchpad.location.offset.top().z ) );
event->Load( &Input, offset );
if( false == simulation::Events.insert( event ) ) {
delete event;
}
}
void
state_serializer::deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) {
if( true == Scratchpad.initialized ) { return; }
simulation::Paths.InitTracks();
simulation::Traction.InitTraction();
simulation::Events.InitEvents();
simulation::Events.InitLaunchers();
simulation::Memory.InitCells();
Scratchpad.initialized = true;
}
void
state_serializer::deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ) {
// legacy section, no longer used nor supported;
skip_until( Input, "endlight" );
}
void
state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ) {
auto const inputline = Input.Line(); // cache in case we need to report error
scene::node_data nodedata;
// common data and node type indicator
Input.getTokens( 4 );
Input
>> nodedata.range_max
>> nodedata.range_min
>> nodedata.name
>> nodedata.type;
// type-based deserialization. not elegant but it'll do
if( nodedata.type == "dynamic" ) {
auto *vehicle { deserialize_dynamic( Input, Scratchpad, nodedata ) };
// vehicle import can potentially fail
if( vehicle == nullptr ) { return; }
if( false == simulation::Vehicles.insert( vehicle ) ) {
ErrorLog( "Bad scenario: vehicle with duplicate name \"" + vehicle->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
if( ( vehicle->MoverParameters->CategoryFlag == 1 ) // trains only
&& ( ( ( vehicle->LightList( side::front ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 )
|| ( ( vehicle->LightList( side::rear ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 ) ) ) {
simulation::Lights.insert( vehicle );
}
}
else if( nodedata.type == "track" ) {
auto *path { deserialize_path( Input, Scratchpad, nodedata ) };
// duplicates of named tracks are currently experimentally allowed
if( false == simulation::Paths.insert( path ) ) {
ErrorLog( "Bad scenario: track with duplicate name \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
/*
delete path;
delete pathnode;
*/
}
simulation::Region->insert_and_register( path );
}
else if( nodedata.type == "traction" ) {
auto *traction { deserialize_traction( Input, Scratchpad, nodedata ) };
// traction loading is optional
if( traction == nullptr ) { return; }
if( false == simulation::Traction.insert( traction ) ) {
ErrorLog( "Bad scenario: traction piece with duplicate name \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert_and_register( traction );
}
else if( nodedata.type == "tractionpowersource" ) {
auto *powersource { deserialize_tractionpowersource( Input, Scratchpad, nodedata ) };
// traction loading is optional
if( powersource == nullptr ) { return; }
if( false == simulation::Powergrid.insert( powersource ) ) {
ErrorLog( "Bad scenario: power grid source with duplicate name \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
/*
// TODO: implement this
simulation::Region.insert_powersource( powersource, Scratchpad );
*/
}
else if( nodedata.type == "model" ) {
if( nodedata.range_min < 0.0 ) {
// 3d terrain
if( false == Scratchpad.binary.terrain ) {
// if we're loading data from text .scn file convert and import
auto *instance { deserialize_model( Input, Scratchpad, nodedata ) };
// model import can potentially fail
if( instance == nullptr ) { return; }
// go through submodels, and import them as shapes
auto const cellcount = instance->TerrainCount() + 1; // zliczenie submodeli
for( auto i = 1; i < cellcount; ++i ) {
auto *submodel = instance->TerrainSquare( i - 1 );
simulation::Region->insert(
scene::shape_node().convert( submodel ),
Scratchpad,
false );
// if there's more than one group of triangles in the cell they're held as children of the primary submodel
submodel = submodel->ChildGet();
while( submodel != nullptr ) {
simulation::Region->insert(
scene::shape_node().convert( submodel ),
Scratchpad,
false );
submodel = submodel->NextGet();
}
}
// with the import done we can get rid of the source model
delete instance;
}
else {
// if binary terrain file was present, we already have this data
skip_until( Input, "endmodel" );
}
}
else {
// regular instance of 3d mesh
auto *instance { deserialize_model( Input, Scratchpad, nodedata ) };
// model import can potentially fail
if( instance == nullptr ) { return; }
if( false == simulation::Instances.insert( instance ) ) {
ErrorLog( "Bad scenario: 3d model instance with duplicate name \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert( instance );
}
}
else if( ( nodedata.type == "triangles" )
|| ( nodedata.type == "triangle_strip" )
|| ( nodedata.type == "triangle_fan" ) ) {
if( false == Scratchpad.binary.terrain ) {
simulation::Region->insert(
scene::shape_node().import(
Input, nodedata ),
Scratchpad,
true );
}
else {
// all shapes were already loaded from the binary version of the file
skip_until( Input, "endtri" );
}
}
else if( ( nodedata.type == "lines" )
|| ( nodedata.type == "line_strip" )
|| ( nodedata.type == "line_loop" ) ) {
if( false == Scratchpad.binary.terrain ) {
simulation::Region->insert(
scene::lines_node().import(
Input, nodedata ),
Scratchpad );
}
else {
// all lines were already loaded from the binary version of the file
skip_until( Input, "endline" );
}
}
else if( nodedata.type == "memcell" ) {
auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) };
if( false == simulation::Memory.insert( memorycell ) ) {
ErrorLog( "Bad scenario: memory memorycell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert( memorycell );
}
else if( nodedata.type == "eventlauncher" ) {
auto *eventlauncher { deserialize_eventlauncher( Input, Scratchpad, nodedata ) };
if( false == simulation::Events.insert( eventlauncher ) ) {
ErrorLog( "Bad scenario: event launcher with duplicate name \"" + eventlauncher->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
// event launchers can be either global, or local with limited range of activation
// each gets assigned different caretaker
if( true == eventlauncher->IsGlobal() ) {
simulation::Events.queue( eventlauncher );
}
else {
simulation::Region->insert( eventlauncher );
}
}
else if( nodedata.type == "sound" ) {
auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) };
if( false == simulation::Sounds.insert( sound ) ) {
ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert( sound );
}
}
void
state_serializer::deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ) {
glm::dvec3 offset;
Input.getTokens( 3 );
Input
>> offset.x
>> offset.y
>> offset.z;
// sumowanie całkowitego przesunięcia
Scratchpad.location.offset.emplace(
offset + (
Scratchpad.location.offset.empty() ?
glm::dvec3() :
Scratchpad.location.offset.top() ) );
}
void
state_serializer::deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ) {
if( false == Scratchpad.location.offset.empty() ) {
Scratchpad.location.offset.pop();
}
else {
ErrorLog( "Bad origin: endorigin instruction with empty origin stack in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
}
}
void
state_serializer::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ) {
Input.getTokens( 3 );
Input
>> Scratchpad.location.rotation.x
>> Scratchpad.location.rotation.y
>> Scratchpad.location.rotation.z;
}
void
state_serializer::deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ) {
// sky model
Input.getTokens( 1 );
Input
>> Global.asSky;
// anything else left in the section has no defined meaning
skip_until( Input, "endsky" );
}
void
state_serializer::deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ) {
// legacy section, no longer supported;
skip_until( Input, "endtest" );
}
void
state_serializer::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ) {
// current scenario time
cParser timeparser( Input.getToken<std::string>() );
timeparser.getTokens( 2, false, ":" );
auto &time = simulation::Time.data();
timeparser
>> time.wHour
>> time.wMinute;
if( true == Global.ScenarioTimeCurrent ) {
// calculate time shift required to match scenario time with local clock
auto timenow = std::time( 0 );
auto const *localtime = std::localtime( &timenow );
Global.ScenarioTimeOffset = ( ( localtime->tm_hour * 60 + localtime->tm_min ) - ( time.wHour * 60 + time.wMinute ) ) / 60.f;
}
// remaining sunrise and sunset parameters are no longer used, as they're now calculated dynamically
// anything else left in the section has no defined meaning
skip_until( Input, "endtime" );
}
void
state_serializer::deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ) {
if( true == Scratchpad.trainset.is_open ) {
// shouldn't happen but if it does wrap up currently open trainset and report an error
deserialize_endtrainset( Input, Scratchpad );
ErrorLog( "Bad scenario: encountered nested trainset definitions in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() ) + ")" );
}
Scratchpad.trainset = scene::scratch_data::trainset_data();
Scratchpad.trainset.is_open = true;
Input.getTokens( 4 );
Input
>> Scratchpad.trainset.name
>> Scratchpad.trainset.track
>> Scratchpad.trainset.offset
>> Scratchpad.trainset.velocity;
}
void
state_serializer::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) {
if( ( false == Scratchpad.trainset.is_open )
|| ( true == Scratchpad.trainset.vehicles.empty() ) ) {
// not bloody likely but we better check for it just the same
ErrorLog( "Bad trainset: empty trainset defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
Scratchpad.trainset.is_open = false;
return;
}
std::size_t vehicleindex { 0 };
for( auto *vehicle : Scratchpad.trainset.vehicles ) {
// go through list of vehicles in the trainset, coupling them together and checking for potential driver
if( ( vehicle->Mechanik != nullptr )
&& ( vehicle->Mechanik->Primary() ) ) {
// primary driver will receive the timetable for this trainset
Scratchpad.trainset.driver = vehicle;
}
if( vehicleindex > 0 ) {
// from second vehicle on couple it with the previous one
Scratchpad.trainset.vehicles[ vehicleindex - 1 ]->AttachPrev(
vehicle,
Scratchpad.trainset.couplings[ vehicleindex - 1 ] );
}
++vehicleindex;
}
if( Scratchpad.trainset.driver != nullptr ) {
// if present, send timetable to the driver
// wysłanie komendy "Timetable" ustawia odpowiedni tryb jazdy
auto *controller = Scratchpad.trainset.driver->Mechanik;
controller->DirectionInitial();
controller->PutCommand(
"Timetable:" + Scratchpad.trainset.name,
Scratchpad.trainset.velocity,
0,
nullptr );
}
if( Scratchpad.trainset.couplings.back() == coupling::faux ) {
// jeśli ostatni pojazd ma sprzęg 0 to założymy mu końcówki blaszane (jak AI się odpali, to sobie poprawi)
Scratchpad.trainset.vehicles.back()->RaLightsSet( -1, light::rearendsignals );
}
// all done
Scratchpad.trainset.is_open = false;
}
// creates path and its wrapper, restoring class data from provided stream
TTrack *
state_serializer::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
// TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done
auto *track = new TTrack( Nodedata );
auto const offset { (
Scratchpad.location.offset.empty() ?
glm::dvec3 { 0.0 } :
glm::dvec3 {
Scratchpad.location.offset.top().x,
Scratchpad.location.offset.top().y,
Scratchpad.location.offset.top().z } ) };
track->Load( &Input, offset );
return track;
}
TTraction *
state_serializer::deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
if( false == Global.bLoadTraction ) {
skip_until( Input, "endtraction" );
return nullptr;
}
// TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done
auto *traction = new TTraction( Nodedata );
auto offset = (
Scratchpad.location.offset.empty() ?
glm::dvec3() :
Scratchpad.location.offset.top() );
traction->Load( &Input, offset );
return traction;
}
TTractionPowerSource *
state_serializer::deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
if( false == Global.bLoadTraction ) {
skip_until( Input, "end" );
return nullptr;
}
auto *powersource = new TTractionPowerSource( Nodedata );
powersource->Load( &Input );
// adjust location
powersource->location( transform( powersource->location(), Scratchpad ) );
return powersource;
}
TMemCell *
state_serializer::deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
auto *memorycell = new TMemCell( Nodedata );
memorycell->Load( &Input );
// adjust location
memorycell->location( transform( memorycell->location(), Scratchpad ) );
return memorycell;
}
TEventLauncher *
state_serializer::deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
glm::dvec3 location;
Input.getTokens( 3 );
Input
>> location.x
>> location.y
>> location.z;
auto *eventlauncher = new TEventLauncher( Nodedata );
eventlauncher->Load( &Input );
eventlauncher->location( transform( location, Scratchpad ) );
return eventlauncher;
}
TAnimModel *
state_serializer::deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
glm::dvec3 location;
glm::vec3 rotation;
Input.getTokens( 4 );
Input
>> location.x
>> location.y
>> location.z
>> rotation.y;
auto *instance = new TAnimModel( Nodedata );
instance->Angles( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii
if( instance->Load( &Input, false ) ) {
instance->location( transform( location, Scratchpad ) );
}
else {
// model nie wczytał się - ignorowanie node
SafeDelete( instance );
}
return instance;
}
TDynamicObject *
state_serializer::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
if( false == Scratchpad.trainset.is_open ) {
// part of trainset data is used when loading standalone vehicles, so clear it just in case
Scratchpad.trainset = scene::scratch_data::trainset_data();
}
auto const inputline { Input.Line() }; // cache in case of errors
// basic attributes
auto const datafolder { Input.getToken<std::string>() };
auto const skinfile { Input.getToken<std::string>() };
auto const mmdfile { Input.getToken<std::string>() };
auto const pathname = (
Scratchpad.trainset.is_open ?
Scratchpad.trainset.track :
Input.getToken<std::string>() );
auto const offset { Input.getToken<double>( false ) };
auto const drivertype { Input.getToken<std::string>() };
auto const couplingdata = (
Scratchpad.trainset.is_open ?
Input.getToken<std::string>() :
"3" );
auto const velocity = (
Scratchpad.trainset.is_open ?
Scratchpad.trainset.velocity :
Input.getToken<float>( false ) );
// extract coupling type and optional parameters
auto const couplingdatawithparams = couplingdata.find( '.' );
auto coupling = (
couplingdatawithparams != std::string::npos ?
std::atoi( couplingdata.substr( 0, couplingdatawithparams ).c_str() ) :
std::atoi( couplingdata.c_str() ) );
if( coupling < 0 ) {
// sprzęg zablokowany (pojazdy nierozłączalne przy manewrach)
coupling = ( -coupling ) | coupling::permanent;
}
if( ( offset != -1.0 )
&& ( std::abs( offset ) > 0.5 ) ) { // maksymalna odległość między sprzęgami - do przemyślenia
// likwidacja sprzęgu, jeśli odległość zbyt duża - to powinno być uwzględniane w fizyce sprzęgów...
coupling = coupling::faux;
}
auto const params = (
couplingdatawithparams != std::string::npos ?
couplingdata.substr( couplingdatawithparams + 1 ) :
"" );
// load amount and type
auto loadcount { Input.getToken<int>( false ) };
auto loadtype = (
loadcount ?
Input.getToken<std::string>() :
"" );
if( loadtype == "enddynamic" ) {
// idiotoodporność: ładunek bez podanego typu nie liczy się jako ładunek
loadcount = 0;
loadtype = "";
}
auto *path = simulation::Paths.find( pathname );
if( path == nullptr ) {
ErrorLog( "Bad scenario: vehicle \"" + Nodedata.name + "\" placed on nonexistent path \"" + pathname + "\" in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
skip_until( Input, "enddynamic" );
return nullptr;
}
if( ( true == Scratchpad.trainset.vehicles.empty() ) // jeśli pierwszy pojazd,
&& ( false == path->m_events0.empty() ) // tor ma Event0
&& ( std::abs( velocity ) <= 1.f ) // a skład stoi
&& ( Scratchpad.trainset.offset >= 0.0 ) // ale może nie sięgać na owy tor
&& ( Scratchpad.trainset.offset < 8.0 ) ) { // i raczej nie sięga
// przesuwamy około pół EU07 dla wstecznej zgodności
Scratchpad.trainset.offset = 8.0;
}
auto *vehicle = new TDynamicObject();
auto const length =
vehicle->Init(
Nodedata.name,
datafolder, skinfile, mmdfile,
path,
( offset == -1.0 ?
Scratchpad.trainset.offset :
Scratchpad.trainset.offset - offset ),
drivertype,
velocity,
Scratchpad.trainset.name,
loadcount, loadtype,
( offset == -1.0 ),
params );
if( length != 0.0 ) { // zero oznacza błąd
// przesunięcie dla kolejnego, minus bo idziemy w stronę punktu 1
Scratchpad.trainset.offset -= length;
// automatically establish permanent connections for couplers which specify them in their definitions
if( ( coupling != 0 )
&& ( vehicle->MoverParameters->Couplers[ ( offset == -1.0 ? side::front : side::rear ) ].AllowedFlag & coupling::permanent ) ) {
coupling |= coupling::permanent;
}
if( true == Scratchpad.trainset.is_open ) {
Scratchpad.trainset.vehicles.emplace_back( vehicle );
Scratchpad.trainset.couplings.emplace_back( coupling );
}
}
else {
if( vehicle->MyTrack != nullptr ) {
// rare failure case where vehicle with length of 0 is added to the track,
// treated as error code and consequently deleted, but still remains on the track
vehicle->MyTrack->RemoveDynamicObject( vehicle );
}
delete vehicle;
skip_until( Input, "enddynamic" );
return nullptr;
}
auto const destination { Input.getToken<std::string>() };
if( destination != "enddynamic" ) {
// optional vehicle destination parameter
vehicle->asDestination = Input.getToken<std::string>();
skip_until( Input, "enddynamic" );
}
return vehicle;
}
sound_source *
state_serializer::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
glm::dvec3 location;
Input.getTokens( 3 );
Input
>> location.x
>> location.y
>> location.z;
// adjust location
location = transform( location, Scratchpad );
auto *sound = new sound_source( sound_placement::external, Nodedata.range_max );
sound->offset( location );
sound->name( Nodedata.name );
sound->deserialize( Input, sound_type::single );
skip_until( Input, "endsound" );
return sound;
}
// skips content of stream until specified token
void
state_serializer::skip_until( cParser &Input, std::string const &Token ) {
std::string token { Input.getToken<std::string>() };
while( ( false == token.empty() )
&& ( token != Token ) ) {
token = Input.getToken<std::string>();
}
}
// transforms provided location by specifed rotation and offset
glm::dvec3
state_serializer::transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ) {
if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) {
auto const rotation = glm::radians( Scratchpad.location.rotation );
Location = glm::rotateY<double>( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji
}
if( false == Scratchpad.location.offset.empty() ) {
Location += Scratchpad.location.offset.top();
}
return Location;
}
// stores class data in specified file, in legacy (text) format
void
state_serializer::export_as_text( std::string const &Scenariofile ) const {
if( Scenariofile == "$.scn" ) {
ErrorLog( "Bad file: scenery export not supported for file \"$.scn\"" );
}
else {
WriteLog( "Scenery data export in progress..." );
}
auto filename { Scenariofile };
while( filename[ 0 ] == '$' ) {
// trim leading $ char rainsted utility may add to the base name for modified .scn files
filename.erase( 0, 1 );
}
erase_extension( filename );
filename = Global.asCurrentSceneryPath + filename + "_export";
std::ofstream scmfile { filename + ".scm" };
// tracks
scmfile << "// paths\n";
for( auto const *path : Paths.sequence() ) {
path->export_as_text( scmfile );
}
// traction
scmfile << "// traction\n";
for( auto const *traction : Traction.sequence() ) {
traction->export_as_text( scmfile );
}
// power grid
scmfile << "// traction power sources\n";
for( auto const *powersource : Powergrid.sequence() ) {
powersource->export_as_text( scmfile );
}
// models
scmfile << "// instanced models\n";
for( auto const *instance : Instances.sequence() ) {
instance->export_as_text( scmfile );
}
// sounds
scmfile << "// sounds\n";
Region->export_as_text( scmfile );
std::ofstream ctrfile { filename + ".ctr" };
// mem cells
ctrfile << "// memory cells\n";
for( auto const *memorycell : Memory.sequence() ) {
if( true == memorycell->is_exportable ) {
memorycell->export_as_text( ctrfile );
}
}
// events
Events.export_as_text( ctrfile );
WriteLog( "Scenery data export done." );
}
} // simulation
//---------------------------------------------------------------------------

View File

@@ -0,0 +1,64 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "parser.h"
#include "scene.h"
namespace simulation {
class state_serializer {
public:
// methods
// restores simulation data from specified file. returns: true on success, false otherwise
bool
deserialize( std::string const &Scenariofile );
// stores class data in specified file, in legacy (text) format
void
export_as_text( std::string const &Scenariofile ) const;
private:
// methods
// restores class data from provided stream
void deserialize( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_config( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_description( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_event( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_light( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_test( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_time( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad );
TTrack * deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TTraction * deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TTractionPowerSource * deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TMemCell * deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TEventLauncher * deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TDynamicObject * deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
sound_source * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
// skips content of stream until specified token
void skip_until( cParser &Input, std::string const &Token );
// transforms provided location by specifed rotation and offset
glm::dvec3 transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad );
};
} // simulation
//---------------------------------------------------------------------------

View File

@@ -12,9 +12,9 @@ http://mozilla.org/MPL/2.0/.
#include "sound.h"
#include "parser.h"
#include "globals.h"
#include "world.h"
#include "train.h"
#include "dynobj.h"
#include "simulation.h"
// constructors
sound_source::sound_source( sound_placement const Placement, float const Range ) :
@@ -910,15 +910,15 @@ sound_source::update_soundproofing() {
int const activecab = (
Global.CabWindowOpen ? 2 :
FreeFlyModeFlag ? 0 :
( Global.pWorld->train() ?
Global.pWorld->train()->Dynamic()->MoverParameters->ActiveCab :
( simulation::Train ?
simulation::Train->Occupied()->ActiveCab :
0 ) );
// location-based gain factor:
std::uintptr_t soundproofingstamp = reinterpret_cast<std::uintptr_t>( (
FreeFlyModeFlag ?
nullptr :
( Global.pWorld->train() ?
Global.pWorld->train()->Dynamic() :
( simulation::Train ?
simulation::Train->Dynamic() :
nullptr ) ) )
+ activecab;
@@ -941,7 +941,7 @@ sound_source::update_soundproofing() {
m_properties.soundproofing = (
soundproofingstamp == 0 ?
EU07_SOUNDPROOFING_STRONG : // listener outside HACK: won't be true if active vehicle has open window
( Global.pWorld->train()->Dynamic() != m_owner ?
( simulation::Train->Dynamic() != m_owner ?
EU07_SOUNDPROOFING_STRONG : // in another vehicle
( activecab == 0 ?
EU07_SOUNDPROOFING_STRONG : // listener in the engine compartment
@@ -952,7 +952,7 @@ sound_source::update_soundproofing() {
m_properties.soundproofing = (
( ( soundproofingstamp == 0 ) || ( true == Global.CabWindowOpen ) ) ?
EU07_SOUNDPROOFING_SOME : // listener outside or has a window open
( Global.pWorld->train()->Dynamic() != m_owner ?
( simulation::Train->Dynamic() != m_owner ?
EU07_SOUNDPROOFING_STRONG : // in another vehicle
( activecab == 0 ?
EU07_SOUNDPROOFING_NONE : // listener in the engine compartment

View File

@@ -2,7 +2,7 @@
#include "uart.h"
#include "Globals.h"
#include "World.h"
#include "simulation.h"
#include "Train.h"
#include "parser.h"
#include "Logs.h"
@@ -130,7 +130,7 @@ void uart_input::poll()
return;
last_update = now;
auto *t = Global.pWorld->train();
auto const *t =simulation::Train;
if (!t)
return;

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,12 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <string>
@@ -30,29 +38,32 @@ struct ui_panel {
class ui_layer {
public:
// parameters:
// constructors:
// constructors
ui_layer() = default;
// destructor:
~ui_layer();
// destructor
virtual ~ui_layer();
// methods:
// methods
static
bool
init( GLFWwindow *Window );
// assign texturing hardware unit
static
void
set_unit( GLint const Textureunit ) { m_textureunit = Textureunit; }
// potentially processes provided input key. returns: true if the input was processed, false otherwise
virtual
bool
on_key( int const Key, int const Action );
// updates state of UI elements
virtual
void
update();
update() {}
// draws requested UI elements
void
render();
//
static
void
set_cursor( int const Mode );
// stores operation progress
@@ -70,12 +81,15 @@ public:
void
clear_texts() { m_panels.clear(); }
void
push_back( std::shared_ptr<ui_panel> Panel ) { m_panels.emplace_back( Panel ); }
push_back( ui_panel *Panel ) { m_panels.emplace_back( Panel ); }
// members:
protected:
// members
static GLFWwindow *m_window;
static bool m_cursorvisible;
private:
// methods:
// methods
// draws background quad with specified earlier texture
void
render_background();
@@ -94,11 +108,9 @@ private:
// draws a quad between coordinates x,y and z,w with uv-coordinates spanning 0-1
void
quad( glm::vec4 const &Coordinates, glm::vec4 const &Color );
// members:
GLFWwindow *m_window { nullptr };
GLint m_textureunit{ GL_TEXTURE0 };
GLuint m_fontbase { (GLuint)-1 }; // numer DL dla znaków w napisach
// members
static GLint m_textureunit;
static GLuint m_fontbase; // numer DL dla znaków w napisach
// progress bar config. TODO: put these together into an object
float m_progress { 0.0f }; // percentage of filled progres bar, to indicate lengthy operations.
@@ -108,16 +120,6 @@ private:
texture_handle m_background { null_handle }; // path to texture used as the background. size depends on mAspect.
GLuint m_texture { 0 };
std::vector<std::shared_ptr<ui_panel> > m_panels;
std::vector<ui_panel *> m_panels;
std::string m_tooltip;
// TODO: clean these legacy components up
std::shared_ptr<ui_panel> UIHeader; // header ui panel
std::shared_ptr<ui_panel> UITable; // schedule or scan table
std::shared_ptr<ui_panel> UITranscripts; // voice transcripts
int tprev; // poprzedni czas
double VelPrev; // poprzednia prędkość
double Acc; // przyspieszenie styczne
bool m_cursorvisible { false };
};
extern ui_layer UILayer;

View File

@@ -281,7 +281,7 @@ std::string ToUpper(std::string const &text) {
void
win1250_to_ascii( std::string &Input ) {
std::unordered_map<char, char> charmap{
std::unordered_map<char, char> const charmap {
{ 165, 'A' }, { 198, 'C' }, { 202, 'E' }, { 163, 'L' }, { 209, 'N' }, { 211, 'O' }, { 140, 'S' }, { 143, 'Z' }, { 175, 'Z' },
{ 185, 'a' }, { 230, 'c' }, { 234, 'e' }, { 179, 'l' }, { 241, 'n' }, { 243, 'o' }, { 156, 's' }, { 159, 'z' }, { 191, 'z' }
};
@@ -293,23 +293,33 @@ win1250_to_ascii( std::string &Input ) {
}
// Ra: tymczasowe rozwiązanie kwestii zagranicznych (czeskich) napisów
char bezogonkowo[] = "E?,?\"_++?%S<STZZ?`'\"\".--??s>stzz"
char charsetconversiontable[] =
"E?,?\"_++?%S<STZZ?`'\"\".--??s>stzz"
" ^^L$A|S^CS<--RZo±,l'uP.,as>L\"lz"
"RAAAALCCCEEEEIIDDNNOOOOxRUUUUYTB"
"raaaalccceeeeiiddnnoooo-ruuuuyt?";
std::string Bezogonkow(std::string str, bool _)
{ // wycięcie liter z ogonkami, bo OpenGL nie umie wyświetlić
for (unsigned int i = 1; i < str.length(); ++i)
if (str[i] & 0x80)
str[i] = bezogonkowo[str[i] & 0x7F];
else if (str[i] < ' ') // znaki sterujące nie są obsługiwane
str[i] = ' ';
else if (_)
if (str[i] == '_') // nazwy stacji nie mogą zawierać spacji
str[i] = ' '; // więc trzeba wyświetlać inaczej
return str;
};
// wycięcie liter z ogonkami
std::string Bezogonkow(std::string Input, bool const Underscorestospaces) {
char const extendedcharsetbit { static_cast<char>( 0x80 ) };
char const space { ' ' };
char const underscore { '_' };
for( auto &input : Input ) {
if( input & extendedcharsetbit ) {
input = charsetconversiontable[ input ^ extendedcharsetbit ];
}
else if( input < space ) {
input = space;
}
else if( Underscorestospaces && ( input == underscore ) ) {
input = space;
}
}
return Input;
}
template <>
bool

View File

@@ -130,7 +130,7 @@ std::string ToUpper(std::string const &text);
// replaces polish letters with basic ascii
void win1250_to_ascii( std::string &Input );
// TODO: unify with win1250_to_ascii()
std::string Bezogonkow( std::string str, bool _ = false );
std::string Bezogonkow( std::string Input, bool const Underscorestospaces = false );
inline
std::string

View File

@@ -1,5 +1,5 @@
#pragma once
#define VERSION_MAJOR 18
#define VERSION_MINOR 718
#define VERSION_MINOR 802
#define VERSION_REVISION 0

View File

@@ -1,5 +1,5 @@
#include "stdafx.h"
#include "World.h"
#include "messaging.h"
#include "utilities.h"
#pragma warning (disable: 4091)
@@ -62,7 +62,7 @@ LRESULT APIENTRY WndProc( HWND hWnd, // handle for this window
// obsługa danych przesłanych przez program sterujący
pDane = (PCOPYDATASTRUCT)lParam;
if( pDane->dwData == MAKE_ID4('E', 'U', '0', '7')) // sygnatura danych
World.OnCommandGet( ( multiplayer::DaneRozkaz *)( pDane->lpData ) );
multiplayer::OnCommandGet( ( multiplayer::DaneRozkaz *)( pDane->lpData ) );
break;
}
}