16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-23 23:49:17 +02:00

Merge branch 'tmj-dev' into milek-dev

This commit is contained in:
milek7
2018-06-15 18:58:03 +02:00
54 changed files with 2310 additions and 927 deletions

View File

@@ -424,13 +424,6 @@ TAnimModel::~TAnimModel()
SafeDelete(pRoot);
}
bool TAnimModel::Init(TModel3d *pNewModel)
{
fBlinkTimer = double(Random(1000 * fOffTime)) / (1000 * fOffTime);
pModel = pNewModel;
return (pModel != nullptr);
}
bool TAnimModel::Init(std::string const &asName, std::string const &asReplacableTexture)
{
if( asReplacableTexture.substr( 0, 1 ) == "*" ) {
@@ -446,17 +439,21 @@ bool TAnimModel::Init(std::string const &asName, std::string const &asReplacable
m_materialdata.textures_alpha = 0x31310031;
}
else{
// tekstura nieprzezroczysta - nie renderować w cyklu
// tekstura nieprzezroczysta - nie renderować w cyklu przezroczystych
m_materialdata.textures_alpha = 0x30300030;
}
// przezroczystych
return (Init(TModelsManager::GetModel(asName)));
fBlinkTimer = double( Random( 1000 * fOffTime ) ) / ( 1000 * fOffTime );
pModel = TModelsManager::GetModel( asName );
return ( pModel != nullptr );
}
bool TAnimModel::Load(cParser *parser, bool ter)
{ // rozpoznanie wpisu modelu i ustawienie świateł
std::string name = parser->getToken<std::string>();
std::string texture = parser->getToken<std::string>(false); // tekstura (zmienia na małe)
std::string texture = parser->getToken<std::string>(); // tekstura (zmienia na małe)
replace_slashes( name );
if (!Init( name, texture ))
{
if (name != "notload")
@@ -505,7 +502,7 @@ bool TAnimModel::Load(cParser *parser, bool ter)
while( ( token != "" )
&& ( token != "endmodel" ) ) {
LightSet( i, std::stod( token ) ); // stan światła jest liczbą z ułamkiem
LightSet( i, std::stof( token ) ); // stan światła jest liczbą z ułamkiem
++i;
token = "";
@@ -564,15 +561,6 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) {
m_framestamp = Framestamp;
};
// calculates piece's bounding radius
void
TAnimModel::radius_() {
if( pModel != nullptr ) {
m_area.radius = pModel->bounding_radius();
}
}
void TAnimModel::RaPrepare()
{ // ustawia światła i animacje we wzorcu modelu przed renderowaniem egzemplarza
bool state; // stan światła
@@ -585,7 +573,11 @@ void TAnimModel::RaPrepare()
state = ( fBlinkTimer < fOnTime );
break;
case ls_Dark: // zapalone, gdy ciemno
state = ( Global.fLuminance <= ( lsLights[i] - 3.0 ) );
state = (
Global.fLuminance <= (
lsLights[i] == 3.f ?
DefaultDarkThresholdLevel :
( lsLights[i] - 3.f ) ) );
break;
default: // zapalony albo zgaszony
state = (lightmode == ls_On);
@@ -781,15 +773,17 @@ void TAnimModel::LightSet(int const n, float const v)
}
case ls_Dark: {
// zapalenie świateł zależne od oświetlenia scenerii
if( v == 3.0 ) {
/*
if( v == 3.f ) {
// standardowy próg zaplania
lsLights[ n ] = 3.0 + DefaultDarkThresholdLevel;
lsLights[ n ] = 3.f + DefaultDarkThresholdLevel;
}
*/
break;
}
}
};
//---------------------------------------------------------------------------
void TAnimModel::AnimUpdate(double dt)
{ // wykonanie zakolejkowanych animacji, nawet gdy modele nie są aktualnie wyświetlane
TAnimContainer *p = TAnimModel::acAnimList;
@@ -799,4 +793,72 @@ void TAnimModel::AnimUpdate(double dt)
p = p->acAnimNext; // na razie bez usuwania z listy, bo głównie obrotnica na nią wchodzi
}
};
// radius() subclass details, calculates node's bounding radius
float
TAnimModel::radius_() {
return (
pModel ?
pModel->bounding_radius() :
0.f );
}
// serialize() subclass details, sends content of the subclass to provided stream
void
TAnimModel::serialize_( std::ostream &Output ) const {
// TODO: implement
}
// deserialize() subclass details, restores content of the subclass from provided stream
void
TAnimModel::deserialize_( std::istream &Input ) {
// TODO: implement
}
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void
TAnimModel::export_as_text_( std::ostream &Output ) const {
// header
Output << "model ";
// location and rotation
Output
<< location().x << ' '
<< location().y << ' '
<< location().z << ' '
<< vAngle.y << ' ';
// 3d shape
auto modelfile { (
pModel ?
pModel->NameGet() + ".t3d" : // rainsted requires model file names to include an extension
"none" ) };
if( modelfile.find( szModelPath ) == 0 ) {
// don't include 'models/' in the path
modelfile.erase( 0, std::string{ szModelPath }.size() );
}
Output << modelfile << ' ';
// texture
auto texturefile { (
m_materialdata.replacable_skins[ 1 ] != null_handle ?
GfxRenderer.Material( m_materialdata.replacable_skins[ 1 ] ).name :
"none" ) };
if( texturefile.find( szTexturePath ) == 0 ) {
// don't include 'textures/' in the path
texturefile.erase( 0, std::string{ szTexturePath }.size() );
}
Output << texturefile << ' ';
// light submodels activation configuration
if( iNumLights > 0 ) {
Output << "lights ";
for( int lightidx = 0; lightidx < iNumLights; ++lightidx ) {
Output << lsLights[ lightidx ] << ' ';
}
}
// footer
Output
<< "endmodel"
<< "\n";
}
//---------------------------------------------------------------------------

View File

@@ -122,7 +122,7 @@ class TAnimAdvanced
};
// opakowanie modelu, określające stan egzemplarza
class TAnimModel : public editor::basic_node {
class TAnimModel : public scene::basic_node {
friend class opengl_renderer;
@@ -133,15 +133,12 @@ public:
~TAnimModel();
// methods
static void AnimUpdate( double dt );
bool Init(TModel3d *pNewModel);
bool Init(std::string const &asName, std::string const &asReplacableTexture);
bool Load(cParser *parser, bool ter = false);
TAnimContainer * AddContainer(std::string const &Name);
TAnimContainer * GetContainer(std::string const &Name = "");
void RaAnglesSet( glm::vec3 Angles ) {
vAngle.x = Angles.x;
vAngle.y = Angles.y;
vAngle.z = Angles.z; };
vAngle = { Angles }; };
void LightSet( int const n, float const v );
void AnimationVND( void *pData, double a, double b, double c, double d );
bool TerrainLoaded();
@@ -159,16 +156,20 @@ public:
// members
static TAnimContainer *acAnimList; // lista animacji z eventem, które muszą być przeliczane również bez wyświetlania
protected:
// calculates piece's bounding radius
void
radius_();
private:
// methods
void RaPrepare(); // ustawienie animacji egzemplarza na wzorcu
void RaAnimate( unsigned int const Framestamp ); // przeliczenie animacji egzemplarza
void Advanced();
// radius() subclass details, calculates node's bounding radius
float radius_();
// serialize() subclass details, sends content of the subclass to provided stream
void serialize_( std::ostream &Output ) const;
// deserialize() subclass details, restores content of the subclass from provided stream
void deserialize_( std::istream &Input );
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void export_as_text_( std::ostream &Output ) const;
// members
TAnimContainer *pRoot { nullptr }; // pojemniki sterujące, tylko dla aniomowanych submodeli
TModel3d *pModel { nullptr };

View File

@@ -75,9 +75,14 @@ void TButton::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *pMod
}
// pass submodel location to defined sounds
auto const nulloffset { glm::vec3{} };
auto const offset { model_offset() };
if( m_soundfxincrease.offset() == nulloffset ) {
m_soundfxincrease.offset( offset );
}
if( m_soundfxdecrease.offset() == nulloffset ) {
m_soundfxdecrease.offset( offset );
}
}
bool

View File

@@ -18,8 +18,7 @@ http://mozilla.org/MPL/2.0/.
//---------------------------------------------------------------------------
void TCamera::Init( Math3D::vector3 NPos, Math3D::vector3 NAngle)
{
void TCamera::Init( Math3D::vector3 NPos, Math3D::vector3 NAngle) {
vUp = Math3D::vector3(0, 1, 0);
Velocity = Math3D::vector3(0, 0, 0);
@@ -31,8 +30,15 @@ void TCamera::Init( Math3D::vector3 NPos, Math3D::vector3 NAngle)
Type = (Global.bFreeFly ? tp_Free : tp_Follow);
};
void TCamera::OnCursorMove(double x, double y) {
void TCamera::Reset() {
Pitch = Yaw = Roll = 0;
m_rotationoffsets = {};
};
void TCamera::OnCursorMove(double x, double y) {
/*
Yaw -= x;
while( Yaw > M_PI ) {
Yaw -= 2 * M_PI;
@@ -45,6 +51,8 @@ void TCamera::OnCursorMove(double x, double y) {
// jeżeli jazda z pojazdem ograniczenie kąta spoglądania w dół i w górę
Pitch = clamp( Pitch, -M_PI_4, M_PI_4 );
}
*/
m_rotationoffsets += glm::dvec3 { y, x, 0.0 };
}
bool
@@ -151,8 +159,9 @@ void TCamera::Update()
OnCommand( command );
}
auto const deltatime = Timer::GetDeltaRenderTime(); // czas bez pauzy
auto const deltatime { Timer::GetDeltaRenderTime() }; // czas bez pauzy
// update position
if( ( Type == tp_Free )
|| ( false == Global.ctrlState )
|| ( true == DebugCameraFlag ) ) {
@@ -170,6 +179,24 @@ void TCamera::Update()
Vec.RotateY( Yaw );
Pos += Vec * 5.0 * deltatime;
}
// update rotation
auto const rotationfactor { std::min( 1.0, 20 * deltatime ) };
Yaw -= rotationfactor * m_rotationoffsets.y;
m_rotationoffsets.y *= ( 1.0 - rotationfactor );
while( Yaw > M_PI ) {
Yaw -= 2 * M_PI;
}
while( Yaw < -M_PI ) {
Yaw += 2 * M_PI;
}
Pitch -= rotationfactor * m_rotationoffsets.x;
m_rotationoffsets.x *= ( 1.0 - rotationfactor );
if( Type == tp_Follow ) {
// jeżeli jazda z pojazdem ograniczenie kąta spoglądania w dół i w górę
Pitch = clamp( Pitch, -M_PI_4, M_PI_4 );
}
}
Math3D::vector3 TCamera::GetDirection() {
@@ -201,11 +228,15 @@ bool TCamera::SetMatrix( glm::dmat4 &Matrix ) {
void TCamera::RaLook()
{ // zmiana kierunku patrzenia - przelicza Yaw
Math3D::vector3 where = LookAt - Pos + Math3D::vector3(0, 3, 0); // trochę w górę od szyn
if ((where.x != 0.0) || (where.z != 0.0))
Yaw = atan2(-where.x, -where.z); // kąt horyzontalny
if( ( where.x != 0.0 ) || ( where.z != 0.0 ) ) {
Yaw = atan2( -where.x, -where.z ); // kąt horyzontalny
m_rotationoffsets.y = 0.0;
}
double l = Math3D::Length3(where);
if (l > 0.0)
Pitch = asin(where.y / l); // kąt w pionie
if( l > 0.0 ) {
Pitch = asin( where.y / l ); // kąt w pionie
m_rotationoffsets.x = 0.0;
}
};
void TCamera::Stop()

View File

@@ -22,14 +22,9 @@ enum TCameraType
class TCamera {
private:
glm::dvec3 m_moverate;
public: // McZapkie: potrzebuje do kiwania na boki
void Init( Math3D::vector3 NPos, Math3D::vector3 NAngle);
inline
void Reset() {
Pitch = Yaw = Roll = 0; };
void Reset();
void OnCursorMove(double const x, double const y);
bool OnCommand( command_data const &Command );
void Update();
@@ -46,4 +41,9 @@ class TCamera {
Math3D::vector3 LookAt; // współrzędne punktu, na który ma patrzeć
Math3D::vector3 vUp;
Math3D::vector3 Velocity;
private:
glm::dvec3 m_moverate;
glm::dvec3 m_rotationoffsets; // requested changes to pitch, yaw and roll
};

View File

@@ -417,15 +417,17 @@ void TController::TableClear()
eSignSkip = nullptr; // nic nie pomijamy
};
TEvent * TController::CheckTrackEvent( TTrack *Track, double const fDirection ) const
std::vector<TEvent *> TController::CheckTrackEvent( TTrack *Track, double const fDirection ) const
{ // sprawdzanie eventów na podanym torze do podstawowego skanowania
TEvent *e = (fDirection > 0) ? Track->evEvent2 : Track->evEvent1;
if (!e)
return NULL;
if (e->bEnabled)
return NULL;
// jednak wszystkie W4 do tabelki, bo jej czyszczenie na przystanku wprowadza zamieszanie
return e;
std::vector<TEvent *> events;
auto const &eventsequence { ( fDirection > 0 ? Track->m_events2 : Track->m_events1 ) };
for( auto const &event : eventsequence ) {
if( ( event.second != nullptr )
&& ( false == event.second->bEnabled ) ) {
events.emplace_back( event.second );
}
}
return events;
}
bool TController::TableAddNew()
@@ -461,7 +463,6 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
TTrack *pTrack{ nullptr }; // zaczynamy od ostatniego analizowanego toru
double fTrackLength{ 0.0 }; // długość aktualnego toru (krótsza dla pierwszego)
double fCurrentDistance{ 0.0 }; // aktualna przeskanowana długość
TEvent *pEvent{ nullptr };
double fLastDir{ 0.0 };
if (iTableDirection != iDirection ) {
@@ -537,7 +538,9 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
WriteLog( "Speed table for " + OwnerName() + " tracing through track " + pTrack->name() );
}
if( ( pEvent = CheckTrackEvent( pTrack, fLastDir ) ) != nullptr ) // jeśli jest semafor na tym torze
auto const events { CheckTrackEvent( pTrack, fLastDir ) };
for( auto *pEvent : events ) {
if( pEvent != nullptr ) // jeśli jest semafor na tym torze
{ // trzeba sprawdzić tabelkę, bo dodawanie drugi raz tego samego przystanku nie jest korzystne
if (TableNotFound(pEvent)) // jeśli nie ma
{
@@ -570,8 +573,8 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
}
}
else {
if ((true == newspeedpoint.IsProperSemaphor(OrderCurrentGet()))
&& (SemNextIndex == -1)) {
if( ( true == newspeedpoint.IsProperSemaphor( OrderCurrentGet() ) )
&& ( SemNextIndex == -1 ) ) {
SemNextIndex = iLast; // sprawdzamy czy pierwszy na drodze
}
if (Global.iWriteLogEnabled & 8) {
@@ -581,6 +584,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
}
}
}
}
} // event dodajemy najpierw, żeby móc sprawdzić, czy tor został dodany po odczytaniu prędkości następnego
if( ( pTrack->VelocityGet() == 0.0 ) // zatrzymanie
@@ -1005,7 +1009,10 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
sSpeedTable[i].iFlags = 0; // W4 nie liczy się już (nie wyśle SetVelocity)
sSpeedTable[i].fVelNext = -1; // można jechać za W4
fLastStopExpDist = -1.0f; // nie ma rozkładu, nie ma usuwania stacji
/*
// NOTE: disabled as it's no longer needed, required time is calculated as part of loading/unloading procedure
WaitingSet(60); // tak ze 2 minuty, aż wszyscy wysiądą
*/
// wykonanie kolejnego rozkazu (Change_direction albo Shunt)
JumpToNextOrder();
// ma się nie ruszać aż do momentu podania sygnału
@@ -1326,7 +1333,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
fNext = v; // istotna jest prędkość na końcu tego odcinka
fDist = d; // dlugość odcinka
}
else if ((fAcc > 0) && (v > 0) && (v <= fNext)) {
else if ((fAcc > 0) && (v >= 0) && (v <= fNext)) {
// jeśli nie ma wskazań do hamowania, można podać drogę i prędkość na jej końcu
fNext = v; // istotna jest prędkość na końcu tego odcinka
fDist = d; // dlugość odcinka (kolejne pozycje mogą wydłużać drogę, jeśli prędkość jest stała)
@@ -2273,7 +2280,7 @@ bool TController::PrepareEngine()
{ // najpierw ustalamy kierunek, jeśli nie został ustalony
if( !iDirection ) {
// jeśli nie ma ustalonego kierunku
if( mvOccupied->V == 0 ) { // ustalenie kierunku, gdy stoi
if( mvOccupied->Vel < 0.01 ) { // ustalenie kierunku, gdy stoi
iDirection = mvOccupied->CabNo; // wg wybranej kabiny
if( !iDirection ) {
// jeśli nie ma ustalonego kierunku
@@ -3221,17 +3228,24 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
iStationStart = TrainParams->StationIndex;
asNextStop = TrainParams->NextStop();
iDrivigFlags |= movePrimary; // skoro dostał rozkład, to jest teraz głównym
NewCommand = Global.asCurrentSceneryPath + NewCommand + ".wav"; // na razie jeden
if (FileExists(NewCommand)) {
NewCommand = Global.asCurrentSceneryPath + NewCommand;
auto lookup =
FileExists(
{ NewCommand },
{ ".ogg", ".flac", ".wav" } );
if( false == lookup.first.empty() ) {
// wczytanie dźwięku odjazdu podawanego bezpośrenido
tsGuardSignal = sound_source( sound_placement::external, 75.f ).deserialize( NewCommand, sound_type::single );
tsGuardSignal = sound_source( sound_placement::external, 75.f ).deserialize( lookup.first + lookup.second, sound_type::single );
iGuardRadio = 0; // nie przez radio
}
else {
NewCommand = NewCommand.insert(NewCommand.rfind('.'),"radio"); // wstawienie przed kropkč
if (FileExists(NewCommand)) {
auto lookup =
FileExists(
{ NewCommand + "radio" },
{ ".ogg", ".flac", ".wav" } );
if( false == lookup.first.empty() ) {
// wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie)
tsGuardSignal = sound_source( sound_placement::internal, 2 * EU07_SOUND_CABCONTROLSCUTOFFRANGE ).deserialize( NewCommand, sound_type::single );
tsGuardSignal = sound_source( sound_placement::internal, 2 * EU07_SOUND_CABCONTROLSCUTOFFRANGE ).deserialize( lookup.first + lookup.second, sound_type::single );
iGuardRadio = iRadioChannel;
}
}
@@ -4008,9 +4022,9 @@ TController::UpdateSituation(double dt) {
// podłączanie do składu
if (iDrivigFlags & moveConnect) {
// jeśli stanął już blisko, unikając zderzenia i można próbować podłączyć
fMinProximityDist = -0.5;
fMinProximityDist = -1.0;
fMaxProximityDist = 0.0; //[m] dojechać maksymalnie
fVelPlus = 0.5; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
fVelPlus = 1.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu
if (AIControllFlag)
{ // to robi tylko AI, wersję dla człowieka trzeba dopiero zrobić
@@ -4018,21 +4032,17 @@ TController::UpdateSituation(double dt) {
bool ok; // true gdy się podłączy (uzyskany sprzęg będzie zgodny z żądanym)
if (pVehicles[0]->DirectionGet() > 0) // jeśli sprzęg 0
{ // sprzęg 0 - próba podczepienia
if (pVehicles[0]->MoverParameters->Couplers[0].Connected) // jeśli jest coś
// wykryte (a
// chyba jest,
// nie?)
if (pVehicles[0]->MoverParameters->Attach(
0, 2, pVehicles[0]->MoverParameters->Couplers[0].Connected,
iCoupler))
{
if( pVehicles[ 0 ]->MoverParameters->Couplers[ 0 ].Connected ) {
// jeśli jest coś wykryte (a chyba jest, nie?)
if( pVehicles[ 0 ]->MoverParameters->Attach(
0, 2, pVehicles[ 0 ]->MoverParameters->Couplers[ 0 ].Connected,
iCoupler ) ) {
// pVehicles[0]->dsbCouplerAttach->SetVolume(DSBVOLUME_MAX);
// pVehicles[0]->dsbCouplerAttach->Play(0,0,0);
}
// WriteLog("CoupleDist[0]="+AnsiString(pVehicles[0]->MoverParameters->Couplers[0].CoupleDist)+",
// Connected[0]="+AnsiString(pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag));
ok = (pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag ==
iCoupler); // udało się? (mogło częściowo)
}
// udało się? (mogło częściowo)
ok = (pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag == iCoupler);
}
else // if (pVehicles[0]->MoverParameters->DirAbsolute<0) //jeśli sprzęg 1
{ // sprzęg 1 - próba podczepienia
@@ -4045,10 +4055,8 @@ TController::UpdateSituation(double dt) {
// pVehicles[0]->dsbCouplerAttach->Play(0,0,0);
}
}
// WriteLog("CoupleDist[1]="+AnsiString(Controlling->Couplers[1].CoupleDist)+",
// Connected[0]="+AnsiString(Controlling->Couplers[1].CouplingFlag));
ok = (pVehicles[0]->MoverParameters->Couplers[1].CouplingFlag ==
iCoupler); // udało się? (mogło częściowo)
// udało się? (mogło częściowo)
ok = (pVehicles[0]->MoverParameters->Couplers[1].CouplingFlag == iCoupler);
}
if (ok)
{ // jeżeli został podłączony
@@ -4602,20 +4610,7 @@ TController::UpdateSituation(double dt) {
}
// sprawdzamy możliwe ograniczenia prędkości
if( OrderCurrentGet() & ( Shunt | Obey_train ) ) {
// w Connect nie, bo moveStopHere odnosi się do stanu po połączeniu
if( ( iDrivigFlags & moveStopHere )
&& ( vel == 0.0 )
&& ( VelNext == 0.0 ) ) {
// jeśli ma czekać na wolną drogę, stoi a wyjazdu nie ma, to ma stać
VelDesired = 0.0;
}
}
if( fStopTime < 0 ) {
// czas postoju przed dalszą jazdą (np. na przystanku)
VelDesired = 0.0; // jak ma czekać, to nie ma jazdy
}
else if( VelSignal >= 0 ) {
if( VelSignal >= 0 ) {
// jeśli skład był zatrzymany na początku i teraz już może jechać
VelDesired =
min_speed(
@@ -4632,10 +4627,19 @@ TController::UpdateSituation(double dt) {
if( VelforDriver >= 0 ) {
// tu jest zero przy zmianie kierunku jazdy
// Ra: tu może być 40, jeśli mechanik nie ma znajomości szlaaku, albo kierowca jeździ 70
VelDesired = min_speed( VelDesired, VelforDriver );
VelDesired =
min_speed(
VelDesired,
VelforDriver );
}
if( ( TrainParams != nullptr )
&& ( TrainParams->CheckTrainLatency() < 5.0 )
if( fStopTime < 0 ) {
// czas postoju przed dalszą jazdą (np. na przystanku)
VelDesired = 0.0; // jak ma czekać, to nie ma jazdy
}
if( ( OrderCurrentGet() & Obey_train ) != 0 ) {
if( ( TrainParams->CheckTrainLatency() < 5.0 )
&& ( TrainParams->TTVmax > 0.0 ) ) {
// jesli nie spozniony to nie przekraczać rozkladowej
VelDesired =
@@ -4643,20 +4647,36 @@ TController::UpdateSituation(double dt) {
VelDesired,
TrainParams->TTVmax );
}
if (VelDesired > 0.0)
if( ( ( iDrivigFlags & moveStopHere ) == 0 )
|| ( ( SemNextIndex != -1 )
if( ( ( iDrivigFlags & moveStopHere ) != 0 )
&& ( vel < 0.01 )
&& ( SemNextIndex != -1 )
&& ( SemNextIndex < sSpeedTable.size() ) // BUG: index can point at non-existing slot. investigate reason(s)
&& ( sSpeedTable[SemNextIndex].fVelNext != 0.0 ) ) ) {
// jeśli można jechać, to odpalić dźwięk kierownika oraz zamknąć drzwi w
// składzie, jeśli nie mamy czekać na sygnał też trzeba odpalić
if (iDrivigFlags & moveGuardSignal)
{ // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas stania
&& ( sSpeedTable[ SemNextIndex ].fVelNext == 0.0 ) ) {
// don't depart if told to wait at passenger stop until allowed to by the signal
VelDesired = 0.0;
}
}
if( ( OrderCurrentGet() & ( Shunt | Obey_train ) ) != 0 ) {
// w Connect nie, bo moveStopHere odnosi się do stanu po połączeniu
if( ( ( iDrivigFlags & moveStopHere ) != 0 )
&& ( vel < 0.01 )
&& ( VelSignal == 0.0 ) ) {
// jeśli ma czekać na wolną drogę, stoi a wyjazdu nie ma, to ma stać
VelDesired = 0.0;
}
}
// end of speed caps checks
if( ( ( OrderCurrentGet() & Obey_train ) != 0 )
&& ( ( iDrivigFlags & moveGuardSignal ) != 0 )
&& ( VelDesired > 0.0 ) ) {
// komunikat od kierownika tu, bo musi być wolna droga i odczekany czas stania
iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać
if( false == tsGuardSignal.empty() ) {
tsGuardSignal.stop();
// w zasadzie to powinien mieć flagę, czy jest dźwiękiem radiowym, czy
// bezpośrednim
// w zasadzie to powinien mieć flagę, czy jest dźwiękiem radiowym, czy bezpośrednim
// albo trzeba zrobić dwa dźwięki, jeden bezpośredni, słyszalny w
// pobliżu, a drugi radiowy, słyszalny w innych lokomotywach
// na razie zakładam, że to nie jest dźwięk radiowy, bo trzeba by zrobić
@@ -4671,8 +4691,7 @@ TController::UpdateSituation(double dt) {
else {
// if (iGuardRadio==iRadioChannel) //zgodność kanału
// if (!FreeFlyModeFlag) //obserwator musi być w środku pojazdu
// (albo może mieć radio przenośne) - kierownik mógłby powtarzać
// przy braku reakcji
// (albo może mieć radio przenośne) - kierownik mógłby powtarzać przy braku reakcji
// TODO: proper system for sending/receiving radio messages
// place the sound in appropriate cab of the manned vehicle
tsGuardSignal.owner( pVehicle );
@@ -4681,8 +4700,8 @@ TController::UpdateSituation(double dt) {
}
}
}
}
if( mvOccupied->V == 0.0 ) {
if( mvOccupied->Vel < 0.01 ) {
// Ra 2014-03: jesli skład stoi, to działa na niego składowa styczna grawitacji
AbsAccS = fAccGravity;
}
@@ -4761,6 +4780,8 @@ TController::UpdateSituation(double dt) {
}
else {
// przy dużej różnicy wysoki stopień (1,00 potrzebnego opoznienia)
if( ( std::max( 100.0, fMaxProximityDist ) + fBrakeDist * braking_distance_multiplier( VelNext ) ) >= ( ActualProximityDist - fMaxProximityDist ) ) {
// don't slow down prematurely; as long as we have room to come to a full stop at a safe distance, we're good
// ensure some minimal coasting speed, otherwise a vehicle entering this zone at very low speed will be crawling forever
auto const brakingpointoffset = VelNext * braking_distance_multiplier( VelNext );
AccDesired = std::min(
@@ -4774,6 +4795,7 @@ TController::UpdateSituation(double dt) {
brakingpointoffset ) )
+ 0.1 ) ); // najpierw hamuje mocniej, potem zluzuje
}
}
AccDesired = std::min( AccDesired, AccPreferred );
}
else {
@@ -5220,13 +5242,22 @@ TController::UpdateSituation(double dt) {
}
if (AIControllFlag)
{ // odhamowywanie składu po zatrzymaniu i zabezpieczanie lokomotywy
if ((OrderList[OrderPos] & (Disconnect | Connect)) ==
0) // przy (p)odłączaniu nie zwalniamy tu hamulca
if ((mvOccupied->V == 0.0) && ((VelDesired == 0.0) || (AccDesired == 0.0)))
if (mvOccupied->BrakeCtrlPos == mvOccupied->Handle->GetPos(bh_RP))
mvOccupied->IncLocalBrakeLevel(1); // dodatkowy na pozycję 1
else
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_RP));
if( ( ( OrderList[ OrderPos ] & ( Disconnect | Connect ) ) == 0 )
&& ( std::abs( fAccGravity ) < 0.01 ) ) {
// przy (p)odłączaniu nie zwalniamy tu hamulca
// only do this on flats, on slopes keep applied the train brake
if( ( mvOccupied->Vel < 0.01 )
&& ( ( VelDesired == 0.0 )
|| ( AccDesired == 0.0 ) ) ) {
if( mvOccupied->BrakeCtrlPos == mvOccupied->Handle->GetPos( bh_RP ) ) {
// dodatkowy na pozycję 1
mvOccupied->IncLocalBrakeLevel( 1 );
}
else {
mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) );
}
}
}
}
break; // rzeczy robione przy jezdzie
} // switch (OrderList[OrderPos])
@@ -5529,12 +5560,17 @@ bool TController::BackwardTrackBusy(TTrack *Track)
TEvent * TController::CheckTrackEventBackward(double fDirection, TTrack *Track)
{ // sprawdzanie eventu w torze, czy jest sygnałowym - skanowanie do tyłu
TEvent *e = (fDirection > 0) ? Track->evEvent2 : Track->evEvent1;
if (e)
if (!e->bEnabled) // jeśli sygnałowy (nie dodawany do kolejki)
if (e->Type == tp_GetValues) // PutValues nie może się zmienić
return e;
return NULL;
// NOTE: this method returns only one event which meets the conditions, due to limitations in the caller
// TBD, TODO: clean up the caller and return all suitable events, as in theory things will go awry if the track has more than one signal
auto const &eventsequence { ( fDirection > 0 ? Track->m_events2 : Track->m_events1 ) };
for( auto const &event : eventsequence ) {
if( ( event.second != nullptr )
&& ( false == event.second->bEnabled )
&& ( event.second->Type == tp_GetValues ) ) {
return event.second;
}
}
return nullptr;
};
TTrack * TController::BackwardTraceRoute(double &fDistance, double &fDirection, TTrack *Track, TEvent *&Event)

View File

@@ -349,7 +349,7 @@ private:
int OrderDirectionChange(int newdir, TMoverParameters *Vehicle);
void Lights(int head, int rear);
// Ra: metody obsługujące skanowanie toru
TEvent *CheckTrackEvent(TTrack *Track, double const fDirection ) const;
std::vector<TEvent *> CheckTrackEvent(TTrack *Track, double const fDirection ) const;
bool TableAddNew();
bool TableNotFound(TEvent const *Event) const;
void TableTraceRoute(double fDistance, TDynamicObject *pVehicle = nullptr);

View File

@@ -52,6 +52,18 @@ GetSubmodelFromName( TModel3d * const Model, std::string const Name ) {
nullptr );
}
// Ra 2015-01: sprawdzenie dostępności tekstury o podanej nazwie
std::string
TextureTest( std::string const &Name ) {
auto const lookup {
FileExists(
{ Global.asCurrentTexturePath + Name, Name, szTexturePath + Name },
{ ".mat", ".dds", ".tga", ".bmp" } ) };
return ( lookup.first + lookup.second );
}
//---------------------------------------------------------------------------
void TAnimPant::AKP_4E()
{ // ustawienie wymiarów dla pantografu AKP-4E
@@ -3033,8 +3045,13 @@ bool TDynamicObject::Update(double dt, double dt1)
masa += p->MoverParameters->TotalMass;
osie += p->MoverParameters->NAxles;
}
double RapidMult = 1.0;
if (((MoverParameters->BrakeDelays & (bdelay_P + bdelay_R)) == (bdelay_P + bdelay_R))
&& (MoverParameters->BrakeDelayFlag & bdelay_P))
RapidMult = MoverParameters->RapidMult;
auto const amax = RapidMult * std::min(FmaxPN / masamax, MoverParameters->MED_amax);
auto const amax = std::min(FmaxPN / masamax, MoverParameters->MED_amax);
if ((MoverParameters->Vel < 0.5) && (MoverParameters->BrakePress > 0.2) ||
(dDoorMoveL > 0.001) || (dDoorMoveR > 0.001))
{
@@ -3062,7 +3079,20 @@ bool TDynamicObject::Update(double dt, double dt1)
{
Fzad = std::max(MoverParameters->StopBrakeDecc * masa, Fzad);
}
if ((Fzad > 1) && (!MEDLogFile.is_open()) && (MoverParameters->Vel > 1))
{
MEDLogFile.open(std::string("MEDLOGS/" + MoverParameters->Name + "_" + to_string(++MEDLogCount) + ".csv"),
std::ios::in | std::ios::out | std::ios::trunc);
MEDLogFile << std::string("t\tVel\tMasa\tOsie\tFmaxPN\tFmaxED\tFfulED\tFrED\tFzad\tFzadED\tFzadPN").c_str();
for(int k=1;k<=np;k++)
{
MEDLogFile << "\tBP" << k;
}
MEDLogFile << "\n";
MEDLogFile.flush();
MEDLogInactiveTime = 0;
MEDLogTime = 0;
}
auto FzadED { 0.0 };
if( ( MoverParameters->EpFuse && (MoverParameters->BrakeHandle != MHZ_EN57))
|| ( ( MoverParameters->BrakeHandle == MHZ_EN57 )
@@ -3197,6 +3227,38 @@ bool TDynamicObject::Update(double dt, double dt1)
MED[0][6] = FzadPN*0.001;
MED[0][7] = nPrzekrF;
if (MEDLogFile.is_open())
{
MEDLogFile << MEDLogTime << "\t" << MoverParameters->Vel << "\t" << masa*0.001 << "\t" << osie << "\t" << FmaxPN*0.001 << "\t" << FmaxED*0.001 << "\t"
<< FfulED*0.001 << "\t" << FrED*0.001 << "\t" << Fzad*0.001 << "\t" << FzadED*0.001 << "\t" << FzadPN*0.001;
for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0, 4); p;
(true == kier ? p = p->NextC(4) : p = p->PrevC(4)))
{
MEDLogFile << "\t" << p->MoverParameters->BrakePress;
}
MEDLogFile << "\n";
if (floor(MEDLogTime + dt1) > floor(MEDLogTime))
{
MEDLogFile.flush();
}
MEDLogTime += dt1;
if ((MoverParameters->Vel < 0.1) || (MoverParameters->MainCtrlPos > 0))
{
MEDLogInactiveTime += dt1;
}
else
{
MEDLogInactiveTime = 0;
}
if (MEDLogInactiveTime > 5)
{
MEDLogFile.flush();
MEDLogFile.close();
}
}
delete[] PrzekrF;
delete[] FzED;
delete[] FzEP;
@@ -4482,7 +4544,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
mdModel = TModelsManager::GetModel(asModel, true);
if (ReplacableSkin != "none")
{
std::string nowheretexture = TextureTest( Global.asCurrentTexturePath + "nowhere" ); // na razie prymitywnie
std::string nowheretexture = TextureTest( "nowhere" ); // na razie prymitywnie
if( false == nowheretexture.empty() ) {
m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Material( nowheretexture );
}
@@ -4552,7 +4614,8 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
}
if( !MoverParameters->LoadAccepted.empty() ) {
if( MoverParameters->EnginePowerSource.SourceType == CurrentCollector ) {
if( ( MoverParameters->EnginePowerSource.SourceType == CurrentCollector )
&& ( asLoadName == "pantstate" ) ) {
// wartość niby "pantstate" - nazwa dla formalności, ważna jest ilość
if( MoverParameters->Load == 1 ) {
MoverParameters->PantFront( true );
@@ -4609,10 +4672,12 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
*/
if( true == pAnimations.empty() )
{ // jeśli nie ma jeszcze tabeli animacji, można odczytać nowe ilości
int co = 0, ile = -1;
int co = 0;
iAnimations = 0;
int ile;
do
{ // kolejne liczby to ilość animacj, -1 to znacznik końca
ile = -1;
parser.getTokens( 1, false );
parser >> ile; // ilość danego typu animacji
if (ile >= 0)
@@ -6209,17 +6274,6 @@ int TDynamicObject::RouteWish(TTrack *tr)
return Mechanik ? Mechanik->CrossRoute(tr) : 0; // wg AI albo prosto
};
std::string TDynamicObject::TextureTest(std::string const &name)
{ // Ra 2015-01: sprawdzenie dostępności tekstury o podanej nazwie
std::vector<std::string> extensions = { ".mat", ".dds", ".tga", ".bmp" };
for( auto const &extension : extensions ) {
if( true == FileExists( name + extension ) ) {
return name + extension;
}
}
return ""; // nie znaleziona
};
void TDynamicObject::DestinationSet(std::string to, std::string numer)
{ // ustawienie stacji docelowej oraz wymiennej tekstury 4, jeśli istnieje plik
// w zasadzie, to każdy wagon mógłby mieć inną stację docelową
@@ -6233,13 +6287,20 @@ void TDynamicObject::DestinationSet(std::string to, std::string numer)
numer = Bezogonkow(numer);
asDestination = to;
to = Bezogonkow(to); // do szukania pliku obcinamy ogonki
if( true == to.empty() ) {
to = "nowhere";
}
// destination textures are kept in the vehicle's directory so we point the current texture path there
auto const currenttexturepath { Global.asCurrentTexturePath };
Global.asCurrentTexturePath = asBaseDir;
// now see if we can find any version of the texture
std::vector<std::string> destinations = {
asBaseDir + numer + "@" + MoverParameters->TypeName,
asBaseDir + numer,
asBaseDir + to + "@" + MoverParameters->TypeName,
asBaseDir + to,
asBaseDir + "nowhere" };
numer + '@' + MoverParameters->TypeName,
numer,
to + '@' + MoverParameters->TypeName,
to,
"nowhere" + '@' + MoverParameters->TypeName,
"nowhere" };
for( auto const &destination : destinations ) {
@@ -6249,6 +6310,8 @@ void TDynamicObject::DestinationSet(std::string to, std::string numer)
break;
}
}
// whether we got anything, restore previous texture path
Global.asCurrentTexturePath = currenttexturepath;
};
void TDynamicObject::OverheadTrack(float o)
@@ -6370,7 +6433,7 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub
// main engine sound
if( true == Vehicle.Mains ) {
if( ( std::fabs( Vehicle.enrot ) > 0.01 )
if( ( std::abs( Vehicle.enrot ) > 0.01 )
// McZapkie-280503: zeby dla dumb dzialal silnik na jalowych obrotach
|| ( Vehicle.EngineType == Dumb ) ) {
@@ -6501,11 +6564,12 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub
std::max( goalvolume, currentvolume - changerate ) :
std::min( goalvolume, currentvolume + changerate ) );
if( volume > 0.05 ) {
engine_turbo
.pitch( 0.4 + engine_turbo_pitch * 0.4 )
.gain( volume )
.play( sound_flags::exclusive | sound_flags::looping );
.gain( volume );
if( volume > 0.05 ) {
engine_turbo.play( sound_flags::exclusive | sound_flags::looping );
}
else {
engine_turbo.stop();
@@ -6539,7 +6603,7 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub
if( ( true == Vehicle.Mains )
&& ( false == motors.empty() ) ) {
if( std::fabs( Vehicle.enrot ) > 0.01 ) {
if( std::abs( Vehicle.enrot ) > 0.01 ) {
auto const &motor { motors.front() };
// frequency calculation

View File

@@ -602,11 +602,14 @@ private:
// zapytanie do AI, po którym segmencie skrzyżowania jechać
int RouteWish(TTrack *tr);
void DestinationSet(std::string to, std::string numer);
std::string TextureTest(std::string const &name);
void OverheadTrack(float o);
double MED[9][8]; // lista zmiennych do debugowania hamulca ED
static std::string const MED_labels[ 8 ];
std::ofstream MEDLogFile; // zapis parametrów hamowania
double MEDLogTime = 0;
double MEDLogInactiveTime = 0;
int MEDLogCount = 0;
};

View File

@@ -196,11 +196,90 @@ bool TEventLauncher::IsGlobal() const {
&& ( dRadius < 0.0 ) ); // bez ograniczenia zasięgu
}
// calculates node's bounding radius
void
// radius() subclass details, calculates node's bounding radius
float
TEventLauncher::radius_() {
m_area.radius = std::sqrt( dRadius );
return std::sqrt( dRadius );
}
// serialize() subclass details, sends content of the subclass to provided stream
void
TEventLauncher::serialize_( std::ostream &Output ) const {
// TODO: implement
}
// deserialize() subclass details, restores content of the subclass from provided stream
void
TEventLauncher::deserialize_( std::istream &Input ) {
// TODO: implement
}
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void
TEventLauncher::export_as_text_( std::ostream &Output ) const {
// header
Output << "eventlauncher ";
// location
Output
<< location().x << ' '
<< location().y << ' '
<< location().z << ' ';
// activation radius
Output
<< ( dRadius > 0 ? std::sqrt( dRadius ) : dRadius ) << ' ';
// activation key
if( iKey != 0 ) {
auto const key { iKey & 0xff };
auto const modifier { ( iKey & 0xff00 ) >> 8 };
if( ( key >= GLFW_KEY_A ) && ( key <= GLFW_KEY_Z ) ) {
Output << static_cast<char>(( 'A' + key - GLFW_KEY_A + ( ( modifier & 1 ) == 0 ? 32 : 0 ) )) << ' ';
}
else if( ( key >= GLFW_KEY_0 ) && ( key <= GLFW_KEY_9 ) ) {
Output << static_cast<char>(( '0' + key - GLFW_KEY_0 )) << ' ';
}
}
else {
Output << "none ";
}
// activation interval or hour
if( DeltaTime != 0 ) {
// cyclical launcher
Output << -DeltaTime << ' ';
}
else {
// single activation at specified time
if( ( iHour < 0 )
&& ( iMinute < 0 ) ) {
Output << DeltaTime << ' ';
}
else {
// NOTE: activation hour might be affected by user-requested time offset
auto const timeoffset{ static_cast<int>( Global.ScenarioTimeOffset * 60 ) };
auto const adjustedtime{ clamp_circular( iHour * 60 + iMinute - timeoffset, 24 * 60 ) };
Output
<< ( adjustedtime / 60 ) % 24
<< ( adjustedtime % 60 )
<< ' ';
}
}
// associated event(s)
Output << ( asEvent1Name.empty() ? "none" : asEvent1Name ) << ' ';
Output << ( asEvent2Name.empty() ? "none" : asEvent2Name ) << ' ';
if( false == asMemCellName.empty() ) {
// conditional event
Output
<< "condition "
<< asMemCellName << ' '
<< szText << ' '
<< ( ( iCheckMask & conditional_memval1 ) != 0 ? to_string( fVal1 ) : "*" ) << ' '
<< ( ( iCheckMask & conditional_memval2 ) != 0 ? to_string( fVal2 ) : "*" ) << ' ';
}
// footer
Output
<< "end"
<< "\n";
}
//---------------------------------------------------------------------------

View File

@@ -14,7 +14,7 @@ http://mozilla.org/MPL/2.0/.
#include "Classes.h"
#include "scenenode.h"
class TEventLauncher : public editor::basic_node {
class TEventLauncher : public scene::basic_node {
public:
// constructor
@@ -37,12 +37,17 @@ public:
int iCheckMask { 0 };
double dRadius { 0.0 };
protected:
// calculates node's bounding radius
void
radius_();
private:
// methods
// radius() subclass details, calculates node's bounding radius
float radius_();
// serialize() subclass details, sends content of the subclass to provided stream
void serialize_( std::ostream &Output ) const;
// deserialize() subclass details, restores content of the subclass from provided stream
void deserialize_( std::istream &Input );
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void export_as_text_( std::ostream &Output ) const;
// members
int iKey { 0 };
double DeltaTime { -1.0 };

283
Event.cpp
View File

@@ -301,9 +301,9 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org)
if (token.find('#') != std::string::npos)
token.erase(token.find('#')); // obcięcie unikatowości
win1250_to_ascii( token ); // get rid of non-ascii chars
bEnabled = false; // nie do kolejki (dla SetVelocity też, ale jak jest do toru
// dowiązany)
Params[6].asCommand = cm_PassengerStopPoint;
// nie do kolejki (dla SetVelocity też, ale jak jest do toru dowiązany)
bEnabled = false;
}
else if (token == "SetVelocity")
{
@@ -325,11 +325,6 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org)
bEnabled = false;
Params[6].asCommand = cm_ShuntVelocity;
}
//else if (str == "SetProximityVelocity")
//{
// bEnabled = false;
// Params[6].asCommand = cm_SetProximityVelocity;
//}
else if (token == "OutsideStation")
{
bEnabled = false; // ma być skanowny, aby AI nie przekraczało W5
@@ -388,6 +383,11 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org)
}
}
} while( token.compare( "endevent" ) != 0 );
while( paramidx < 8 ) {
// HACK: mark unspecified lights with magic value
Params[ paramidx ].asdouble = -2.0;
++paramidx;
}
break;
}
case tp_Visible: // zmiana wyświetlania obiektu
@@ -400,7 +400,7 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org)
case tp_Velocity:
parser->getTokens();
*parser >> token;
Params[0].asdouble = atof(token.c_str()) * 0.28;
Params[0].asdouble = atof(token.c_str()) * ( 1000.0 / 3600.0 );
parser->getTokens();
*parser >> token;
break;
@@ -417,7 +417,6 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org)
*parser >> token;
break;
case tp_Exit:
asNodeName = ExchangeCharInString( asNodeName, '_', ' ' );
parser->getTokens();
*parser >> token;
break;
@@ -534,8 +533,7 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org)
*parser >> token;
break;
case tp_Multiple: {
int paramidx { 0 };
bool ti { false }; // flaga dla else
bool conditionalelse { false }; // flaga dla else
parser->getTokens();
*parser >> token;
@@ -546,12 +544,7 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org)
if( token != "else" ) {
if( token.substr( 0, 5 ) != "none_" ) {
// eventy rozpoczynające się od "none_" są ignorowane
ext_params.push_back(std::make_pair(TParam(), 0));
ext_params[ paramidx ].first.asText = new char[ token.size() + 1 ];
strcpy( ext_params[ paramidx ].first.asText, token.c_str() );
// oflagowanie dla eventów "else"
ext_params[paramidx].second = (int)ti;
++paramidx;
m_children.emplace_back( token, nullptr, ( conditionalelse == false ) );
}
else {
WriteLog( "Multi-event \"" + asName + "\" ignored link to event \"" + token + "\"" );
@@ -559,8 +552,8 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org)
}
else {
// zmiana flagi dla słowa "else"
ti = !ti;
iFlags |= conditional_anyelse;
conditionalelse = !conditionalelse;
m_conditionalelse = true;
}
parser->getTokens();
*parser >> token;
@@ -597,6 +590,185 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org)
}
};
// sends basic content of the class in legacy (text) format to provided stream
void
TEvent::export_as_text( std::ostream &Output ) const {
if( Type == tp_Unknown ) { return; }
// header
Output << "event ";
// name
Output << asName << ' ';
// type
std::vector<std::string> const types {
"unknown", "sound", "exit", "disable", "velocity", "animation", "lights",
"updatevalues", "getvalues", "putvalues", "switch", "dynvel", "trackvel",
"multiple", "addvalues", "copyvalues", "whois", "logvalues", "visible",
"voltage", "message", "friction" };
Output << types[ Type ] << ' ';
// delay
Output << fDelay << ' ';
// associated node
Output << ( asNodeName.empty() ? "none" : asNodeName ) << ' ';
// type-specific attributes
switch( Type ) {
case tp_AddValues:
case tp_UpdateValues: {
Output
<< ( ( iFlags & update_memstring ) == 0 ? "*" : Params[ 0 ].asText ) << ' '
<< ( ( iFlags & update_memval1 ) == 0 ? "*" : to_string( Params[ 1 ].asdouble ) ) << ' '
<< ( ( iFlags & update_memval2 ) == 0 ? "*" : to_string( Params[ 2 ].asdouble ) ) << ' ';
break;
}
case tp_CopyValues: {
// NOTE: there's no way to get the original parameter value if it doesn't point to existing memcell
Output
<< ( Params[ 9 ].asMemCell == nullptr ? "none" : Params[ 9 ].asMemCell->name() ) << ' '
<< ( iFlags & ( update_memstring | update_memval1 | update_memval2 ) ) << ' ';
break;
}
case tp_PutValues: {
Output
// location
<< Params[ 3 ].asdouble << ' '
<< Params[ 4 ].asdouble << ' '
<< Params[ 5 ].asdouble << ' '
// command
<< Params[ 0 ].asText << ' '
<< Params[ 1 ].asdouble << ' '
<< Params[ 2 ].asdouble << ' ';
break;
}
case tp_Multiple: {
for( auto const &childevent : m_children ) {
if( std::get<TEvent *>( childevent ) == nullptr ) { continue; }
if( true == std::get<bool>( childevent ) ) {
Output << std::get<std::string>( childevent ) << ' ';
}
}
// optional 'else' block
if( true == m_conditionalelse ) {
Output << "else ";
for( auto const &childevent : m_children ) {
if( std::get<TEvent *>( childevent ) == nullptr ) { continue; }
if( false == std::get<bool>( childevent ) ) {
Output << std::get<std::string>( childevent ) << ' ';
}
}
}
break;
}
case tp_Visible: {
Output << Params[ 0 ].asInt << ' ';
break;
}
case tp_Switch: {
Output << Params[ 0 ].asInt << ' ';
if( ( Params[ 1 ].asdouble != -1.0 )
|| ( Params[ 2 ].asdouble != -1.0 ) ) {
Output << Params[ 1 ].asdouble << ' ';
}
if( Params[ 2 ].asdouble != -1.0 ) {
Output << Params[ 2 ].asdouble << ' ';
}
break;
}
case tp_Lights: {
auto lightidx { 0 };
while( ( lightidx < iMaxNumLights )
&& ( Params[ lightidx ].asdouble > -2.0 ) ) {
Output << Params[ lightidx ].asdouble << ' ';
++lightidx;
}
break;
}
case tp_Animation: {
// animation type
Output << (
Params[ 0 ].asInt == 1 ? "rotate" :
Params[ 0 ].asInt == 2 ? "translate" :
// NOTE: .vmd animation doesn't preserve file name, can't be exported. TODO: fix this
Params[ 0 ].asInt == 8 ? "digital" :
"none" )
<< ' ';
// submodel
Output << (
Params[ 9 ].asAnimContainer != nullptr ?
Params[ 9 ].asAnimContainer->NameGet() :
"none" ) << ' ';
// animation parameters
Output
<< Params[ 1 ].asdouble << ' '
<< Params[ 2 ].asdouble << ' '
<< Params[ 3 ].asdouble << ' '
<< Params[ 4 ].asdouble << ' ';
break;
}
case tp_Sound: {
// playback mode
Output << Params[ 0 ].asInt << ' ';
// optional radio channel
if( Params[ 1 ].asdouble > 0.0 ) {
Output << Params[ 1 ].asdouble << ' ';
}
break;
}
case tp_DynVel:
case tp_TrackVel:
case tp_Voltage:
case tp_Friction: {
Output << Params[ 0 ].asdouble << ' ';
break;
}
case tp_Velocity: {
Output << Params[ 0 ].asdouble * ( 3600.0 / 1000.0 ) << ' ';
break;
}
case tp_WhoIs: {
Output
<< ( iFlags & ( update_memstring | update_memval1 | update_memval2 ) ) << ' ';
break;
}
default: {
break;
}
}
// optional conditions
// NOTE: for flexibility condition check and export is performed for all event types rather than only for these which support it currently
auto const isconditional { ( conditional_trackoccupied | conditional_trackfree | conditional_propability | conditional_memcompare ) };
if( ( ( iFlags & isconditional ) != 0 ) ) {
Output << "condition ";
if( ( iFlags & conditional_trackoccupied ) != 0 ) {
Output << "trackoccupied ";
}
else if( ( iFlags & conditional_trackfree ) != 0 ) {
Output << "trackfree ";
}
else if( ( iFlags & conditional_propability ) != 0 ) {
Output
<< "propability "
<< Params[ 10 ].asdouble << ' ';
}
else if( ( iFlags & conditional_memcompare ) != 0 ) {
Output
<< "memcompare "
<< ( ( iFlags & conditional_memstring ) == 0 ? "*" : Params[ 10 ].asText ) << ' '
<< ( ( iFlags & conditional_memval1 ) == 0 ? "*" : to_string( Params[ 11 ].asdouble ) ) << ' '
<< ( ( iFlags & conditional_memval2 ) == 0 ? "*" : to_string( Params[ 12 ].asdouble ) ) << ' ';
}
}
if( fRandomDelay != 0.0 ) {
Output
<< "randomdelay "
<< fRandomDelay << ' ';
}
// footer
Output
<< "endevent"
<< "\n";
}
void TEvent::AddToQuery( TEvent *Event, TEvent *&Start ) {
TEvent *target( Start );
@@ -994,6 +1166,10 @@ event_manager::CheckQuery() {
case tp_Lights: {
if( m_workevent->Params[ 9 ].asModel ) {
for( i = 0; i < iMaxNumLights; ++i ) {
if( m_workevent->Params[ i ].asdouble == -2.0 ) {
// processed all supplied values, bail out
break;
}
if( m_workevent->Params[ i ].asdouble >= 0 ) {
// -1 zostawia bez zmiany
m_workevent->Params[ 9 ].asModel->LightSet(
@@ -1005,8 +1181,8 @@ event_manager::CheckQuery() {
break;
}
case tp_Visible: {
if( m_workevent->Params[ 9 ].asEditorNode )
m_workevent->Params[ 9 ].asEditorNode->visible( m_workevent->Params[ 0 ].asInt > 0 );
if( m_workevent->Params[ 9 ].asSceneNode )
m_workevent->Params[ 9 ].asSceneNode->visible( m_workevent->Params[ 0 ].asInt > 0 );
break;
}
case tp_Velocity: {
@@ -1014,7 +1190,6 @@ event_manager::CheckQuery() {
break;
}
case tp_Exit: {
Global.iTextMode = -1; // wyłączenie takie samo jak sekwencja F10 -> Y
return false;
}
case tp_Sound: {
@@ -1115,17 +1290,20 @@ event_manager::CheckQuery() {
Error("Event \"DynVel\" is obsolete");
break;
case tp_Multiple: {
auto const bCondition = EventConditon(m_workevent);
if( ( bCondition )
|| ( m_workevent->iFlags & conditional_anyelse ) ) {
auto const condition { EventConditon( m_workevent ) };
if( ( true == condition )
|| ( true == m_workevent->m_conditionalelse ) ) {
// warunek spelniony albo było użyte else
WriteLog("Type: Multi-event");
for (i = 0; i < m_workevent->ext_params.size(); ++i) {
// dodawane do kolejki w kolejności zapisania
if( m_workevent->ext_params[ i ].first.asEvent ) {
if( bCondition != (bool)m_workevent->ext_params[i].second ) {
if( m_workevent->ext_params[ i ].first.asEvent != m_workevent )
AddToQuery( m_workevent->ext_params[ i ].first.asEvent, m_workevent->Activator ); // normalnie dodać
for( auto &childevent : m_workevent->m_children ) {
auto *childeventdata { std::get<TEvent*>( childevent ) };
if( childeventdata == nullptr ) { continue; }
if( std::get<bool>( childevent ) != condition ) { continue; }
if( childeventdata != m_workevent ) {
// normalnie dodać
AddToQuery( childeventdata, m_workevent->Activator );
}
else {
// jeśli ma być rekurencja to musi mieć sensowny okres powtarzania
if( m_workevent->fDelay >= 5.0 ) {
@@ -1133,11 +1311,9 @@ event_manager::CheckQuery() {
}
}
}
}
}
if( Global.iMultiplayer ) {
// dajemy znać do serwera o wykonaniu
if( ( m_workevent->iFlags & conditional_anyelse ) == 0 ) {
if( false == m_workevent->m_conditionalelse ) {
// jednoznaczne tylko, gdy nie było else
if( m_workevent->Activator ) {
multiplayer::WyslijEvent( m_workevent->asName, m_workevent->Activator->name() );
@@ -1397,7 +1573,6 @@ event_manager::InitEvents() {
else {
ErrorLog( "Bad event: animation event \"" + event->asName + "\" cannot find model instance \"" + event->asNodeName + "\"" );
}
event->asNodeName = "";
break;
}
case tp_Lights: {
@@ -1407,12 +1582,11 @@ event_manager::InitEvents() {
event->Params[ 9 ].asModel = instance;
else
ErrorLog( "Bad event: lights event \"" + event->asName + "\" cannot find model instance \"" + event->asNodeName + "\"" );
event->asNodeName = "";
break;
}
case tp_Visible: {
// ukrycie albo przywrócenie obiektu
editor::basic_node *node = simulation::Instances.find( event->asNodeName ); // najpierw model
scene::basic_node *node = simulation::Instances.find( event->asNodeName ); // najpierw model
if( node == nullptr ) {
// albo tory?
node = simulation::Paths.find( event->asNodeName );
@@ -1422,12 +1596,11 @@ event_manager::InitEvents() {
node = simulation::Traction.find( event->asNodeName );
}
if( node != nullptr )
event->Params[ 9 ].asEditorNode = node;
event->Params[ 9 ].asSceneNode = node;
else {
event->m_ignored = true;
ErrorLog( "Bad event: visibility event \"" + event->asName + "\" cannot find item \"" + event->asNodeName + "\"" );
}
event->asNodeName = "";
break;
}
case tp_Switch: {
@@ -1453,7 +1626,6 @@ event_manager::InitEvents() {
else {
ErrorLog( "Bad event: switch event \"" + event->asName + "\" cannot find track \"" + event->asNodeName + "\"" );
}
event->asNodeName = "";
break;
}
case tp_Sound: {
@@ -1463,7 +1635,6 @@ event_manager::InitEvents() {
event->Params[ 9 ].tsTextSound = sound;
else
ErrorLog( "Bad event: sound event \"" + event->asName + "\" cannot find static sound \"" + event->asNodeName + "\"" );
event->asNodeName = "";
break;
}
case tp_TrackVel: {
@@ -1479,7 +1650,6 @@ event_manager::InitEvents() {
ErrorLog( "Bad event: track velocity event \"" + event->asName + "\" cannot find track \"" + event->asNodeName + "\"" );
}
}
event->asNodeName = "";
break;
}
case tp_DynVel: {
@@ -1493,7 +1663,6 @@ event_manager::InitEvents() {
else
ErrorLog( "Bad event: vehicle velocity event \"" + event->asName + "\" cannot find vehicle \"" + event->asNodeName + "\"" );
}
event->asNodeName = "";
break;
}
case tp_Multiple: {
@@ -1519,15 +1688,10 @@ event_manager::InitEvents() {
event->iFlags &= ~( conditional_memstring | conditional_memval1 | conditional_memval2 );
}
}
for( int i = 0; i < event->ext_params.size(); ++i ) {
if( event->ext_params[ i ].first.asText != nullptr ) {
cellastext = event->ext_params[ i ].first.asText;
SafeDeleteArray( event->ext_params[ i ].first.asText );
event->ext_params[ i ].first.asEvent = FindEvent( cellastext );
if( event->ext_params[ i ].first.asEvent == nullptr ) {
// Ra: tylko w logu informacja o braku
ErrorLog( "Bad event: multi-event \"" + event->asName + "\" cannot find event \"" + cellastext + "\"" );
}
for( auto &childevent : event->m_children ) {
std::get<TEvent *>( childevent ) = FindEvent( std::get<std::string>( childevent ) );
if( std::get<TEvent *>( childevent ) == nullptr ) {
ErrorLog( "Bad event: multi-event \"" + event->asName + "\" cannot find event \"" + std::get<std::string>( childevent ) + "\"" );
}
}
break;
@@ -1541,7 +1705,6 @@ event_manager::InitEvents() {
else
ErrorLog( "Bad event: voltage event \"" + event->asName + "\" cannot find power source \"" + event->asNodeName + "\"" );
}
event->asNodeName = "";
break;
}
case tp_Message: {
@@ -1590,6 +1753,20 @@ event_manager::InitLaunchers() {
}
}
// sends basic content of the class in legacy (text) format to provided stream
void
event_manager::export_as_text( std::ostream &Output ) const {
Output << "// events\n";
for( auto const *event : m_events ) {
event->export_as_text( Output );
}
Output << "// event launchers\n";
for( auto const *launcher : m_launchers.sequence() ) {
launcher->export_as_text( Output );
}
}
// legacy method, verifies condition for specified event
bool
event_manager::EventConditon( TEvent *Event ) {

56
Event.h
View File

@@ -17,7 +17,6 @@ http://mozilla.org/MPL/2.0/.
enum TEventType {
tp_Unknown,
tp_Sound,
tp_SoundPos,
tp_Exit,
tp_Disable,
tp_Velocity,
@@ -31,7 +30,6 @@ enum TEventType {
tp_TrackVel,
tp_Multiple,
tp_AddValues,
// tp_Ignored, // NOTE: refactored to separate flag
tp_CopyValues,
tp_WhoIs,
tp_LogValues,
@@ -51,7 +49,6 @@ const int update_only = 0x00000FF; // wartość graniczna
const int conditional_memstring = 0x0000100; // porównanie tekstu
const int conditional_memval1 = 0x0000200; // porównanie pierwszej wartości liczbowej
const int conditional_memval2 = 0x0000400; // porównanie drugiej wartości
const int conditional_anyelse = 0x0FF0000; // do sprawdzania, czy są odwrócone warunki
const int conditional_trackoccupied = 0x1000000; // jeśli tor zajęty
const int conditional_trackfree = 0x2000000; // jeśli tor wolny
const int conditional_propability = 0x4000000; // zależnie od generatora lizcb losowych
@@ -61,7 +58,7 @@ union TParam
{
void *asPointer;
TMemCell *asMemCell;
editor::basic_node *asEditorNode;
scene::basic_node *asSceneNode;
glm::dvec3 const *asLocation;
TTrack *asTrack;
TAnimModel *asModel;
@@ -69,7 +66,6 @@ union TParam
TTrain *asTrain;
TDynamicObject *asDynamic;
TEvent *asEvent;
bool asBool;
double asdouble;
int asInt;
sound_source *tsTextSound;
@@ -80,10 +76,27 @@ union TParam
class TEvent // zmienne: ev*
{ // zdarzenie
private:
void Conditions(cParser *parser, std::string s);
public:
public:
// types
// wrapper for binding between editor-supplied name, event, and execution conditional flag
using conditional_event = std::tuple<std::string, TEvent *, bool>;
// constructors
TEvent(std::string const &m = "");
~TEvent();
// metody
void Load(cParser *parser, Math3D::vector3 const &org);
// sends basic content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
static void AddToQuery( TEvent *Event, TEvent *&Start );
std::string CommandGet();
TCommandType Command();
double ValueGet(int n);
glm::dvec3 PositionGet() const;
bool StopCommand();
void StopCommandSent();
void Append(TEvent *e);
// members
std::string asName;
bool m_ignored { false }; // replacement for tp_ignored
bool bEnabled = false; // false gdy ma nie być dodawany do kolejki (skanowanie sygnałów)
@@ -95,27 +108,15 @@ class TEvent // zmienne: ev*
TDynamicObject *Activator = nullptr;
TParam Params[13]; // McZapkie-070502 //Ra: zamienić to na union/struct
// this is already mess, so one more hack won't make it much worse...
// stores TParam union and magic flag (eg. else flag)
std::vector<std::pair<TParam, int>> ext_params;
unsigned int iFlags = 0; // zamiast Params[8] z flagami warunku
std::string asNodeName; // McZapkie-100302 - dodalem zeby zapamietac nazwe toru
TEvent *evJoined = nullptr; // kolejny event z tą samą nazwą - od wersji 378
double fRandomDelay = 0.0; // zakres dodatkowego opóźnienia // standardowo nie będzie dodatkowego losowego opóźnienia
public:
// metody
TEvent(std::string const &m = "");
~TEvent();
void Load(cParser *parser, Math3D::vector3 const &org);
static void AddToQuery( TEvent *Event, TEvent *&Start );
std::string CommandGet();
TCommandType Command();
double ValueGet(int n);
glm::dvec3 PositionGet() const;
bool StopCommand();
void StopCommandSent();
void Append(TEvent *e);
std::vector<conditional_event> m_children; // events which are placed in the query when this event is executed
bool m_conditionalelse { false }; // TODO: make a part of condition struct
private:
void Conditions( cParser *parser, std::string s );
};
class event_manager {
@@ -156,6 +157,9 @@ public:
// legacy method, initializes event launchers after deserialization from scenario file
void
InitLaunchers();
// sends basic content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
private:
// types

View File

@@ -61,12 +61,19 @@ void TGauge::Init(TSubModel *Submodel, TGaugeType Type, float Scale, float Offse
else // a banan może być z optymalizacją?
Submodel->WillBeAnimated(); // wyłączenie ignowania jedynkowego transformu
// pass submodel location to defined sounds
auto const nulloffset { glm::vec3{} };
auto const offset{ model_offset() };
if( m_soundfxincrease.offset() == nulloffset ) {
m_soundfxincrease.offset( offset );
}
if( m_soundfxdecrease.offset() == nulloffset ) {
m_soundfxdecrease.offset( offset );
}
for( auto &soundfxrecord : m_soundfxvalues ) {
if( soundfxrecord.second.offset() == nulloffset ) {
soundfxrecord.second.offset( offset );
}
}
};
bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, TModel3d *md2, double mul ) {

View File

@@ -355,7 +355,7 @@ enum TBrakeSystem { Individual, Pneumatic, ElectroPneumatic };
/*podtypy hamulcow zespolonych*/
enum TBrakeSubSystem { ss_None, ss_W, ss_K, ss_KK, ss_Hik, ss_ESt, ss_KE, ss_LSt, ss_MT, ss_Dako };
enum TBrakeValve { NoValve, W, W_Lu_VI, W_Lu_L, W_Lu_XR, K, Kg, Kp, Kss, Kkg, Kkp, Kks, Hikg1, Hikss, Hikp1, KE, SW, EStED, NESt3, ESt3, LSt, ESt4, ESt3AL2, EP1, EP2, M483, CV1_L_TR, CV1, CV1_R, Other };
enum TBrakeHandle { NoHandle, West, FV4a, M394, M254, FVel1, FVel6, D2, Knorr, FD1, BS2, testH, St113, MHZ_P, MHZ_T, MHZ_EN57 };
enum TBrakeHandle { NoHandle, West, FV4a, M394, M254, FVel1, FVel6, D2, Knorr, FD1, BS2, testH, St113, MHZ_P, MHZ_T, MHZ_EN57, MHZ_K5P };
/*typy hamulcow indywidualnych*/
enum TLocalBrake { NoBrake, ManualBrake, PneumaticBrake, HydraulicBrake };
/*dla osob/towar: opoznienie hamowania/odhamowania*/
@@ -669,6 +669,7 @@ struct heat_data {
// bool okienko { true }; // window in the engine compartment
// system configuration
bool auxiliary_water_circuit { false }; // cooling system has an extra water circuit
double fan_speed { 0.075 }; // cooling fan rpm; either fraction of engine rpm, or absolute value if negative
// heat exchange factors
double kw { 0.35 };
double kv { 0.6 };
@@ -740,6 +741,7 @@ public:
double Mred = 0.0; /*Ra: zredukowane masy wirujące; potrzebne do obliczeń hamowania*/
double TotalMass = 0.0; /*wyliczane przez ComputeMass*/
double HeatingPower = 0.0;
double EngineHeatingRPM { 0.0 }; // guaranteed engine revolutions with heating enabled
double LightPower = 0.0; /*moc pobierana na ogrzewanie/oswietlenie*/
double BatteryVoltage = 0.0; /*Winger - baterie w elektrykach*/
bool Battery = false; /*Czy sa zalavzone baterie*/

View File

@@ -1614,7 +1614,7 @@ void TMoverParameters::OilPumpCheck( double const Timestep ) {
OilPump.pressure_target = (
enrot > 0.1 ? interpolate( minpressure, OilPump.pressure_maximum, static_cast<float>( clamp( enrot / maxrevolutions, 0.0, 1.0 ) ) ) * OilPump.resource_amount :
true == OilPump.is_active ? minpressure :
true == OilPump.is_active ? std::min( minpressure + 0.1f, OilPump.pressure_maximum ) : // slight pressure margin to give time to switch off the pump and start the engine
0.f );
if( OilPump.pressure_present < OilPump.pressure_target ) {
@@ -3300,7 +3300,7 @@ void TMoverParameters::CompressorCheck(double dt)
CompressedVolume +=
CompressorSpeed
* ( 2.0 * MaxCompressor - Compressor ) / MaxCompressor
* ( DElist[ MainCtrlPos ].RPM / DElist[ MainCtrlPosNo ].RPM )
* ( ( 60.0 * std::abs( enrot ) ) / DElist[ MainCtrlPosNo ].RPM )
* dt;
}
else {
@@ -3465,7 +3465,7 @@ void TMoverParameters::CompressorCheck(double dt)
// the compressor is coupled with the diesel engine, engine revolutions affect the output
if( false == CompressorGovernorLock ) {
auto const enginefactor { (
EngineType == DieselElectric ? ( DElist[ MainCtrlPos ].RPM / DElist[ MainCtrlPosNo ].RPM ) :
EngineType == DieselElectric ? ( ( 60.0 * std::abs( enrot ) ) / DElist[ MainCtrlPosNo ].RPM ) :
EngineType == DieselEngine ? ( std::abs( enrot ) / nmax ) :
1.0 ) }; // shouldn't ever get here but, eh
CompressedVolume +=
@@ -4424,8 +4424,7 @@ double TMoverParameters::CouplerForce(int CouplerN, double dt)
// Q: 20160714
// oblicza sile trakcyjna lokomotywy (dla elektrowozu tez calkowity prad)
// *************************************************************************************************
double TMoverParameters::TractionForce(double dt)
{
double TMoverParameters::TractionForce( double dt ) {
double PosRatio, dmoment, dtrans, tmp;
Ft = 0;
@@ -4441,13 +4440,15 @@ double TMoverParameters::TractionForce(double dt)
if( ( true == Heating )
&& ( HeatingPower > 0 )
&& ( MainCtrlPosNo > MainCtrlPos ) ) {
int i = MainCtrlPosNo;
while( DElist[ i - 2 ].RPM / 60.0 > tmp ) {
--i;
}
tmp = DElist[ i ].RPM / 60.0;
&& ( EngineHeatingRPM > 0 ) ) {
// bump engine revolutions up if needed, when heating is on
tmp =
std::max(
tmp,
std::min(
DElist[ MainCtrlPosNo ].RPM,
EngineHeatingRPM )
/ 60.0 );
}
}
else {
@@ -4577,13 +4578,35 @@ double TMoverParameters::TractionForce(double dt)
}
}
switch( EngineType ) {
case Dumb: {
PosRatio = ( MainCtrlPos + ScndCtrlPos ) / ( MainCtrlPosNo + ScndCtrlPosNo + 0.01 );
EnginePower = 1000.0 * Power * PosRatio;
break;
}
case DieselEngine: {
EnginePower = ( 2 * dizel_Mstand + dmoment ) * enrot * ( 2.0 * M_PI / 1000.0 );
if( MainCtrlPos > 1 ) {
// dodatkowe opory z powodu sprezarki}
dmoment -= dizel_Mstand * ( 0.2 * enrot / dizel_nmax );
}
break;
}
case DieselElectric: {
EnginePower = 0; // the actual calculation is done in two steps later in the method
break;
}
default: {
break;
}
}
if (ActiveDir != 0)
switch (EngineType)
{
case Dumb:
{
PosRatio = (MainCtrlPos + ScndCtrlPos) / (MainCtrlPosNo + ScndCtrlPosNo + 0.01);
if (Mains && (ActiveDir != 0) && (CabNo != 0))
if (Mains && (CabNo != 0))
{
if (Vel > 0.1)
{
@@ -4595,7 +4618,6 @@ double TMoverParameters::TractionForce(double dt)
}
else
Ft = 0;
EnginePower = 1000.0 * Power * PosRatio;
break;
} // Dumb
@@ -4680,11 +4702,6 @@ double TMoverParameters::TractionForce(double dt)
case DieselEngine:
{
EnginePower = ( 2 * dizel_Mstand + dmoment ) * enrot * ( 2.0 * M_PI / 1000.0 );
if( MainCtrlPos > 1 ) {
// dodatkowe opory z powodu sprezarki}
dmoment -= dizel_Mstand * ( 0.2 * enrot / dizel_nmax );
}
Mm = dmoment; //bylo * dizel_engage
Mw = Mm * dtrans; // dmoment i dtrans policzone przy okazji enginerotation
Fw = Mw * 2.0 / WheelDiameter / NPoweredAxles;
@@ -4702,6 +4719,7 @@ double TMoverParameters::TractionForce(double dt)
if( ( true == Mains ) && ( MainCtrlPos > 0 ) ) {
Voltage = ( SST[ MainCtrlPos ].Umax * AnPos ) + ( SST[ MainCtrlPos ].Umin * ( 1.0 - AnPos ) );
// NOTE: very crude way to approximate power generated at current rpm instead of instant top output
// NOTE, TODO: doesn't take into account potentially increased revolutions if heating is on, fix it
auto const rpmratio { 60.0 * enrot / DElist[ MainCtrlPos ].RPM };
tmp = rpmratio * ( SST[ MainCtrlPos ].Pmax * AnPos ) + ( SST[ MainCtrlPos ].Pmin * ( 1.0 - AnPos ) );
Ft = tmp * 1000.0 / ( abs( tmpV ) + 1.6 );
@@ -4718,6 +4736,7 @@ double TMoverParameters::TractionForce(double dt)
if( true == Heating ) { power -= HeatingPower; }
if( power < 0.0 ) { power = 0.0; }
// NOTE: very crude way to approximate power generated at current rpm instead of instant top output
// NOTE, TODO: doesn't take into account potentially increased revolutions if heating is on, fix it
auto const currentgenpower { (
DElist[ MainCtrlPos ].RPM > 0 ?
DElist[ MainCtrlPos ].GenPower * ( 60.0 * enrot / DElist[ MainCtrlPos ].RPM ) :
@@ -4833,6 +4852,8 @@ double TMoverParameters::TractionForce(double dt)
else {
if( AutoRelayFlag ) {
auto const shuntfieldstate { ScndCtrlPos };
switch( RelayType ) {
case 0: {
@@ -4978,6 +4999,10 @@ double TMoverParameters::TractionForce(double dt)
break;
}
} // switch RelayType
if( ScndCtrlPos != shuntfieldstate ) {
SetFlag( SoundFlag, ( sound::relay | sound::shuntfield ) );
}
}
}
break;
@@ -4993,9 +5018,7 @@ double TMoverParameters::TractionForce(double dt)
}
}
if( true == Mains ) {
//tempomat
if (ScndCtrlPosNo > 1)
{
if (ScndCtrlPos != NewSpeed)
@@ -5019,7 +5042,6 @@ double TMoverParameters::TractionForce(double dt)
}
}
dtrans = Hamulec->GetEDBCP();
if (((DoorLeftOpened) || (DoorRightOpened)))
DynamicBrakeFlag = true;
@@ -5239,6 +5261,20 @@ double TMoverParameters::TractionForce(double dt)
break;
}
} // case EngineType
switch( EngineType ) {
case DieselElectric: {
// rough approximation of extra effort to overcome friction etc
auto const rpmratio{ 60.0 * enrot / DElist[ MainCtrlPosNo ].RPM };
EnginePower += rpmratio * 0.2 * DElist[ MainCtrlPosNo ].GenPower;
break;
}
default: {
break;
}
}
return Ft;
}
@@ -5986,6 +6022,18 @@ bool TMoverParameters::dizel_AutoGearCheck(void)
case 2:
dizel_EngageSwitch(1.0);
break;
case 3:
if (Vel>dizel_minVelfullengage)
dizel_EngageSwitch(1.0);
else
dizel_EngageSwitch(0.5);
break;
case 4:
if (Vel>dizel_minVelfullengage)
dizel_EngageSwitch(1.0);
else
dizel_EngageSwitch(0.66);
break;
default:
if (hydro_TC && hydro_TC_Fill>0.01)
dizel_EngageSwitch(1.0);
@@ -6125,6 +6173,18 @@ double TMoverParameters::dizel_fillcheck(int mcp)
else
nreg = dizel_nmin;
break;
case 3:
if ((dizel_automaticgearstatus == 0) && (Vel > dizel_minVelfullengage))
nreg = dizel_nmax;
else
nreg = dizel_nmin;
break;
case 4:
if ((dizel_automaticgearstatus == 0) && (Vel > dizel_minVelfullengage))
nreg = dizel_nmax;
else
nreg = dizel_nmin * 0.75 + dizel_nmax * 0.25;
break;
default:
realfill = 0; // sluczaj
break;
@@ -6401,7 +6461,7 @@ void TMoverParameters::dizel_Heat( double const dt ) {
&& ( dizel_heat.water_aux.config.temp_cooling > 0 )
&& ( dizel_heat.temperatura2 > dizel_heat.water_aux.config.temp_cooling - ( dizel_heat.water_aux.is_warm ? 8 : 0 ) ) ) );
auto const PTC2 { ( dizel_heat.water_aux.is_warm /*or PTC2p*/ ? 1 : 0 ) };
dizel_heat.rpmwz2 = PTC2 * 80 * rpm / ( ( 0.5 * rpm ) + 500 );
dizel_heat.rpmwz2 = PTC2 * ( dizel_heat.fan_speed >= 0 ? ( rpm * dizel_heat.fan_speed ) : ( dizel_heat.fan_speed * -1 ) );
dizel_heat.zaluzje2 = ( dizel_heat.water_aux.config.shutters ? ( PTC2 == 1 ) : true ); // no shutters is an equivalent to having them open
auto const zaluzje2 { ( dizel_heat.zaluzje2 ? 1 : 0 ) };
// auxiliary water circuit heat transfer values
@@ -6429,7 +6489,7 @@ void TMoverParameters::dizel_Heat( double const dt ) {
&& ( dizel_heat.water.config.temp_cooling > 0 )
&& ( dizel_heat.temperatura1 > dizel_heat.water.config.temp_cooling - ( dizel_heat.water.is_warm ? 8 : 0 ) ) ) );
auto const PTC1 { ( dizel_heat.water.is_warm /*or PTC1p*/ ? 1 : 0 ) };
dizel_heat.rpmwz = PTC1 * 80 * rpm / ( ( 0.5 * rpm ) + 500 );
dizel_heat.rpmwz = PTC1 * ( dizel_heat.fan_speed >= 0 ? ( rpm * dizel_heat.fan_speed ) : ( dizel_heat.fan_speed * -1 ) );
dizel_heat.zaluzje1 = ( dizel_heat.water.config.shutters ? ( PTC1 == 1 ) : true ); // no shutters is an equivalent to having them open
auto const zaluzje1 { ( dizel_heat.zaluzje1 ? 1 : 0 ) };
// primary water circuit heat transfer values
@@ -8030,6 +8090,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) {
{ "test", testH },
{ "D2", D2 },
{ "MHZ_EN57", MHZ_EN57 },
{ "MHZ_K5P", MHZ_K5P },
{ "M394", M394 },
{ "Knorr", Knorr },
{ "Westinghouse", West },
@@ -8385,6 +8446,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) {
ImaxHi = 2;
ImaxLo = 1;
}
extract_value( EngineHeatingRPM, "HeatingRPM", Input, "" );
break;
}
case ElectricInductionMotor: {
@@ -8415,8 +8477,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) {
extract_value( eimc[ eimc_p_eped ], "edep", Input, "" );
extract_value( EIMCLogForce, "eimclf", Input, "" );
Flat = ( extract_value( "Flat", Input ) == "1" );
extract_value( Flat, "Flat", Input, "");
break;
}
default: {
@@ -8450,6 +8511,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) {
extract_value( dizel_heat.water_aux.config.shutters, "WaterAuxShutters", Input, "" );
extract_value( dizel_heat.oil.config.temp_min, "OilMinTemperature", Input, "" );
extract_value( dizel_heat.oil.config.temp_max, "OilMaxTemperature", Input, "" );
extract_value( dizel_heat.fan_speed, "WaterCoolingFanSpeed", Input, "" );
// water heater
extract_value( WaterHeater.config.temp_min, "HeaterMinTemperature", Input, "" );
extract_value( WaterHeater.config.temp_max, "HeaterMaxTemperature", Input, "" );
@@ -8836,6 +8898,9 @@ bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir)
case St113:
Handle = std::make_shared<TSt113>();
break;
case MHZ_K5P:
Handle = std::make_shared<TMHZ_K5P>();
break;
default:
Handle = std::make_shared<TDriverHandle>();
}
@@ -9515,6 +9580,7 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C
else {
OK = false;
}
SendCtrlToNext( Command, CValue1, CValue2, Couplertype );
}
else if (Command == "Sandbox")
{

View File

@@ -23,6 +23,7 @@ Copyright (C) 2007-2014 Maciej Cierniak
static double const DPL = 0.25;
double const TFV4aM::pos_table[11] = {-2, 6, -1, 0, -2, 1, 4, 6, 0, 0, 0};
double const TMHZ_EN57::pos_table[11] = {-1, 10, -1, 0, 0, 2, 9, 10, 0, 0, 0};
double const TMHZ_K5P::pos_table[11] = { -1, 3, -1, 0, 1, 1, 2, 3, 0, 0, 0 };
double const TM394::pos_table[11] = {-1, 5, -1, 0, 1, 2, 4, 5, 0, 0, 0};
double const TH14K1::BPT_K[6][2] = {{10, 0}, {4, 1}, {0, 1}, {4, 0}, {4, -1}, {15, -1}};
double const TH14K1::pos_table[11] = {-1, 4, -1, 0, 1, 2, 3, 4, 0, 0, 0};
@@ -2516,7 +2517,7 @@ bool TFV4aM::EQ(double pos, double i_pos)
return (pos <= i_pos + 0.5) && (pos > i_pos - 0.5);
}
//---FV4a/M--- nowonapisany kran bez poprawki IC
//---MHZ_EN57--- manipulator hamulca zespolonego do EN57
double TMHZ_EN57::GetPF( double i_bcp, double PP, double HP, double dt, double ep ) {
static int const LBDelay = 100;
@@ -2656,6 +2657,119 @@ bool TMHZ_EN57::EQ(double pos, double i_pos)
return (pos <= i_pos + 0.5) && (pos > i_pos - 0.5);
}
//---MHZ_K5P--- manipulator hamulca zespolonego Knorr 5-ciopozycyjny
double TMHZ_K5P::GetPF(double i_bcp, double PP, double HP, double dt, double ep) {
static int const LBDelay = 100;
double LimPP;
double dpPipe;
double dpMainValve;
double ActFlowSpeed;
double DP;
double pom;
for (int idx = 0; idx < 5; ++idx) {
Sounds[idx] = 0;
}
DP = 0;
i_bcp = Max0R(Min0R(i_bcp, 2.999), -0.999); // na wszelki wypadek, zeby nie wyszlo poza zakres
if ((TP > 0))
{
DP = 0.004;
TP = TP - DP * dt;
Sounds[s_fv4a_t] = DP;
}
else
{
TP = 0;
}
if (EQ(i_bcp, 1)) //odcięcie - nie rób nic
LimPP = CP;
else if (i_bcp > 1) //hamowanie
LimPP = 3.4;
else //luzowanie
LimPP = 5.0;
pom = CP;
LimPP = Min0R(LimPP + TP + RedAdj, HP); // pozycja + czasowy lub zasilanie
ActFlowSpeed = 4;
if ((LimPP > CP)) // podwyzszanie szybkie
CP = CP + 9 * Min0R(abs(LimPP - CP), 0.05) * PR(CP, LimPP) * dt; // zbiornik sterujacy;
else
CP = CP + 9 * Min0R(abs(LimPP - CP), 0.05) * PR(CP, LimPP) * dt; // zbiornik sterujacy
LimPP = pom; // cp
dpPipe = Min0R(HP, LimPP);
if (dpPipe > PP)
dpMainValve = -PFVa(HP, PP, ActFlowSpeed / LBDelay, dpPipe, 0.4);
else
dpMainValve = PFVd(PP, 0, ActFlowSpeed / LBDelay, dpPipe, 0.4);
if (EQ(i_bcp, -1))
{
if ((TP < 1))
TP = TP + 0.03 * dt;
}
if (EQ(i_bcp, 3))
{
DP = PF(0, PP, 2 * ActFlowSpeed / LBDelay);
dpMainValve = DP;
Sounds[s_fv4a_e] = DP;
Sounds[s_fv4a_u] = 0;
Sounds[s_fv4a_b] = 0;
Sounds[s_fv4a_x] = 0;
}
else
{
if (dpMainValve > 0)
Sounds[s_fv4a_b] = dpMainValve;
else
Sounds[s_fv4a_u] = -dpMainValve;
}
return dpMainValve * dt;
}
void TMHZ_K5P::Init(double Press)
{
CP = Press;
}
void TMHZ_K5P::SetReductor(double nAdj)
{
RedAdj = nAdj;
}
double TMHZ_K5P::GetSound(int i)
{
if (i > 4)
return 0;
else
return Sounds[i];
}
double TMHZ_K5P::GetPos(int i)
{
return pos_table[i];
}
double TMHZ_K5P::GetCP()
{
return RP;
}
bool TMHZ_K5P::EQ(double pos, double i_pos)
{
return (pos <= i_pos + 0.5) && (pos > i_pos - 0.5);
}
//---M394--- Matrosow
double TM394::GetPF(double i_bcp, double PP, double HP, double dt, double ep)

View File

@@ -591,6 +591,31 @@ class TMHZ_EN57 : public TDriverHandle {
{}
};
class TMHZ_K5P : public TDriverHandle {
private:
double CP = 0.0; //zbiornik sterujący
double TP = 0.0; //zbiornik czasowy
double RP = 0.0; //zbiornik redukcyjny
double RedAdj = 0.0; //dostosowanie reduktora cisnienia (krecenie kapturkiem)
bool Fala = false;
static double const pos_table[11]; //= { -2, 10, -1, 0, 0, 2, 9, 10, 0, 0, 0 };
bool EQ(double pos, double i_pos);
public:
double GetPF(double i_bcp, double PP, double HP, double dt, double ep)/*override*/;
void Init(double Press)/*override*/;
void SetReductor(double nAdj)/*override*/;
double GetSound(int i)/*override*/;
double GetPos(int i)/*override*/;
double GetCP()/*override*/;
inline TMHZ_K5P(void) :
TDriverHandle()
{}
};
/* FBS2= class(TTDriverHandle)
private
CP, TP, RP: real; //zbiornik sterujący, czasowy, redukcyjny

View File

@@ -162,6 +162,41 @@ void TMemCell::AssignEvents(TEvent *e)
OnSent = e;
};
// serialize() subclass details, sends content of the subclass to provided stream
void
TMemCell::serialize_( std::ostream &Output ) const {
// TODO: implement
}
// deserialize() subclass details, restores content of the subclass from provided stream
void
TMemCell::deserialize_( std::istream &Input ) {
// TODO: implement
}
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void
TMemCell::export_as_text_( std::ostream &Output ) const {
// header
Output << "memcell ";
// location
Output
<< location().x << ' '
<< location().y << ' '
<< location().z << ' '
// cell data
<< szText << ' '
<< fValue1 << ' '
<< fValue2 << ' '
// associated track
<< ( asTrackName.empty() ? "none" : asTrackName ) << ' '
// footer
<< "endmemcell"
<< "\n";
}
// legacy method, initializes traction after deserialization from scenario file

View File

@@ -13,10 +13,11 @@ http://mozilla.org/MPL/2.0/.
#include "scenenode.h"
#include "Names.h"
class TMemCell : public editor::basic_node {
class TMemCell : public scene::basic_node {
public:
std::string asTrackName; // McZapkie-100302 - zeby nazwe toru na ktory jest Putcommand wysylane pamietac
bool is_exportable { true }; // export helper; autogenerated cells don't need to be exported
explicit TMemCell( scene::node_data const &Nodedata );
@@ -49,6 +50,15 @@ public:
void AssignEvents(TEvent *e);
private:
// methods
// serialize() subclass details, sends content of the subclass to provided stream
void serialize_( std::ostream &Output ) const;
// deserialize() subclass details, restores content of the subclass from provided stream
void deserialize_( std::istream &Input );
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void export_as_text_( std::ostream &Output ) const;
// members
// content
std::string szText;
double fValue1 { 0.0 };

View File

@@ -1252,8 +1252,6 @@ bool TModel3d::LoadFromFile(std::string const &FileName, bool dynamic)
LoadFromBinFile(asBinary, dynamic);
asBinary = ""; // wyłączenie zapisu
Init();
// cache the file name, in case someone wants it later
m_filename = name + ".e3d";
}
else
{
@@ -1264,10 +1262,10 @@ bool TModel3d::LoadFromFile(std::string const &FileName, bool dynamic)
// pojazdy dopiero po ustawieniu animacji
Init(); // generowanie siatek i zapis E3D
}
}
}
// cache the file name, in case someone wants it later
m_filename = name + ".t3d";
}
}
m_filename = name;
bool const result =
Root ? (iSubModelsCount > 0) : false; // brak pliku albo problem z wczytaniem
if (false == result)

View File

@@ -63,5 +63,8 @@ public:
type_sequence &
sequence() {
return m_items; }
type_sequence const &
sequence() const {
return m_items; }
};

View File

@@ -18,8 +18,36 @@ http://mozilla.org/MPL/2.0/.
//---------------------------------------------------------------------------
// 101206 Ra: trapezoidalne drogi
// 110806 Ra: odwrócone mapowanie wzdłuż - Point1 == 1.0
// helper, restores content of a 3d vector from provided input stream
// TODO: review and clean up the helper routines, there's likely some redundant ones
Math3D::vector3 LoadPoint( cParser &Input ) {
// pobranie współrzędnych punktu
Input.getTokens( 3 );
Math3D::vector3 point;
Input
>> point.x
>> point.y
>> point.z;
return point;
}
void
segment_data::deserialize( cParser &Input, Math3D::vector3 const &Offset ) {
points[ segment_data::point::start ] = LoadPoint( Input ) + Offset;
Input.getTokens();
Input >> rolls[ 0 ];
points[ segment_data::point::control1 ] = LoadPoint( Input );
points[ segment_data::point::control2 ] = LoadPoint( Input );
points[ segment_data::point::end ] = LoadPoint( Input ) + Offset;
Input.getTokens( 2 );
Input
>> rolls[ 1 ]
>> radius;
}
TSegment::TSegment(TTrack *owner) :
pOwner( owner )
@@ -62,6 +90,7 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M
// poprawienie przechyłki
fRoll1 = glm::radians(fNewRoll1); // Ra: przeliczone jest bardziej przydatne do obliczeń
fRoll2 = glm::radians(fNewRoll2);
bCurve = bIsCurve;
if (Global.bRollFix)
{ // Ra: poprawianie przechyłki
// Przechyłka powinna być na środku wewnętrznej szyny, a standardowo jest w osi
@@ -91,7 +120,6 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M
// kąt w planie, żeby nie liczyć wielokrotnie
// Ra: ten kąt jeszcze do przemyślenia jest
fDirection = -std::atan2(Point2.x - Point1.x, Point2.z - Point1.z);
bCurve = bIsCurve;
if (bCurve)
{ // przeliczenie współczynników wielomianu, będzie mniej mnożeń i można policzyć pochodne
vC = 3.0 * (CPointOut - Point1); // t^1
@@ -99,9 +127,9 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M
vA = Point2 - Point1 - vC - vB; // t^3
fLength = ComputeLength();
}
else
fLength = (Point1 - Point2).Length();
fStep = fNewStep;
else {
fLength = ( Point1 - Point2 ).Length();
}
if (fLength <= 0) {
ErrorLog( "Bad track: zero length spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" );
@@ -111,17 +139,25 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M
*/
}
if( ( pOwner->eType == tt_Switch )
&& ( fStep * 3.0 > fLength ) ) {
// NOTE: a workaround for too short switches (less than 3 segments) messing up animation/generation of blades
fStep = fLength / 3.0;
}
fStoop = std::atan2((Point2.y - Point1.y), fLength); // pochylenie toru prostego, żeby nie liczyć wielokrotnie
SafeDeleteArray(fTsBuffer);
fStep = fNewStep;
// NOTE: optionally replace this part with the commented version, after solving geometry issues with double switches
if( ( pOwner->eType == tt_Switch )
&& ( fStep * ( 3.0 * Global.SplineFidelity ) > fLength ) ) {
// NOTE: a workaround for too short switches (less than 3 segments) messing up animation/generation of blades
fStep = fLength / ( 3.0 * Global.SplineFidelity );
}
iSegCount = static_cast<int>( std::ceil( fLength / fStep ) ); // potrzebne do VBO
/*
iSegCount = (
pOwner->eType == tt_Switch ?
6 * Global.SplineFidelity :
static_cast<int>( std::ceil( fLength / fStep ) ) ); // potrzebne do VBO
*/
fStep = fLength / iSegCount; // update step to equalize size of individual pieces
SafeDeleteArray( fTsBuffer );
fTsBuffer = new double[ iSegCount + 1 ];
fTsBuffer[ 0 ] = 0.0;
for( int i = 1; i < iSegCount; ++i ) {

View File

@@ -14,6 +14,24 @@ http://mozilla.org/MPL/2.0/.
#include "openglgeometrybank.h"
#include "utilities.h"
struct segment_data {
// types
enum point {
start = 0,
control1,
control2,
end
};
// members
std::array<glm::dvec3, 4> points {};
std::array<float, 2> rolls {};
float radius {};
// constructors
segment_data() = default;
// methods
void deserialize( cParser &Input, Math3D::vector3 const &Offset );
};
class TSegment
{ // aproksymacja toru (zwrotnica ma dwa takie, jeden z nich jest aktywny)
private:

View File

@@ -823,55 +823,22 @@ texture_manager::create( std::string Filename, bool const Loadnow ) {
Filename.erase( 0, 1 );
}
std::vector<std::string> extensions{ { ".dds" }, { ".tga" }, { ".png" }, { ".bmp" }, { ".ext" } };
// try to locate requested texture in the databank
auto lookup = find_in_databank( Filename + Global.szDefaultExt );
auto lookup { find_in_databank( Filename ) };
if( lookup != npos ) {
// start with the default extension...
return lookup;
}
else {
// ...then try recognized file extensions other than default
for( auto const &extension : extensions ) {
if( extension == Global.szDefaultExt ) {
// we already tried this one
continue;
}
lookup = find_in_databank( Filename + extension );
if( lookup != npos ) {
return lookup;
}
}
}
// if we don't have the texture in the databank, check if it's on disk
std::string filename = find_on_disk( Filename + Global.szDefaultExt );
if( true == filename.empty() ) {
// if the default lookup fails, try other known extensions
for( auto const &extension : extensions ) {
auto const disklookup { find_on_disk( Filename ) };
if( extension == Global.szDefaultExt ) {
// we already tried this one
continue;
}
filename = find_on_disk( Filename + extension );
if( false == filename.empty() ) {
// we found something, don't bother with others
break;
}
}
}
if( true == filename.empty() ) {
if( true == disklookup.first.empty() ) {
// there's nothing matching in the databank nor on the disk, report failure
ErrorLog( "Bad file: failed do locate texture file \"" + Filename + "\"", logtype::file );
return npos;
}
auto texture = new opengl_texture();
texture->name = filename;
texture->name = disklookup.first + disklookup.second;
if( Filename.find('#') != std::string::npos ) {
// temporary code for legacy assets -- textures with names beginning with # are to be sharpened
traits += '#';
@@ -879,9 +846,9 @@ texture_manager::create( std::string Filename, bool const Loadnow ) {
texture->traits = traits;
auto const textureindex = (texture_handle)m_textures.size();
m_textures.emplace_back( texture, std::chrono::steady_clock::time_point() );
m_texturemappings.emplace( filename, textureindex );
m_texturemappings.emplace( disklookup.first, textureindex );
WriteLog( "Created texture object for \"" + filename + "\"", logtype::texture );
WriteLog( "Created texture object for \"" + disklookup.first + disklookup.second + "\"", logtype::texture );
if( true == Loadnow ) {
@@ -998,7 +965,7 @@ texture_manager::info() const {
texture_handle
texture_manager::find_in_databank( std::string const &Texturename ) const {
std::vector<std::string> filenames {
std::vector<std::string> const filenames {
Global.asCurrentTexturePath + Texturename,
Texturename,
szTexturePath + Texturename };
@@ -1009,19 +976,34 @@ texture_manager::find_in_databank( std::string const &Texturename ) const {
return lookup->second;
}
}
// all lookups failed
return npos;
}
// checks whether specified file exists.
std::string
std::pair<std::string, std::string>
texture_manager::find_on_disk( std::string const &Texturename ) const {
return(
FileExists( Global.asCurrentTexturePath + Texturename ) ? Global.asCurrentTexturePath + Texturename :
FileExists( Texturename ) ? Texturename :
FileExists( szTexturePath + Texturename ) ? szTexturePath + Texturename :
"" );
std::vector<std::string> const filenames {
Global.asCurrentTexturePath + Texturename,
Texturename,
szTexturePath + Texturename };
auto lookup =
FileExists(
filenames,
{ Global.szDefaultExt } );
if( false == lookup.first.empty() ) {
return lookup;
}
// if the first attempt fails, try entire extension list
// NOTE: slightly wasteful as it means preferred extension is tested twice, but, eh
return (
FileExists(
filenames,
{ ".dds", ".tga", ".bmp", ".ext" } ) );
}
//---------------------------------------------------------------------------

View File

@@ -20,6 +20,8 @@ struct opengl_texture {
static DDPIXELFORMAT deserialize_ddpf(std::istream&);
static DDSCAPS2 deserialize_ddscaps(std::istream&);
// constructors
opengl_texture() = default;
// methods
void
load();
@@ -120,7 +122,7 @@ private:
texture_handle
find_in_databank( std::string const &Texturename ) const;
// checks whether specified file exists. returns name of the located file, or empty string.
std::string
std::pair<std::string, std::string>
find_on_disk( std::string const &Texturename ) const;
void
delete_textures();

641
Track.cpp
View File

@@ -345,14 +345,6 @@ void TTrack::ConnectNextNext(TTrack *pTrack, int typ)
}
}
Math3D::vector3 LoadPoint(cParser *parser)
{ // pobranie współrzędnych punktu
Math3D::vector3 p;
parser->getTokens(3);
*parser >> p.x >> p.y >> p.z;
return p;
}
void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
{ // pobranie obiektu trajektorii ruchu
Math3D::vector3 pt, vec, p1, p2, cp1, cp2, p3, p4, cp3, cp4; // dodatkowe punkty potrzebne do skrzyżowań
@@ -475,20 +467,44 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
WriteLog("unvis");
Init(); // ustawia SwitchExtension
double segsize = 5.0; // długość odcinka segmentowania
switch (eType)
{ // Ra: łuki segmentowane co 5m albo 314-kątem foremnym
case tt_Table: // obrotnica jest prawie jak zwykły tor
// path data
// all subtypes contain at least one path
m_paths.emplace_back();
m_paths.back().deserialize( *parser, pOrigin );
switch( eType ) {
case tt_Switch:
case tt_Cross:
case tt_Tributary: {
// these subtypes contain additional path
m_paths.emplace_back();
m_paths.back().deserialize( *parser, pOrigin );
break;
}
default: {
break;
}
}
switch (eType) {
// Ra: łuki segmentowane co 5m albo 314-kątem foremnym
case tt_Table: {
// obrotnica jest prawie jak zwykły tor
iAction |= 2; // flaga zmiany położenia typu obrotnica
case tt_Normal:
p1 = LoadPoint(parser) + pOrigin; // pobranie współrzędnych P1
parser->getTokens();
*parser >> r1; // pobranie przechyłki w P1
cp1 = LoadPoint(parser); // pobranie współrzędnych punktów kontrolnych
cp2 = LoadPoint(parser);
p2 = LoadPoint(parser) + pOrigin; // pobranie współrzędnych P2
parser->getTokens(2);
*parser >> r2 >> fRadius; // pobranie przechyłki w P1 i promienia
fRadius = fabs(fRadius); // we wpisie może być ujemny
}
case tt_Normal: {
// pobranie współrzędnych P1
auto const &path { m_paths[ 0 ] };
p1 = path.points[ segment_data::point::start ];
// pobranie współrzędnych punktów kontrolnych
cp1 = path.points[ segment_data::point::control1 ];
cp2 = path.points[ segment_data::point::control2 ];
// pobranie współrzędnych P2
p2 = path.points[ segment_data::point::end ];
r1 = path.rolls[ 0 ];
r2 = path.rolls[ 1 ];
fRadius = std::abs( path.radius ); // we wpisie może być ujemny
if (iCategoryFlag & 1)
{ // zero na główce szyny
p1.y += 0.18;
@@ -504,7 +520,11 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
if( fRadius != 0 ) {
// gdy podany promień
segsize = clamp( std::abs( fRadius ) * ( 0.02 / Global.SplineFidelity ), 2.0 / Global.SplineFidelity, 10.0 );
segsize =
clamp(
std::abs( fRadius ) * ( 0.02 / Global.SplineFidelity ),
2.0 / Global.SplineFidelity,
10.0 / Global.SplineFidelity );
}
else {
// HACK: crude check whether claimed straight is an actual straight piece
@@ -514,7 +534,11 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
}
else {
// HACK: divide roughly in 10 segments.
segsize = clamp( ( p1 - p2 ).Length() * ( 0.1 / Global.SplineFidelity ), 2.0 / Global.SplineFidelity, 10.0 );
segsize =
clamp(
( p1 - p2 ).Length() * 0.1,
2.0 / Global.SplineFidelity,
10.0 / Global.SplineFidelity );
}
}
@@ -531,6 +555,7 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
if ((r1 != 0) || (r2 != 0))
iTrapezoid = 1; // są przechyłki do uwzględniania w rysowaniu
if (eType == tt_Table) // obrotnica ma doklejkę
{ // SwitchExtension=new TSwitchExtension(this,1); //dodatkowe zmienne dla obrotnicy
SwitchExtension->Segments[0]->Init(p1, p2, segsize); // kopia oryginalnego toru
@@ -551,56 +576,92 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
fTexRatio2 = w / h; // proporcja boków
}
break;
case tt_Cross: // skrzyżowanie dróg - 4 punkty z wektorami kontrolnymi
segsize = 1.0; // specjalne segmentowanie ze względu na małe promienie
}
case tt_Cross: {
// skrzyżowanie dróg - 4 punkty z wektorami kontrolnymi
// segsize = 1.0; // specjalne segmentowanie ze względu na małe promienie
}
case tt_Tributary: // dopływ
case tt_Switch: // zwrotnica
case tt_Switch: { // zwrotnica
iAction |= 1; // flaga zmiany położenia typu zwrotnica lub skrzyżowanie dróg
// problemy z animacją iglic powstaje, gdzy odcinek prosty ma zmienną przechyłkę
// wtedy dzieli się na dodatkowe odcinki (po 0.2m, bo R=0) i animację diabli biorą
// Ra: na razie nie podejmuję się przerabiania iglic
// SwitchExtension=new TSwitchExtension(this,eType==tt_Cross?6:2); //zwrotnica ma doklejkę
auto const &path { m_paths[ 0 ] };
p1 = path.points[ segment_data::point::start ];
// pobranie współrzędnych punktów kontrolnych
cp1 = path.points[ segment_data::point::control1 ];
cp2 = path.points[ segment_data::point::control2 ];
// pobranie współrzędnych P2
p2 = path.points[ segment_data::point::end ];
r1 = path.rolls[ 0 ];
r2 = path.rolls[ 1 ];
fRadiusTable[0] = std::abs( path.radius ); // we wpisie może być ujemny
p1 = LoadPoint(parser) + pOrigin; // pobranie współrzędnych P1
parser->getTokens();
*parser >> r1;
cp1 = LoadPoint(parser);
cp2 = LoadPoint(parser);
p2 = LoadPoint(parser) + pOrigin; // pobranie współrzędnych P2
parser->getTokens(2);
*parser >> r2 >> fRadiusTable[0];
fRadiusTable[0] = fabs(fRadiusTable[0]); // we wpisie może być ujemny
if (iCategoryFlag & 1)
{ // zero na główce szyny
p1.y += 0.18;
p2.y += 0.18;
// na przechyłce doliczyć jeszcze pół przechyłki?
}
if (fRadiusTable[0] > 0)
segsize = clamp( 0.2 + fRadiusTable[0] * 0.02, 2.0, 5.0 );
else if (eType != tt_Cross) // dla skrzyżowań muszą być podane kontrolne
{ // jak promień zerowy, to przeliczamy punkty kontrolne
cp1 = (p1 + p1 + p2) / 3.0 - p1; // jak jest prosty, to się zoptymalizuje
cp2 = (p1 + p2 + p2) / 3.0 - p2;
segsize = 5.0;
} // ułomny prosty
if (!(cp1 == Math3D::vector3(0, 0, 0)) && !(cp2 == Math3D::vector3(0, 0, 0)))
SwitchExtension->Segments[0]->Init(p1, p1 + cp1, p2 + cp2, p2, segsize, r1, r2);
else
SwitchExtension->Segments[0]->Init(p1, p2, segsize, r1, r2);
if( eType != tt_Cross ) {
// dla skrzyżowań muszą być podane kontrolne
if( ( ( ( p1 + p1 + p2 ) / 3.0 - p1 - cp1 ).Length() < 0.02 )
|| ( ( ( p1 + p2 + p2 ) / 3.0 - p2 + cp1 ).Length() < 0.02 ) ) {
// "prostowanie" prostych z kontrolnymi, dokładność 2cm
cp1 = cp2 = Math3D::vector3( 0, 0, 0 );
}
}
if( fRadiusTable[ 0 ] != 0 ) {
// gdy podany promień
segsize =
clamp(
std::abs( fRadiusTable[ 0 ] ) * ( 0.02 / Global.SplineFidelity ),
2.0 / Global.SplineFidelity,
10.0 / Global.SplineFidelity );
}
else {
// HACK: crude check whether claimed straight is an actual straight piece
if( ( cp1 == Math3D::vector3() )
&& ( cp2 == Math3D::vector3() ) ) {
segsize = 10.0; // for straights, 10m per segment works good enough
}
else {
// HACK: divide roughly in 10 segments.
segsize =
clamp(
( p1 - p2 ).Length() * 0.1,
2.0 / Global.SplineFidelity,
10.0 / Global.SplineFidelity );
}
}
if( ( cp1 == Math3D::vector3( 0, 0, 0 ) )
&& ( cp2 == Math3D::vector3( 0, 0, 0 ) ) ) {
// Ra: hm, czasem dla prostego są podane...
// gdy prosty, kontrolne wyliczane przy zmiennej przechyłce
SwitchExtension->Segments[ 0 ]->Init( p1, p2, segsize, r1, r2 );
}
else {
// gdy łuk (ustawia bCurve=true)
SwitchExtension->Segments[ 0 ]->Init( p1, cp1 + p1, cp2 + p2, p2, segsize, r1, r2 );
}
auto const &path2 { m_paths[ 1 ] };
p3 = path2.points[ segment_data::point::start ];
// pobranie współrzędnych punktów kontrolnych
cp3 = path2.points[ segment_data::point::control1 ];
cp4 = path2.points[ segment_data::point::control2 ];
// pobranie współrzędnych P2
p4 = path2.points[ segment_data::point::end ];
r3 = path2.rolls[ 0 ];
r4 = path2.rolls[ 1 ];
fRadiusTable[1] = std::abs( path2.radius ); // we wpisie może być ujemny
p3 = LoadPoint(parser) + pOrigin; // pobranie współrzędnych P3
parser->getTokens();
*parser >> r3;
cp3 = LoadPoint(parser);
cp4 = LoadPoint(parser);
p4 = LoadPoint(parser) + pOrigin; // pobranie współrzędnych P4
parser->getTokens(2);
*parser >> r4 >> fRadiusTable[1];
fRadiusTable[1] = fabs(fRadiusTable[1]); // we wpisie może być ujemny
if (iCategoryFlag & 1)
{ // zero na główce szyny
p3.y += 0.18;
@@ -608,25 +669,54 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
// na przechyłce doliczyć jeszcze pół przechyłki?
}
if (fRadiusTable[1] > 0)
segsize = clamp( 0.2 + fRadiusTable[ 1 ] * 0.02, 2.0, 5.0 );
else if (eType != tt_Cross) // dla skrzyżowań muszą być podane kontrolne
{ // jak promień zerowy, to przeliczamy punkty kontrolne
cp3 = (p3 + p3 + p4) / 3.0 - p3; // jak jest prosty, to się zoptymalizuje
cp4 = (p3 + p4 + p4) / 3.0 - p4;
segsize = 5.0;
} // ułomny prosty
if (!(cp3 == Math3D::vector3(0, 0, 0)) && !(cp4 == Math3D::vector3(0, 0, 0)))
{ // dla skrzyżowania dróg dać odwrotnie końce, żeby brzegi generować lewym
if (eType != tt_Cross)
SwitchExtension->Segments[1]->Init(p3, p3 + cp3, p4 + cp4, p4, segsize, r3, r4);
else
SwitchExtension->Segments[1]->Init(p4, p4 + cp4, p3 + cp3, p3, segsize, r4, r3); // odwrócony
if( eType != tt_Cross ) {
// dla skrzyżowań muszą być podane kontrolne
if( ( ( ( p3 + p3 + p4 ) / 3.0 - p3 - cp3 ).Length() < 0.02 )
|| ( ( ( p3 + p4 + p4 ) / 3.0 - p4 + cp3 ).Length() < 0.02 ) ) {
// "prostowanie" prostych z kontrolnymi, dokładność 2cm
cp3 = cp4 = Math3D::vector3( 0, 0, 0 );
}
}
if( fRadiusTable[ 1 ] != 0 ) {
// gdy podany promień
segsize =
clamp(
std::abs( fRadiusTable[ 1 ] ) * ( 0.02 / Global.SplineFidelity ),
2.0 / Global.SplineFidelity,
10.0 / Global.SplineFidelity );
}
else {
// HACK: crude check whether claimed straight is an actual straight piece
if( ( cp3 == Math3D::vector3() )
&& ( cp4 == Math3D::vector3() ) ) {
segsize = 10.0; // for straights, 10m per segment works good enough
}
else {
// HACK: divide roughly in 10 segments.
segsize =
clamp(
( p3 - p4 ).Length() * 0.1,
2.0 / Global.SplineFidelity,
10.0 / Global.SplineFidelity );
}
}
if( ( cp3 == Math3D::vector3( 0, 0, 0 ) )
&& ( cp4 == Math3D::vector3( 0, 0, 0 ) ) ) {
// Ra: hm, czasem dla prostego są podane...
// gdy prosty, kontrolne wyliczane przy zmiennej przechyłce
SwitchExtension->Segments[ 1 ]->Init( p3, p4, segsize, r3, r4 );
}
else {
if( eType != tt_Cross ) {
SwitchExtension->Segments[ 1 ]->Init( p3, p3 + cp3, p4 + cp4, p4, segsize, r3, r4 );
}
else {
// dla skrzyżowania dróg dać odwrotnie końce, żeby brzegi generować lewym
SwitchExtension->Segments[ 1 ]->Init( p4, p4 + cp4, p3 + cp3, p3, segsize, r4, r3 ); // odwrócony
}
}
else
SwitchExtension->Segments[1]->Init(p3, p4, segsize, r3, r4);
if (eType == tt_Cross)
{ // Ra 2014-07: dla skrzyżowań będą dodatkowe segmenty
@@ -648,10 +738,10 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
{
Math3D::vector3 v1, v2;
double a1, a2;
v1 = SwitchExtension->Segments[0]->FastGetPoint_1() -
SwitchExtension->Segments[0]->FastGetPoint_0();
v2 = SwitchExtension->Segments[1]->FastGetPoint_1() -
SwitchExtension->Segments[1]->FastGetPoint_0();
v1 = SwitchExtension->Segments[0]->FastGetPoint_1()
- SwitchExtension->Segments[0]->FastGetPoint_0();
v2 = SwitchExtension->Segments[1]->FastGetPoint_1()
- SwitchExtension->Segments[1]->FastGetPoint_0();
a1 = atan2(v1.x, v1.z);
a2 = atan2(v2.x, v2.z);
a2 = a2 - a1;
@@ -663,6 +753,9 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
}
break;
}
}
// optional attributes
parser->getTokens();
*parser >> token;
str = token;
@@ -672,46 +765,46 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
{
parser->getTokens();
*parser >> token;
asEvent0Name = token;
m_events0.emplace_back( token, nullptr );
}
else if (str == "event1")
{
parser->getTokens();
*parser >> token;
asEvent1Name = token;
m_events1.emplace_back( token, nullptr );
}
else if (str == "event2")
{
parser->getTokens();
*parser >> token;
asEvent2Name = token;
m_events2.emplace_back( token, nullptr );
}
else if (str == "eventall0")
{
parser->getTokens();
*parser >> token;
asEventall0Name = token;
m_events0all.emplace_back( token, nullptr );
}
else if (str == "eventall1")
{
parser->getTokens();
*parser >> token;
asEventall1Name = token;
m_events1all.emplace_back( token, nullptr );
}
else if (str == "eventall2")
{
parser->getTokens();
*parser >> token;
asEventall2Name = token;
m_events2all.emplace_back( token, nullptr );
}
else if (str == "velocity")
{
parser->getTokens();
*parser >> fVelocity; //*0.28; McZapkie-010602
if (SwitchExtension) // jeśli tor ruchomy
if (std::fabs(fVelocity) >= 1.0) //żeby zero nie ograniczało dożywotnio
SwitchExtension->fVelocity = static_cast<float>(fVelocity); // zapamiętanie głównego ograniczenia; a
// np. -40 ogranicza tylko na bok
if (std::abs(fVelocity) >= 1.0) //żeby zero nie ograniczało dożywotnio
// zapamiętanie głównego ograniczenia; a np. -40 ogranicza tylko na bok
SwitchExtension->fVelocity = static_cast<float>(fVelocity);
}
else if (str == "isolated")
{ // obwód izolowany, do którego tor należy
@@ -778,127 +871,44 @@ void TTrack::Load(cParser *parser, Math3D::vector3 pOrigin)
/ 3.0 } );
}
// TODO: refactor this mess
bool TTrack::AssignEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2)
{
bool bError = false;
bool TTrack::AssignEvents() {
if( NewEvent0 == nullptr ) {
if( false == asEvent0Name.empty() ) {
ErrorLog( "Bad event: event \"" + asEvent0Name + "\" assigned to track \"" + m_name + "\" does not exist" );
bError = true;
}
bool lookupfail { false };
std::vector< std::pair< std::string, event_sequence * > > const eventsequences {
{ "event0", &m_events0 }, { "eventall0", &m_events0all },
{ "event1", &m_events1 }, { "eventall1", &m_events1all },
{ "event2", &m_events2 }, { "eventall2", &m_events2all } };
for( auto &eventsequence : eventsequences ) {
for( auto &event : *( eventsequence.second ) ) {
event.second = simulation::Events.FindEvent( event.first );
if( event.second != nullptr ) {
m_events = true;
}
else {
if( evEvent0 == nullptr ) {
evEvent0 = NewEvent0;
asEvent0Name = "";
iEvents |= 1; // sumaryczna informacja o eventach
ErrorLog( "Bad event: event \"" + event.first + "\" assigned to track \"" + m_name + "\" does not exist" );
lookupfail = true;
}
else {
ErrorLog( "Bad track: event \"" + NewEvent0->asName + "\" cannot be assigned to track, track already has one" );
bError = true;
}
}
if( NewEvent1 == nullptr ) {
if( false == asEvent1Name.empty() ) {
ErrorLog( "Bad event: event \"" + asEvent1Name + "\" assigned to track \"" + m_name + "\" does not exist" );
bError = true;
auto const trackname { name() };
if( ( Global.iHiddenEvents & 1 )
&& ( false == trackname.empty() ) ) {
// jeśli podana jest nazwa torów, można szukać eventów skojarzonych przez nazwę
for( auto &eventsequence : eventsequences ) {
auto *event = simulation::Events.FindEvent( trackname + ':' + eventsequence.first );
if( event != nullptr ) {
// HACK: auto-associated events come with empty lookup string, to avoid including them in the text format export
eventsequence.second->emplace_back( "", event );
m_events = true;
}
}
else {
if( evEvent1 == nullptr ) {
evEvent1 = NewEvent1;
asEvent1Name = "";
iEvents |= 2; // sumaryczna informacja o eventach
}
else {
ErrorLog( "Bad track: event \"" + NewEvent1->asName + "\" cannot be assigned to track, track already has one" );
bError = true;
}
}
if( NewEvent2 == nullptr ) {
if( false == asEvent2Name.empty() ) {
ErrorLog( "Bad event: event \"" + asEvent2Name + "\" assigned to track \"" + m_name + "\" does not exist" );
bError = true;
}
}
else {
if( evEvent2 == nullptr ) {
evEvent2 = NewEvent2;
asEvent2Name = "";
iEvents |= 4; // sumaryczna informacja o eventach
}
else {
ErrorLog( "Bad track: event \"" + NewEvent2->asName + "\" cannot be assigned to track, track already has one" );
bError = true;
}
}
return ( bError == false );
}
bool TTrack::AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2)
{
bool bError = false;
if( NewEvent0 == nullptr ) {
if( false == asEventall0Name.empty() ) {
ErrorLog( "Bad event: event \"" + asEventall0Name + "\" assigned to track \"" + m_name + "\" does not exist" );
bError = true;
}
}
else {
if( evEventall0 == nullptr ) {
evEventall0 = NewEvent0;
asEventall0Name = "";
iEvents |= 8; // sumaryczna informacja o eventach
}
else {
ErrorLog( "Bad track: event \"" + NewEvent0->asName + "\" cannot be assigned to track, track already has one" );
bError = true;
}
}
if( NewEvent1 == nullptr ) {
if( false == asEventall1Name.empty() ) {
ErrorLog( "Bad event: event \"" + asEventall1Name + "\" assigned to track \"" + m_name + "\" does not exist" );
bError = true;
}
}
else {
if( evEventall1 == nullptr ) {
evEventall1 = NewEvent1;
asEventall1Name = "";
iEvents |= 16; // sumaryczna informacja o eventach
}
else {
ErrorLog( "Bad track: event \"" + NewEvent1->asName + "\" cannot be assigned to track, track already has one" );
bError = true;
}
}
if( NewEvent2 == nullptr ) {
if( false == asEventall2Name.empty() ) {
ErrorLog( "Bad event: event \"" + asEventall2Name + "\" assigned to track \"" + m_name + "\" does not exist" );
bError = true;
}
}
else {
if( evEventall2 == nullptr ) {
evEventall2 = NewEvent2;
asEventall2Name = "";
iEvents |= 32; // sumaryczna informacja o eventach
}
else {
ErrorLog( "Bad track: event \"" + NewEvent2->asName + "\" cannot be assigned to track, track already has one" );
bError = true;
}
}
return ( bError == false );
return ( lookupfail == false );
}
bool TTrack::AssignForcedEvents(TEvent *NewEventPlus, TEvent *NewEventMinus)
@@ -1406,6 +1416,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
{szyna[ i ].texture.x, 0.f} };
}
// TODO, TBD: change all track geometry to triangles, to allow packing data in less, larger buffers
auto const bladelength { 2 * Global.SplineFidelity };
if (SwitchExtension->RightSwitch)
{ // nowa wersja z SPKS, ale odwrotnie lewa/prawa
gfx::vertex_array vertices;
@@ -1414,11 +1425,11 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength );
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength, 1.0, 2 );
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength, 1.0, bladelength );
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
// left blade
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 );
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, bladelength, SwitchExtension->fOffset2 );
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
}
@@ -1427,11 +1438,11 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength );
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength, 1.0, 2 );
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength, 1.0, bladelength );
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
// right blade
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -fMaxOffset + SwitchExtension->fOffset1 );
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, bladelength, -fMaxOffset + SwitchExtension->fOffset1 );
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
}
@@ -1444,11 +1455,11 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength ); // lewa szyna normalna cała
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength, 1.0, 2 ); // prawa szyna za iglicą
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength, 1.0, bladelength ); // prawa szyna za iglicą
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
// right blade
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 );
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, bladelength, -SwitchExtension->fOffset2 );
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
}
@@ -1457,11 +1468,11 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength ); // prawa szyna normalnie cała
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength, 1.0, 2 ); // lewa szyna za iglicą
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength, 1.0, bladelength ); // lewa szyna za iglicą
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
// left blade
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, fMaxOffset - SwitchExtension->fOffset1 );
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, bladelength, fMaxOffset - SwitchExtension->fOffset1 );
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
vertices.clear();
}
@@ -2222,9 +2233,13 @@ bool TTrack::Switch(int i, float const t, float const d)
iNextDirection = SwitchExtension->iNextDirection[i];
iPrevDirection = SwitchExtension->iPrevDirection[i];
fRadius = fRadiusTable[i]; // McZapkie: wybor promienia toru
if (SwitchExtension->fVelocity <=
-2) //-1 oznacza maksymalną prędkość, a dalsze ujemne to ograniczenie na bok
if( SwitchExtension->fVelocity <= -2 ) {
//-1 oznacza maksymalną prędkość, a dalsze ujemne to ograniczenie na bok
fVelocity = i ? -SwitchExtension->fVelocity : -1;
}
else {
fVelocity = SwitchExtension->fVelocity;
}
if (SwitchExtension->pOwner ? SwitchExtension->pOwner->RaTrackAnimAdd(this) :
true) // jeśli nie dodane do animacji
{ // nie ma się co bawić
@@ -2457,17 +2472,18 @@ TTrack * TTrack::RaAnimate()
gfx::vertex_array vertices;
auto const bladelength { 2 * Global.SplineFidelity };
if (SwitchExtension->RightSwitch)
{ // nowa wersja z SPKS, ale odwrotnie lewa/prawa
if( m_material1 ) {
// left blade
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 );
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, bladelength, SwitchExtension->fOffset2 );
GfxRenderer.Replace( vertices, Geometry1[ 2 ] );
vertices.clear();
}
if( m_material2 ) {
// right blade
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -fMaxOffset + SwitchExtension->fOffset1 );
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, bladelength, -fMaxOffset + SwitchExtension->fOffset1 );
GfxRenderer.Replace( vertices, Geometry2[ 2 ] );
vertices.clear();
}
@@ -2475,13 +2491,13 @@ TTrack * TTrack::RaAnimate()
else { // lewa działa lepiej niż prawa
if( m_material1 ) {
// right blade
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 );
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, bladelength, -SwitchExtension->fOffset2 );
GfxRenderer.Replace( vertices, Geometry1[ 2 ] );
vertices.clear();
}
if( m_material2 ) {
// left blade
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, fMaxOffset - SwitchExtension->fOffset1 );
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, bladelength, fMaxOffset - SwitchExtension->fOffset1 );
GfxRenderer.Replace( vertices, Geometry2[ 2 ] );
vertices.clear();
}
@@ -2661,32 +2677,184 @@ TTrack::endpoints() const {
}
}
// calculates path's bounding radius
void
// radius() subclass details, calculates node's bounding radius
float
TTrack::radius_() {
auto const points = endpoints();
auto const points { endpoints() };
auto radius { 0.f };
for( auto &point : points ) {
m_area.radius = std::max(
m_area.radius,
radius = std::max(
radius,
static_cast<float>( glm::length( m_area.center - point ) ) ); // extra margin to prevent driven vehicle from flicking
}
return radius;
}
// serialize() subclass details, sends content of the subclass to provided stream
void
TTrack::serialize_( std::ostream &Output ) const {
// TODO: implement
}
// deserialize() subclass details, restores content of the subclass from provided stream
void
TTrack::deserialize_( std::istream &Input ) {
// TODO: implement
}
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void
TTrack::export_as_text_( std::ostream &Output ) const {
// header
Output << "track ";
// type
Output << (
eType == tt_Normal ? (
iCategoryFlag == 1 ? "normal" :
iCategoryFlag == 2 ? "road" :
iCategoryFlag == 4 ? "river" :
"none" ) :
eType == tt_Switch ? "switch" :
eType == tt_Cross ? "cross" :
eType == tt_Table ? "turn" :
eType == tt_Tributary ? "tributary" :
"none" )
<< ' ';
// basic attributes
Output
<< Length() << ' '
<< fTrackWidth << ' '
<< fFriction << ' '
<< fSoundDistance << ' '
<< iQualityFlag << ' '
<< iDamageFlag << ' ';
// environment
Output << (
eEnvironment == e_flat ? "flat" :
eEnvironment == e_bridge ? "bridge" :
eEnvironment == e_tunnel ? "tunnel" :
eEnvironment == e_bank ? "bank" :
eEnvironment == e_canyon ? "canyon" :
eEnvironment == e_mountains ? "mountains" :
"none" )
<< ' ';
// visibility
// NOTE: 'invis' would be less wrong than 'unvis', but potentially incompatible with old 3rd party tools
Output << ( m_visible ? "vis" : "unvis" ) << ' ';
if( m_visible ) {
// texture parameters are supplied only if the path is set as visible
auto texturefile { (
m_material1 != null_handle ?
GfxRenderer.Material( m_material1 ).name :
"none" ) };
if( texturefile.find( szTexturePath ) == 0 ) {
// don't include 'textures/' in the path
texturefile.erase( 0, std::string{ szTexturePath }.size() );
}
Output
<< texturefile << ' '
<< fTexLength << ' ';
texturefile = (
m_material2 != null_handle ?
GfxRenderer.Material( m_material2 ).name :
"none" );
if( texturefile.find( szTexturePath ) == 0 ) {
// don't include 'textures/' in the path
texturefile.erase( 0, std::string{ szTexturePath }.size() );
}
Output << texturefile << ' ';
Output
<< (fTexHeight1 - fTexHeightOffset ) * ( ( iCategoryFlag & 4 ) ? -1 : 1 ) << ' '
<< fTexWidth << ' '
<< fTexSlope << ' ';
}
// path data
for( auto const &path : m_paths ) {
Output
<< path.points[ segment_data::point::start ].x << ' '
<< path.points[ segment_data::point::start ].y << ' '
<< path.points[ segment_data::point::start ].z << ' '
<< path.rolls[ 0 ] << ' '
<< path.points[ segment_data::point::control1 ].x << ' '
<< path.points[ segment_data::point::control1 ].y << ' '
<< path.points[ segment_data::point::control1 ].z << ' '
<< path.points[ segment_data::point::control2 ].x << ' '
<< path.points[ segment_data::point::control2 ].y << ' '
<< path.points[ segment_data::point::control2 ].z << ' '
<< path.points[ segment_data::point::end ].x << ' '
<< path.points[ segment_data::point::end ].y << ' '
<< path.points[ segment_data::point::end ].z << ' '
<< path.rolls[ 1 ] << ' '
<< path.radius << ' ';
}
// optional attributes
std::vector< std::pair< std::string, event_sequence const * > > const eventsequences {
{ "event0", &m_events0 }, { "eventall0", &m_events0all },
{ "event1", &m_events1 }, { "eventall1", &m_events1all },
{ "event2", &m_events2 }, { "eventall2", &m_events2all } };
for( auto &eventsequence : eventsequences ) {
for( auto &event : *( eventsequence.second ) ) {
if( false == event.first.empty() ) {
Output
<< eventsequence.first << ' '
<< event.first << ' ';
}
}
}
if( ( SwitchExtension )
&& ( SwitchExtension->fVelocity != -1.0 ) ) {
Output << "velocity " << SwitchExtension->fVelocity << ' ';
}
else {
if( fVelocity != -1.0 ) {
Output << "velocity " << fVelocity << ' ';
}
}
if( pIsolated ) {
Output << "isolated " << pIsolated->asName << ' ';
}
if( fOverhead != -1.0 ) {
Output << "overhead " << fOverhead << ' ';
}
// footer
Output
<< "endtrack"
<< "\n";
}
void TTrack::MovedUp1(float const dh)
{ // poprawienie przechyłki wymaga wydłużenia podsypki
fTexHeight1 += dh;
fTexHeightOffset += dh;
};
void TTrack::VelocitySet(float v)
{ // ustawienie prędkości z ograniczeniem do pierwotnej wartości (zapisanej w scenerii)
if (SwitchExtension ? SwitchExtension->fVelocity >= 0.0 : false)
{ // zwrotnica może mieć odgórne ograniczenie, nieprzeskakiwalne eventem
if (v > SwitchExtension->fVelocity ? true : v < 0.0)
return void(fVelocity =
SwitchExtension->fVelocity); // maksymalnie tyle, ile było we wpisie
// ustawienie prędkości z ograniczeniem do pierwotnej wartości (zapisanej w scenerii)
void TTrack::VelocitySet(float v) {
// TBD, TODO: add a variable to preserve potential speed limit set by the track configuration on basic track pieces
if( ( SwitchExtension )
&& ( SwitchExtension->fVelocity != -1 ) ) {
// zwrotnica może mieć odgórne ograniczenie, nieprzeskakiwalne eventem
fVelocity =
min_speed<float>(
v,
( SwitchExtension->fVelocity > 0 ?
SwitchExtension->fVelocity : // positive limit applies to both switch tracks
( SwitchExtension->CurrentIndex == 0 ?
-1 : // negative limit applies only to the diverging track
-SwitchExtension->fVelocity ) ) );
}
else {
fVelocity = v; // nie ma ograniczenia
}
};
double TTrack::VelocityGet()
@@ -2784,31 +2952,10 @@ path_table::InitTracks() {
for( auto first = std::rbegin(m_items); first != std::rend(m_items); ++first ) {
auto *track = *first;
#endif
track->AssignEvents(
simulation::Events.FindEvent( track->asEvent0Name ),
simulation::Events.FindEvent( track->asEvent1Name ),
simulation::Events.FindEvent( track->asEvent2Name ) );
track->AssignallEvents(
simulation::Events.FindEvent( track->asEventall0Name ),
simulation::Events.FindEvent( track->asEventall1Name ),
simulation::Events.FindEvent( track->asEventall2Name ) );
track->AssignEvents();
auto const trackname { track->name() };
if( ( Global.iHiddenEvents & 1 )
&& ( false == trackname.empty() ) ) {
// jeśli podana jest nazwa torów, można szukać eventów skojarzonych przez nazwę
track->AssignEvents(
simulation::Events.FindEvent( trackname + ":event0" ),
simulation::Events.FindEvent( trackname + ":event1" ),
simulation::Events.FindEvent( trackname + ":event2" ) );
track->AssignallEvents(
simulation::Events.FindEvent( trackname + ":eventall0" ),
simulation::Events.FindEvent( trackname + ":eventall1" ),
simulation::Events.FindEvent( trackname + ":eventall2" ) );
}
switch (track->eType) {
// TODO: re-enable
case tt_Table: {
@@ -2950,6 +3097,8 @@ path_table::InitTracks() {
scene::node_data nodedata;
nodedata.name = isolated->asName;
auto *memorycell = new TMemCell( nodedata ); // to nie musi mieć nazwy, nazwa w wyszukiwarce wystarczy
// NOTE: autogenerated cells aren't exported; they'll be autogenerated anew when exported file is loaded
memorycell->is_exportable = false;
simulation::Memory.insert( memorycell );
isolated->pMemCell = memorycell; // wskaźnik komóki przekazany do odcinka izolowanego
}

39
Track.h
View File

@@ -124,7 +124,7 @@ private:
};
// trajektoria ruchu - opakowanie
class TTrack : public editor::basic_node {
class TTrack : public scene::basic_node {
friend class opengl_renderer;
@@ -142,20 +142,33 @@ private:
float fTexRatio1 = 1.0f; // proporcja boków tekstury nawierzchni (żeby zaoszczędzić na rozmiarach tekstur...)
float fTexRatio2 = 1.0f; // proporcja boków tekstury chodnika (żeby zaoszczędzić na rozmiarach tekstur...)
float fTexHeight1 = 0.6f; // wysokość brzegu względem trajektorii
float fTexHeightOffset = 0.f; // potential adjustment to account for the path roll
float fTexWidth = 0.9f; // szerokość boku
float fTexSlope = 0.9f;
glm::dvec3 m_origin;
material_handle m_material1 = 0; // tekstura szyn albo nawierzchni
material_handle m_material2 = 0; // tekstura automatycznej podsypki albo pobocza
typedef std::vector<gfx::geometry_handle> geometryhandle_sequence;
using geometryhandle_sequence = std::vector<gfx::geometry_handle>;
geometryhandle_sequence Geometry1; // geometry chunks textured with texture 1
geometryhandle_sequence Geometry2; // geometry chunks textured with texture 2
std::vector<segment_data> m_paths; // source data for owned paths
public:
typedef std::deque<TDynamicObject *> dynamics_sequence;
using dynamics_sequence = std::deque<TDynamicObject *>;
using event_sequence = std::vector<std::pair<std::string, TEvent *> >;
dynamics_sequence Dynamics;
int iEvents = 0; // Ra: flaga informująca o obecności eventów
event_sequence
m_events0all,
m_events1all,
m_events2all,
m_events0,
m_events1,
m_events2;
bool m_events { false }; // Ra: flaga informująca o obecności eventów
/*
TEvent *evEventall0 = nullptr; // McZapkie-140302: wyzwalany gdy pojazd stoi
TEvent *evEventall1 = nullptr;
TEvent *evEventall2 = nullptr;
@@ -168,6 +181,7 @@ public:
std::string asEvent0Name;
std::string asEvent1Name;
std::string asEvent2Name;
*/
int iNextDirection = 0; // 0:Point1, 1:Point2, 3:do odchylonego na zwrotnicy
int iPrevDirection = 0; // domyślnie wirtualne odcinki dołączamy stroną od Point1
TTrackType eType = tt_Normal; // domyślnie zwykły
@@ -231,8 +245,7 @@ public:
SwitchExtension->iRoads - 1 :
1 ); }
void Load(cParser *parser, Math3D::vector3 pOrigin);
bool AssignEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2);
bool AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2);
bool AssignEvents();
bool AssignForcedEvents(TEvent *NewEventPlus, TEvent *NewEventMinus);
bool CheckDynamicObject(TDynamicObject *Dynamic);
bool AddDynamicObject(TDynamicObject *Dynamic);
@@ -270,10 +283,16 @@ public:
double VelocityGet();
void ConnectionsLog();
protected:
// calculates path's bounding radius
void
radius_();
private:
// radius() subclass details, calculates node's bounding radius
float radius_();
// serialize() subclass details, sends content of the subclass to provided stream
void serialize_( std::ostream &Output ) const;
// deserialize() subclass details, restores content of the subclass from provided stream
void deserialize_( std::istream &Input );
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void export_as_text_( std::ostream &Output ) const;
};

View File

@@ -529,18 +529,6 @@ double TTraction::VoltageGet(double u, double i)
return 0.0; // gdy nie podłączony wcale?
};
// calculates path's bounding radius
void
TTraction::radius_() {
auto const points = endpoints();
for( auto &point : points ) {
m_area.radius = std::max(
m_area.radius,
static_cast<float>( glm::length( m_area.center - point ) ) );
}
}
glm::vec3
TTraction::wire_color() const {
@@ -581,6 +569,7 @@ TTraction::wire_color() const {
color.r *= Global.DayLight.ambient[ 0 ];
color.g *= Global.DayLight.ambient[ 1 ];
color.b *= Global.DayLight.ambient[ 2 ];
color *= 0.5f;
}
else {
// tymczasowo pokazanie zasilanych odcinków
@@ -676,6 +665,77 @@ TTraction::wire_color() const {
return color;
}
// radius() subclass details, calculates node's bounding radius
float
TTraction::radius_() {
auto const points { endpoints() };
auto radius { 0.f };
for( auto &point : points ) {
radius = std::max(
radius,
static_cast<float>( glm::length( m_area.center - point ) ) );
}
return radius;
}
// serialize() subclass details, sends content of the subclass to provided stream
void
TTraction::serialize_( std::ostream &Output ) const {
// TODO: implement
}
// deserialize() subclass details, restores content of the subclass from provided stream
void
TTraction::deserialize_( std::istream &Input ) {
// TODO: implement
}
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void
TTraction::export_as_text_( std::ostream &Output ) const {
// header
Output << "traction ";
// basic attributes
Output
<< asPowerSupplyName << ' '
<< NominalVoltage << ' '
<< MaxCurrent << ' '
<< ( fResistivity * 1000 ) << ' '
<< (
Material == 2 ? "al" :
Material == 0 ? "none" :
"cu" ) << ' '
<< WireThickness << ' '
<< DamageFlag << ' ';
// path data
Output
<< pPoint1.x << ' ' << pPoint1.y << ' ' << pPoint1.z << ' '
<< pPoint2.x << ' ' << pPoint2.y << ' ' << pPoint2.z << ' '
<< pPoint3.x << ' ' << pPoint3.y << ' ' << pPoint3.z << ' '
<< pPoint4.x << ' ' << pPoint4.y << ' ' << pPoint4.z << ' ';
// minimum height
Output << ( ( pPoint3.y - pPoint1.y + pPoint4.y - pPoint2.y ) * 0.5 - fHeightDifference ) << ' ';
// segment length
Output << static_cast<int>( iNumSections ? glm::length( pPoint1 - pPoint2 ) / iNumSections : 0.0 ) << ' ';
// wire data
Output
<< Wires << ' '
<< WireOffset << ' ';
// visibility
// NOTE: 'invis' would be less wrong than 'unvis', but potentially incompatible with old 3rd party tools
Output << ( m_visible ? "vis" : "unvis" ) << ' ';
// optional attributes
if( false == asParallel.empty() ) {
Output << "parallel " << asParallel << ' ';
}
// footer
Output
<< "endtraction"
<< "\n";
}
// legacy method, initializes traction after deserialization from scenario file

View File

@@ -19,7 +19,7 @@ http://mozilla.org/MPL/2.0/.
class TTractionPowerSource;
class TTraction : public editor::basic_node {
class TTraction : public scene::basic_node {
friend class opengl_renderer;
@@ -74,13 +74,18 @@ class TTraction : public editor::basic_node {
void PowerSet(TTractionPowerSource *ps);
double VoltageGet(double u, double i);
protected:
// calculates piece's bounding radius
void
radius_();
private:
// methods
glm::vec3 wire_color() const;
// radius() subclass details, calculates node's bounding radius
float radius_();
// serialize() subclass details, sends content of the subclass to provided stream
void serialize_( std::ostream &Output ) const;
// deserialize() subclass details, restores content of the subclass from provided stream
void deserialize_( std::istream &Input );
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void export_as_text_( std::ostream &Output ) const;
};

View File

@@ -141,6 +141,52 @@ void TTractionPowerSource::PowerSet(TTractionPowerSource *ps)
// else ErrorLog("nie może być więcej punktów zasilania niż dwa");
};
// serialize() subclass details, sends content of the subclass to provided stream
void
TTractionPowerSource::serialize_( std::ostream &Output ) const {
// TODO: implement
}
// deserialize() subclass details, restores content of the subclass from provided stream
void
TTractionPowerSource::deserialize_( std::istream &Input ) {
// TODO: implement
}
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void
TTractionPowerSource::export_as_text_( std::ostream &Output ) const {
// header
Output << "tractionpowersource ";
// placement
Output
<< location().x << ' '
<< location().y << ' '
<< location().z << ' ';
// basic attributes
Output
<< NominalVoltage << ' '
<< VoltageFrequency << ' '
<< InternalRes << ' '
<< MaxOutputCurrent << ' '
<< FastFuseTimeOut << ' '
<< FastFuseRepetition << ' '
<< SlowFuseTimeOut << ' ';
// optional attributes
if( true == Recuperation ) {
Output << "recuperation ";
}
if( true == bSection ) {
Output << "section ";
}
// footer
Output
<< "end"
<< "\n";
}
// legacy method, calculates changes in simulation state over specified time

View File

@@ -13,9 +13,33 @@ http://mozilla.org/MPL/2.0/.
#include "scenenode.h"
#include "Names.h"
class TTractionPowerSource : public editor::basic_node {
class TTractionPowerSource : public scene::basic_node {
private:
public:
// constructor
TTractionPowerSource( scene::node_data const &Nodedata );
// methods
void Init(double const u, double const i);
bool Load(cParser *parser);
bool Update(double dt);
double CurrentGet(double res);
void VoltageSet(double const v) {
NominalVoltage = v; };
void PowerSet(TTractionPowerSource *ps);
// members
TTractionPowerSource *psNode[ 2 ] = { nullptr, nullptr }; // zasilanie na końcach dla sekcji
bool bSection = false; // czy jest sekcją
private:
// methods
// serialize() subclass details, sends content of the subclass to provided stream
void serialize_( std::ostream &Output ) const;
// deserialize() subclass details, restores content of the subclass from provided stream
void deserialize_( std::istream &Input );
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
void export_as_text_( std::ostream &Output ) const;
// members
double NominalVoltage = 0.0;
double VoltageFrequency = 0.0;
double InternalRes = 0.2;
@@ -34,20 +58,6 @@ class TTractionPowerSource : public editor::basic_node {
double FuseTimer = 0.0;
int FuseCounter = 0;
public:
// zmienne publiczne
TTractionPowerSource *psNode[ 2 ] = { nullptr, nullptr }; // zasilanie na końcach dla sekcji
bool bSection = false; // czy jest sekcją
TTractionPowerSource( scene::node_data const &Nodedata );
void Init(double const u, double const i);
bool Load(cParser *parser);
bool Update(double dt);
double CurrentGet(double res);
void VoltageSet(double const v) {
NominalVoltage = v; };
void PowerSet(TTractionPowerSource *ps);
};

112
Train.cpp
View File

@@ -207,6 +207,7 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = {
{ user_command::mubrakingindicatortoggle, &TTrain::OnCommand_mubrakingindicatortoggle },
{ user_command::reverserincrease, &TTrain::OnCommand_reverserincrease },
{ user_command::reverserdecrease, &TTrain::OnCommand_reverserdecrease },
{ user_command::reverserforwardhigh, &TTrain::OnCommand_reverserforwardhigh },
{ user_command::reverserforward, &TTrain::OnCommand_reverserforward },
{ user_command::reverserneutral, &TTrain::OnCommand_reverserneutral },
{ user_command::reverserbackward, &TTrain::OnCommand_reverserbackward },
@@ -744,12 +745,22 @@ void TTrain::OnCommand_mastercontrollerdecreasefast( TTrain *Train, command_data
void TTrain::OnCommand_mastercontrollerset( TTrain *Train, command_data const &Command ) {
auto const targetposition { std::min<int>( Command.param1, Train->mvControlled->MainCtrlPosNo ) };
while( targetposition < Train->mvControlled->MainCtrlPos ) {
Train->mvControlled->DecMainCtrl( 1 );
auto positionchange {
std::min<int>(
Command.param1,
( Train->mvControlled->CoupledCtrl ?
Train->mvControlled->MainCtrlPosNo + Train->mvControlled->ScndCtrlPosNo :
Train->mvControlled->MainCtrlPosNo ) )
- ( Train->mvControlled->CoupledCtrl ?
Train->mvControlled->MainCtrlPos + Train->mvControlled->ScndCtrlPos :
Train->mvControlled->MainCtrlPos ) };
while( ( positionchange < 0 )
&& ( true == Train->mvControlled->DecMainCtrl( 1 ) ) ) {
++positionchange;
}
while( targetposition > Train->mvControlled->MainCtrlPos ) {
Train->mvControlled->IncMainCtrl( 1 );
while( ( positionchange > 0 )
&& ( true == Train->mvControlled->IncMainCtrl( 1 ) ) ) {
--positionchange;
}
}
@@ -854,11 +865,15 @@ void TTrain::OnCommand_secondcontrollerdecreasefast( TTrain *Train, command_data
void TTrain::OnCommand_secondcontrollerset( TTrain *Train, command_data const &Command ) {
auto const targetposition { std::min<int>( Command.param1, Train->mvControlled->ScndCtrlPosNo ) };
while( targetposition < Train->mvControlled->ScndCtrlPos ) {
Train->mvControlled->DecScndCtrl( 1 );
while( ( targetposition < Train->mvControlled->ScndCtrlPos )
&& ( true == Train->mvControlled->DecScndCtrl( 1 ) ) ) {
// all work is done in the header
;
}
while( targetposition > Train->mvControlled->ScndCtrlPos ) {
Train->mvControlled->IncScndCtrl( 1 );
while( ( targetposition > Train->mvControlled->ScndCtrlPos )
&& ( true == Train->mvControlled->IncScndCtrl( 1 ) ) ) {
// all work is done in the header
;
}
}
@@ -1458,9 +1473,20 @@ void TTrain::OnCommand_reverserdecrease( TTrain *Train, command_data const &Comm
}
}
void TTrain::OnCommand_reverserforwardhigh( TTrain *Train, command_data const &Command ) {
if( Command.action == GLFW_PRESS ) {
OnCommand_reverserforward( Train, Command );
OnCommand_reverserincrease( Train, Command );
}
}
void TTrain::OnCommand_reverserforward( TTrain *Train, command_data const &Command ) {
if( Command.action == GLFW_PRESS ) {
// HACK: try to move the reverser one position back, in case it's set to "high forward"
OnCommand_reverserdecrease( Train, Command );
if( Train->mvOccupied->ActiveDir < 1 ) {
@@ -4322,7 +4348,7 @@ bool TTrain::Update( double const Deltatime )
tor = DynamicObject->GetTrack(); // McZapkie-180203
// McZapkie: predkosc wyswietlana na tachometrze brana jest z obrotow kol
float maxtacho = 3;
auto const maxtacho { 3.0 };
fTachoVelocity = static_cast<float>( std::min( std::abs(11.31 * mvControlled->WheelDiameter * mvControlled->nrot), mvControlled->Vmax * 1.05) );
{ // skacze osobna zmienna
float ff = simulation::Time.data().wSecond; // skacze co sekunde - pol sekundy
@@ -4338,12 +4364,12 @@ bool TTrain::Update( double const Deltatime )
}
if (fTachoVelocity > 1) // McZapkie-270503: podkrecanie tachometru
{
if (fTachoCount < maxtacho)
fTachoCount += Deltatime * 3; // szybciej zacznij stukac
// szybciej zacznij stukac
fTachoCount = std::min( maxtacho, fTachoCount + Deltatime * 3 );
}
else if( fTachoCount > 0 ) {
// schodz powoli - niektore haslery to ze 4 sekundy potrafia stukac
fTachoCount -= Deltatime * 0.66;
fTachoCount = std::max( 0.0, fTachoCount - Deltatime * 0.66 );
}
// Ra 2014-09: napięcia i prądy muszą być ustalone najpierw, bo wysyłane są ewentualnie na PoKeys
@@ -5171,10 +5197,8 @@ bool TTrain::Update( double const Deltatime )
//---------
// hunter-080812: poprawka na ogrzewanie w elektrykach - usuniete uzaleznienie od przetwornicy
if( ( mvControlled->Heating == true )
&& ( ( mvControlled->ConverterFlag )
|| ( ( mvControlled->EngineType == ElectricSeriesMotor )
&& ( mvControlled->Mains == true )
&& ( mvControlled->ConvOvldFlag == false ) ) ) )
&& ( mvControlled->ConvOvldFlag == false ) )
btLampkaOgrzewanieSkladu.Turn( true );
else
btLampkaOgrzewanieSkladu.Turn( false );
@@ -5887,6 +5911,10 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
sound->offset( nullvector );
sound->owner( DynamicObject );
}
// reset view angles
pMechViewAngle = { 0.0, 0.0 };
Global.pCamera->Pitch = pMechViewAngle.x;
Global.pCamera->Yaw = pMechViewAngle.y;
pyScreens.reset(this);
pyScreens.setLookupPath(DynamicObject->asBaseDir);
@@ -5939,41 +5967,59 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
{
// jeśli znaleziony wpis kabiny
Cabine[cabindex].Load(*parser);
// NOTE: the next part is likely to break if sitpos doesn't follow pos
// NOTE: the position and angle definitions depend on strict entry order
// TODO: refactor into more flexible arrangement
parser->getTokens();
*parser >> token;
if( token == std::string( "driver" + std::to_string( cabindex ) + "angle:" ) ) {
// camera view angle
parser->getTokens( 2, false );
// angle is specified in degrees but internally stored in radians
glm::vec2 viewangle;
*parser
>> viewangle.y // yaw first, then pitch
>> viewangle.x;
pMechViewAngle = glm::radians( viewangle );
Global.pCamera->Pitch = pMechViewAngle.x;
Global.pCamera->Yaw = pMechViewAngle.y;
parser->getTokens();
*parser >> token;
}
if (token == std::string("driver" + std::to_string(cabindex) + "pos:"))
{
// pozycja poczatkowa maszynisty
parser->getTokens(3, false);
*parser >> pMechOffset.x >> pMechOffset.y >> pMechOffset.z;
pMechSittingPosition.x = pMechOffset.x;
pMechSittingPosition.y = pMechOffset.y;
pMechSittingPosition.z = pMechOffset.z;
}
// ABu: pozycja siedzaca mechanika
*parser
>> pMechOffset.x
>> pMechOffset.y
>> pMechOffset.z;
pMechSittingPosition = pMechOffset;
parser->getTokens();
*parser >> token;
}
// ABu: pozycja siedzaca mechanika
if (token == std::string("driver" + std::to_string(cabindex) + "sitpos:"))
{
// ABu 180404 pozycja siedzaca maszynisty
parser->getTokens(3, false);
*parser >> pMechSittingPosition.x >> pMechSittingPosition.y >> pMechSittingPosition.z;
parse = true;
*parser
>> pMechSittingPosition.x
>> pMechSittingPosition.y
>> pMechSittingPosition.z;
parser->getTokens();
*parser >> token;
}
// else parse=false;
do
{
// ABu: wstawione warunki, wczesniej tylko to:
// str=Parser->GetNextSymbol().LowerCase();
if (parse == true) {
do {
if( parse == true ) {
token = "";
parser->getTokens();
*parser >> token;
}
else
{
else {
parse = true;
}
// inicjacja kabiny

View File

@@ -198,6 +198,7 @@ class TTrain
static void OnCommand_mubrakingindicatortoggle( TTrain *Train, command_data const &Command );
static void OnCommand_reverserincrease( TTrain *Train, command_data const &Command );
static void OnCommand_reverserdecrease( TTrain *Train, command_data const &Command );
static void OnCommand_reverserforwardhigh( TTrain *Train, command_data const &Command );
static void OnCommand_reverserforward( TTrain *Train, command_data const &Command );
static void OnCommand_reverserneutral( TTrain *Train, command_data const &Command );
static void OnCommand_reverserbackward( TTrain *Train, command_data const &Command );
@@ -594,6 +595,7 @@ public: // reszta może by?publiczna
Math3D::vector3 pMechSittingPosition; // ABu 180404
Math3D::vector3 MirrorPosition(bool lewe);
glm::vec2 pMechViewAngle { 0.0, 0.0 }; // camera pitch and yaw values, preserved while in external view
private:
double fBlinkTimer;

View File

@@ -101,7 +101,7 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
{ // pętla przesuwająca wózek przez kolejne tory, aż do trafienia w jakiś
if( pCurrentTrack == nullptr ) { return false; } // nie ma toru, to nie ma przesuwania
// TODO: refactor following block as track method
if( pCurrentTrack->iEvents ) { // sumaryczna informacja o eventach
if( pCurrentTrack->m_events ) { // sumaryczna informacja o eventach
// omijamy cały ten blok, gdy tor nie ma on żadnych eventów (większość nie ma)
if( ( std::abs( fDistance ) < 0.01 )
&& ( Owner->GetVelocity() < 0.01 ) ) {
@@ -109,14 +109,18 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
if( ( Owner->Mechanik != nullptr )
&& ( Owner->Mechanik->Primary() ) ) {
// tylko dla jednego członu
if( ( pCurrentTrack->evEvent0 )
&& ( pCurrentTrack->evEvent0->iQueued == 0 ) ) {
simulation::Events.AddToQuery( pCurrentTrack->evEvent0, Owner );
for( auto &event : pCurrentTrack->m_events0 ) {
if( ( event.second != nullptr )
&& ( event.second->iQueued == 0 ) ) {
simulation::Events.AddToQuery( event.second, Owner );
}
}
if( ( pCurrentTrack->evEventall0 )
&& ( pCurrentTrack->evEventall0->iQueued == 0 ) ) {
simulation::Events.AddToQuery( pCurrentTrack->evEventall0, Owner );
}
for( auto &event : pCurrentTrack->m_events0all ) {
if( ( event.second != nullptr )
&& ( event.second->iQueued == 0 ) ) {
simulation::Events.AddToQuery( event.second, Owner );
}
}
}
else if (fDistance < 0) {
@@ -127,20 +131,27 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
&& ( Owner->Mechanik->Primary() ) ) {
// tylko dla jednego członu
// McZapkie-280503: wyzwalanie event tylko dla pojazdow z obsada
if( ( true == bPrimary )
&& ( pCurrentTrack->evEvent1 != nullptr )
&& ( pCurrentTrack->evEvent1->iQueued == 0 ) ) {
if( true == bPrimary ) {
for( auto &event : pCurrentTrack->m_events1 ) {
if( ( event.second != nullptr )
&& ( event.second->iQueued == 0 ) ) {
// dodanie do kolejki
simulation::Events.AddToQuery( pCurrentTrack->evEvent1, Owner );
simulation::Events.AddToQuery( event.second, Owner );
}
}
}
}
}
if( SetFlag( iEventallFlag, -1 ) ) {
// McZapkie-280503: wyzwalanie eventall dla wszystkich pojazdow
if( ( true == bPrimary )
&& ( pCurrentTrack->evEventall1 != nullptr )
&& ( pCurrentTrack->evEventall1->iQueued == 0 ) ) {
simulation::Events.AddToQuery( pCurrentTrack->evEventall1, Owner ); // dodanie do kolejki
if( true == bPrimary ) {
for( auto &event : pCurrentTrack->m_events1all ) {
if( ( event.second != nullptr )
&& ( event.second->iQueued == 0 ) ) {
// dodanie do kolejki
simulation::Events.AddToQuery( event.second, Owner );
}
}
}
}
}
@@ -151,19 +162,27 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
if( ( Owner->Mechanik != nullptr )
&& ( Owner->Mechanik->Primary() ) ) {
// tylko dla jednego członu
if( ( true == bPrimary )
&& ( pCurrentTrack->evEvent2 != nullptr )
&& ( pCurrentTrack->evEvent2->iQueued == 0 ) ) {
simulation::Events.AddToQuery( pCurrentTrack->evEvent2, Owner );
if( true == bPrimary ) {
for( auto &event : pCurrentTrack->m_events2 ) {
if( ( event.second != nullptr )
&& ( event.second->iQueued == 0 ) ) {
// dodanie do kolejki
simulation::Events.AddToQuery( event.second, Owner );
}
}
}
}
}
if( SetFlag( iEventallFlag, -2 ) ) {
// sprawdza i zeruje na przyszłość, true jeśli zmieni z 2 na 0
if( ( true == bPrimary )
&& ( pCurrentTrack->evEventall2 != nullptr )
&& ( pCurrentTrack->evEventall2->iQueued == 0 ) ) {
simulation::Events.AddToQuery( pCurrentTrack->evEventall2, Owner );
if( true == bPrimary ) {
for( auto &event : pCurrentTrack->m_events2all ) {
if( ( event.second != nullptr )
&& ( event.second->iQueued == 0 ) ) {
// dodanie do kolejki
simulation::Events.AddToQuery( event.second, Owner );
}
}
}
}
}
@@ -258,18 +277,33 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
{ // gdy zostaje na tym samym torze (przesuwanie już nie zmienia toru)
if (bPrimary)
{ // tylko gdy początkowe ustawienie, dodajemy eventy stania do kolejki
if (Owner->MoverParameters->ActiveCab != 0)
// if (Owner->MoverParameters->CabNo!=0)
{
if (pCurrentTrack->evEvent1 && pCurrentTrack->evEvent1->fDelay <= -1.0f)
simulation::Events.AddToQuery(pCurrentTrack->evEvent1, Owner);
if (pCurrentTrack->evEvent2 && pCurrentTrack->evEvent2->fDelay <= -1.0f)
simulation::Events.AddToQuery(pCurrentTrack->evEvent2, Owner);
if (Owner->MoverParameters->ActiveCab != 0) {
for( auto &event : pCurrentTrack->m_events1 ) {
if( ( event.second != nullptr )
&& ( event.second->fDelay <= -1.0 ) ) {
simulation::Events.AddToQuery( event.second, Owner );
}
}
for( auto &event : pCurrentTrack->m_events2 ) {
if( ( event.second != nullptr )
&& ( event.second->fDelay <= -1.0 ) ) {
simulation::Events.AddToQuery( event.second, Owner );
}
}
}
for( auto &event : pCurrentTrack->m_events1all ) {
if( ( event.second != nullptr )
&& ( event.second->fDelay <= -1.0 ) ) {
simulation::Events.AddToQuery( event.second, Owner );
}
}
for( auto &event : pCurrentTrack->m_events2all ) {
if( ( event.second != nullptr )
&& ( event.second->fDelay <= -1.0 ) ) {
simulation::Events.AddToQuery( event.second, Owner );
}
}
if (pCurrentTrack->evEventall1 && pCurrentTrack->evEventall1->fDelay <= -1.0f)
simulation::Events.AddToQuery(pCurrentTrack->evEventall1, Owner);
if (pCurrentTrack->evEventall2 && pCurrentTrack->evEventall2->fDelay <= -1.0f)
simulation::Events.AddToQuery(pCurrentTrack->evEventall2, Owner);
}
fCurrentDistance = s;
// fDistance=0;

View File

@@ -329,7 +329,6 @@ bool TWorld::Init( GLFWwindow *Window ) {
void TWorld::OnKeyDown(int cKey) {
// dump keypress info in the log
if( !Global.iPause ) {
// podczas pauzy klawisze nie działają
std::string keyinfo;
auto keyname = glfwGetKeyName( cKey, 0 );
@@ -372,7 +371,6 @@ void TWorld::OnKeyDown(int cKey) {
WriteLog( "Key pressed: " + keymodifiers + "[" + keyinfo + "]" );
}
}
// actual key processing
// TODO: redo the input system
@@ -522,6 +520,14 @@ void TWorld::OnKeyDown(int cKey) {
}
break;
}
case GLFW_KEY_F11: {
// 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
@@ -626,6 +632,7 @@ void TWorld::InOutKey( bool const Near )
if (Train) {
// cache current cab position so there's no need to set it all over again after each out-in switch
Train->pMechSittingPosition = Train->pMechOffset;
Train->Dynamic()->bDisplayCab = false;
DistantView( Near );
}
@@ -637,9 +644,8 @@ void TWorld::InOutKey( bool const Near )
if (Train)
{
Train->Dynamic()->bDisplayCab = true;
Train->Dynamic()->ABuSetModelShake(
Math3D::vector3(0, 0, 0)); // zerowanie przesunięcia przed powrotem?
// Camera.Stop(); //zatrzymanie ruchu
// zerowanie przesunięcia przed powrotem?
Train->Dynamic()->ABuSetModelShake( { 0, 0, 0 } );
Train->MechStop();
FollowView(); // na pozycję mecha
}
@@ -713,14 +719,12 @@ void TWorld::FollowView(bool wycisz) {
// tu ustawić nową, bo od niej liczą się odległości
Global.pCameraPosition = Camera.Pos;
}
else if (Train)
{ // korekcja ustawienia w kabinie - OK
if( wycisz ) {
// wyciszenie dźwięków z poprzedniej pozycji
// trzymanie prawego w kabinie daje marny efekt
// TODO: re-implement, old one kinda didn't really work
}
else if (Train) {
Camera.Pos = Train->pMechPosition;
// potentially restore cached camera angles
Camera.Pitch = Train->pMechViewAngle.x;
Camera.Yaw = Train->pMechViewAngle.y;
Camera.Roll = std::atan(Train->pMechShake.x * Train->fMechRoll); // hustanie kamery na boki
Camera.Pitch -= 0.5 * std::atan(Train->vMechVelocity.z * Train->fMechPitch); // hustanie kamery przod tyl
@@ -969,6 +973,14 @@ TWorld::Update_Camera( double const Deltatime ) {
if( DebugCameraFlag ) { DebugCamera.Update(); }
else { Camera.Update(); } // uwzględnienie ruchu wywołanego klawiszami
if( ( false == FreeFlyModeFlag )
&& ( false == Global.CabWindowOpen )
&& ( Train != nullptr ) ) {
// cache cab camera view angles in case of view type switch
Train->pMechViewAngle = { Camera.Pitch, Camera.Yaw };
}
// reset window state, it'll be set again if applicable in a check below
Global.CabWindowOpen = false;
@@ -977,7 +989,7 @@ TWorld::Update_Camera( double const Deltatime ) {
&& ( false == DebugCameraFlag ) ) {
// jeśli jazda w kabinie, przeliczyć trzeba parametry kamery
auto tempangle = Controlled->VectorFront() * ( Controlled->MoverParameters->ActiveCab == -1 ? -1 : 1 );
double modelrotate = atan2( -tempangle.x, tempangle.z );
// double modelrotate = atan2( -tempangle.x, tempangle.z );
if( ( true == Global.ctrlState )
&& ( ( glfwGetKey( Global.window, GLFW_KEY_LEFT ) == GLFW_TRUE )
@@ -1005,6 +1017,11 @@ TWorld::Update_Camera( double const Deltatime ) {
}
else {
// patrzenie standardowe
// potentially restore view angle after returning from external view
// TODO: mirror view toggle as separate method
Camera.Pitch = Train->pMechViewAngle.x;
Camera.Yaw = Train->pMechViewAngle.y;
Camera.Pos = Train->GetWorldMechPosition(); // Train.GetPosition1();
if( !Global.iPause ) {
// podczas pauzy nie przeliczać kątów przypadkowymi wartościami
@@ -1014,8 +1031,8 @@ TWorld::Update_Camera( double const Deltatime ) {
// Ra: tu jest uciekanie kamery w górę!!!
Camera.Pitch -= 0.5 * atan( Train->vMechVelocity.z * Train->fMechPitch );
}
// ABu011104: rzucanie pudlem
/*
// ABu011104: rzucanie pudlem
vector3 temp;
if( abs( Train->pMechShake.y ) < 0.25 )
temp = vector3( 0, 0, 6 * Train->pMechShake.y );
@@ -1027,7 +1044,6 @@ TWorld::Update_Camera( double const Deltatime ) {
Controlled->ABuSetModelShake( temp );
// ABu: koniec rzucania
*/
if( Train->Dynamic()->MoverParameters->ActiveCab == 0 )
Camera.LookAt = Train->GetWorldMechPosition() + Train->GetDirection() * 5.0; // gdy w korytarzu
else // patrzenie w kierunku osi pojazdu, z uwzględnieniem kabiny

View File

@@ -159,8 +159,9 @@ buffer_manager::emplace( std::string Filename ) {
m_buffers.emplace_back( Filename );
// NOTE: we store mapping without file type extension, to simplify lookups
erase_extension( Filename );
m_buffermappings.emplace(
Filename.erase( Filename.rfind( '.' ) ),
Filename,
handle );
return handle;
@@ -169,26 +170,22 @@ buffer_manager::emplace( std::string Filename ) {
audio::buffer_handle
buffer_manager::find_buffer( std::string const &Buffername ) const {
auto lookup = m_buffermappings.find( Buffername );
if( lookup != m_buffermappings.end() )
auto const lookup = m_buffermappings.find( Buffername );
if( lookup != std::end( m_buffermappings ) )
return lookup->second;
else
return null_handle;
}
std::string
buffer_manager::find_file( std::string const &Filename ) const {
std::vector<std::string> const extensions { ".ogg", ".flac", ".wav" };
auto const lookup {
FileExists(
{ Filename },
{ ".ogg", ".flac", ".wav" } ) };
for( auto const &extension : extensions ) {
if( FileExists( Filename + extension ) ) {
// valid name on success
return Filename + extension;
}
}
return {}; // empty string on failure
return lookup.first + lookup.second;
}
} // audio

View File

@@ -60,6 +60,7 @@ commanddescription_sequence Commands_descriptions = {
{ "sandboxactivate", command_target::vehicle },
{ "reverserincrease", command_target::vehicle },
{ "reverserdecrease", command_target::vehicle },
{ "reverserforwardhigh", command_target::vehicle },
{ "reverserforward", command_target::vehicle },
{ "reverserneutral", command_target::vehicle },
{ "reverserbackward", command_target::vehicle },

View File

@@ -55,6 +55,7 @@ enum class user_command {
sandboxactivate,
reverserincrease,
reverserdecrease,
reverserforwardhigh,
reverserforward,
reverserneutral,
reverserbackward,

View File

@@ -249,6 +249,8 @@ keyboard_input::default_bindings() {
{ GLFW_KEY_D },
// reverserdecrease
{ GLFW_KEY_R },
// reverserforwardhigh
{ -1 },
// reverserforward
{ -1 },
// reverserneutral

View File

@@ -14,15 +14,6 @@ http://mozilla.org/MPL/2.0/.
#include "Globals.h"
#include "utilities.h"
// helper, returns potential path part from provided file name
std::string path( std::string const &Filename )
{
return (
Filename.rfind( '/' ) != std::string::npos ?
Filename.substr( 0, Filename.rfind( '/' ) + 1 ) :
"" );
}
bool
opengl_material::deserialize( cParser &Input, bool const Loadnow ) {
@@ -120,23 +111,21 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) {
filename.erase( 0, 1 );
}
filename += ".mat";
// try to locate requested material in the databank
auto const databanklookup = find_in_databank( filename );
auto const databanklookup { find_in_databank( filename ) };
if( databanklookup != null_handle ) {
return databanklookup;
}
// if this fails, try to look for it on disk
opengl_material material;
auto const disklookup = find_on_disk( filename );
if( disklookup != "" ) {
cParser materialparser( disklookup, cParser::buffer_FILE );
auto const disklookup { find_on_disk( filename ) };
if( false == disklookup.first.empty() ) {
cParser materialparser( disklookup.first + disklookup.second, cParser::buffer_FILE );
if( false == material.deserialize( materialparser, Loadnow ) ) {
// deserialization failed but the .mat file does exist, so we give up at this point
return null_handle;
}
material.name = disklookup;
material.name = disklookup.first;
}
else {
// if there's no .mat file, this could be legacy method of referring just to diffuse texture directly, make a material out of it in such case
@@ -148,7 +137,7 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) {
// use texture path and name to tell the newly created materials apart
filename = GfxRenderer.Texture( material.texture1 ).name;
erase_extension( filename );
material.name = filename + ".mat";
material.name = filename;
material.has_alpha = GfxRenderer.Texture( material.texture1 ).has_alpha;
}
@@ -162,7 +151,7 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) {
material_handle
material_manager::find_in_databank( std::string const &Materialname ) const {
std::vector<std::string> filenames {
std::vector<std::string> const filenames {
Global.asCurrentTexturePath + Materialname,
Materialname,
szTexturePath + Materialname };
@@ -173,20 +162,19 @@ material_manager::find_in_databank( std::string const &Materialname ) const {
return lookup->second;
}
}
// all lookups failed
return null_handle;
}
// checks whether specified file exists.
// NOTE: this is direct copy of the method used by texture manager. TBD, TODO: refactor into common routine?
std::string
// NOTE: technically could be static, but we might want to switch from global texture path to instance-specific at some point
std::pair<std::string, std::string>
material_manager::find_on_disk( std::string const &Materialname ) const {
return(
FileExists( Global.asCurrentTexturePath + Materialname ) ? Global.asCurrentTexturePath + Materialname :
FileExists( Materialname ) ? Materialname :
FileExists( szTexturePath + Materialname ) ? szTexturePath + Materialname :
"" );
return (
FileExists(
{ Global.asCurrentTexturePath + Materialname, Materialname, szTexturePath + Materialname },
{ ".mat" } ) );
}
//---------------------------------------------------------------------------

View File

@@ -25,6 +25,9 @@ struct opengl_material {
bool has_alpha { false }; // alpha state, calculated from presence of alpha in texture1
std::string name;
// constructors
opengl_material() = default;
// methods
bool
deserialize( cParser &Input, bool const Loadnow );
@@ -59,7 +62,7 @@ private:
material_handle
find_in_databank( std::string const &Materialname ) const;
// checks whether specified file exists. returns name of the located file, or empty string.
std::string
std::pair<std::string, std::string>
find_on_disk( std::string const &Materialname ) const;
// members:
material_sequence m_materials;

View File

@@ -228,6 +228,17 @@ basic_cell::deserialize( std::istream &Input ) {
|| ( false == m_lines.empty() ) );
}
// sends content of the class in legacy (text) format to provided stream
void
basic_cell::export_as_text( std::ostream &Output ) const {
// text format export dumps only relevant basic objects
// sounds
for( auto const *sound : m_sounds ) {
sound->export_as_text( Output );
}
}
// adds provided shape to the cell
void
basic_cell::insert( shape_node Shape ) {
@@ -421,7 +432,7 @@ basic_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlyco
std::tie( vehiclenearest, leastdistance ) = std::tie( vehicle, distance );
}
}
return std::tie( vehiclenearest, leastdistance );
return { vehiclenearest, leastdistance };
}
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
@@ -438,7 +449,7 @@ basic_cell::find( glm::dvec3 const &Point, TTrack const *Exclude ) const {
endpointid = path->TestPoint( &point );
if( endpointid >= 0 ) {
return std::tie( path, endpointid );
return { path, endpointid };
}
}
return { nullptr, -1 };
@@ -457,7 +468,7 @@ basic_cell::find( glm::dvec3 const &Point, TTraction const *Exclude ) const {
endpointid = traction->TestPoint( Point );
if( endpointid >= 0 ) {
return std::tie( traction, endpointid );
return { traction, endpointid };
}
}
return { nullptr, -1 };
@@ -530,7 +541,7 @@ basic_cell::create_geometry( gfx::geometrybank_handle const &Bank ) {
// adjusts cell bounding area to enclose specified node
void
basic_cell::enclose_area( editor::basic_node *Node ) {
basic_cell::enclose_area( scene::basic_node *Node ) {
m_area.radius = std::max(
m_area.radius,
@@ -645,6 +656,16 @@ basic_section::deserialize( std::istream &Input ) {
}
}
// sends content of the class in legacy (text) format to provided stream
void
basic_section::export_as_text( std::ostream &Output ) const {
// text format export dumps only relevant basic objects from non-empty cells
for( auto const &cell : m_cells ) {
cell.export_as_text( Output );
}
}
// adds provided shape to the section
void
basic_section::insert( shape_node Shape ) {
@@ -708,7 +729,7 @@ basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onl
std::tie( vehiclenearest, distancenearest ) = std::tie( vehiclefound, distancefound );
}
}
return std::tie( vehiclenearest, distancenearest );
return { vehiclenearest, distancenearest };
}
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
@@ -949,6 +970,18 @@ basic_region::deserialize( std::string const &Scenariofile ) {
return true;
}
// sends content of the class in legacy (text) format to provided stream
void
basic_region::export_as_text( std::ostream &Output ) const {
for( auto *section : m_sections ) {
// text format export dumps only relevant basic objects from non-empty sections
if( section != nullptr ) {
section->export_as_text( Output );
}
}
}
// legacy method, links specified path piece with potential neighbours
void
basic_region::TrackJoin( TTrack *Track ) {
@@ -1212,7 +1245,7 @@ basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool co
std::tie( nearestvehicle, nearestdistance ) = std::tie( foundvehicle, founddistance );
}
}
return std::tie( nearestvehicle, nearestdistance );
return { nearestvehicle, nearestdistance };
}
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
@@ -1225,7 +1258,7 @@ basic_region::find_path( glm::dvec3 const &Point, TTrack const *Exclude ) {
return section( Point ).find( Point, Exclude );
}
return std::make_tuple<TTrack *, int>( nullptr, -1 );
return { nullptr, -1 };
}
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
@@ -1238,7 +1271,7 @@ basic_region::find_traction( glm::dvec3 const &Point, TTraction const *Exclude )
return section( Point ).find( Point, Exclude );
}
return std::make_tuple<TTraction *, int>( nullptr, -1 );
return { nullptr, -1 };
}
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece

11
scene.h
View File

@@ -89,6 +89,9 @@ public:
// restores content of the class from provided stream
void
deserialize( std::istream &Input );
// sends content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
// adds provided shape to the cell
void
insert( shape_node Shape );
@@ -149,7 +152,7 @@ private:
using eventlauncher_sequence = std::vector<TEventLauncher *>;
// methods
void
enclose_area( editor::basic_node *Node );
enclose_area( scene::basic_node *Node );
// members
scene::bounding_area m_area { glm::dvec3(), static_cast<float>( 0.5 * M_SQRT2 * EU07_CELLSIZE ) };
bool m_active { false }; // whether the cell holds any actual data
@@ -199,6 +202,9 @@ public:
// restores content of the class from provided stream
void
deserialize( std::istream &Input );
// sends content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
// adds provided shape to the section
void
insert( shape_node Shape );
@@ -289,6 +295,9 @@ public:
// restores content of the class from file with specified name. returns: true on success, false otherwise
bool
deserialize( std::string const &Scenariofile );
// sends content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
// legacy method, links specified path piece with potential neighbours
void
TrackJoin( TTrack *Track );

View File

@@ -139,7 +139,7 @@ shape_node::deserialize( std::istream &Input ) {
// restores content of the node from provided input stream
shape_node &
shape_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) {
shape_node::import( cParser &Input, scene::node_data const &Nodedata ) {
// import common data
m_name = Nodedata.name;
@@ -520,7 +520,7 @@ lines_node::deserialize( std::istream &Input ) {
// restores content of the node from provded input stream
lines_node &
lines_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) {
lines_node::import( cParser &Input, scene::node_data const &Nodedata ) {
// import common data
m_name = Nodedata.name;
@@ -681,12 +681,9 @@ memory_node::deserialize( cParser &Input, node_data const &Nodedata ) {
memorycell.Load( &Input );
}
*/
} // scene
namespace editor {
basic_node::basic_node( scene::node_data const &Nodedata ) :
m_name( Nodedata.name )
{
@@ -697,23 +694,69 @@ basic_node::basic_node( scene::node_data const &Nodedata ) :
std::numeric_limits<double>::max() );
}
// sends content of the class to provided stream
void
basic_node::serialize( std::ostream &Output ) const {
// bounding area
m_area.serialize( Output );
// visibility
sn_utils::ls_float64( Output, m_rangesquaredmin );
sn_utils::ls_float64( Output, m_rangesquaredmax );
sn_utils::s_bool( Output, m_visible );
// name
sn_utils::s_str( Output, m_name );
// template method implementation
serialize_( Output );
}
// restores content of the class from provided stream
void
basic_node::deserialize( std::istream &Input ) {
// bounding area
m_area.deserialize( Input );
// visibility
m_rangesquaredmin = sn_utils::ld_float64( Input );
m_rangesquaredmax = sn_utils::ld_float64( Input );
m_visible = sn_utils::d_bool( Input );
// name
m_name = sn_utils::d_str( Input );
// template method implementation
deserialize_( Input );
}
// sends basic content of the class in legacy (text) format to provided stream
void
basic_node::export_as_text( std::ostream &Output ) const {
Output
// header
<< "node"
// visibility
<< ' ' << ( m_rangesquaredmax < std::numeric_limits<double>::max() ? std::sqrt( m_rangesquaredmax ) : -1 )
<< ' ' << std::sqrt( m_rangesquaredmin )
// name
<< ' ' << m_name << ' ';
// template method implementation
export_as_text_( Output );
}
float const &
basic_node::radius() {
if( m_area.radius == -1.0 ) {
// calculate if needed
radius_();
m_area.radius = radius_();
}
return m_area.radius;
}
// radius() subclass details, calculates node's bounding radius
// by default nodes are 'virtual don't extend from their center point
void
float
basic_node::radius_() {
m_area.radius = 0.f;
return 0.f;
}
} // editor
} // scene
//---------------------------------------------------------------------------

View File

@@ -112,7 +112,7 @@ public:
deserialize( std::istream &Input );
// restores content of the node from provided input stream
shape_node &
deserialize( cParser &Input, scene::node_data const &Nodedata );
import( cParser &Input, scene::node_data const &Nodedata );
// imports data from provided submodel
shape_node &
convert( TSubModel const *Submodel );
@@ -201,7 +201,7 @@ public:
deserialize( std::istream &Input );
// restores content of the node from provided input stream
lines_node &
deserialize( cParser &Input, scene::node_data const &Nodedata );
import( cParser &Input, scene::node_data const &Nodedata );
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
merge( lines_node &Lines );
@@ -295,12 +295,9 @@ private:
TTrack * m_path;
};
*/
} // scene
namespace editor {
// base interface for nodes which can be actvated in scenario editor
struct basic_node {
@@ -310,6 +307,15 @@ public:
// destructor
virtual ~basic_node() = default;
// methods
// sends content of the class to provided stream
void
serialize( std::ostream &Output ) const;
// restores content of the class from provided stream
void
deserialize( std::istream &Input );
// sends basic content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
std::string const &
name() const;
void
@@ -324,15 +330,23 @@ public:
visible() const;
protected:
// methods
// radius() subclass details, calculates node's bounding radius
virtual void radius_();
// members
scene::bounding_area m_area;
bool m_visible { true };
double m_rangesquaredmin { 0.0 }; // visibility range, min
double m_rangesquaredmax { 0.0 }; // visibility range, max
bool m_visible { true }; // visibility flag
std::string m_name;
private:
// methods
// radius() subclass details, calculates node's bounding radius
virtual float radius_();
// serialize() subclass details, sends content of the subclass to provided stream
virtual void serialize_( std::ostream &Output ) const = 0;
// deserialize() subclass details, restores content of the subclass from provided stream
virtual void deserialize_( std::istream &Input ) = 0;
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
virtual void export_as_text_( std::ostream &Output ) const = 0;
};
inline
@@ -365,6 +379,6 @@ basic_node::visible() const {
return m_visible;
}
} // editor
} // scene
//---------------------------------------------------------------------------

View File

@@ -16,6 +16,7 @@ http://mozilla.org/MPL/2.0/.
#include "renderer.h"
#include "World.h"
namespace simulation {
state_manager State;
@@ -58,8 +59,6 @@ state_manager::deserialize( std::string const &Scenariofile ) {
// as long as the scenario file wasn't rainsted-created base file override
Region->serialize( Scenariofile );
}
Global.iPause &= ~0x10; // koniec pauzy wczytywania
return true;
}
@@ -419,7 +418,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad
if( false == Scratchpad.binary.terrain ) {
simulation::Region->insert_shape(
scene::shape_node().deserialize(
scene::shape_node().import(
Input, nodedata ),
Scratchpad,
true );
@@ -436,7 +435,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad
if( false == Scratchpad.binary.terrain ) {
simulation::Region->insert_lines(
scene::lines_node().deserialize(
scene::lines_node().import(
Input, nodedata ),
Scratchpad );
}
@@ -449,7 +448,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad
auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) };
if( false == simulation::Memory.insert( memorycell ) ) {
ErrorLog( "Bad scenario: memory cell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
ErrorLog( "Bad scenario: memory memorycell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
/*
// TODO: implement this
@@ -805,7 +804,7 @@ state_manager::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratch
}
if( ( true == Scratchpad.trainset.vehicles.empty() ) // jeśli pierwszy pojazd,
&& ( false == path->asEvent0Name.empty() ) // tor ma Event0
&& ( 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
@@ -912,6 +911,65 @@ state_manager::transform( glm::dvec3 Location, scene::scratch_data const &Scratc
return Location;
}
// stores class data in specified file, in legacy (text) format
void
state_manager::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

@@ -37,6 +37,9 @@ public:
update( double Deltatime, int Iterationcount );
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
@@ -71,6 +74,7 @@ private:
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 );
};
extern state_manager State;

View File

@@ -295,6 +295,43 @@ sound_source::deserialize_soundset( cParser &Input ) {
}
}
// sends content of the class in legacy (text) format to provided stream
// NOTE: currently exports only the main sound
void
sound_source::export_as_text( std::ostream &Output ) const {
if( sound( sound_id::main ).buffer == null_handle ) { return; }
// generic node header
Output
<< "node "
// visibility
<< m_range << ' '
<< 0 << ' '
// name
<< m_name << ' ';
// sound node header
Output
<< "sound ";
// location
Output
<< m_offset.x << ' '
<< m_offset.y << ' '
<< m_offset.z << ' ';
// sound data
auto soundfile { audio::renderer.buffer( sound( sound_id::main ).buffer ).name };
if( soundfile.find( szSoundPath ) == 0 ) {
// don't include 'sounds/' in the path
soundfile.erase( 0, std::string{ szSoundPath }.size() );
}
Output
<< soundfile << ' ';
// footer
Output
<< "endsound"
<< "\n";
}
// copies list of sounds from provided source
sound_source &
sound_source::copy_sounds( sound_source const &Source ) {

View File

@@ -60,6 +60,9 @@ public:
deserialize( cParser &Input, sound_type const Legacytype, int const Legacyparameters = 0, int const Chunkrange = 100 );
sound_source &
deserialize( std::string const &Input, sound_type const Legacytype, int const Legacyparameters = 0 );
// sends content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
// copies list of sounds from provided source
sound_source &
copy_sounds( sound_source const &Source );

View File

@@ -116,6 +116,7 @@ ui_layer::on_key( int const Key, int const Action ) {
case GLFW_KEY_F8:
case GLFW_KEY_F9:
case GLFW_KEY_F10:
case GLFW_KEY_F11:
case GLFW_KEY_F12: { // ui mode selectors
if( ( true == Global.ctrlState )
@@ -303,11 +304,17 @@ ui_layer::update() {
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( static_cast<int>( std::floor( driver->VelDesired ) ) ) + " km/h"
+ ", next limit: " + std::to_string( static_cast<int>( std::floor( controlled->Mechanik->VelNext ) ) ) + " km/h"
+ " in " + to_string( controlled->Mechanik->ActualProximityDist * 0.001, 1 ) + " km)";
+ " (limit: " + std::to_string( speedlimit ) + " km/h";
auto const nextspeedlimit { static_cast<int>( std::floor( controlled->Mechanik->VelNext ) ) };
if( nextspeedlimit != speedlimit ) {
uitextline2 +=
", new limit: " + std::to_string( nextspeedlimit ) + " km/h"
+ " in " + to_string( controlled->Mechanik->ActualProximityDist * 0.001, 1 ) + " km";
}
uitextline2 += ")";
uitextline3 +=
" Pressure: " + to_string( mover->BrakePress * 100.0, 2 ) + " kPa"
+ " (train pipe: " + to_string( mover->PipePress * 100.0, 2 ) + " kPa)";

View File

@@ -328,6 +328,20 @@ bool FileExists( std::string const &Filename ) {
return( true == file.is_open() );
}
std::pair<std::string, std::string>
FileExists( std::vector<std::string> const &Names, std::vector<std::string> const &Extensions ) {
for( auto const &name : Names ) {
for( auto const &extension : Extensions ) {
if( FileExists( name + extension ) ) {
return { name, extension };
}
}
}
// nothing found
return { {}, {} };
}
// returns time of last modification for specified file
std::time_t
last_modified( std::string const &Filename ) {
@@ -363,3 +377,13 @@ replace_slashes( std::string &Filename ) {
std::begin( Filename ), std::end( Filename ),
'\\', '/' );
}
// returns potential path part from provided file name
std::string
substr_path( std::string const &Filename ) {
return (
Filename.rfind( '/' ) != std::string::npos ?
Filename.substr( 0, Filename.rfind( '/' ) + 1 ) :
"" );
}

View File

@@ -179,6 +179,8 @@ extract_value( bool &Variable, std::string const &Key, std::string const &Input,
bool FileExists( std::string const &Filename );
std::pair<std::string, std::string> FileExists( std::vector<std::string> const &Names, std::vector<std::string> const &Extensions );
// returns time of last modification for specified file
std::time_t last_modified( std::string const &Filename );
@@ -190,6 +192,9 @@ erase_extension( std::string &Filename );
void
replace_slashes( std::string &Filename );
// returns potential path part from provided file name
std::string substr_path( std::string const &Filename );
template <typename Type_>
void SafeDelete( Type_ &Pointer ) {
delete Pointer;
@@ -227,6 +232,8 @@ template <typename Type_>
Type_
min_speed( Type_ const Left, Type_ const Right ) {
if( Left == Right ) { return Left; }
return std::min(
( Left != -1 ?
Left :

View File

@@ -1 +1 @@
#define VERSION_INFO "M7 22.05.2018, based on tmj aa520aa3"
#define VERSION_INFO "M7 15.06.2018, based on tmj e54468f8"