From aa9626901f995189ed67700120e16c2af0d0c870 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sun, 6 May 2018 04:05:43 +0200 Subject: [PATCH] build 180505. animated mirrors and doorsteps, support for spotlight hotspot width attribute, per-sound radio message range, support for combined tachometer sound, minor bug fixes --- DynObj.cpp | 349 +++++++++++++++++++++++++++++++-------------- DynObj.h | 38 +++-- McZapkie/MOVER.h | 7 +- McZapkie/Mover.cpp | 12 +- Model3d.cpp | 6 +- Train.cpp | 14 +- renderer.cpp | 36 ++++- renderer.h | 1 + scene.cpp | 5 +- simulation.cpp | 6 +- sound.cpp | 7 + sound.h | 3 + uilayer.cpp | 2 +- version.h | 2 +- 14 files changed, 351 insertions(+), 137 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 2ea215d2..3b7b73bf 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -70,8 +70,7 @@ void TAnimPant::AKP_4E() }; //--------------------------------------------------------------------------- int TAnim::TypeSet(int i, int fl) -{ // ustawienie typu animacji i zależnej od - // niego ilości animowanych submodeli +{ // ustawienie typu animacji i zależnej od niego ilości animowanych submodeli fMaxDist = -1.0; // normalnie nie pokazywać switch (i) { // maska 0x000F: ile używa wskaźników na submodele (0 gdy jeden, @@ -104,16 +103,19 @@ int TAnim::TypeSet(int i, int fl) case 6: iFlags = 0x068; break; // 6-tłok i rozrząd - 8 submodeli + case 7: + iFlags = 0x070; + break; // doorstep + case 8: + iFlags = 0x080; + break; // mirror default: iFlags = 0; } yUpdate = nullptr; return iFlags & 15; // ile wskaźników rezerwować dla danego typu animacji }; -TAnim::TAnim() -{ // potrzebne to w ogóle? - iFlags = -1; // nieznany typ - destruktor nic nie usuwa -}; + TAnim::~TAnim() { // usuwanie animacji switch (iFlags & 0xF0) @@ -124,13 +126,15 @@ TAnim::~TAnim() case 0x50: // 5-pantograf delete fParamPants; break; - case 0x60: // 6-tłok i rozrząd + default: break; } }; +/* void TAnim::Parovoz(){ // animowanie tłoka i rozrządu parowozu }; +*/ //--------------------------------------------------------------------------- TDynamicObject * TDynamicObject::FirstFind(int &coupler_nr, int cf) { // szukanie skrajnego połączonego pojazdu w pociagu @@ -400,11 +404,6 @@ void TDynamicObject::UpdateBoogie(TAnim *pAnim) void TDynamicObject::UpdateDoorTranslate(TAnim *pAnim) { // animacja drzwi - przesuw - // WriteLog("Dla drzwi nr:", i); - // WriteLog("Wspolczynnik", DoorSpeedFactor[i]); - // Ra: te współczynniki są bez sensu, bo modyfikują wektor przesunięcia - // w efekcie drzwi otwierane na zewnątrz będą odlatywac dowolnie daleko :) - // ograniczyłem zakres ruchu funkcją max if (pAnim->smAnimated) { if( pAnim->iNumber & 1 ) { @@ -427,9 +426,7 @@ void TDynamicObject::UpdateDoorTranslate(TAnim *pAnim) void TDynamicObject::UpdateDoorRotate(TAnim *pAnim) { // animacja drzwi - obrót if (pAnim->smAnimated) - { // if (MoverParameters->DoorOpenMethod==2) //obrotowe - // albo dwójłomne (trzeba kombinowac - // submodelami i ShiftL=90,R=180) + { if (pAnim->iNumber & 1) pAnim->smAnimated->SetRotate(float3(1, 0, 0), dDoorMoveR); else @@ -440,9 +437,7 @@ void TDynamicObject::UpdateDoorRotate(TAnim *pAnim) void TDynamicObject::UpdateDoorFold(TAnim *pAnim) { // animacja drzwi - obrót if (pAnim->smAnimated) - { // if (MoverParameters->DoorOpenMethod==2) //obrotowe - // albo dwójłomne (trzeba kombinowac - // submodelami i ShiftL=90,R=180) + { if (pAnim->iNumber & 1) { pAnim->smAnimated->SetRotate(float3(0, 0, 1), dDoorMoveR); @@ -458,7 +453,6 @@ void TDynamicObject::UpdateDoorFold(TAnim *pAnim) else { pAnim->smAnimated->SetRotate(float3(0, 0, 1), dDoorMoveL); - // SubModel->SetRotate(float3(0,1,0),fValue*360.0); TSubModel *sm = pAnim->smAnimated->ChildGet(); // skrzydło mniejsze if (sm) { @@ -471,6 +465,35 @@ void TDynamicObject::UpdateDoorFold(TAnim *pAnim) } }; +void TDynamicObject::UpdateDoorPlug(TAnim *pAnim) +{ // animacja drzwi - odskokprzesuw + if (pAnim->smAnimated) { + + if( pAnim->iNumber & 1 ) { + pAnim->smAnimated->SetTranslate( + Math3D::vector3 { + std::min( + dDoorMoveR * 2, + MoverParameters->DoorMaxPlugShift ), + 0.0, + std::max( + 0.0, + dDoorMoveR - MoverParameters->DoorMaxPlugShift * 0.5f ) } ); + } + else { + pAnim->smAnimated->SetTranslate( + Math3D::vector3 { + std::min( + dDoorMoveL * 2, + MoverParameters->DoorMaxPlugShift ), + 0.0, + std::max( + 0.0, + dDoorMoveL - MoverParameters->DoorMaxPlugShift * 0.5f ) } ); + } + } +} + void TDynamicObject::UpdatePant(TAnim *pAnim) { // animacja pantografu - 4 obracane ramiona, ślizg piąty float a, b, c; @@ -487,37 +510,66 @@ void TDynamicObject::UpdatePant(TAnim *pAnim) pAnim->smElement[3]->SetRotate(float3(-1, 0, 0), c); if (pAnim->smElement[4]) pAnim->smElement[4]->SetRotate(float3(-1, 0, 0), b); //ślizg -}; +} -void TDynamicObject::UpdateDoorPlug(TAnim *pAnim) -{ // animacja drzwi - odskokprzesuw - if (pAnim->smAnimated) { +// doorstep animation, shift +void TDynamicObject::UpdatePlatformTranslate( TAnim *pAnim ) { - if( pAnim->iNumber & 1 ) { - pAnim->smAnimated->SetTranslate( - Math3D::vector3 { - std::min( - dDoorMoveR * 2, - MoverParameters->DoorMaxPlugShift ), - 0.0, - std::max( - 0.0, - dDoorMoveR - MoverParameters->DoorMaxPlugShift * 0.5 ) } ); - } - else { - pAnim->smAnimated->SetTranslate( - Math3D::vector3 { - std::min( - dDoorMoveL * 2, - MoverParameters->DoorMaxPlugShift ), - 0.0, - std::max( - 0.0, - dDoorMoveL - MoverParameters->DoorMaxPlugShift * 0.5f ) } ); - } + if( pAnim->smAnimated == nullptr ) { return; } + + if( pAnim->iNumber & 1 ) { + pAnim->smAnimated->SetTranslate( + Math3D::vector3{ + interpolate( 0.0, MoverParameters->PlatformMaxShift, dDoorstepMoveR ), + 0.0, + 0.0 } ); } -}; + else { + pAnim->smAnimated->SetTranslate( + Math3D::vector3{ + interpolate( 0.0, MoverParameters->PlatformMaxShift, dDoorstepMoveL ), + 0.0, + 0.0 } ); + } +} +// doorstep animation, rotate +void TDynamicObject::UpdatePlatformRotate( TAnim *pAnim ) { + + if( pAnim->smAnimated == nullptr ) { return; } + + if( pAnim->iNumber & 1 ) + pAnim->smAnimated->SetRotate( + float3( 0, 1, 0 ), + interpolate( 0.0, MoverParameters->PlatformMaxShift, dDoorstepMoveR ) ); + else + pAnim->smAnimated->SetRotate( + float3( 0, 1, 0 ), + interpolate( 0.0, MoverParameters->PlatformMaxShift, dDoorstepMoveL ) ); +} + +// mirror animation, rotate +void TDynamicObject::UpdateMirror( TAnim *pAnim ) { + + if( pAnim->smAnimated == nullptr ) { return; } + + // only animate the mirror if it's located on the same end of the vehicle as the active cab + auto const isactive { ( + ( ( pAnim->iNumber & 0xf ) >> 4 ) == ( MoverParameters->ActiveCab > 0 ? side::front : side::rear ) ? + 1.0 : + 0.0 ) }; + + if( pAnim->iNumber & 1 ) + pAnim->smAnimated->SetRotate( + float3( 0, 1, 0 ), + interpolate( 0.0, MoverParameters->MirrorMaxShift, dMirrorMoveR * isactive ) ); + else + pAnim->smAnimated->SetRotate( + float3( 0, 1, 0 ), + interpolate( 0.0, MoverParameters->MirrorMaxShift, dMirrorMoveL * isactive ) ); +} + +/* void TDynamicObject::UpdateLeverDouble(TAnim *pAnim) { // animacja gałki zależna od double pAnim->smAnimated->SetRotate(float3(1, 0, 0), pAnim->fSpeed * *pAnim->fDoubleBase); @@ -537,7 +589,7 @@ void TDynamicObject::UpdateLeverEnum(TAnim *pAnim) // pAnim->fParam[0]; - dodać lepkość pAnim->smAnimated->SetRotate(float3(1, 0, 0), pAnim->fParam[*pAnim->iIntBase]); }; - +*/ // sets light levels for registered interior sections void TDynamicObject::toggle_lights() { @@ -586,10 +638,14 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) if (ObjSqrDist < ( 400 * 400 ) ) // gdy bliżej niż 400m { - for (int i = 0; i < iAnimations; ++i) // wykonanie kolejnych animacji - if (ObjSqrDist < pAnimations[ i ].fMaxDist) - if (pAnimations[ i ].yUpdate) // jeśli zdefiniowana funkcja - pAnimations[ i ].yUpdate( &pAnimations[ i ] ); // aktualizacja animacji (położenia submodeli + for( auto &animation : pAnimations ) { + // wykonanie kolejnych animacji + if( ( ObjSqrDist < animation.fMaxDist ) + && ( animation.yUpdate ) ) { + // jeśli zdefiniowana funkcja aktualizacja animacji (położenia submodeli + animation.yUpdate( &animation ); + } + } if( ( mdModel != nullptr ) && ( ObjSqrDist < ( 50 * 50 ) ) ) { @@ -1655,13 +1711,6 @@ TDynamicObject::TDynamicObject() { // w MMD) // ustawienie liczby modeli animowanych podczas konstruowania obiektu a nie na 0 // prowadzi prosto do wysypów jeśli źle zdefiniowane mmd - iAnimType[ANIM_WHEELS] = 0; // 0-osie (8) - iAnimType[ANIM_DOORS] = 0; // 1-drzwi (8) - iAnimType[ANIM_LEVERS] = 0; // 2-wahacze (4) - np. nogi konia - iAnimType[ANIM_BUFFERS] = 0; // 3-zderzaki (4) - iAnimType[ANIM_BOOGIES] = 0; // 4-wózki (2) - iAnimType[ANIM_PANTS] = 0; // 5-pantografy (2) - iAnimType[ANIM_STEAMS] = 0; // 6-tłoki (napęd parowozu) iAnimations = 0; // na razie nie ma żadnego pAnimated = NULL; fShade = 0.0; // standardowe oświetlenie na starcie @@ -3603,6 +3652,60 @@ bool TDynamicObject::Update(double dt, double dt1) dDoorMoveR -= dt1 * MoverParameters->DoorCloseSpeed; dDoorMoveR = std::max( dDoorMoveR, 0.0 ); } + // doorsteps + if( ( dDoorstepMoveL < 1.0 ) + && ( true == MoverParameters->DoorLeftOpened ) ) { + dDoorstepMoveL = std::min( + 1.0, + dDoorstepMoveL + MoverParameters->PlatformSpeed * dt1 ); + } + if( ( dDoorstepMoveL > 0.0 ) + && ( false == MoverParameters->DoorLeftOpened ) ) { + dDoorstepMoveL = std::max( + 0.0, + dDoorstepMoveL - MoverParameters->PlatformSpeed * dt1 ); + } + if( ( dDoorstepMoveR < 1.0 ) + && ( true == MoverParameters->DoorRightOpened ) ) { + dDoorstepMoveR = std::min( + 1.0, + dDoorstepMoveR + MoverParameters->PlatformSpeed * dt1 ); + } + if( ( dDoorstepMoveR > 0.0 ) + && ( false == MoverParameters->DoorRightOpened ) ) { + dDoorstepMoveR = std::max( + 0.0, + dDoorstepMoveR - MoverParameters->PlatformSpeed * dt1 ); + } + // mirrors + if( MoverParameters->Vel > 5.0 ) { + // automatically fold mirrors when above velocity threshold + if( dMirrorMoveL > 0.0 ) { + dMirrorMoveL = std::max( + 0.0, + dMirrorMoveL - 1.0 * dt1 ); + } + if( dMirrorMoveR > 0.0 ) { + dMirrorMoveR = std::max( + 0.0, + dMirrorMoveR - 1.0 * dt1 ); + } + } + else { + // unfold mirror on the side with open doors, if not moving too fast + if( ( dMirrorMoveL < 1.0 ) + && ( true == MoverParameters->DoorLeftOpened ) ) { + dMirrorMoveL = std::min( + 1.0, + dMirrorMoveL + 1.0 * dt1 ); + } + if( ( dMirrorMoveR < 1.0 ) + && ( true == MoverParameters->DoorRightOpened ) ) { + dMirrorMoveR = std::min( + 1.0, + dMirrorMoveR + 1.0 * dt1 ); + } + } // compartment lights // if the vehicle has a controller, we base the light state on state of the controller otherwise we check the vehicle itself @@ -4338,43 +4441,23 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, { // kolejne liczby to ilość animacj, -1 to znacznik końca parser.getTokens( 1, false ); parser >> ile; // ilość danego typu animacji - // if (co==ANIM_PANTS) - // if (!Global.bLoadTraction) - // if (!DebugModeFlag) //w debugmode pantografy mają "niby działać" - // ile=0; //wyłączenie animacji pantografów - if (co < ANIM_TYPES) - if (ile >= 0) - { - iAnimType[co] = ile; // zapamiętanie - iAnimations += ile; // ogólna ilość animacji - } + if (ile >= 0) + { + iAnimType[co] = ile; // zapamiętanie + iAnimations += ile; // ogólna ilość animacji + } + else { + iAnimType[co] = 0; + } ++co; - } while (ile >= 0); //-1 to znacznik końca + } while ( (ile >= 0) && (co < ANIM_TYPES) ); //-1 to znacznik końca - while( co < ANIM_TYPES ) { - iAnimType[ co++ ] = 0; // zerowanie pozostałych - } - parser.getTokens(); parser >> token; // NOTE: should this be here? seems at best superfluous + parser.getTokens(); parser >> token; } - // WriteLog("Total animations: "+AnsiString(iAnimations)); } if( true == pAnimations.empty() ) { // Ra: tworzenie tabeli animacji, jeśli jeszcze nie było -/* - // disabled as default animation amounts are no longer supported - if( !iAnimations ) { - // jeśli nie podano jawnie, ile ma być animacji - iAnimations = 28; // tyle było kiedyś w każdym pojeździe (2 wiązary wypadły) - } -*/ - /* //pojazd może mieć pantograf do innych celów niż napęd - if (MoverParameters->EnginePowerSource.SourceType!=CurrentCollector) - {//nie będzie pantografów, to się trochę uprości - iAnimations-=iAnimType[ANIM_PANTS]; //domyślnie były 2 pantografy - iAnimType[ANIM_PANTS]=0; - } - */ pAnimations.resize( iAnimations ); int i, j, k = 0, sm = 0; for (j = 0; j < ANIM_TYPES; ++j) @@ -4787,23 +4870,6 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, parser >> fWahaczeAmp; } } - /* - else if (str == AnsiString("engineer:")) - { // nazwa submodelu maszynisty - str = Parser->GetNextSymbol(); - smMechanik0 = mdModel->GetFromName(str.c_str()); - if (!smMechanik0) - { // jak nie ma bez numerka, to może jest z - // numerkiem? - smMechanik0 = mdModel->GetFromName(AnsiString(str + "1").c_str()); - smMechanik1 = mdModel->GetFromName(AnsiString(str + "2").c_str()); - } - // aby dało się go obracać, musi mieć włączoną animację w T3D! - // if (!smMechanik1) //jeśli drugiego nie ma - // if (smMechanik0) //a jest pierwszy - // smMechanik0->WillBeAnimated(); //to będziemy go obracać - } - */ else if( token == "animdoorprefix:" ) { // nazwa animowanych drzwi @@ -4838,9 +4904,80 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, } pAnimations[i + j].iNumber = i; // parzyste działają inaczej niż nieparzyste pAnimations[i + j].fMaxDist = 300 * 300; // drzwi to z daleka widać +/* + // NOTE: no longer used pAnimations[i + j].fSpeed = Random(150); // oryginalny koncept z DoorSpeedFactor pAnimations[i + j].fSpeed = (pAnimations[i + j].fSpeed + 100) / 100; - // Ra: te współczynniki są bez sensu, bo modyfikują wektor przesunięcia +*/ + } + } + } + + else if( token == "animstepprefix:" ) { + // animated doorstep submodel name prefix + int i, j; + parser.getTokens(1, false); parser >> token; + for (i = 0, j = 0; i < ANIM_DOORSTEPS; ++i) + j += iAnimType[i]; // zliczanie wcześniejszych animacji + for (i = 0; i < iAnimType[ANIM_DOORSTEPS]; ++i) // liczba drzwi + { // NBMX wrzesien 2003: wyszukiwanie drzwi o nazwie str* + // ustalenie submodelu + asAnimName = token + std::to_string(i + 1); + pAnimations[i + j].smAnimated = mdModel->GetFromName(asAnimName); + if (pAnimations[i + j].smAnimated) + { //++iAnimatedDoors; + pAnimations[i + j].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu + switch (MoverParameters->PlatformOpenMethod) + { // od razu zapinamy potrzebny typ animacji + case 1: // shift + pAnimations[ i + j ].yUpdate = std::bind( &TDynamicObject::UpdatePlatformTranslate, this, std::placeholders::_1 ); + break; + case 2: // rotate + pAnimations[ i + j ].yUpdate = std::bind( &TDynamicObject::UpdatePlatformRotate, this, std::placeholders::_1 ); + break; + default: + break; + } + pAnimations[i + j].iNumber = i; // parzyste działają inaczej niż nieparzyste + pAnimations[i + j].fMaxDist = 150 * 150; // drzwi to z daleka widać +/* + // NOTE: no longer used + pAnimations[i + j].fSpeed = Random(150); // oryginalny koncept z DoorSpeedFactor + pAnimations[i + j].fSpeed = (pAnimations[i + j].fSpeed + 100) / 100; +*/ + } + } + } + + else if( token == "animmirrorprefix:" ) { + // animated mirror submodel name prefix + int i, j; + parser.getTokens( 1, false ); parser >> token; + for( i = 0, j = 0; i < ANIM_MIRRORS; ++i ) + j += iAnimType[ i ]; // zliczanie wcześniejszych animacji + for( i = 0; i < iAnimType[ ANIM_MIRRORS ]; ++i ) // liczba drzwi + { // NBMX wrzesien 2003: wyszukiwanie drzwi o nazwie str* + // ustalenie submodelu + asAnimName = token + std::to_string( i + 1 ); + pAnimations[ i + j ].smAnimated = mdModel->GetFromName( asAnimName ); + if( pAnimations[ i + j ].smAnimated ) { + pAnimations[ i + j ].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu + // od razu zapinamy potrzebny typ animacji + auto const offset { pAnimations[ i + j ].smAnimated->offset() }; + pAnimations[ i + j ].yUpdate = std::bind( &TDynamicObject::UpdateMirror, this, std::placeholders::_1 ); + // we don't expect more than 2-4 mirrors, so it should be safe to store submodel location (front/rear) in the higher bits + // parzyste działają inaczej niż nieparzyste + pAnimations[ i + j ].iNumber = + ( ( pAnimations[ i + j ].smAnimated->offset().z > 0 ? + side::front : + side::rear ) << 4 ) + + i; + pAnimations[ i + j ].fMaxDist = 150 * 150; // drzwi to z daleka widać +/* + // NOTE: no longer used + pAnimations[i + j].fSpeed = Random(150); // oryginalny koncept z DoorSpeedFactor + pAnimations[i + j].fSpeed = (pAnimations[i + j].fSpeed + 100) / 100; +*/ } } } diff --git a/DynObj.h b/DynObj.h index 3ab1373b..b2094cce 100644 --- a/DynObj.h +++ b/DynObj.h @@ -23,7 +23,6 @@ http://mozilla.org/MPL/2.0/. //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- -int const ANIM_TYPES = 7; // Ra: ilość typów animacji int const ANIM_WHEELS = 0; // koła int const ANIM_DOORS = 1; // drzwi int const ANIM_LEVERS = 2; // elementy obracane (wycieraczki, koła skrętne, przestawiacze, klocki ham.) @@ -31,6 +30,9 @@ int const ANIM_BUFFERS = 3; // elementy przesuwane (zderzaki) int const ANIM_BOOGIES = 4; // wózki (są skręcane w dwóch osiach) int const ANIM_PANTS = 5; // pantografy int const ANIM_STEAMS = 6; // napęd parowozu +int const ANIM_DOORSTEPS = 7; +int const ANIM_MIRRORS = 8; +int const ANIM_TYPES = 9; // Ra: ilość typów animacji class TAnim; //typedef void(__closure *TUpdate)(TAnim *pAnim); // typ funkcji aktualizującej położenie submodeli @@ -103,7 +105,14 @@ class TAnimPant class TAnim { // klasa animowanej części pojazdu (koła, drzwi, pantografy, burty, napęd parowozu, siłowniki // itd.) - public: +public: +// constructor + TAnim() = default; +// destructor + ~TAnim(); +// methods + int TypeSet( int i, int fl = 0 ); // ustawienie typu +// members union { TSubModel *smAnimated; // animowany submodel (jeśli tylko jeden, np. oś) @@ -125,16 +134,15 @@ class TAnim int *iIntBase; // jakiś int w fizyce }; // void _fastcall Update(); //wskaźnik do funkcji aktualizacji animacji - int iFlags; // flagi animacji + int iFlags{ 0 }; // flagi animacji float fMaxDist; // do jakiej odległości wykonywana jest animacja float fSpeed; // parametr szybkości animacji int iNumber; // numer kolejny obiektu - public: - TAnim(); - ~TAnim(); + TUpdate yUpdate; // metoda TDynamicObject aktualizująca animację - int TypeSet(int i, int fl = 0); // ustawienie typu +/* void Parovoz(); // wykonanie obliczeń animacji +*/ }; //--------------------------------------------------------------------------- @@ -215,16 +223,15 @@ public: *Material() const { return &m_materialdata; } // tymczasowo udostępnione do wyszukiwania drutu - int iAnimType[ ANIM_TYPES ]; // 0-osie,1-drzwi,2-obracane,3-zderzaki,4-wózki,5-pantografy,6-tłoki + std::array iAnimType{ 0 }; // 0-osie,1-drzwi,2-obracane,3-zderzaki,4-wózki,5-pantografy,6-tłoki private: int iAnimations; // liczba obiektów animujących -/* - TAnim *pAnimations; // obiekty animujące (zawierają wskaźnik do funkcji wykonującej animację) -*/ std::vector pAnimations; TSubModel ** pAnimated; // lista animowanych submodeli (może być ich więcej niż obiektów animujących) double dWheelAngle[3]; // kąty obrotu kół: 0=przednie toczne, 1=napędzające i wiązary, 2=tylne toczne +/* void UpdateNone(TAnim *pAnim){}; // animacja pusta (funkcje ustawiania submodeli, gdy blisko kamery) +*/ void UpdateAxle(TAnim *pAnim); // animacja osi void UpdateBoogie(TAnim *pAnim); // animacja wózka void UpdateDoorTranslate(TAnim *pAnim); // animacja drzwi - przesuw @@ -232,10 +239,15 @@ private: void UpdateDoorFold(TAnim *pAnim); // animacja drzwi - składanie void UpdateDoorPlug(TAnim *pAnim); // animacja drzwi - odskokowo-przesuwne void UpdatePant(TAnim *pAnim); // animacja pantografu + void UpdatePlatformTranslate(TAnim *pAnim); // doorstep animation, shift + void UpdatePlatformRotate(TAnim *pAnim); // doorstep animation, rotate + void UpdateMirror(TAnim *pAnim); // mirror animation +/* void UpdateLeverDouble(TAnim *pAnim); // animacja gałki zależna od double void UpdateLeverFloat(TAnim *pAnim); // animacja gałki zależna od float void UpdateLeverInt(TAnim *pAnim); // animacja gałki zależna od int (wartość) void UpdateLeverEnum(TAnim *pAnim); // animacja gałki zależna od int (lista kątów) +*/ void toggle_lights(); // switch light levels for registered interior sections private: // Ra: ciąg dalszy animacji, dopiero do ogarnięcia // ABuWozki 060504 @@ -257,6 +269,10 @@ private: double NoVoltTime; // czas od utraty zasilania double dDoorMoveL; // NBMX double dDoorMoveR; // NBMX + double dDoorstepMoveL{ 0.0 }; + double dDoorstepMoveR{ 0.0 }; + double dMirrorMoveL{ 0.0 }; + double dMirrorMoveR{ 0.0 }; TSubModel *smBrakeSet; // nastawa hamulca (wajcha) TSubModel *smLoadSet; // nastawa ładunku (wajcha) TSubModel *smWiper; // wycieraczka (poniekąd też wajcha) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 17bef5a5..4430987e 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -943,9 +943,10 @@ public: double DoorOpenSpeed = 1.0; double DoorCloseSpeed = 1.0; /*predkosc otwierania i zamykania w j.u. */ double DoorMaxShiftL = 0.5; double DoorMaxShiftR = 0.5; double DoorMaxPlugShift = 0.1;/*szerokosc otwarcia lub kat*/ int DoorOpenMethod = 2; /*sposob otwarcia - 1: przesuwne, 2: obrotowe, 3: trójelementowe*/ - double PlatformSpeed = 0.25; /*szybkosc stopnia*/ - double PlatformMaxShift = 0.5; /*wysuniecie stopnia*/ - int PlatformOpenMethod = 1; /*sposob animacji stopnia*/ + double PlatformSpeed = 0.5; /*szybkosc stopnia*/ + double PlatformMaxShift { 45.0 }; /*wysuniecie stopnia*/ + int PlatformOpenMethod { 2 }; /*sposob animacji stopnia*/ + double MirrorMaxShift { 90.0 }; bool ScndS = false; /*Czy jest bocznikowanie na szeregowej*/ double SpeedCtrlDelay = 2; /*opoznienie dzialania tempomatu z wybieralna predkoscia*/ /*--sekcja zmiennych*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index d098da3d..a85795e3 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1591,8 +1591,8 @@ void TMoverParameters::FuelPumpCheck( double const Timestep ) { FuelPump.is_enabled = ( dizel_startup || Mains ); } FuelPump.is_active = ( - ( true == FuelPump.is_enabled ) - && ( true == Battery ) ); + ( true == Battery ) + && ( true == FuelPump.is_enabled ) ); } // oil pump status update @@ -7863,8 +7863,8 @@ void TMoverParameters::LoadFIZ_Doors( std::string const &line ) { extract_value( DoorCloseSpeed, "CloseSpeed", line, "" ); extract_value( DoorMaxShiftL, "DoorMaxShiftL", line, "" ); extract_value( DoorMaxShiftR, "DoorMaxShiftR", line, "" ); + extract_value( DoorMaxPlugShift, "DoorMaxShiftPlug", line, "" ); - DoorOpenMethod = 2; //obrót, default std::string openmethod; extract_value( openmethod, "DoorOpenMethod", line, "" ); if( openmethod == "Shift" ) { DoorOpenMethod = 1; } //przesuw else if( openmethod == "Fold" ) { DoorOpenMethod = 3; } //3 submodele się obracają @@ -7876,11 +7876,11 @@ void TMoverParameters::LoadFIZ_Doors( std::string const &line ) { std::string doorblocked; extract_value( doorblocked, "DoorBlocked", line, "" ); DoorBlocked = ( doorblocked == "Yes" ); - extract_value( DoorMaxPlugShift, "DoorMaxShiftPlug", line, "" ); extract_value( PlatformSpeed, "PlatformSpeed", line, "" ); - extract_value( PlatformMaxShift, "PlatformMaxSpeed", line, "" ); + extract_value( PlatformMaxShift, "PlatformMaxShift", line, "" ); + + extract_value( MirrorMaxShift, "MirrorMaxShift", line, "" ); - PlatformOpenMethod = 2; // obrót, default std::string platformopenmethod; extract_value( platformopenmethod, "PlatformOpenMethod", line, "" ); if( platformopenmethod == "Shift" ) { PlatformOpenMethod = 1; } // przesuw } diff --git a/Model3d.cpp b/Model3d.cpp index 697c3552..f1fadfbe 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1222,12 +1222,16 @@ TSubModel::offset( float const Geometrytestoffsetthreshold ) const { bool TModel3d::LoadFromFile(std::string const &FileName, bool dynamic) { // wczytanie modelu z pliku +/* + // NOTE: disabled, this work is now done by the model manager std::string name = ToLower(FileName); // trim extension if needed if( name.rfind( '.' ) != std::string::npos ) { name.erase(name.rfind('.')); } +*/ + auto const name { FileName }; asBinary = name + ".e3d"; if (FileExists(asBinary)) @@ -1255,7 +1259,7 @@ bool TModel3d::LoadFromFile(std::string const &FileName, bool dynamic) Root ? (iSubModelsCount > 0) : false; // brak pliku albo problem z wczytaniem if (false == result) { - ErrorLog("Bad model: failed to load 3d model \"" + FileName + "\""); + ErrorLog("Bad model: failed to load 3d model \"" + name + "\""); } return result; }; diff --git a/Train.cpp b/Train.cpp index 2bfe85f2..bcba552d 100644 --- a/Train.cpp +++ b/Train.cpp @@ -5580,7 +5580,13 @@ TTrain::update_sounds( double const Deltatime ) { } if( fTachoCount > 3.f ) { - dsbHasler.play( sound_flags::exclusive | sound_flags::looping ); + auto const frequency { ( + true == dsbHasler.is_combined() ? + fTachoVelocity * 0.01 : + 1.0 ) }; + dsbHasler + .pitch( frequency ) + .play( sound_flags::exclusive | sound_flags::looping ); } else if( fTachoCount < 1.f ) { dsbHasler.stop(); @@ -6139,8 +6145,10 @@ TTrain::radio_message( sound_source *Message, int const Channel ) { // skip message playback if the radio isn't able to receive it return; } - auto const radiorange { 7500 * 7500 }; - if( glm::length2( Message->location() - glm::dvec3 { DynamicObject->GetPosition() } ) > radiorange ) { + auto const soundrange { Message->range() }; + if( ( soundrange > 0 ) + && ( glm::length2( Message->location() - glm::dvec3 { DynamicObject->GetPosition() } ) > ( soundrange * soundrange ) ) ) { + // skip message playback if the receiver is outside of the emitter's range return; } diff --git a/renderer.cpp b/renderer.cpp index 64f27146..b8366cf3 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1939,6 +1939,38 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator ++first; } +#ifdef EU07_USE_DEBUG_SOUNDEMITTERS + // sound emitters + if( DebugModeFlag ) { + switch( m_renderpass.draw_mode ) { + case rendermode::color: { + + ::glPushAttrib( GL_ENABLE_BIT ); + ::glDisable( GL_TEXTURE_2D ); + ::glColor3f( 0.36f, 0.75f, 0.35f ); + + for( auto const &audiosource : audio::renderer.m_sources ) { + + ::glPushMatrix(); + auto const position = audiosource.properties.location - m_renderpass.camera.position(); + ::glTranslated( position.x, position.y, position.z ); + + ::gluSphere( m_quadric, 0.1, 4, 2 ); + + ::glPopMatrix(); + } + + ::glPopAttrib(); + + break; + } + default: { + break; + } + } + } +#endif + } void @@ -2451,7 +2483,9 @@ opengl_renderer::Render( TSubModel *Submodel ) { // kąt większy niż maksymalny stożek swiatła float lightlevel = 1.f; // TODO, TBD: parameter to control light strength // view angle attenuation - float const anglefactor = ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( 1.0f - Submodel->fCosFalloffAngle ); + float const anglefactor = clamp( + ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( Submodel->fCosHotspotAngle - Submodel->fCosFalloffAngle ), + 0.f, 1.f ); // distance attenuation. NOTE: since it's fixed pipeline with built-in gamma correction we're using linear attenuation // we're capping how much effect the distance attenuation can have, otherwise the lights get too tiny at regular distances float const distancefactor = std::max( 0.5f, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / Submodel->fSquareMaxDist ); diff --git a/renderer.h b/renderer.h index 852cb70e..f9b0281e 100644 --- a/renderer.h +++ b/renderer.h @@ -24,6 +24,7 @@ http://mozilla.org/MPL/2.0/. //#define EU07_USE_DEBUG_SHADOWMAP //#define EU07_USE_DEBUG_CABSHADOWMAP //#define EU07_USE_DEBUG_CAMERA +//#define EU07_USE_DEBUG_SOUNDEMITTERS struct opengl_light : public basic_light { diff --git a/scene.cpp b/scene.cpp index 9fef581d..be68f31f 100644 --- a/scene.cpp +++ b/scene.cpp @@ -20,6 +20,7 @@ http://mozilla.org/MPL/2.0/. namespace scene { std::string const EU07_FILEEXTENSION_REGION { ".sbt" }; +std::uint32_t const EU07_FILEVERSION_REGION { MAKE_ID4( 'S', 'B', 'T', 1 ) }; // legacy method, finds and assigns traction piece to specified pantograph of provided vehicle void @@ -883,7 +884,7 @@ basic_region::serialize( std::string const &Scenariofile ) const { // region file version 1 // header: EU07SBT + version (0-255) sn_utils::ls_uint32( output, MAKE_ID4( 'E', 'U', '0', '7' ) ); - sn_utils::ls_uint32( output, MAKE_ID4( 'S', 'B', 'T', 1 ) ); + sn_utils::ls_uint32( output, EU07_FILEVERSION_REGION ); // sections // TBD, TODO: build table of sections and file offsets, if we postpone section loading until they're within range std::uint32_t sectioncount { 0 }; @@ -928,7 +929,7 @@ basic_region::deserialize( std::string const &Scenariofile ) { uint32_t headertype { sn_utils::ld_uint32( input ) }; if( ( headermain != MAKE_ID4( 'E', 'U', '0', '7' ) - || ( headertype != MAKE_ID4( 'S', 'B', 'T', 1 ) ) ) ) { + || ( headertype != EU07_FILEVERSION_REGION ) ) ) { // wrong file type WriteLog( "Bad file: \"" + filename + "\" is of either unrecognized type or version" ); return false; diff --git a/simulation.cpp b/simulation.cpp index 978ad2ea..4499ba68 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -713,11 +713,13 @@ state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpa auto *instance = new TAnimModel( Nodedata ); instance->RaAnglesSet( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii - if( false == instance->Load( &Input, false ) ) { + if( instance->Load( &Input, false ) ) { + instance->location( transform( location, Scratchpad ) ); + } + else { // model nie wczytał się - ignorowanie node SafeDelete( instance ); } - instance->location( transform( location, Scratchpad ) ); return instance; } diff --git a/sound.cpp b/sound.cpp index 2c1360f3..d634b385 100644 --- a/sound.cpp +++ b/sound.cpp @@ -838,6 +838,13 @@ sound_source::location() const { + m_owner->VectorFront() * m_offset.z }; } +// returns defined range of the sound +float const +sound_source::range() const { + + return m_range; +} + void sound_source::update_counter( sound_handle const Sound, int const Value ) { diff --git a/sound.h b/sound.h index 6ea03fda..55a0a1ed 100644 --- a/sound.h +++ b/sound.h @@ -111,6 +111,9 @@ public: // returns location of the sound source in simulation region space glm::dvec3 const location() const; + // returns defined range of the sound + float const + range() const; // members float m_amplitudefactor { 1.f }; // helper, value potentially used by gain calculation diff --git a/uilayer.cpp b/uilayer.cpp index 0898df17..3e81b53f 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -434,7 +434,7 @@ ui_layer::update() { auto const train { Global.pWorld->train() }; if( ( train != nullptr ) && ( train->Dynamic() == vehicle ) ) { - uitextline2 += " R: " + std::to_string( train->RadioChannel() ); + uitextline2 += ( vehicle->MoverParameters->Radio ? " R: " : " r: " ) + std::to_string( train->RadioChannel() ); } /* uitextline2 += diff --git a/version.h b/version.h index 43026737..96a56e56 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 18 -#define VERSION_MINOR 429 +#define VERSION_MINOR 505 #define VERSION_REVISION 0