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

Merge branch 'tmj-dev'

This commit is contained in:
milek7
2018-02-24 16:05:35 +01:00
25 changed files with 678 additions and 338 deletions

View File

@@ -80,6 +80,7 @@ set(SOURCES
"utilities.cpp" "utilities.cpp"
"light.cpp" "light.cpp"
"uitranscripts.cpp" "uitranscripts.cpp"
"station.cpp"
) )
set (PREFIX "") set (PREFIX "")

View File

@@ -891,8 +891,6 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
mvOccupied->DoorLeft(true); mvOccupied->DoorLeft(true);
if (p2 & prawe) if (p2 & prawe)
mvOccupied->DoorRight(true); mvOccupied->DoorRight(true);
// if (p2&3) //żeby jeszcze poczekał chwilę, zanim zamknie
// WaitingSet(10); //10 sekund (wziąć z rozkładu????)
} }
} }
else else
@@ -916,50 +914,72 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
// pojazd podłączony z tyłu (patrząc od czoła) // pojazd podłączony z tyłu (patrząc od czoła)
p = p->Next(); p = p->Next();
} }
// if (p7&3) //żeby jeszcze poczekał chwilę, zanim zamknie }
// WaitingSet(10); //10 sekund (wziąć z rozkładu????) }
if (TrainParams->UpdateMTable( simulation::Time, asNextStop) ) {
// to się wykona tylko raz po zatrzymaniu na W4
if( TrainParams->StationIndex < TrainParams->StationCount ) {
// jeśli są dalsze stacje, bez trąbienia przed odjazdem
// also ignore any horn cue that may be potentially set below 1 km/h and before the actual full stop
iDrivigFlags &= ~( moveStartHorn | moveStartHornNow );
}
// perform loading/unloading
auto const exchangetime { simulation::Station.update_load( pVehicles[ 0 ], *TrainParams ) };
// TBD: adjust time to load exchange size
if( fStopTime > -55 ) {
// na końcu rozkładu się ustawia 60s i tu by było skrócenie
// WaitingSet( 15.0 + Random( 15.0 ) ); // 10 sekund (wziąć z rozkładu????) - czekanie
// with door open on both sides calculated loading time is halved
// p7=platform side (1:left, 2:right, 3:both)
auto const platformside = static_cast<int>( std::floor( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ) ) % 10;
WaitingSet(
platformside == 3 ?
exchangetime * 0.5 :
exchangetime );
}
// update brake settings and ai braking tables
// NOTE: this calculation is run after completing loading/unloading
// remember to move it to a more fitting spot, when loading/unloading taks some actual time
AutoRewident(); // nastawianie hamulca do jazdy pociągowej
if( TrainParams->CheckTrainLatency() < 0.0 ) {
// odnotowano spóźnienie
iDrivigFlags |= moveLate;
}
else {
// przyjazd o czasie
iDrivigFlags &= ~moveLate;
} }
if( fStopTime > -5 ) // na końcu rozkładu się ustawia 60s i tu by było skrócenie if (TrainParams->DirectionChange()) {
WaitingSet( 15.0 + Random( 15.0 ) ); // 10 sekund (wziąć z rozkładu????) - czekanie // jeśli "@" w rozkładzie, to wykonanie dalszych komend
// niezależne od sposobu obsługi drzwi, bo opóźnia również kierownika // wykonanie kolejnej komendy, nie dotyczy ostatniej stacji
} if (iDrivigFlags & movePushPull) {
if (TrainParams->UpdateMTable( simulation::Time, asNextStop) ) // SN61 ma się też nie ruszać, chyba że ma wagony
{ // to się wykona tylko raz po zatrzymaniu na W4
if (TrainParams->CheckTrainLatency() < 0.0)
iDrivigFlags |= moveLate; // odnotowano spóźnienie
else
iDrivigFlags &= ~moveLate; // przyjazd o czasie
if (TrainParams->DirectionChange()) // jeśli "@" w rozkładzie, to
// wykonanie dalszych komend
{ // wykonanie kolejnej komendy, nie dotyczy ostatniej stacji
if (iDrivigFlags & movePushPull) // SN61 ma się też nie ruszać,
// chyba że ma wagony
{
iDrivigFlags |= moveStopHere; // EZT ma stać przy peronie iDrivigFlags |= moveStopHere; // EZT ma stać przy peronie
if (OrderNextGet() != Change_direction) if (OrderNextGet() != Change_direction) {
{
OrderPush(Change_direction); // zmiana kierunku OrderPush(Change_direction); // zmiana kierunku
OrderPush(TrainParams->StationIndex < OrderPush(
TrainParams->StationCount ? TrainParams->StationIndex < TrainParams->StationCount ?
Obey_train : Obey_train :
Shunt); // to dalej wg rozkładu Shunt); // to dalej wg rozkładu
} }
} }
else // a dla lokomotyw... else {
iDrivigFlags &= // a dla lokomotyw...
~(moveStopPoint | moveStopHere); // pozwolenie na // pozwolenie na przejechanie za W4 przed czasem i nie ma stać
// przejechanie za W4 iDrivigFlags &= ~( moveStopPoint | moveStopHere );
// przed czasem i nie }
// ma stać // przejście do kolejnego rozkazu (zmiana kierunku, odczepianie)
JumpToNextOrder(); // przejście do kolejnego rozkazu (zmiana JumpToNextOrder();
// kierunku, odczepianie) // ma nie podjeżdżać pod W4 po przeciwnej stronie
iDrivigFlags &= ~moveStopCloser; // ma nie podjeżdżać pod W4 po iDrivigFlags &= ~moveStopCloser;
// przeciwnej stronie // ten W4 nie liczy się już zupełnie (nie wyśle SetVelocity)
sSpeedTable[i].iFlags = 0; // ten W4 nie liczy się już zupełnie sSpeedTable[i].iFlags = 0;
// (nie wyśle SetVelocity) // jechać
sSpeedTable[i].fVelNext = -1; // jechać sSpeedTable[i].fVelNext = -1;
continue; // nie analizować prędkości // nie analizować prędkości
continue;
} }
} }
if (OrderCurrentGet() == Shunt) if (OrderCurrentGet() == Shunt)
@@ -969,7 +989,6 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
} }
if (TrainParams->StationIndex < TrainParams->StationCount) if (TrainParams->StationIndex < TrainParams->StationCount)
{ // jeśli są dalsze stacje, czekamy do godziny odjazdu { // jeśli są dalsze stacje, czekamy do godziny odjazdu
if (TrainParams->IsTimeToGo(simulation::Time.data().wHour, simulation::Time.data().wMinute)) if (TrainParams->IsTimeToGo(simulation::Time.data().wHour, simulation::Time.data().wMinute))
{ // z dalszą akcją czekamy do godziny odjazdu { // z dalszą akcją czekamy do godziny odjazdu
/* potencjalny problem z ruszaniem z w4 /* potencjalny problem z ruszaniem z w4
@@ -992,16 +1011,16 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
+ ": at " + std::to_string(simulation::Time.data().wHour) + ":" + std::to_string(simulation::Time.data().wMinute) + ": at " + std::to_string(simulation::Time.data().wHour) + ":" + std::to_string(simulation::Time.data().wMinute)
+ " next " + asNextStop); // informacja + " next " + asNextStop); // informacja
#endif #endif
if (int(floor(sSpeedTable[i].evEvent->ValueGet(1))) & 1) if( int( floor( sSpeedTable[ i ].evEvent->ValueGet( 1 ) ) ) & 1 ) {
iDrivigFlags |= moveStopHere; // nie podjeżdżać do semafora, // nie podjeżdżać do semafora, jeśli droga nie jest wolna
// jeśli droga nie jest wolna iDrivigFlags |= moveStopHere;
else }
iDrivigFlags &= ~moveStopHere; //po czasie jedź dalej else {
iDrivigFlags |= moveStopCloser; // do następnego W4 podjechać //po czasie jedź dalej
// blisko (z dociąganiem) iDrivigFlags &= ~moveStopHere;
iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed odjazdem }
sSpeedTable[i].iFlags = iDrivigFlags |= moveStopCloser; // do następnego W4 podjechać blisko (z dociąganiem)
0; // nie liczy się już zupełnie (nie wyśle SetVelocity) sSpeedTable[i].iFlags = 0; // nie liczy się już zupełnie (nie wyśle SetVelocity)
sSpeedTable[i].fVelNext = -1; // można jechać za W4 sSpeedTable[i].fVelNext = -1; // można jechać za W4
if (go == cm_Unknown) // jeśli nie było komendy wcześniej if (go == cm_Unknown) // jeśli nie było komendy wcześniej
go = cm_Ready; // gotów do odjazdu z W4 (semafor może go = cm_Ready; // gotów do odjazdu z W4 (semafor może
@@ -1772,34 +1791,55 @@ void TController::AutoRewident()
p = 0; // będziemy tu liczyć wagony od lokomotywy dla nastawy GP p = 0; // będziemy tu liczyć wagony od lokomotywy dla nastawy GP
while (d) while (d)
{ // 3. Nastawianie { // 3. Nastawianie
switch (ustaw) if( ( true == AIControllFlag )
{ || ( d != pVehicle ) ) {
case bdelay_P: // towarowy P - lokomotywa na G, reszta na P. // don't touch human-controlled vehicle, but others are free game
d->MoverParameters->BrakeDelaySwitch(d->MoverParameters->Power > 1 ? bdelay_G : switch( ustaw ) {
bdelay_P);
break; case bdelay_P: {
case bdelay_G: // towarowy G - wszystko na G, jeśli nie ma to P (powinno się wyłączyć // towarowy P - lokomotywa na G, reszta na P.
// hamulec) d->MoverParameters->BrakeDelaySwitch(
d->MoverParameters->BrakeDelaySwitch( d->MoverParameters->Power > 1 ?
TestFlag(d->MoverParameters->BrakeDelays, bdelay_G) ? bdelay_G : bdelay_P); bdelay_G :
break; bdelay_P );
case bdelay_R: // towarowy GP - lokomotywa oraz 5 pierwszych pojazdów przy niej na G, reszta break;
// na P }
if (d->MoverParameters->Power > 1) case bdelay_G: {
{ // towarowy G - wszystko na G, jeśli nie ma to P (powinno się wyłączyć hamulec)
d->MoverParameters->BrakeDelaySwitch(bdelay_G); d->MoverParameters->BrakeDelaySwitch(
p = 0; // a jak będzie druga w środku? TestFlag( d->MoverParameters->BrakeDelays, bdelay_G ) ?
bdelay_G :
bdelay_P );
break;
}
case bdelay_R: {
// towarowy GP - lokomotywa oraz 5 pierwszych pojazdów przy niej na G, reszta na P
if( d->MoverParameters->Power > 1 ) {
d->MoverParameters->BrakeDelaySwitch( bdelay_G );
p = 0; // a jak będzie druga w środku?
}
else {
d->MoverParameters->BrakeDelaySwitch(
++p <= 5 ?
bdelay_G :
bdelay_P );
}
break;
}
case 16 + bdelay_R: {
// pasażerski R - na R, jeśli nie ma to P
d->MoverParameters->BrakeDelaySwitch(
TestFlag( d->MoverParameters->BrakeDelays, bdelay_R ) ?
bdelay_R :
bdelay_P );
break;
}
case 16 + bdelay_P: {
// pasażerski P - wszystko na P
d->MoverParameters->BrakeDelaySwitch( bdelay_P );
break;
}
} }
else
d->MoverParameters->BrakeDelaySwitch(++p <= 5 ? bdelay_G : bdelay_P);
break;
case 16 + bdelay_R: // pasażerski R - na R, jeśli nie ma to P
d->MoverParameters->BrakeDelaySwitch(
TestFlag(d->MoverParameters->BrakeDelays, bdelay_R) ? bdelay_R : bdelay_P);
break;
case 16 + bdelay_P: // pasażerski P - wszystko na P
d->MoverParameters->BrakeDelaySwitch(bdelay_P);
break;
} }
d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła)
} }
@@ -2094,22 +2134,16 @@ void TController::SetVelocity(double NewVel, double NewVelNext, TStopReason r)
if (OrderList[OrderPos] ? if (OrderList[OrderPos] ?
OrderList[OrderPos] & (Obey_train | Shunt | Connect | Prepare_engine) : OrderList[OrderPos] & (Obey_train | Shunt | Connect | Prepare_engine) :
true) // jeśli jedzie w dowolnym trybie true) // jeśli jedzie w dowolnym trybie
if ((mvOccupied->Vel < if ((mvOccupied->Vel < 1.0)) // jesli stoi (na razie, bo chyba powinien też, gdy hamuje przed semaforem)
1.0)) // jesli stoi (na razie, bo chyba powinien też, gdy hamuje przed semaforem)
if (iDrivigFlags & moveStartHorn) // jezeli trąbienie włączone if (iDrivigFlags & moveStartHorn) // jezeli trąbienie włączone
if (!(iDrivigFlags & (moveStartHornDone | moveConnect))) // jeśli nie zatrąbione if (!(iDrivigFlags & (moveStartHornDone | moveConnect)))
// i nie jest to moment // jeśli nie zatrąbione i nie jest to moment podłączania składu
// podłączania składu if (mvOccupied->CategoryFlag & 1)
if (mvOccupied->CategoryFlag & 1) // tylko pociągi trąbią (unimogi tylko na // tylko pociągi trąbią (unimogi tylko na torach, więc trzeba raczej sprawdzać tor)
// torach, więc trzeba raczej sprawdzać if ((NewVel >= 1.0) || (NewVel < 0.0)) {
// tor) // o ile prędkość jest znacząca
if ((NewVel >= 1.0) || (NewVel < 0.0)) // o ile prędkość jest znacząca // zatrąb po odhamowaniu
{ // fWarningDuration=0.3; //czas trąbienia iDrivigFlags |= moveStartHornNow;
// if (AIControllFlag) //jak siedzi krasnoludek, to włączy trąbienie
// mvOccupied->WarningSignal=pVehicle->iHornWarning; //wysokość tonu
// (2=wysoki)
// iDrivigFlags|=moveStartHornDone; //nie trąbić aż do ruszenia
iDrivigFlags |= moveStartHornNow; // zatrąb po odhamowaniu
} }
} }
VelSignal = NewVel; // prędkość zezwolona na aktualnym odcinku VelSignal = NewVel; // prędkość zezwolona na aktualnym odcinku
@@ -3648,12 +3682,9 @@ TController::UpdateSituation(double dt) {
// but when enabled all the time it produces silly effect // but when enabled all the time it produces silly effect
// przy prowadzeniu samochodu trzeba każdą oś odsuwać oddzielnie, inaczej kicha wychodzi // przy prowadzeniu samochodu trzeba każdą oś odsuwać oddzielnie, inaczej kicha wychodzi
if (mvOccupied->CategoryFlag & 2) // jeśli samochód if (mvOccupied->CategoryFlag & 2) // jeśli samochód
// if (fabs(mvOccupied->OffsetTrackH)<mvOccupied->Dim.W) //Ra: szerokość drogi tu // if (fabs(mvOccupied->OffsetTrackH)<mvOccupied->Dim.W) //Ra: szerokość drogi tu powinna być?
// powinna być? if (!mvOccupied->ChangeOffsetH(-0.01 * mvOccupied->Vel * dt)) // ruch w poprzek drogi
if (!mvOccupied->ChangeOffsetH(-0.01 * mvOccupied->Vel * dt)) // ruch w poprzek mvOccupied->ChangeOffsetH(0.01 * mvOccupied->Vel * dt); // Ra: co to miało być, to nie wiem
// drogi
mvOccupied->ChangeOffsetH(0.01 * mvOccupied->Vel *
dt); // Ra: co to miało być, to nie wiem
*/ */
} }
@@ -3720,7 +3751,7 @@ TController::UpdateSituation(double dt) {
if( fWarningDuration < 0.05 ) if( fWarningDuration < 0.05 )
mvOccupied->WarningSignal = 0; // a tu się kończy mvOccupied->WarningSignal = 0; // a tu się kończy
} }
if( mvOccupied->Vel >= 3.0 ) { if( mvOccupied->Vel >= 5.0 ) {
// jesli jedzie, można odblokować trąbienie, bo się wtedy nie włączy // jesli jedzie, można odblokować trąbienie, bo się wtedy nie włączy
iDrivigFlags &= ~moveStartHornDone; // zatrąbi dopiero jak następnym razem stanie iDrivigFlags &= ~moveStartHornDone; // zatrąbi dopiero jak następnym razem stanie
iDrivigFlags |= moveStartHorn; // i trąbić przed następnym ruszeniem iDrivigFlags |= moveStartHorn; // i trąbić przed następnym ruszeniem
@@ -3729,7 +3760,7 @@ TController::UpdateSituation(double dt) {
if( ( true == TestFlag( iDrivigFlags, moveStartHornNow ) ) if( ( true == TestFlag( iDrivigFlags, moveStartHornNow ) )
&& ( true == Ready ) && ( true == Ready )
&& ( iEngineActive != 0 ) && ( iEngineActive != 0 )
&& ( fStopTime >= 0 ) ) { && ( mvControlling->MainCtrlPos > 0 ) ) {
// uruchomienie trąbienia przed ruszeniem // uruchomienie trąbienia przed ruszeniem
fWarningDuration = 0.3; // czas trąbienia fWarningDuration = 0.3; // czas trąbienia
mvOccupied->WarningSignal = pVehicle->iHornWarning; // wysokość tonu (2=wysoki) mvOccupied->WarningSignal = pVehicle->iHornWarning; // wysokość tonu (2=wysoki)

View File

@@ -556,14 +556,14 @@ TDynamicObject::toggle_lights() {
for( auto &sectionlight : SectionLightLevels ) { for( auto &sectionlight : SectionLightLevels ) {
std::string const &compartmentname = sectionlight.compartment->pName; std::string const &compartmentname = sectionlight.compartment->pName;
if( ( compartmentname.find( "corridor" ) != std::string::npos ) if( ( compartmentname.find( "corridor" ) == 0 )
|| ( compartmentname.find( "korytarz" ) != std::string::npos ) ) { || ( compartmentname.find( "korytarz" ) == 0 ) ) {
// corridors are lit 100% of time // corridors are lit 100% of time
sectionlight.level = 0.75f; sectionlight.level = 0.75f;
} }
else if( else if(
( compartmentname.find( "compartment" ) != std::string::npos ) ( compartmentname.find( "compartment" ) == 0 )
|| ( compartmentname.find( "przedzial" ) != std::string::npos ) ) { || ( compartmentname.find( "przedzial" ) == 0 ) ) {
// compartments are lit with 75% probability // compartments are lit with 75% probability
sectionlight.level = ( Random() < 0.75 ? 0.75f : 0.15f ); sectionlight.level = ( Random() < 0.75 ? 0.75f : 0.15f );
} }
@@ -978,6 +978,22 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist)
section.load->SetLightLevel( section.level, true ); section.load->SetLightLevel( section.level, true );
} }
} }
// load chunks visibility
for( auto const &section : SectionLoadVisibility ) {
section.submodel->iVisible = section.visible;
if( false == section.visible ) {
// if the section root isn't visible we can skip meddling with its children
continue;
}
// if the section root is visible set the state of section chunks
auto *sectionchunk { section.submodel->ChildGet() };
auto visiblechunkcount { section.visible_chunks };
while( sectionchunk != nullptr ) {
sectionchunk->iVisible = ( visiblechunkcount > 0 );
--visiblechunkcount;
sectionchunk = sectionchunk->NextGet();
}
}
} }
// ABu 29.01.05 koniec przeklejenia ************************************* // ABu 29.01.05 koniec przeklejenia *************************************
@@ -1797,8 +1813,7 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
{ {
size_t dlugosc = MoreParams.length(); size_t dlugosc = MoreParams.length();
ActPar = ToUpper(MoreParams.substr(0, kropka)); // pierwszy parametr; ActPar = ToUpper(MoreParams.substr(0, kropka)); // pierwszy parametr;
MoreParams = MoreParams.substr(kropka + 1, dlugosc - kropka); // reszta do dalszej MoreParams = MoreParams.substr(kropka + 1, dlugosc - kropka); // reszta do dalszej obrobki
// obrobki
kropka = MoreParams.find("."); kropka = MoreParams.find(".");
if (ActPar.substr(0, 1) == "B") // jesli hamulce if (ActPar.substr(0, 1) == "B") // jesli hamulce
@@ -1910,6 +1925,41 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
{ {
} }
} // koniec hamulce } // koniec hamulce
else if( ( ActPar.size() >= 3 )
&& ( ActPar.substr( 0, 2 ) == "WF" ) ) {
// wheel flat
// TODO: convert this whole mess to something more elegant one day
ActPar.erase( 0, 2 );
auto fixedflatsize { 0 };
{
// fixed size flat
auto const indexend { ActPar.find_first_not_of( "1234567890", 0 ) };
fixedflatsize = std::atoi( ActPar.substr( 0, indexend ).c_str() );
ActPar.erase( 0, indexend );
}
// optional parameters
auto randomflatsize { 0 };
auto randomflatchance { 100 };
while( false == ActPar.empty() ) {
if( ActPar[ 0 ] == 'R' ) {
// random flat size
auto const indexstart { 1 };
auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) };
randomflatsize = std::atoi( ActPar.substr( indexstart, indexend ).c_str() );
ActPar.erase( 0, indexend );
}
else if( ActPar[ 0 ] == 'P' ) {
// random flat probability
auto const indexstart { 1 };
auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) };
randomflatchance = std::atoi( ActPar.substr( indexstart, indexend ).c_str() );
ActPar.erase( 0, indexend );
}
}
if( Random(0, 100) <= randomflatchance ) {
MoverParameters->WheelFlat += fixedflatsize + Random( 0, randomflatsize );
}
}
/* else if (ActPar.substr(0, 1) == "") // tu mozna wpisac inny prefiks i inne rzeczy /* else if (ActPar.substr(0, 1) == "") // tu mozna wpisac inny prefiks i inne rzeczy
{ {
// jakies inne prefiksy // jakies inne prefiksy
@@ -2012,9 +2062,9 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
// check the low poly interior for potential compartments of interest, ie ones which can be individually lit // check the low poly interior for potential compartments of interest, ie ones which can be individually lit
// TODO: definition of relevant compartments in the .mmd file // TODO: definition of relevant compartments in the .mmd file
TSubModel *submodel { nullptr }; TSubModel *submodel { nullptr };
if( ( submodel = mdLowPolyInt->GetFromName( "cab1" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); } if( ( submodel = mdLowPolyInt->GetFromName( "cab1" ) ) != nullptr ) { SectionLightLevels.push_back( { submodel, nullptr, 0.0f } ); }
if( ( submodel = mdLowPolyInt->GetFromName( "cab2" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); } if( ( submodel = mdLowPolyInt->GetFromName( "cab2" ) ) != nullptr ) { SectionLightLevels.push_back( { submodel, nullptr, 0.0f } ); }
if( ( submodel = mdLowPolyInt->GetFromName( "cab0" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); } if( ( submodel = mdLowPolyInt->GetFromName( "cab0" ) ) != nullptr ) { SectionLightLevels.push_back( { submodel, nullptr, 0.0f } ); }
// passenger car compartments // passenger car compartments
std::vector<std::string> nameprefixes = { "corridor", "korytarz", "compartment", "przedzial" }; std::vector<std::string> nameprefixes = { "corridor", "korytarz", "compartment", "przedzial" };
int compartmentindex; int compartmentindex;
@@ -2029,19 +2079,17 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
std::to_string( compartmentindex ) ); std::to_string( compartmentindex ) );
submodel = mdLowPolyInt->GetFromName( compartmentname ); submodel = mdLowPolyInt->GetFromName( compartmentname );
if( submodel != nullptr ) { if( submodel != nullptr ) {
// if specified compartment was found we check also for potential matching section in the currently assigned load SectionLightLevels.push_back( {
// NOTE: if the load gets changed this will invalidate stored pointers. TODO: rebuild the table on load change
SectionLightLevels.emplace_back(
submodel, submodel,
( mdLoad != nullptr ? nullptr, // pointers to load sections are generated afterwards
mdLoad->GetFromName( compartmentname ): 0.0f } );
nullptr ),
0.0f );
} }
++compartmentindex; ++compartmentindex;
} while( ( submodel != nullptr ) } while( ( submodel != nullptr )
|| ( compartmentindex < 2 ) ); // chain can start from prefix00 or prefix01 || ( compartmentindex < 2 ) ); // chain can start from prefix00 or prefix01
} }
update_load_sections();
update_load_visibility();
} }
// wyszukiwanie zderzakow // wyszukiwanie zderzakow
if( mdModel ) { if( mdModel ) {
@@ -2430,31 +2478,115 @@ bool TDynamicObject::UpdateForce(double dt, double dt1, bool FullVer)
return true; return true;
} }
void TDynamicObject::LoadUpdate() void TDynamicObject::LoadUpdate() {
{ // przeładowanie modelu ładunku // przeładowanie modelu ładunku
// Ra: nie próbujemy wczytywać modeli miliony razy podczas renderowania!!! // Ra: nie próbujemy wczytywać modeli miliony razy podczas renderowania!!!
if ((mdLoad == NULL) && (MoverParameters->Load > 0)) if( ( mdLoad == nullptr )
{ && ( MoverParameters->Load > 0 ) ) {
std::string asLoadName =
asBaseDir + MoverParameters->LoadType + ".t3d"; // zapamiętany katalog pojazdu if( false == MoverParameters->LoadType.empty() ) {
// asLoadName=MoverParameters->LoadType;
// if (MoverParameters->LoadType!=AnsiString("passengers")) Global.asCurrentTexturePath = asBaseDir; // bieżąca ścieżka do tekstur to dynamic/...
Global.asCurrentTexturePath = asBaseDir; // bieżąca ścieżka do tekstur to dynamic/...
mdLoad = TModelsManager::GetModel(asLoadName.c_str()); // nowy ładunek // try first specialized version of the load model, vehiclename_loadname
Global.asCurrentTexturePath = auto const specializedloadfilename { asBaseDir + MoverParameters->TypeName + "_" + MoverParameters->LoadType };
std::string(szTexturePath); // z powrotem defaultowa sciezka do tekstur if( ( true == FileExists( specializedloadfilename + ".e3d" ) )
// Ra: w MMD można by zapisać położenie modelu ładunku (np. węgiel) w || ( true == FileExists( specializedloadfilename + ".t3d" ) ) ) {
// zależności od mdLoad = TModelsManager::GetModel( specializedloadfilename, true );
// załadowania }
if( mdLoad == nullptr ) {
// if this fails, try generic load model
mdLoad = TModelsManager::GetModel( asBaseDir + MoverParameters->LoadType, true );
}
if( mdLoad != nullptr ) {
// TODO: discern from vehicle component which merely uses vehicle directory and has no animations, so it can be initialized outright
// and actual vehicles which get their initialization after their animations are set up
mdLoad->Init();
}
// update bindings between lowpoly sections and potential load chunks placed inside them
update_load_sections();
Global.asCurrentTexturePath = std::string( szTexturePath ); // z powrotem defaultowa sciezka do tekstur
}
// Ra: w MMD można by zapisać położenie modelu ładunku (np. węgiel) w zależności od załadowania
}
else if( MoverParameters->Load == 0 ) {
// nie ma ładunku
mdLoad = nullptr;
// erase bindings between lowpoly sections and potential load chunks placed inside them
update_load_sections();
} }
else if (MoverParameters->Load == 0)
mdLoad = NULL; // nie ma ładunku
// if ((mdLoad==NULL)&&(MoverParameters->Load>0))
// {
// mdLoad=NULL; //Ra: to jest tu bez sensu - co autor miał na myśli?
// }
MoverParameters->LoadStatus &= 3; // po zakończeniu będzie równe zero MoverParameters->LoadStatus &= 3; // po zakończeniu będzie równe zero
}; }
void
TDynamicObject::update_load_sections() {
SectionLoadVisibility.clear();
for( auto &section : SectionLightLevels ) {
section.load = (
mdLoad != nullptr ?
mdLoad->GetFromName( section.compartment->pName ) :
nullptr );
if( ( section.load != nullptr )
&& ( section.load->count_children() > 0 ) ) {
SectionLoadVisibility.push_back( { section.load, false } );
}
}
shuffle_load_sections();
}
void
TDynamicObject::update_load_visibility() {
if( Random() < 0.25 ) {
shuffle_load_sections();
}
auto loadpercentage { (
MoverParameters->MaxLoad == 0.0 ?
0.0 :
100.0 * MoverParameters->Load / MoverParameters->MaxLoad ) };
auto const sectionloadpercentage { (
SectionLoadVisibility.empty() ?
0.0 :
100.0 / SectionLoadVisibility.size() ) };
// set as many sections as we can, given overall load percentage and how much of full percentage is covered by each chunk
std::for_each(
std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ),
[&]( section_visibility &section ) {
section.visible = ( loadpercentage > 0.0 );
section.visible_chunks = 0;
auto const sectionchunkcount { section.submodel->count_children() };
auto const sectionchunkloadpercentage{ (
sectionchunkcount == 0 ?
0.0 :
sectionloadpercentage / sectionchunkcount ) };
auto *sectionchunk { section.submodel->ChildGet() };
while( sectionchunk != nullptr ) {
if( loadpercentage > 0.0 ) {
++section.visible_chunks;
loadpercentage -= sectionchunkloadpercentage;
}
sectionchunk = sectionchunk->NextGet();
} } );
}
void
TDynamicObject::shuffle_load_sections() {
std::shuffle( std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ), Global.random_engine );
// shift chunks assigned to corridors to the end of the list, so they show up last
std::stable_partition(
std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ),
[]( section_visibility const &section ) {
return (
( section.submodel->pName.find( "compartment" ) == 0 )
|| ( section.submodel->pName.find( "przedzial" ) == 0 ) ); } );
}
/* /*
Ra: Ra:
@@ -3639,7 +3771,7 @@ void TDynamicObject::RenderSounds() {
} }
// NBMX Obsluga drzwi, MC: zuniwersalnione // NBMX Obsluga drzwi, MC: zuniwersalnione
if( ( true == MoverParameters->DoorLeftOpened ) if( ( true == MoverParameters->DoorLeftOpened )
&& ( dDoorMoveL < MoverParameters->DoorMaxShiftL ) ) { && ( dDoorMoveL < MoverParameters->DoorMaxShiftL ) ) {
for( auto &door : m_doorsounds ) { for( auto &door : m_doorsounds ) {
if( door.rsDoorClose.offset().x > 0.f ) { if( door.rsDoorClose.offset().x > 0.f ) {
@@ -3650,7 +3782,7 @@ void TDynamicObject::RenderSounds() {
} }
} }
if( ( false == MoverParameters->DoorLeftOpened ) if( ( false == MoverParameters->DoorLeftOpened )
&& ( dDoorMoveL > 0.01 ) ) { && ( dDoorMoveL > 0.01 ) ) {
for( auto &door : m_doorsounds ) { for( auto &door : m_doorsounds ) {
if( door.rsDoorClose.offset().x > 0.f ) { if( door.rsDoorClose.offset().x > 0.f ) {
@@ -3661,7 +3793,7 @@ void TDynamicObject::RenderSounds() {
} }
} }
if( ( true == MoverParameters->DoorRightOpened ) if( ( true == MoverParameters->DoorRightOpened )
&& ( dDoorMoveR < MoverParameters->DoorMaxShiftR ) ) { && ( dDoorMoveR < MoverParameters->DoorMaxShiftR ) ) {
for( auto &door : m_doorsounds ) { for( auto &door : m_doorsounds ) {
if( door.rsDoorClose.offset().x < 0.f ) { if( door.rsDoorClose.offset().x < 0.f ) {
@@ -3672,7 +3804,7 @@ void TDynamicObject::RenderSounds() {
} }
} }
if( ( false == MoverParameters->DoorRightOpened ) if( ( false == MoverParameters->DoorRightOpened )
&& ( dDoorMoveR > 0.01 ) ) { && ( dDoorMoveR > 0.01 ) ) {
for( auto &door : m_doorsounds ) { for( auto &door : m_doorsounds ) {
if( door.rsDoorClose.offset().x < 0.f ) { if( door.rsDoorClose.offset().x < 0.f ) {
@@ -3855,7 +3987,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
std::string asFileName = BaseDir + TypeName + ".mmd"; std::string asFileName = BaseDir + TypeName + ".mmd";
std::string asLoadName; std::string asLoadName;
if( false == MoverParameters->LoadType.empty() ) { if( false == MoverParameters->LoadType.empty() ) {
asLoadName = BaseDir + MoverParameters->LoadType + ".t3d"; asLoadName = BaseDir + MoverParameters->LoadType;
} }
std::string asAnimName; std::string asAnimName;

View File

@@ -195,13 +195,16 @@ public:
TSubModel *compartment; TSubModel *compartment;
TSubModel *load; TSubModel *load;
float level; float level;
section_light( TSubModel *Compartment, TSubModel *Load, float const Level ) :
compartment(Compartment), load(Load), level(Level)
{}
}; };
std::vector<section_light> SectionLightLevels; // table of light levels for specific compartments of associated 3d model std::vector<section_light> SectionLightLevels; // table of light levels for specific compartments of associated 3d model
bool SectionLightsActive { false }; // flag indicating whether section lights were set. bool SectionLightsActive { false }; // flag indicating whether section lights were set.
float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?) float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?)
struct section_visibility {
TSubModel *submodel;
bool visible;
int visible_chunks;
};
std::vector<section_visibility> SectionLoadVisibility; // visibility of specific sections of the load 3d model
private: private:
// zmienne i metody do animacji submodeli; Ra: sprzatam animacje w pojeździe // zmienne i metody do animacji submodeli; Ra: sprzatam animacje w pojeździe
@@ -465,6 +468,9 @@ private:
void AttachPrev(TDynamicObject *Object, int iType = 1); void AttachPrev(TDynamicObject *Object, int iType = 1);
bool UpdateForce(double dt, double dt1, bool FullVer); bool UpdateForce(double dt, double dt1, bool FullVer);
void LoadUpdate(); void LoadUpdate();
void update_load_sections();
void update_load_visibility();
void shuffle_load_sections();
bool Update(double dt, double dt1); bool Update(double dt, double dt1);
bool FastUpdate(double dt); bool FastUpdate(double dt);
void Move(double fDistance); void Move(double fDistance);

View File

@@ -259,7 +259,7 @@ int main(int argc, char *argv[])
} }
#endif #endif
Global.asVersion = "M7 16.02.2018 (based on tmj 180215)"; Global.asVersion = "M7 24.02.2018 (based on tmj 7440b1d)";
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
{ {

View File

@@ -69,7 +69,7 @@ struct global_settings {
float fFriction{ 1.f }; // mnożnik tarcia - KURS90 float fFriction{ 1.f }; // mnożnik tarcia - KURS90
bool bLiveTraction{ true }; bool bLiveTraction{ true };
float Overcast{ 0.1f }; // NOTE: all this weather stuff should be moved elsewhere float Overcast{ 0.1f }; // NOTE: all this weather stuff should be moved elsewhere
GLfloat FogColor[ 3 ] = { 0.6f, 0.7f, 0.8f }; glm::vec3 FogColor = { 0.6f, 0.7f, 0.8f };
double fFogStart{ 1700 }; double fFogStart{ 1700 };
double fFogEnd{ 2000 }; double fFogEnd{ 2000 };
std::string Season{}; // season of the year, based on simulation date std::string Season{}; // season of the year, based on simulation date

View File

@@ -1021,7 +1021,7 @@ public:
int PulseForceCount = 0; int PulseForceCount = 0;
/*dla drezyny, silnika spalinowego i parowego*/ /*dla drezyny, silnika spalinowego i parowego*/
double eAngle = 1.5; double eAngle = M_PI * 0.5;
/*-dla wagonow*/ /*-dla wagonow*/
double Load = 0.0; /*masa w T lub ilosc w sztukach - zaladowane*/ double Load = 0.0; /*masa w T lub ilosc w sztukach - zaladowane*/

View File

@@ -8516,7 +8516,8 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C
else if (Command == "PantFront") /*Winger 160204*/ else if (Command == "PantFront") /*Winger 160204*/
{ // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów { // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów
// Czemu EZT ma być traktowane inaczej? Ukrotnienie ma, a człon może być odwrócony // Czemu EZT ma być traktowane inaczej? Ukrotnienie ma, a człon może być odwrócony
if ((TrainType == dt_EZT)) if ((TrainType == dt_EZT)
|| (TrainType == dt_ET41))
{ //'ezt' { //'ezt'
if ((CValue1 == 1)) if ((CValue1 == 1))
{ {
@@ -8560,7 +8561,8 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C
} }
else if (Command == "PantRear") /*Winger 160204, ABu 310105 i 030305*/ else if (Command == "PantRear") /*Winger 160204, ABu 310105 i 030305*/
{ // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów { // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów
if ((TrainType == dt_EZT)) if ((TrainType == dt_EZT)
||(TrainType == dt_ET41))
{ //'ezt' { //'ezt'
if ((CValue1 == 1)) if ((CValue1 == 1))
{ {

View File

@@ -729,6 +729,25 @@ void TSubModel::NextAdd(TSubModel *SubModel)
Next = SubModel; Next = SubModel;
}; };
int TSubModel::count_siblings() {
auto siblingcount { 0 };
auto *sibling { Next };
while( sibling != nullptr ) {
++siblingcount;
sibling = sibling->Next;
}
return siblingcount;
}
int TSubModel::count_children() {
return (
Child == nullptr ?
0 :
1 + Child->count_siblings() );
}
int TSubModel::FlagsCheck() int TSubModel::FlagsCheck()
{ // analiza koniecznych zmian pomiędzy submodelami { // analiza koniecznych zmian pomiędzy submodelami
// samo pomijanie glBindTexture() nie poprawi wydajności // samo pomijanie glBindTexture() nie poprawi wydajności

View File

@@ -161,6 +161,8 @@ public:
void NextAdd(TSubModel *SubModel); void NextAdd(TSubModel *SubModel);
TSubModel * NextGet() { return Next; }; TSubModel * NextGet() { return Next; };
TSubModel * ChildGet() { return Child; }; TSubModel * ChildGet() { return Child; };
int count_siblings();
int count_children();
int TriangleAdd(TModel3d *m, material_handle tex, int tri); int TriangleAdd(TModel3d *m, material_handle tex, int tri);
void SetRotate(float3 vNewRotateAxis, float fNewAngle); void SetRotate(float3 vNewRotateAxis, float fNewAngle);
void SetRotateXYZ( Math3D::vector3 vNewAngles); void SetRotateXYZ( Math3D::vector3 vNewAngles);

View File

@@ -10,48 +10,31 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h" #include "stdafx.h"
#include "Spring.h" #include "Spring.h"
TSpring::TSpring() void TSpring::Init(double nKs, double nKd) {
{
vForce1 = vForce2 = Math3D::vector3(0, 0, 0);
Ks = 0;
Kd = 0;
restLen = 0;
}
TSpring::~TSpring()
{
}
void TSpring::Init(double nrestLen, double nKs, double nKd)
{
Ks = nKs; Ks = nKs;
Kd = nKd; Kd = nKd;
restLen = nrestLen;
} }
Math3D::vector3 TSpring::ComputateForces( Math3D::vector3 const &pPosition1, Math3D::vector3 const &pPosition2) Math3D::vector3 TSpring::ComputateForces( Math3D::vector3 const &pPosition1, Math3D::vector3 const &pPosition2) {
{
double dist, Hterm, Dterm; Math3D::vector3 springForce;
Math3D::vector3 springForce, deltaV;
// p1 = &system[spring->p1]; // p1 = &system[spring->p1];
// p2 = &system[spring->p2]; // p2 = &system[spring->p2];
// VectorDifference(&p1->pos,&p2->pos,&deltaP); // Vector distance // VectorDifference(&p1->pos,&p2->pos,&deltaP); // Vector distance
auto deltaP = pPosition1 - pPosition2; auto deltaP = pPosition1 - pPosition2;
// dist = VectorLength(&deltaP); // Magnitude of // dist = VectorLength(&deltaP); // Magnitude of deltaP
// deltaP auto dist = deltaP.Length();
dist = deltaP.Length(); if( dist > restLen ) {
if (dist != 0.0 ) {
// Hterm = (dist - spring->restLen) * spring->Ks; // Ks * (dist - rest) // Hterm = (dist - spring->restLen) * spring->Ks; // Ks * (dist - rest)
Hterm = ( dist - restLen ) * Ks; // Ks * (dist - rest) auto Hterm = ( dist - restLen ) * Ks; // Ks * (dist - rest)
// VectorDifference(&p1->v,&p2->v,&deltaV); // Delta Velocity Vector // VectorDifference(&p1->v,&p2->v,&deltaV); // Delta Velocity Vector
deltaV = pPosition1 - pPosition2; auto deltaV = pPosition1 - pPosition2;
// Dterm = (DotProduct(&deltaV,&deltaP) * spring->Kd) / dist; // Damping Term // Dterm = (DotProduct(&deltaV,&deltaP) * spring->Kd) / dist; // Damping Term
// Dterm = (DotProduct(deltaV,deltaP) * Kd) / dist; auto Dterm = (DotProduct(deltaV,deltaP) * Kd) / dist;
Dterm = 0; //Dterm = 0;
// ScaleVector(&deltaP,1.0f / dist, &springForce); // Normalize Distance Vector // ScaleVector(&deltaP,1.0f / dist, &springForce); // Normalize Distance Vector
// ScaleVector(&springForce,-(Hterm + Dterm),&springForce); // Calc Force // ScaleVector(&springForce,-(Hterm + Dterm),&springForce); // Calc Force
@@ -60,14 +43,7 @@ Math3D::vector3 TSpring::ComputateForces( Math3D::vector3 const &pPosition1, Mat
// VectorDifference(&p2->f,&springForce,&p2->f); // - Force on Particle 2 // VectorDifference(&p2->f,&springForce,&p2->f); // - Force on Particle 2
} }
vForce1 = springForce;
vForce2 = springForce;
return springForce; return springForce;
} }
void TSpring::Render()
{
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@@ -11,9 +11,8 @@ http://mozilla.org/MPL/2.0/.
#define ParticlesH #define ParticlesH
#include "dumb3d.h" #include "dumb3d.h"
/*
#define STATIC_THRESHOLD 0.17f #define STATIC_THRESHOLD 0.17f
// efine STATIC_THRESHOLD 0.03f
const double m_Kd = 0.02f; // DAMPING FACTOR const double m_Kd = 0.02f; // DAMPING FACTOR
const double m_Kr = 0.8f; // 1.0 = SUPERBALL BOUNCE 0.0 = DEAD WEIGHT const double m_Kr = 0.8f; // 1.0 = SUPERBALL BOUNCE 0.0 = DEAD WEIGHT
const double m_Ksh = 5.0f; // HOOK'S SPRING CONSTANT const double m_Ksh = 5.0f; // HOOK'S SPRING CONSTANT
@@ -21,22 +20,20 @@ const double m_Ksd = 0.1f; // SPRING DAMPING CONSTANT
const double m_Csf = 0.9f; // Default Static Friction const double m_Csf = 0.9f; // Default Static Friction
const double m_Ckf = 0.7f; // Default Kinetic Friction const double m_Ckf = 0.7f; // Default Kinetic Friction
*/
class TSpring {
class TSpring public:
{ TSpring() = default;
public:
TSpring();
~TSpring();
// void Init(TParticnp1, TParticle *np2, double nKs= 0.5f, double nKd= 0.002f, // void Init(TParticnp1, TParticle *np2, double nKs= 0.5f, double nKd= 0.002f,
// double nrestLen= -1.0f); // double nrestLen= -1.0f);
void Init(double nrestLen, double nKs = 0.5f, double nKd = 0.002f); void Init(double nKs = 0.5f, double nKd = 0.002f);
Math3D::vector3 ComputateForces( Math3D::vector3 const &pPosition1, Math3D::vector3 const &pPosition2); Math3D::vector3 ComputateForces( Math3D::vector3 const &pPosition1, Math3D::vector3 const &pPosition2);
void Render(); private:
Math3D::vector3 vForce1, vForce2; // members
double restLen; // LENGTH OF SPRING AT REST double restLen { 0.01 }; // LENGTH OF SPRING AT REST
double Ks; // SPRING CONSTANT double Ks { 0.0 }; // SPRING CONSTANT
double Kd; // SPRING DAMPING double Kd { 0.0 }; // SPRING DAMPING
private:
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

132
Train.cpp
View File

@@ -380,7 +380,7 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d)
} }
} }
*/ */
MechSpring.Init(0.015, 250); MechSpring.Init(250);
vMechVelocity = Math3D::vector3(0, 0, 0); vMechVelocity = Math3D::vector3(0, 0, 0);
pMechOffset = Math3D::vector3( 0, 0, 0 ); pMechOffset = Math3D::vector3( 0, 0, 0 );
fMechSpringX = 1; fMechSpringX = 1;
@@ -3312,9 +3312,10 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman
} }
} }
else { else {
// in the rear cab sides are reversed // in the rear cab sides are reversed...
if( Train->mvOccupied->DoorRight( true ) ) { if( Train->mvOccupied->DoorRight( true ) ) {
Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch ); // ...but so are the switches
Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch );
} }
} }
} }
@@ -3326,9 +3327,10 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman
} }
} }
else { else {
// in the rear cab sides are reversed // in the rear cab sides are reversed...
if( Train->mvOccupied->DoorRight( false ) ) { if( Train->mvOccupied->DoorRight( false ) ) {
Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch ); // ...but so are the switches
Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch );
} }
} }
} }
@@ -3353,9 +3355,10 @@ void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Comma
} }
} }
else { else {
// in the rear cab sides are reversed // in the rear cab sides are reversed...
if( Train->mvOccupied->DoorLeft( true ) ) { if( Train->mvOccupied->DoorLeft( true ) ) {
Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch ); // ...but so are the switches
Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch );
} }
} }
} }
@@ -3367,9 +3370,10 @@ void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Comma
} }
} }
else { else {
// in the rear cab sides are reversed // in the rear cab sides are reversed...
if( Train->mvOccupied->DoorLeft( false ) ) { if( Train->mvOccupied->DoorLeft( false ) ) {
Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch ); // ...but so are the switches
Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch );
} }
} }
} }
@@ -3667,50 +3671,72 @@ void TTrain::UpdateMechPosition(double dt)
// Granice mozna ustalic doswiadczalnie. Ja proponuje 14:20 // Granice mozna ustalic doswiadczalnie. Ja proponuje 14:20
double const iVel = std::min( DynamicObject->GetVelocity(), 150.0 ); double const iVel = std::min( DynamicObject->GetVelocity(), 150.0 );
if( !Global.iSlowMotion // musi być pełna prędkość if( ( false == Global.iSlowMotion ) // musi być pełna prędkość
&& ( pMechOffset.y < 4.0 ) ) // Ra 15-01: przy oglądaniu pantografu bujanie przeszkadza && ( pMechOffset.y < 4.0 ) ) // Ra 15-01: przy oglądaniu pantografu bujanie przeszkadza
{ {
Math3D::vector3 shakevector;
if( ( mvOccupied->EngineType == DieselElectric )
|| ( mvOccupied->EngineType == DieselEngine ) ) {
if( std::abs( mvOccupied->enrot ) > 0.0 ) {
// engine vibration
shakevector.x +=
( std::cos( mvOccupied->eAngle * 4.0 ) * dt * EngineShake.scale )
// fade in with rpm above threshold
* clamp(
( mvOccupied->enrot - EngineShake.fadein_offset ) * EngineShake.fadein_factor,
0.0, 1.0 )
// fade out with rpm above threshold
* interpolate(
1.0, 0.0,
clamp(
( mvOccupied->enrot - EngineShake.fadeout_offset ) * EngineShake.fadeout_factor,
0.0, 1.0 ) );
}
}
if( iVel > 0.5 ) { if( iVel > 0.5 ) {
// acceleration-driven base shake // acceleration-driven base shake
shake += 1.25 * MechSpring.ComputateForces( shakevector += Math3D::vector3(
Math3D::vector3( -mvOccupied->AccN * dt * 5.0, // highlight side sway
-mvControlled->AccN * dt * 5.0, // highlight side sway -mvOccupied->AccVert * dt,
-mvControlled->AccVert * dt, -mvOccupied->AccSVBased * dt * 1.25 ); // accent acceleration/deceleration
-mvControlled->AccSVBased * dt * 1.25 ), // accent acceleration/deceleration
pMechShake );
if( Random( iVel ) > 25.0 ) {
// extra shake at increased velocity
shake += MechSpring.ComputateForces(
Math3D::vector3(
( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * fMechSpringX,
( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * fMechSpringY,
( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * fMechSpringZ )
* 1.25,
pMechShake );
// * (( 200 - DynamicObject->MyTrack->iQualityFlag ) * 0.0075 ); // scale to 75-150% based on track quality
}
// shake *= 0.85;
} }
shake += 1.25 * MechSpring.ComputateForces( shakevector, pMechShake );
if( Random( iVel ) > 25.0 ) {
// extra shake at increased velocity
shake += MechSpring.ComputateForces(
Math3D::vector3(
( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * fMechSpringX,
( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * fMechSpringY,
( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * fMechSpringZ )
* 1.25,
pMechShake );
// * (( 200 - DynamicObject->MyTrack->iQualityFlag ) * 0.0075 ); // scale to 75-150% based on track quality
}
shake *= 0.85;
vMechVelocity -= ( shake + vMechVelocity * 100 ) * ( fMechSpringX + fMechSpringY + fMechSpringZ ) / ( 200 ); vMechVelocity -= ( shake + vMechVelocity * 100 ) * ( fMechSpringX + fMechSpringY + fMechSpringZ ) / ( 200 );
// shake *= 0.95 * dt; // shake damping // shake *= 0.95 * dt; // shake damping
// McZapkie: // McZapkie:
pMechShake += vMechVelocity * dt; pMechShake += vMechVelocity * dt;
if( ( pMechShake.y > fMechMaxSpring )
|| ( pMechShake.y < -fMechMaxSpring ) ) {
vMechVelocity.y = -vMechVelocity.y;
}
// Ra 2015-01: dotychczasowe rzucanie // Ra 2015-01: dotychczasowe rzucanie
pMechOffset += vMechMovement * dt; pMechOffset += vMechMovement * dt;
if( ( pMechShake.y > fMechMaxSpring ) || ( pMechShake.y < -fMechMaxSpring ) )
vMechVelocity.y = -vMechVelocity.y;
// ABu011104: 5*pMechShake.y, zeby ladnie pudlem rzucalo :) // ABu011104: 5*pMechShake.y, zeby ladnie pudlem rzucalo :)
pMechPosition = pMechOffset + Math3D::vector3( 1.5 * pMechShake.x, 2.0 * pMechShake.y, 1.5 * pMechShake.z ); pMechPosition = pMechOffset + Math3D::vector3( 1.5 * pMechShake.x, 2.0 * pMechShake.y, 1.5 * pMechShake.z );
// vMechMovement = 0.5 * vMechMovement;
// pMechShake = interpolate( pMechShake, Math3D::vector3(), clamp( dt, 0.0, 1.0 ) );
} }
else { // hamowanie rzucania przy spadku FPS else { // hamowanie rzucania przy spadku FPS
pMechShake -= pMechShake * std::min( dt, 1.0 ); // po tym chyba potrafią zostać jakieś ułamki, które powodują zjazd pMechShake -= pMechShake * std::min( dt, 1.0 ); // po tym chyba potrafią zostać jakieś ułamki, które powodują zjazd
pMechOffset += vMechMovement * dt; pMechOffset += vMechMovement * dt;
vMechVelocity.y = 0.5 * vMechVelocity.y; vMechVelocity.y = 0.5 * vMechVelocity.y;
pMechPosition = pMechOffset + Math3D::vector3( pMechShake.x, 5 * pMechShake.y, pMechShake.z ); pMechPosition = pMechOffset + Math3D::vector3( pMechShake.x, 5 * pMechShake.y, pMechShake.z );
// vMechMovement = 0.5 * vMechMovement;
} }
// numer kabiny (-1: kabina B) // numer kabiny (-1: kabina B)
if( DynamicObject->Mechanik ) // może nie być? if( DynamicObject->Mechanik ) // może nie być?
@@ -4838,17 +4864,20 @@ bool TTrain::Update( double const Deltatime )
// NOTE: crude way to have the pantographs go back up if they're dropped due to insufficient pressure etc // NOTE: crude way to have the pantographs go back up if they're dropped due to insufficient pressure etc
// TODO: rework it into something more elegant, when redoing the whole consist/unit/cab etc arrangement // TODO: rework it into something more elegant, when redoing the whole consist/unit/cab etc arrangement
if( ( mvControlled->Battery ) if( false == DynamicObject->Mechanik->AIControllFlag ) {
|| ( mvControlled->ConverterFlag ) ) { // don't mess with the ai driving, at least not while switches don't follow ai-set vehicle state
if( ggPantAllDownButton.GetDesiredValue() < 0.05 ) { if( ( mvControlled->Battery )
// the 'lower all' button overrides state of switches, while active itself || ( mvControlled->ConverterFlag ) ) {
if( ( false == mvControlled->PantFrontUp ) if( ggPantAllDownButton.GetDesiredValue() < 0.05 ) {
&& ( ggPantFrontButton.GetDesiredValue() >= 0.95 ) ) { // the 'lower all' button overrides state of switches, while active itself
mvControlled->PantFront( true ); if( ( false == mvControlled->PantFrontUp )
} && ( ggPantFrontButton.GetDesiredValue() >= 0.95 ) ) {
if( ( false == mvControlled->PantRearUp ) mvControlled->PantFront( true );
&& ( ggPantRearButton.GetDesiredValue() >= 0.95 ) ) { }
mvControlled->PantRear( true ); if( ( false == mvControlled->PantRearUp )
&& ( ggPantRearButton.GetDesiredValue() >= 0.95 ) ) {
mvControlled->PantRear( true );
}
} }
} }
} }
@@ -4878,7 +4907,7 @@ bool TTrain::Update( double const Deltatime )
m_radiomessages.erase( m_radiomessages.erase(
std::remove_if( std::remove_if(
std::begin( m_radiomessages ), std::end( m_radiomessages ), std::begin( m_radiomessages ), std::end( m_radiomessages ),
[]( sound_source &source ) { []( sound_source const &source ) {
return ( false == source.is_playing() ); } ), return ( false == source.is_playing() ); } ),
std::end( m_radiomessages ) ); std::end( m_radiomessages ) );
} }
@@ -5320,7 +5349,7 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName)
parser parser
>> ks >> ks
>> kd; >> kd;
MechSpring.Init(MechSpring.restLen, ks, kd); MechSpring.Init(ks, kd);
parser.getTokens(6, false); parser.getTokens(6, false);
parser parser
>> fMechSpringX >> fMechSpringX
@@ -5330,6 +5359,15 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName)
>> fMechRoll >> fMechRoll
>> fMechPitch; >> fMechPitch;
} }
else if( token == "enginespring:" ) {
parser.getTokens( 5, false );
parser
>> EngineShake.scale
>> EngineShake.fadein_offset
>> EngineShake.fadein_factor
>> EngineShake.fadeout_offset
>> EngineShake.fadeout_factor;
}
} while (token != ""); } while (token != "");
} }

View File

@@ -503,6 +503,13 @@ public: // reszta może by?publiczna
double fMechMaxSpring; double fMechMaxSpring;
double fMechRoll; double fMechRoll;
double fMechPitch; double fMechPitch;
struct engineshake_config {
float scale { 2.f };
float fadein_offset { 1.5f };
float fadein_factor { 0.3f };
float fadeout_offset { 10.f };
float fadeout_factor { 0.5f };
} EngineShake;
sound_source dsbReverserKey { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-121211 sound_source dsbReverserKey { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-121211
sound_source dsbNastawnikJazdy { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; sound_source dsbNastawnikJazdy { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE };

View File

@@ -46,6 +46,7 @@ namespace simulation {
simulation_time Time; simulation_time Time;
basic_station Station;
} }
#ifdef _WIN32 #ifdef _WIN32
@@ -1093,10 +1094,13 @@ TWorld::Update_Camera( double const Deltatime ) {
else { else {
// patrzenie standardowe // patrzenie standardowe
Camera.Pos = Train->GetWorldMechPosition(); // Train.GetPosition1(); Camera.Pos = Train->GetWorldMechPosition(); // Train.GetPosition1();
if( !Global.iPause ) { // podczas pauzy nie przeliczać kątów przypadkowymi wartościami if( !Global.iPause ) {
Camera.Roll = atan( Train->pMechShake.x * Train->fMechRoll ); // hustanie kamery na boki // podczas pauzy nie przeliczać kątów przypadkowymi wartościami
Camera.Pitch -= 0.5 * atan( Train->vMechVelocity.z * Train->fMechPitch ); // hustanie kamery przod tyl //Ra: tu // hustanie kamery na boki
// jest uciekanie kamery w górę!!! Camera.Roll = atan( Train->vMechVelocity.x * Train->fMechRoll );
// hustanie kamery przod tyl
// Ra: tu jest uciekanie kamery w górę!!!
Camera.Pitch -= 0.5 * atan( Train->vMechVelocity.z * Train->fMechPitch );
} }
// ABu011104: rzucanie pudlem // ABu011104: rzucanie pudlem
/* /*
@@ -1368,7 +1372,7 @@ TWorld::Update_UI() {
if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_M ) == bdelay_M ) if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_M ) == bdelay_M )
uitextline2 += "+Mg"; uitextline2 += "+Mg";
uitextline2 += ", Load: " + to_string( vehicle->MoverParameters->LoadFlag, 0 ); uitextline2 += ", Load: " + to_string( vehicle->MoverParameters->LoadFlag, 0 ) + " (" + to_string( vehicle->MoverParameters->Load, 0 ) + ")";
uitextline2 += uitextline2 +=
"; Pant: " "; Pant: "
@@ -1614,7 +1618,8 @@ TWorld::Update_UI() {
Acc = ( vehicle->MoverParameters->Vel - VelPrev ) / 3.6; Acc = ( vehicle->MoverParameters->Vel - VelPrev ) / 3.6;
VelPrev = vehicle->MoverParameters->Vel; VelPrev = vehicle->MoverParameters->Vel;
} }
uitextline2 += ( "; As=" ) + to_string( Acc, 2 ); // przyspieszenie wzdłużne uitextline2 += "; As=" + to_string( Acc, 2 ); // przyspieszenie wzdłużne
uitextline2 += " eAngle=" + to_string( std::cos( vehicle->MoverParameters->eAngle ), 2 );
uitextline3 = uitextline3 =
"cyl.ham. " + to_string( vehicle->MoverParameters->BrakePress, 2 ) "cyl.ham. " + to_string( vehicle->MoverParameters->BrakePress, 2 )
@@ -2241,17 +2246,16 @@ world_environment::update() {
// tonal impact of skydome color is inversely proportional to how high the sun is above the horizon // tonal impact of skydome color is inversely proportional to how high the sun is above the horizon
// (this is pure conjecture, aimed more to 'look right' than be accurate) // (this is pure conjecture, aimed more to 'look right' than be accurate)
float const ambienttone = clamp( 1.0f - ( Global.SunAngle / 90.0f ), 0.0f, 1.0f ); float const ambienttone = clamp( 1.0f - ( Global.SunAngle / 90.0f ), 0.0f, 1.0f );
Global.DayLight.ambient[ 0 ] = interpolate( skydomehsv.z, skydomecolour.x, ambienttone ); Global.DayLight.ambient[ 0 ] = interpolate( skydomehsv.z, skydomecolour.r, ambienttone );
Global.DayLight.ambient[ 1 ] = interpolate( skydomehsv.z, skydomecolour.y, ambienttone ); Global.DayLight.ambient[ 1 ] = interpolate( skydomehsv.z, skydomecolour.g, ambienttone );
Global.DayLight.ambient[ 2 ] = interpolate( skydomehsv.z, skydomecolour.z, ambienttone ); Global.DayLight.ambient[ 2 ] = interpolate( skydomehsv.z, skydomecolour.b, ambienttone );
Global.fLuminance = intensity; Global.fLuminance = intensity;
// update the fog. setting it to match the average colour of the sky dome is cheap // update the fog. setting it to match the average colour of the sky dome is cheap
// but quite effective way to make the distant items blend with background better // but quite effective way to make the distant items blend with background better
Global.FogColor[ 0 ] = skydomecolour.x; // NOTE: base brightness calculation provides scaled up value, so we bring it back to 'real' one here
Global.FogColor[ 1 ] = skydomecolour.y; Global.FogColor = m_skydome.GetAverageHorizonColor();
Global.FogColor[ 2 ] = skydomecolour.z;
} }
void void

View File

@@ -20,6 +20,7 @@ http://mozilla.org/MPL/2.0/.
#include "stars.h" #include "stars.h"
#include "skydome.h" #include "skydome.h"
#include "messaging.h" #include "messaging.h"
#include "station.h"
// wrapper for simulation time // wrapper for simulation time
class simulation_time { class simulation_time {
@@ -59,6 +60,8 @@ namespace simulation {
extern simulation_time Time; extern simulation_time Time;
extern basic_station Station; // temporary object, for station functionality tests
} }
class opengl_renderer; class opengl_renderer;

View File

@@ -1049,7 +1049,7 @@ opengl_renderer::setup_drawing( bool const Alpha ) {
// setup fog // setup fog
if( Global.fFogEnd > 0 ) { if( Global.fFogEnd > 0 ) {
// fog setup // fog setup
::glFogfv( GL_FOG_COLOR, Global.FogColor ); ::glFogfv( GL_FOG_COLOR, glm::value_ptr( Global.FogColor ) );
::glFogf( GL_FOG_DENSITY, static_cast<GLfloat>( 1.0 / Global.fFogEnd ) ); ::glFogf( GL_FOG_DENSITY, static_cast<GLfloat>( 1.0 / Global.fFogEnd ) );
::glEnable( GL_FOG ); ::glEnable( GL_FOG );
} }
@@ -1828,6 +1828,37 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator
Render( memcell ); Render( memcell );
} }
} }
#endif
#ifdef EU07_USE_DEBUG_CULLING
// debug
::glLineWidth( 2.f );
float const width = cell->m_area.radius;
float const height = cell->m_area.radius * 0.2f;
glDisable( GL_LIGHTING );
glDisable( GL_TEXTURE_2D );
glColor3ub( 255, 128, 128 );
glBegin( GL_LINE_LOOP );
glVertex3f( -width, height, width );
glVertex3f( -width, height, -width );
glVertex3f( width, height, -width );
glVertex3f( width, height, width );
glEnd();
glBegin( GL_LINE_LOOP );
glVertex3f( -width, 0, width );
glVertex3f( -width, 0, -width );
glVertex3f( width, 0, -width );
glVertex3f( width, 0, width );
glEnd();
glBegin( GL_LINES );
glVertex3f( -width, height, width ); glVertex3f( -width, 0, width );
glVertex3f( -width, height, -width ); glVertex3f( -width, 0, -width );
glVertex3f( width, height, -width ); glVertex3f( width, 0, -width );
glVertex3f( width, height, width ); glVertex3f( width, 0, width );
glEnd();
glColor3ub( 255, 255, 255 );
glEnable( GL_TEXTURE_2D );
glEnable( GL_LIGHTING );
glLineWidth( 1.f );
#endif #endif
// post-render cleanup // post-render cleanup
::glPopMatrix(); ::glPopMatrix();

View File

@@ -242,7 +242,7 @@ void CSkyDome::RebuildColors() {
zenithluminance = PerezFunctionO1( perezluminance, m_thetasun, zenithluminance ); zenithluminance = PerezFunctionO1( perezluminance, m_thetasun, zenithluminance );
// start with fresh average for the new pass // start with fresh average for the new pass
glm::vec3 averagecolor { 0.0f, 0.0f, 0.0f }; glm::vec3 averagecolor, averagehorizoncolor;
// trough all vertices // trough all vertices
glm::vec3 vertex; glm::vec3 vertex;
@@ -332,14 +332,16 @@ void CSkyDome::RebuildColors() {
} }
// save // save
m_colours[ i ] = color; m_colours[ i ] = color;
averagecolor += color * 8.0f; // save for edge cases each vertex goes in 8 triangles averagecolor += color;
if( ( m_vertices.size() - i ) <= ( m_tesselation * 2 ) ) {
// calculate horizon colour from the bottom band of tris
averagehorizoncolor += color;
}
} }
// NOTE: average reduced to 25% makes nice tint value for clouds lit from behind // NOTE: average reduced to 25% makes nice tint value for clouds lit from behind
// down the road we could interpolate between it and full strength average, to improve accuracy of cloud appearance // down the road we could interpolate between it and full strength average, to improve accuracy of cloud appearance
m_averagecolour = averagecolor / static_cast<float>( m_indices.size() ); m_averagecolour = glm::max( glm::vec3(), averagecolor / static_cast<float>( m_vertices.size() ) );
m_averagecolour.r = std::max( m_averagecolour.r, 0.0f ); m_averagehorizoncolour = glm::max( glm::vec3(), averagehorizoncolor / static_cast<float>( m_tesselation * 2 ) );
m_averagecolour.g = std::max( m_averagecolour.g, 0.0f );
m_averagecolour.b = std::max( m_averagecolour.b, 0.0f );
if( m_coloursbuffer != -1 ) { if( m_coloursbuffer != -1 ) {
// the colour buffer was already initialized, so on this run we update its content // the colour buffer was already initialized, so on this run we update its content

View File

@@ -23,7 +23,8 @@ public:
void Render(); void Render();
// retrieves average colour of the sky dome // retrieves average colour of the sky dome
glm::vec3 GetAverageColor() { return m_averagecolour; } glm::vec3 GetAverageColor() { return m_averagecolour * 8.f / 6.f; }
glm::vec3 GetAverageHorizonColor() { return m_averagehorizoncolour; }
private: private:
// shading parametrs // shading parametrs
@@ -35,6 +36,7 @@ private:
float m_overcast; float m_overcast;
float m_gammacorrection; float m_gammacorrection;
glm::vec3 m_averagecolour; glm::vec3 m_averagecolour;
glm::vec3 m_averagehorizoncolour;
// data // data
int const m_tesselation; int const m_tesselation;

View File

@@ -454,8 +454,8 @@ sound_source::stop( bool const Skipend ) {
if( ( false == Skipend ) if( ( false == Skipend )
&& ( sound( sound_id::end ).buffer != null_handle ) && ( sound( sound_id::end ).buffer != null_handle )
&& ( sound( sound_id::end ).buffer != sound( sound_id::main ).buffer ) // end == main can happen in malformed legacy cases /* && ( sound( sound_id::end ).buffer != sound( sound_id::main ).buffer ) */ // end == main can happen in malformed legacy cases
/* && ( sound( sound_id::end ).playing == 0 ) */ ) { /* && ( sound( sound_id::end ).playing == 0 ) */ ) {
// spawn potentially defined sound end sample, if the emitter is currently active // spawn potentially defined sound end sample, if the emitter is currently active
insert( sound_id::end ); insert( sound_id::end );
} }
@@ -482,19 +482,31 @@ sound_source::update_basic( audio::openal_source &Source ) {
if( true == Source.is_playing ) { if( true == Source.is_playing ) {
if( ( true == m_stop ) auto const soundhandle { Source.sounds[ Source.sound_index ] };
&& ( Source.sounds[ Source.sound_index ] != sound_id::end ) ) {
// kill the sound if stop was requested, unless it's sound bookend sample if( sound( sound_id::begin ).buffer != null_handle ) {
Source.stop(); // potentially a multipart sound
update_counter( Source.sounds[ Source.sound_index ], -1 ); // detect the moment when the sound moves from startup sample to the main
if( false == is_playing() ) { if( ( false == Source.is_looping )
m_stop = false; && ( soundhandle == sound_id::main ) ) {
// when it happens update active sample counters, and activate the looping
update_counter( sound_id::begin, -1 );
update_counter( soundhandle, 1 );
Source.loop( TestFlag( m_flags, sound_flags::looping ) );
} }
}
if( ( true == m_stop )
&& ( soundhandle != sound_id::end ) ) {
// kill the sound if stop was requested, unless it's sound bookend sample
update_counter( soundhandle, -1 );
Source.stop();
m_stop = is_playing(); // end the stop mode when all active sounds are dead
return; return;
} }
/* /*
if( ( true == m_stopend ) if( ( true == m_stopend )
&& ( Source.sounds[ Source.sound_index ] == sound_id::end ) ) { && ( soundhandle == sound_id::end ) ) {
// kill the sound if it's the bookend sample and stopping it was requested // kill the sound if it's the bookend sample and stopping it was requested
Source.stop(); Source.stop();
update_counter( sound_id::end, -1 ); update_counter( sound_id::end, -1 );
@@ -504,25 +516,13 @@ sound_source::update_basic( audio::openal_source &Source ) {
return; return;
} }
*/ */
if( sound( sound_id::begin ).buffer != null_handle ) {
// potentially a multipart sound
// detect the moment when the sound moves from startup sample to the main
auto const soundhandle { Source.sounds[ Source.sound_index ] };
if( ( false == Source.is_looping )
&& ( soundhandle == sound_id::main ) ) {
// when it happens update active sample counters, and activate the looping
update_counter( sound_id::begin, -1 );
update_counter( soundhandle, 1 );
Source.loop( true );
}
}
// check and update if needed current sound properties // check and update if needed current sound properties
update_location(); update_location();
update_soundproofing(); update_soundproofing();
Source.sync_with( m_properties ); Source.sync_with( m_properties );
if( Source.sync != sync_state::good ) { if( Source.sync != sync_state::good ) {
// if the sync went wrong we let the renderer kill its part of the emitter, and update our playcounter(s) to match // if the sync went wrong we let the renderer kill its part of the emitter, and update our playcounter(s) to match
update_counter( Source.sounds[ Source.sound_index ], -1 ); update_counter( soundhandle, -1 );
} }
} }
@@ -533,10 +533,9 @@ sound_source::update_basic( audio::openal_source &Source ) {
// the emitter wasn't yet started // the emitter wasn't yet started
auto const soundhandle { Source.sounds[ Source.sound_index ] }; auto const soundhandle { Source.sounds[ Source.sound_index ] };
// emitter initialization // emitter initialization
if( ( soundhandle == sound_id::main ) if( soundhandle == sound_id::main ) {
&& ( true == TestFlag( m_flags, sound_flags::looping ) ) ) {
// main sample can be optionally set to loop // main sample can be optionally set to loop
Source.loop( true ); Source.loop( TestFlag( m_flags, sound_flags::looping ) );
} }
Source.range( m_range ); Source.range( m_range );
Source.pitch( m_pitchvariation ); Source.pitch( m_pitchvariation );
@@ -570,6 +569,18 @@ sound_source::update_combined( audio::openal_source &Source ) {
auto const soundhandle { Source.sounds[ Source.sound_index ] }; auto const soundhandle { Source.sounds[ Source.sound_index ] };
if( sound( sound_id::begin ).buffer != null_handle ) {
// potentially a multipart sound
// detect the moment when the sound moves from startup sample to the main
if( ( false == Source.is_looping )
&& ( soundhandle == ( sound_id::chunk | 0 ) ) ) {
// when it happens update active sample counters, and activate the looping
update_counter( sound_id::begin, -1 );
update_counter( soundhandle, 1 );
Source.loop( true );
}
}
if( ( true == m_stop ) if( ( true == m_stop )
&& ( soundhandle != sound_id::end ) ) { && ( soundhandle != sound_id::end ) ) {
// kill the sound if stop was requested, unless it's sound bookend sample // kill the sound if stop was requested, unless it's sound bookend sample
@@ -582,7 +593,7 @@ sound_source::update_combined( audio::openal_source &Source ) {
} }
/* /*
if( ( true == m_stopend ) if( ( true == m_stopend )
&& ( Source.sounds[ Source.sound_index ] == sound_id::end ) ) { && ( soundhandle == sound_id::end ) ) {
// kill the sound if it's the bookend sample and stopping it was requested // kill the sound if it's the bookend sample and stopping it was requested
Source.stop(); Source.stop();
update_counter( sound_id::end, -1 ); update_counter( sound_id::end, -1 );
@@ -592,19 +603,6 @@ sound_source::update_combined( audio::openal_source &Source ) {
return; return;
} }
*/ */
if( sound( sound_id::begin ).buffer != null_handle ) {
// potentially a multipart sound
// detect the moment when the sound moves from startup sample to the main
auto const soundhandle { Source.sounds[ Source.sound_index ] };
if( ( false == Source.is_looping )
&& ( soundhandle == ( sound_id::chunk | 0 ) ) ) {
// when it happens update active sample counters, and activate the looping
update_counter( sound_id::begin, -1 );
update_counter( soundhandle, 1 );
Source.loop( true );
}
}
if( ( soundhandle & sound_id::chunk ) != 0 ) { if( ( soundhandle & sound_id::chunk ) != 0 ) {
// for sound chunks, test whether the chunk should still be active given current value of the controlling variable // for sound chunks, test whether the chunk should still be active given current value of the controlling variable
auto const soundpoint { compute_combined_point() }; auto const soundpoint { compute_combined_point() };
@@ -629,7 +627,7 @@ sound_source::update_combined( audio::openal_source &Source ) {
Source.sync_with( m_properties ); Source.sync_with( m_properties );
if( Source.sync != sync_state::good ) { if( Source.sync != sync_state::good ) {
// if the sync went wrong we let the renderer kill its part of the emitter, and update our playcounter(s) to match // if the sync went wrong we let the renderer kill its part of the emitter, and update our playcounter(s) to match
update_counter( Source.sounds[ Source.sound_index ], -1 ); update_counter( soundhandle, -1 );
} }
// ...and restore base properties // ...and restore base properties
m_properties = baseproperties; m_properties = baseproperties;
@@ -796,7 +794,7 @@ sound_source::empty() const {
bool bool
sound_source::is_playing( bool const Includesoundends ) const { sound_source::is_playing( bool const Includesoundends ) const {
auto isplaying { ( sound( sound_id::begin ).playing + sound( sound_id::main ).playing ) > 0 }; auto isplaying { ( sound( sound_id::begin ).playing > 0 ) || ( sound( sound_id::main ).playing > 0 ) };
if( ( false == isplaying ) if( ( false == isplaying )
&& ( false == m_soundchunks.empty() ) ) { && ( false == m_soundchunks.empty() ) ) {
// for emitters with sample tables check also if any of the chunks is active // for emitters with sample tables check also if any of the chunks is active
@@ -836,8 +834,9 @@ sound_source::location() const {
void void
sound_source::update_counter( sound_handle const Sound, int const Value ) { sound_source::update_counter( sound_handle const Sound, int const Value ) {
sound( Sound ).playing = std::max( 0, sound( Sound ).playing + Value ); // sound( Sound ).playing = std::max( 0, sound( Sound ).playing + Value );
// assert( sound( Sound ).playing >= 0 ); sound( Sound ).playing += Value;
assert( sound( Sound ).playing >= 0 );
} }
void void

10
sound.h
View File

@@ -179,15 +179,15 @@ private:
template <class Iterator_> template <class Iterator_>
void void
insert( Iterator_ First, Iterator_ Last ) { insert( Iterator_ First, Iterator_ Last ) {
uint32_sequence sounds; update_counter( *First, 1 );
std::vector<audio::buffer_handle> buffers; std::vector<audio::buffer_handle> buffers;
uint32_sequence sounds;
std::for_each( std::for_each(
First, Last, First, Last,
[&]( sound_handle const &soundhandle ) { [&]( sound_handle const &soundhandle ) {
sounds.emplace_back( soundhandle ); buffers.emplace_back( sound( soundhandle ).buffer );
buffers.emplace_back( sound( soundhandle ).buffer ); } ); sounds.emplace_back( soundhandle ); } );
audio::renderer.insert( std::begin( buffers ), std::end( buffers ), this, sounds ); audio::renderer.insert( std::begin( buffers ), std::end( buffers ), this, sounds ); }
update_counter( *First, 1 ); }
sound_data & sound_data &
sound( sound_handle const Sound ); sound( sound_handle const Sound );
sound_data const & sound_data const &

73
station.cpp Normal file
View File

@@ -0,0 +1,73 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "station.h"
#include "DynObj.h"
#include "mtable.h"
// exchanges load with consist attached to specified vehicle, operating on specified schedule
double
basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule ) {
auto const firststop { Schedule.StationIndex == 1 };
auto const laststop { Schedule.StationIndex == Schedule.StationCount };
// HACK: determine whether current station is a (small) stop from the presence of "po" at the name end
auto const stationname { Schedule.TimeTable[ Schedule.StationIndex ].StationName };
auto const stationequipment { Schedule.TimeTable[ Schedule.StationIndex ].StationWare };
auto const trainstop { (
( ( stationname.size() >= 2 ) && ( stationname.substr( stationname.size() - 2 ) == "po" ) )
|| ( stationequipment.find( "po" ) != std::string::npos ) ) };
// train stops exchange smaller groups than regular stations
// NOTE: this is crude and unaccurate, but for now will do
auto const stationsizemodifier { ( trainstop ? 1.0 : 2.0 ) };
// go through all vehicles and update their load
// NOTE: for the time being we limit ourselves to passenger-carrying cars only
auto exchangetime { 0.0 };
auto *vehicle { First };
while( vehicle != nullptr ) {
auto &parameters { *vehicle->MoverParameters };
if( ( true == parameters.LoadType.empty() )
&& ( parameters.LoadAccepted.find( "passengers" ) != std::string::npos ) ) {
// set the type for empty cars
parameters.LoadType = "passengers";
}
if( parameters.LoadType == "passengers" ) {
// NOTE: for the time being we're doing simple, random load change calculation
// TODO: exchange driven by station parameters and time of the day
auto unloadcount = static_cast<int>(
firststop ? 0 :
laststop ? parameters.Load :
std::min(
parameters.Load,
Random( parameters.MaxLoad * 0.10 * stationsizemodifier ) ) );
auto loadcount = static_cast<int>(
laststop ?
0 :
Random( parameters.MaxLoad * 0.15 * stationsizemodifier ) );
if( true == firststop ) {
// slightly larger group at the initial station
loadcount *= 2;
}
parameters.Load = std::min( parameters.MaxLoad, parameters.Load - unloadcount + loadcount );
vehicle->LoadUpdate();
vehicle->update_load_visibility();
exchangetime = std::max( exchangetime, unloadcount / parameters.UnLoadSpeed + loadcount / parameters.LoadSpeed);
}
vehicle = vehicle->Next();
}
return exchangetime;
}

22
station.h Normal file
View File

@@ -0,0 +1,22 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "Classes.h"
// a simple station, performs freight and passenger exchanges with visiting consists
class basic_station {
public:
// methods
// exchanges load with consist attached to specified vehicle, operating on specified schedule; returns: time needed for exchange, in seconds
double
update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule );
};

View File

@@ -90,12 +90,22 @@ ui_layer::render() {
glDisable( GL_LIGHTING ); glDisable( GL_LIGHTING );
glDisable( GL_DEPTH_TEST ); glDisable( GL_DEPTH_TEST );
glDisable( GL_ALPHA_TEST ); glDisable( GL_ALPHA_TEST );
glEnable( GL_BLEND ); glEnable( GL_TEXTURE_2D );
glEnable( GL_BLEND );
::glColor4fv( glm::value_ptr( colors::white ) );
// render code here // render code here
render_background(); render_background();
render_texture(); render_texture();
glDisable( GL_TEXTURE_2D );
glDisable( GL_TEXTURE_CUBE_MAP );
render_progress(); render_progress();
glDisable( GL_BLEND );
render_panels(); render_panels();
render_tooltip(); render_tooltip();
@@ -129,9 +139,6 @@ ui_layer::render_progress() {
if( (m_progress == 0.0f) && (m_subtaskprogress == 0.0f) ) return; if( (m_progress == 0.0f) && (m_subtaskprogress == 0.0f) ) return;
glPushAttrib( GL_ENABLE_BIT );
glDisable( GL_TEXTURE_2D );
glm::vec2 origin, size; glm::vec2 origin, size;
if( m_progressbottom == true ) { if( m_progressbottom == true ) {
origin = glm::vec2{ 0.0f, 768.0f - 20.0f }; origin = glm::vec2{ 0.0f, 768.0f - 20.0f };
@@ -177,8 +184,6 @@ ui_layer::render_progress() {
( 0.5f * ( Global.iWindowHeight - height ) + origin.y * heightratio ) + ( charsize ) + ( ( size.y * heightratio - textheight ) * 0.5f * heightratio ) ); ( 0.5f * ( Global.iWindowHeight - height ) + origin.y * heightratio ) + ( charsize ) + ( ( size.y * heightratio - textheight ) * 0.5f * heightratio ) );
print( m_progresstext ); print( m_progresstext );
} }
glPopAttrib();
} }
void void
@@ -186,9 +191,6 @@ ui_layer::render_panels() {
if( m_panels.empty() ) { return; } if( m_panels.empty() ) { return; }
glPushAttrib( GL_ENABLE_BIT );
glDisable( GL_TEXTURE_2D );
float const width = std::min( 4.f / 3.f, static_cast<float>(Global.iWindowWidth) / std::max( 1, Global.iWindowHeight ) ) * Global.iWindowHeight; float const width = std::min( 4.f / 3.f, static_cast<float>(Global.iWindowWidth) / std::max( 1, Global.iWindowHeight ) ) * Global.iWindowHeight;
float const height = Global.iWindowHeight / 768.f; float const height = Global.iWindowHeight / 768.f;
@@ -199,7 +201,7 @@ ui_layer::render_panels() {
int lineidx = 0; int lineidx = 0;
for( auto const &line : panel->text_lines ) { for( auto const &line : panel->text_lines ) {
::glColor4fv( &line.color.x ); ::glColor4fv( glm::value_ptr( line.color ) );
::glRasterPos2f( ::glRasterPos2f(
0.5f * ( Global.iWindowWidth - width ) + panel->origin_x * height, 0.5f * ( Global.iWindowWidth - width ) + panel->origin_x * height,
panel->origin_y * height + 20.f * lineidx ); panel->origin_y * height + 20.f * lineidx );
@@ -207,8 +209,6 @@ ui_layer::render_panels() {
++lineidx; ++lineidx;
} }
} }
glPopAttrib();
} }
void void
@@ -219,13 +219,9 @@ ui_layer::render_tooltip() {
glm::dvec2 mousepos; glm::dvec2 mousepos;
glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y ); glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y );
glPushAttrib( GL_ENABLE_BIT ); ::glColor4fv( glm::value_ptr( colors::white ) );
glDisable( GL_TEXTURE_2D );
::glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
::glRasterPos2f( mousepos.x + 20.0f, mousepos.y + 25.0f ); ::glRasterPos2f( mousepos.x + 20.0f, mousepos.y + 25.0f );
print( m_tooltip ); print( m_tooltip );
glPopAttrib();
} }
void void
@@ -254,8 +250,7 @@ void
ui_layer::render_texture() { ui_layer::render_texture() {
if( m_texture != 0 ) { if( m_texture != 0 ) {
::glColor4f( 1.f, 1.f, 1.f, 1.f ); ::glColor4fv( glm::value_ptr( colors::white ) );
::glDisable( GL_BLEND );
GfxRenderer.Bind_Texture( null_handle ); GfxRenderer.Bind_Texture( null_handle );
::glBindTexture( GL_TEXTURE_2D, m_texture ); ::glBindTexture( GL_TEXTURE_2D, m_texture );
@@ -273,8 +268,6 @@ ui_layer::render_texture() {
glEnd(); glEnd();
::glBindTexture( GL_TEXTURE_2D, 0 ); ::glBindTexture( GL_TEXTURE_2D, 0 );
::glEnable( GL_BLEND );
} }
} }

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#define VERSION_MAJOR 18 #define VERSION_MAJOR 18
#define VERSION_MINOR 215 #define VERSION_MINOR 223
#define VERSION_REVISION 0 #define VERSION_REVISION 0