diff --git a/Driver.cpp b/Driver.cpp index 73989938..a96a60b2 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1534,12 +1534,15 @@ TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche, LogFile.open( std::string( "physicslog/" + VehicleName + ".dat" ), std::ios::in | std::ios::out | std::ios::trunc ); #if LOGPRESS == 0 - LogFile << std::string( " Time [s] Velocity [m/s] Acceleration [m/ss] Coupler.Dist[m] " - "Coupler.Force[N] TractionForce [kN] FrictionForce [kN] " - "BrakeForce [kN] BrakePress [MPa] PipePress [MPa] " - "MotorCurrent [A] MCP SCP BCP LBP DmgFlag Command CVal1 CVal2" ) - .c_str() + LogFile + << "Time[s] Velocity[m/s] Acceleration[m/ss] " + << "Coupler.Dist[m] Coupler.Force[N] TractionForce[kN] FrictionForce[kN] BrakeForce[kN] " + << "BrakePress[MPa] PipePress[MPa] MotorCurrent[A] " + << "MCP SCP BCP LBP Direction Command CVal1 CVal2 " + << "Security Wheelslip " + << "EngineTemp[Deg] OilTemp[Deg] WaterTemp[Deg] WaterAuxTemp[Deg]" << "\r\n"; + LogFile << std::fixed << std::setprecision( 4 ); #endif #if LOGPRESS == 1 LogFile << string( "t\tVel\tAcc\tPP\tVVP\tBP\tBVP\tCVP" ).c_str() << "\n"; @@ -3538,20 +3541,40 @@ void TController::PhysicsLog() if (LogFile.is_open()) { #if LOGPRESS == 0 - LogFile << ElapsedTime << " " << fabs(11.31 * mvOccupied->WheelDiameter * mvOccupied->nrot) - << " "; - LogFile << mvControlling->AccS << " " << mvOccupied->Couplers[1].Dist << " " - << mvOccupied->Couplers[1].CForce << " "; - LogFile << mvOccupied->Ft << " " << mvOccupied->Ff << " " << mvOccupied->Fb << " " - << mvOccupied->BrakePress << " "; - LogFile << mvOccupied->PipePress << " " << mvControlling->Im << " " - << int(mvControlling->MainCtrlPos) << " "; - LogFile << int(mvControlling->ScndCtrlPos) << " " << int(mvOccupied->BrakeCtrlPos) - << " " << int(mvOccupied->LocalBrakePos) << " "; - LogFile << int(mvControlling->ActiveDir) << " " << mvOccupied->CommandIn.Command.c_str() - << " " << mvOccupied->CommandIn.Value1 << " "; - LogFile << mvOccupied->CommandIn.Value2 << " " << int(mvControlling->SecuritySystem.Status) - << " " << int(mvControlling->SlippingWheels) << "\r\n"; +/* +<< "Time[s] Velocity[m/s] Acceleration[m/ss] " +<< "Coupler.Dist[m] Coupler.Force[N] TractionForce[kN] FrictionForce[kN] BrakeForce[kN] " +<< "BrakePress[MPa] PipePress[MPa] MotorCurrent[A] " +<< "MCP SCP BCP LBP DmgFlag Command CVal1 CVal2 " +<< "EngineTemp[Deg] OilTemp[Deg] WaterTemp[Deg] WaterAuxTemp[Deg]" +*/ + LogFile + << ElapsedTime << " " + << fabs(11.31 * mvOccupied->WheelDiameter * mvOccupied->nrot) << " " + << mvControlling->AccS << " " + << mvOccupied->Couplers[1].Dist << " " + << mvOccupied->Couplers[1].CForce << " " + << mvOccupied->Ft << " " + << mvOccupied->Ff << " " + << mvOccupied->Fb << " " + << mvOccupied->BrakePress << " " + << mvOccupied->PipePress << " " + << mvControlling->Im << " " + << int(mvControlling->MainCtrlPos) << " " + << int(mvControlling->ScndCtrlPos) << " " + << int(mvOccupied->BrakeCtrlPos) << " " + << int(mvOccupied->LocalBrakePos) << " " + << int(mvControlling->ActiveDir) << " " + << ( mvOccupied->CommandIn.Command.empty() ? "none" : mvOccupied->CommandIn.Command.c_str() ) << " " + << mvOccupied->CommandIn.Value1 << " " + << mvOccupied->CommandIn.Value2 << " " + << int(mvControlling->SecuritySystem.Status) << " " + << int(mvControlling->SlippingWheels) << " " + << mvControlling->dizel_heat.Ts << " " + << mvControlling->dizel_heat.To << " " + << mvControlling->dizel_heat.temperatura1 << " " + << mvControlling->dizel_heat.temperatura2 + << "\r\n"; #endif #if LOGPRESS == 1 LogFile << ElapsedTime << "\t" << fabs(11.31 * mvOccupied->WheelDiameter * mvOccupied->nrot) diff --git a/DynObj.cpp b/DynObj.cpp index d304c765..58c14472 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -42,6 +42,16 @@ std::string const TDynamicObject::MED_labels[] = { bool TDynamicObject::bDynamicRemove { false }; +// helper, locates submodel with specified name in specified 3d model; returns: pointer to the submodel, or null +TSubModel * +GetSubmodelFromName( TModel3d * const Model, std::string const Name ) { + + return ( + Model ? + Model->GetFromName( Name ) : + nullptr ); +} + //--------------------------------------------------------------------------- void TAnimPant::AKP_4E() { // ustawienie wymiarów dla pantografu AKP-4E @@ -3633,14 +3643,31 @@ bool TDynamicObject::Update(double dt, double dt1) } // NBMX Obsluga drzwi, MC: zuniwersalnione + // TODO: fully generalized door assembly if( ( dDoorMoveL < MoverParameters->DoorMaxShiftL ) && ( true == MoverParameters->DoorLeftOpened ) ) { - dDoorMoveL += dt1 * MoverParameters->DoorOpenSpeed; - dDoorMoveL = std::min( dDoorMoveL, MoverParameters->DoorMaxShiftL ); + // open left door + if( ( MoverParameters->TrainType == dt_EZT ) + || ( MoverParameters->TrainType == dt_DMU ) ) { + // multi-unit vehicles typically open door only after unfolding the doorstep + if( ( MoverParameters->PlatformMaxShift == 0.0 ) // no wait if no doorstep + || ( MoverParameters->PlatformOpenMethod == 2 ) // no wait for rotating doorstep + || ( dDoorstepMoveL == 1.0 ) ) { + dDoorMoveL = std::min( + MoverParameters->DoorMaxShiftL, + dDoorMoveL + MoverParameters->DoorOpenSpeed * dt1 ); + } + } + else { + dDoorMoveL = std::min( + MoverParameters->DoorMaxShiftL, + dDoorMoveL + MoverParameters->DoorOpenSpeed * dt1 ); + } DoorDelayL = 0.f; } if( ( dDoorMoveL > 0.0 ) && ( false == MoverParameters->DoorLeftOpened ) ) { + // close left door DoorDelayL += dt1; if( DoorDelayL > MoverParameters->DoorCloseDelay ) { dDoorMoveL -= dt1 * MoverParameters->DoorCloseSpeed; @@ -3649,12 +3676,28 @@ bool TDynamicObject::Update(double dt, double dt1) } if( ( dDoorMoveR < MoverParameters->DoorMaxShiftR ) && ( true == MoverParameters->DoorRightOpened ) ) { - dDoorMoveR += dt1 * MoverParameters->DoorOpenSpeed; - dDoorMoveR = std::min( dDoorMoveR, MoverParameters->DoorMaxShiftR ); + // open right door + if( ( MoverParameters->TrainType == dt_EZT ) + || ( MoverParameters->TrainType == dt_DMU ) ) { + // multi-unit vehicles typically open door only after unfolding the doorstep + if( ( MoverParameters->PlatformMaxShift == 0.0 ) // no wait if no doorstep + || ( MoverParameters->PlatformOpenMethod == 2 ) // no wait for rotating doorstep + || ( dDoorstepMoveR == 1.0 ) ) { + dDoorMoveR = std::min( + MoverParameters->DoorMaxShiftR, + dDoorMoveR + MoverParameters->DoorOpenSpeed * dt1 ); + } + } + else { + dDoorMoveR = std::min( + MoverParameters->DoorMaxShiftR, + dDoorMoveR + MoverParameters->DoorOpenSpeed * dt1 ); + } DoorDelayR = 0.f; } if( ( dDoorMoveR > 0.0 ) && ( false == MoverParameters->DoorRightOpened ) ) { + // close right door DoorDelayR += dt1; if( DoorDelayR > MoverParameters->DoorCloseDelay ) { dDoorMoveR -= dt1 * MoverParameters->DoorCloseSpeed; @@ -3664,6 +3707,7 @@ bool TDynamicObject::Update(double dt, double dt1) // doorsteps if( ( dDoorstepMoveL < 1.0 ) && ( true == MoverParameters->DoorLeftOpened ) ) { + // unfold left doorstep dDoorstepMoveL = std::min( 1.0, dDoorstepMoveL + MoverParameters->PlatformSpeed * dt1 ); @@ -3671,12 +3715,25 @@ bool TDynamicObject::Update(double dt, double dt1) if( ( dDoorstepMoveL > 0.0 ) && ( false == MoverParameters->DoorLeftOpened ) && ( DoorDelayL > MoverParameters->DoorCloseDelay ) ) { - dDoorstepMoveL = std::max( - 0.0, - dDoorstepMoveL - MoverParameters->PlatformSpeed * dt1 ); + // fold left doorstep + if( ( MoverParameters->TrainType == dt_EZT ) + || ( MoverParameters->TrainType == dt_DMU ) ) { + // multi-unit vehicles typically fold the doorstep only after closing the door + if( dDoorMoveL == 0.0 ) { + dDoorstepMoveL = std::max( + 0.0, + dDoorstepMoveL - MoverParameters->PlatformSpeed * dt1 ); + } + } + else { + dDoorstepMoveL = std::max( + 0.0, + dDoorstepMoveL - MoverParameters->PlatformSpeed * dt1 ); + } } if( ( dDoorstepMoveR < 1.0 ) && ( true == MoverParameters->DoorRightOpened ) ) { + // unfold right doorstep dDoorstepMoveR = std::min( 1.0, dDoorstepMoveR + MoverParameters->PlatformSpeed * dt1 ); @@ -3684,9 +3741,21 @@ bool TDynamicObject::Update(double dt, double dt1) if( ( dDoorstepMoveR > 0.0 ) && ( false == MoverParameters->DoorRightOpened ) && ( DoorDelayR > MoverParameters->DoorCloseDelay ) ) { - dDoorstepMoveR = std::max( - 0.0, - dDoorstepMoveR - MoverParameters->PlatformSpeed * dt1 ); + // fold right doorstep + if( ( MoverParameters->TrainType == dt_EZT ) + || ( MoverParameters->TrainType == dt_DMU ) ) { + // multi-unit vehicles typically fold the doorstep only after closing the door + if( dDoorMoveR == 0.0 ) { + dDoorstepMoveR = std::max( + 0.0, + dDoorstepMoveR - MoverParameters->PlatformSpeed * dt1 ); + } + } + else { + dDoorstepMoveR = std::max( + 0.0, + dDoorstepMoveR - MoverParameters->PlatformSpeed * dt1 ); + } } // mirrors if( MoverParameters->Vel > 5.0 ) { @@ -4045,10 +4114,13 @@ void TDynamicObject::RenderSounds() { } } // NBMX Obsluga drzwi, MC: zuniwersalnione + // TODO: fully generalized door assembly if( true == MoverParameters->DoorLeftOpened ) { // open left door // door sounds - if( dDoorMoveL < MoverParameters->DoorMaxShiftL ) { + // due to potential wait for the doorstep we play the sound only during actual animation + if( ( dDoorMoveL > 0.0 ) + && ( dDoorMoveL < MoverParameters->DoorMaxShiftL ) ) { for( auto &door : m_doorsounds ) { if( door.rsDoorClose.offset().x > 0.f ) { // determine left side doors from their offset @@ -4095,7 +4167,9 @@ void TDynamicObject::RenderSounds() { if( true == MoverParameters->DoorRightOpened ) { // open right door // door sounds - if( dDoorMoveR < MoverParameters->DoorMaxShiftR ) { + // due to potential wait for the doorstep we play the sound only during actual animation + if( ( dDoorMoveR > 0.0 ) + && ( dDoorMoveR < MoverParameters->DoorMaxShiftR ) ) { for( auto &door : m_doorsounds ) { if( door.rsDoorClose.offset().x < 0.f ) { // determine right side doors from their offset @@ -4358,7 +4432,7 @@ void TDynamicObject::RenderSounds() { // wczytywanie pliku z danymi multimedialnymi (dzwieki) void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, std::string ReplacableSkin ) { - double dSDist; + replace_slashes( BaseDir ); Global.asCurrentDynamicPath = BaseDir; std::string asFileName = BaseDir + TypeName + ".mmd"; std::string asLoadName; @@ -4406,10 +4480,9 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, asModel = BaseDir + asModel; // McZapkie 2002-07-20: dynamics maja swoje modele w dynamics/basedir Global.asCurrentTexturePath = BaseDir; // biezaca sciezka do tekstur to dynamic/... mdModel = TModelsManager::GetModel(asModel, true); - assert( mdModel != nullptr ); // TODO: handle this more gracefully than all going to shit if (ReplacableSkin != "none") { - std::string nowheretexture = TextureTest(Global.asCurrentTexturePath + "nowhere"); // na razie prymitywnie + std::string nowheretexture = TextureTest( Global.asCurrentTexturePath + "nowhere" ); // na razie prymitywnie if( false == nowheretexture.empty() ) { m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Material( nowheretexture ); } @@ -4423,9 +4496,8 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, int skinindex = 0; std::string texturename; nameparser >> texturename; while( ( texturename != "" ) && ( skinindex < 4 ) ) { - erase_extension( texturename ); std::replace(texturename.begin(), texturename.end(), '\\', '/'); - m_materialdata.replacable_skins[ skinindex + 1 ] = GfxRenderer.Fetch_Material( Global.asCurrentTexturePath + texturename ); + m_materialdata.replacable_skins[ skinindex + 1 ] = GfxRenderer.Fetch_Material( texturename ); ++skinindex; texturename = ""; nameparser >> texturename; } @@ -4436,7 +4508,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, erase_extension( ReplacableSkin ); int skinindex = 0; do { - material_handle material = GfxRenderer.Fetch_Material( Global.asCurrentTexturePath + ReplacableSkin + "," + std::to_string( skinindex + 1 ), true ); + material_handle material = GfxRenderer.Fetch_Material( ReplacableSkin + "," + std::to_string( skinindex + 1 ), true ); if( material == null_handle ) { break; } @@ -4446,12 +4518,12 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, m_materialdata.multi_textures = skinindex; if( m_materialdata.multi_textures == 0 ) { // zestaw nie zadziałał, próbujemy normanie - m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( Global.asCurrentTexturePath + ReplacableSkin ); + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( ReplacableSkin ); } } } else { - m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( Global.asCurrentTexturePath + ReplacableSkin ); + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( ReplacableSkin ); } if( GfxRenderer.Material( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) { // tekstura -1 z kanałem alfa - nie renderować w cyklu nieprzezroczystych @@ -4579,7 +4651,13 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, // ABu: wnetrze lowpoly parser.getTokens(); parser >> asModel; - std::replace(asModel.begin(), asModel.end(), '\\', '/'); + + replace_slashes( asModel ); + if( asModel[ 0 ] == '/' ) { + // filename can potentially begin with a slash, and we don't need it + asModel.erase( 0, 1 ); + } + asModel = BaseDir + asModel; // McZapkie-200702 - dynamics maja swoje modele w dynamic/basedir Global.asCurrentTexturePath = BaseDir; // biezaca sciezka do tekstur to dynamic/... mdLowPolyInt = TModelsManager::GetModel(asModel, true); @@ -4589,7 +4667,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, // Ra 15-01: gałka nastawy hamulca parser.getTokens(); parser >> asAnimName; - smBrakeMode = mdModel->GetFromName(asAnimName); + smBrakeMode = GetSubmodelFromName( mdModel, asAnimName ); // jeszcze wczytać kąty obrotu dla poszczególnych ustawień } @@ -4597,7 +4675,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, // Ra 15-01: gałka nastawy hamulca parser.getTokens(); parser >> asAnimName; - smLoadMode = mdModel->GetFromName(asAnimName); + smLoadMode = GetSubmodelFromName( mdModel, asAnimName ); // jeszcze wczytać kąty obrotu dla poszczególnych ustawień } @@ -4609,7 +4687,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, for (i = 0; i < iAnimType[ANIM_WHEELS]; ++i) // liczba osi { // McZapkie-050402: wyszukiwanie kol o nazwie str* asAnimName = token + std::to_string(i + 1); - pAnimations[i].smAnimated = mdModel->GetFromName(asAnimName); // ustalenie submodelu + pAnimations[i].smAnimated = GetSubmodelFromName( mdModel, asAnimName ); if (pAnimations[i].smAnimated) { //++iAnimatedAxles; pAnimations[i].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu @@ -4672,7 +4750,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, for (int i = 0; i < iAnimType[ANIM_PANTS]; i++) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* asAnimName = token + std::to_string(i + 1); - sm = mdModel->GetFromName(asAnimName); + sm = GetSubmodelFromName( mdModel, asAnimName ); pants[i].smElement[0] = sm; // jak NULL, to nie będzie animowany if (sm) { // w EP09 wywalało się tu z powodu NULL @@ -4681,27 +4759,22 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, // m(3)[1]=m[3][1]+0.054; //w górę o wysokość ślizgu (na razie tak) if ((mdModel->Flags() & 0x8000) == 0) // jeśli wczytano z T3D m.InitialRotate(); // może być potrzebny dodatkowy obrót, jeśli wczytano z T3D, tzn. przed wykonaniem Init() - pants[i].fParamPants->vPos.z = - m[3][0]; // przesunięcie w bok (asymetria) - pants[i].fParamPants->vPos.y = - m[3][1]; // przesunięcie w górę odczytane z modelu + pants[i].fParamPants->vPos.z = m[3][0]; // przesunięcie w bok (asymetria) + pants[i].fParamPants->vPos.y = m[3][1]; // przesunięcie w górę odczytane z modelu if ((sm = pants[i].smElement[0]->ChildGet()) != NULL) { // jeśli ma potomny, można policzyć długość (odległość potomnego od osi obrotu) m = float4x4(*sm->GetMatrix()); // wystarczyłby wskaźnik, nie trzeba kopiować // może trzeba: pobrać macierz dolnego ramienia, wyzerować przesunięcie, przemnożyć przez macierz górnego pants[i].fParamPants->fHoriz = -fabs(m[3][1]); - pants[i].fParamPants->fLenL1 = - hypot(m[3][1], m[3][2]); // po osi OX nie potrzeba - pants[i].fParamPants->fAngleL0 = - atan2(fabs(m[3][2]), fabs(m[3][1])); + pants[i].fParamPants->fLenL1 = hypot(m[3][1], m[3][2]); // po osi OX nie potrzeba + pants[i].fParamPants->fAngleL0 = atan2(fabs(m[3][2]), fabs(m[3][1])); // if (pants[i].fParamPants->fAngleL0fAngleL0+=M_PI; //gdyby w odwrotną stronę wyszło // if // ((pants[i].fParamPants->fAngleL0<0.03)||(pants[i].fParamPants->fAngleL0>0.09)) // //normalnie ok. 0.05 // pants[i].fParamPants->fAngleL0=pants[i].fParamPants->fAngleL; - pants[i].fParamPants->fAngleL = pants[i].fParamPants->fAngleL0; // początkowy kąt dolnego - // ramienia + pants[i].fParamPants->fAngleL = pants[i].fParamPants->fAngleL0; // początkowy kąt dolnego ramienia if ((sm = sm->ChildGet()) != NULL) { // jeśli dalej jest ślizg, można policzyć długość górnego ramienia m = float4x4(*sm->GetMatrix()); // wystarczyłby wskaźnik, @@ -4727,12 +4800,9 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, float det = Det(m); if (std::fabs(det - 1.0) < 0.001) // dopuszczamy 1 promil błędu na skalowaniu ślizgu { // skalowanie jest w normie, można pobrać wymiary z modelu - pants[i].fParamPants->fHeight = - sm->MaxY(m); // przeliczenie maksimum wysokości wierzchołków względem macierzy - pants[i].fParamPants->fHeight -= - m[3][1]; // odjęcie wysokości pivota ślizgu - pants[i].fParamPants->vPos.x = - m[3][2]; // przy okazji odczytać z modelu pozycję w długości + pants[i].fParamPants->fHeight = sm->MaxY(m); // przeliczenie maksimum wysokości wierzchołków względem macierzy + pants[i].fParamPants->fHeight -= m[3][1]; // odjęcie wysokości pivota ślizgu + pants[i].fParamPants->vPos.x = m[3][2]; // przy okazji odczytać z modelu pozycję w długości // ErrorLog("Model OK: "+asModel+", // height="+pants[i].fParamPants->fHeight); // ErrorLog("Model OK: "+asModel+", @@ -4740,8 +4810,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, } else { // gdy ktoś przesadził ze skalowaniem - pants[i].fParamPants->fHeight = - 0.0; // niech będzie odczyt z pantfactors: + pants[i].fParamPants->fHeight = 0.0; // niech będzie odczyt z pantfactors: ErrorLog( "Bad model: " + asModel + ", scale of " + (sm->pName) + " is " + std::to_string(100.0 * det) + "%", logtype::model ); @@ -4749,8 +4818,11 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, } } } - else - ErrorLog("Bad model: " + asFileName + " - missed submodel " + asAnimName, logtype::model); // brak ramienia + else { + // brak ramienia + pants[ i ].fParamPants->fHeight = 0.0; // niech będzie odczyt z pantfactors: + ErrorLog( "Bad model: " + asFileName + " - missed submodel " + asAnimName, logtype::model ); + } } } @@ -4763,7 +4835,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* asAnimName = token + std::to_string( i + 1 ); - sm = mdModel->GetFromName( asAnimName ); + sm = GetSubmodelFromName( mdModel, asAnimName ); pants[ i ].smElement[ 1 ] = sm; // jak NULL, to nie będzie animowany if( sm ) { // w EP09 wywalało się tu z powodu NULL sm->WillBeAnimated(); @@ -4792,43 +4864,49 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, else if( token == "animpantrg1prefix:" ) { // prefiks ramion górnych 1 parser.getTokens(); parser >> token; - if( pants ) { - for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { + if( pants ) { + for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* - asAnimName = token + std::to_string( i + 1 ); - pants[ i ].smElement[ 2 ] = mdModel->GetFromName( asAnimName ); - pants[ i ].smElement[ 2 ]->WillBeAnimated(); - } + asAnimName = token + std::to_string( i + 1 ); + pants[ i ].smElement[ 2 ] = GetSubmodelFromName( mdModel, asAnimName ); + if( pants[ i ].smElement[ 2 ] ) { + pants[ i ].smElement[ 2 ]->WillBeAnimated(); + } } + } } else if( token == "animpantrg2prefix:" ) { // prefiks ramion górnych 2 - parser.getTokens(); parser >> token; - if( pants ) { - for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { + parser.getTokens(); parser >> token; + if( pants ) { + for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* - asAnimName = token + std::to_string( i + 1 ); - pants[ i ].smElement[ 3 ] = mdModel->GetFromName( asAnimName ); - pants[ i ].smElement[ 3 ]->WillBeAnimated(); - } + asAnimName = token + std::to_string( i + 1 ); + pants[ i ].smElement[ 3 ] = GetSubmodelFromName( mdModel, asAnimName ); + if( pants[ i ].smElement[ 3 ] ) { + pants[ i ].smElement[ 3 ]->WillBeAnimated(); + } } + } } else if( token == "animpantslprefix:" ) { // prefiks ślizgaczy parser.getTokens(); parser >> token; - if( pants ) { - for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { + if( pants ) { + for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* - asAnimName = token + std::to_string( i + 1 ); - pants[ i ].smElement[ 4 ] = mdModel->GetFromName( asAnimName ); - pants[ i ].smElement[ 4 ]->WillBeAnimated(); - pants[ i ].yUpdate = std::bind( &TDynamicObject::UpdatePant, this, std::placeholders::_1 ); - pants[ i ].fMaxDist = 300 * 300; // nie podnosić w większej odległości - pants[ i ].iNumber = i; - } + asAnimName = token + std::to_string( i + 1 ); + pants[ i ].smElement[ 4 ] = GetSubmodelFromName( mdModel, asAnimName ); + if( pants[ i ].smElement[ 4 ] ) { + pants[ i ].smElement[ 4 ]->WillBeAnimated(); + pants[ i ].yUpdate = std::bind( &TDynamicObject::UpdatePant, this, std::placeholders::_1 ); + pants[ i ].fMaxDist = 300 * 300; // nie podnosić w większej odległości + pants[ i ].iNumber = i; + } } + } } else if( token == "pantfactors:" ) { @@ -4853,34 +4931,38 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, if( pants ) { for( int i = 0; i < iAnimType[ ANIM_PANTS ]; ++i ) { // przepisanie współczynników do pantografów (na razie nie będzie lepiej) - pants[ i ].fParamPants->fAngleL = pants[ i ].fParamPants->fAngleL0; // początkowy kąt dolnego ramienia - pants[ i ].fParamPants->fAngleU = pants[ i ].fParamPants->fAngleU0; // początkowy kąt + auto &pantograph { *(pants[ i ].fParamPants) }; + + pantograph.fAngleL = pantograph.fAngleL0; // początkowy kąt dolnego ramienia + pantograph.fAngleU = pantograph.fAngleU0; // początkowy kąt // pants[i].fParamPants->PantWys=1.22*sin(pants[i].fParamPants->fAngleL)+1.755*sin(pants[i].fParamPants->fAngleU); // //wysokość początkowa // pants[i].fParamPants->PantWys=1.176289*sin(pants[i].fParamPants->fAngleL)+1.724482197*sin(pants[i].fParamPants->fAngleU); // //wysokość początkowa - if( pants[ i ].fParamPants->fHeight == 0.0 ) // gdy jest nieprawdopodobna wartość (np. nie znaleziony ślizg) + if( pantograph.fHeight == 0.0 ) // gdy jest nieprawdopodobna wartość (np. nie znaleziony ślizg) { // gdy pomiary modelu nie udały się, odczyt podanych parametrów z MMD - pants[ i ].fParamPants->vPos.x = + pantograph.vPos.x = ( i & 1 ) ? pant2x : pant1x; - pants[ i ].fParamPants->fHeight = + pantograph.fHeight = ( i & 1 ) ? pant2h : pant1h; // wysokość ślizgu jest zapisana w MMD } - pants[ i ].fParamPants->PantWys = - pants[ i ].fParamPants->fLenL1 * sin( pants[ i ].fParamPants->fAngleL ) + - pants[ i ].fParamPants->fLenU1 * sin( pants[ i ].fParamPants->fAngleU ) + - pants[ i ].fParamPants->fHeight; // wysokość początkowa - // pants[i].fParamPants->vPos.y=panty-panth-pants[i].fParamPants->PantWys; - // //np. 4.429-0.097=4.332=~4.335 - // pants[i].fParamPants->vPos.z=0; //niezerowe dla pantografów - // asymetrycznych - pants[ i ].fParamPants->PantTraction = pants[ i ].fParamPants->PantWys; + pantograph.PantWys = + pantograph.fLenL1 * sin( pantograph.fAngleL ) + + pantograph.fLenU1 * sin( pantograph.fAngleU ) + + pantograph.fHeight; // wysokość początkowa + // pants[i].fParamPants->vPos.y=panty-panth-pants[i].fParamPants->PantWys; //np. 4.429-0.097=4.332=~4.335 + if( pantograph.vPos.y == 0.0 ) { + // crude fallback, place the pantograph(s) atop of the vehicle-sized box + pantograph.vPos.y = MoverParameters->Dim.H - pantograph.fHeight - pantograph.PantWys; + } + // pants[i].fParamPants->vPos.z=0; //niezerowe dla pantografów asymetrycznych + pantograph.PantTraction = pantograph.PantWys; // połowa szerokości ślizgu; jest w "Power: CSW=" - pants[ i ].fParamPants->fWidth = 0.5 * MoverParameters->EnginePowerSource.CollectorParameters.CSW; + pantograph.fWidth = 0.5 * MoverParameters->EnginePowerSource.CollectorParameters.CSW; // create sound emitters for the pantograph m_pantographsounds.emplace_back(); @@ -4956,11 +5038,13 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, // prefiks wahaczy parser.getTokens(); parser >> token; asAnimName = ""; - for (int i = 1; i <= 4; i++) - { // McZapkie-050402: wyszukiwanie max 4 wahaczy o nazwie str* - asAnimName = token + std::to_string(i); - smWahacze[i - 1] = mdModel->GetFromName(asAnimName); - smWahacze[i - 1]->WillBeAnimated(); + for( int i = 1; i <= 4; ++i ) { + // McZapkie-050402: wyszukiwanie max 4 wahaczy o nazwie str* + asAnimName = token + std::to_string( i ); + smWahacze[ i - 1 ] = GetSubmodelFromName( mdModel, asAnimName ); + if( smWahacze[ i - 1 ] ) { + smWahacze[ i - 1 ]->WillBeAnimated(); + } } parser.getTokens(); parser >> token; if( token == "pendulumamplitude:" ) { @@ -4979,7 +5063,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, { // NBMX wrzesien 2003: wyszukiwanie drzwi o nazwie str* // ustalenie submodelu asAnimName = token + std::to_string(i + 1); - pAnimations[i + j].smAnimated = mdModel->GetFromName(asAnimName); + pAnimations[ i + j ].smAnimated = GetSubmodelFromName( mdModel, asAnimName ); if (pAnimations[i + j].smAnimated) { //++iAnimatedDoors; pAnimations[i + j].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu @@ -5021,7 +5105,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, { // NBMX wrzesien 2003: wyszukiwanie drzwi o nazwie str* // ustalenie submodelu asAnimName = token + std::to_string(i + 1); - pAnimations[i + j].smAnimated = mdModel->GetFromName(asAnimName); + pAnimations[i + j].smAnimated = GetSubmodelFromName( mdModel, asAnimName ); if (pAnimations[i + j].smAnimated) { //++iAnimatedDoors; pAnimations[i + j].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu @@ -5057,7 +5141,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, { // NBMX wrzesien 2003: wyszukiwanie drzwi o nazwie str* // ustalenie submodelu asAnimName = token + std::to_string( i + 1 ); - pAnimations[ i + j ].smAnimated = mdModel->GetFromName( asAnimName ); + pAnimations[ i + j ].smAnimated = GetSubmodelFromName( mdModel, asAnimName ); if( pAnimations[ i + j ].smAnimated ) { pAnimations[ i + j ].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu // od razu zapinamy potrzebny typ animacji @@ -5092,6 +5176,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, parser.getTokens(); parser >> token; if( token == "wheel_clatter:" ){ // polozenia osi w/m srodka pojazdu + double dSDist; parser.getTokens( 1, false ); parser >> dSDist; while( ( ( token = parser.getToken() ) != "" ) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index ecf35362..36ef0e90 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -947,7 +947,7 @@ public: int DoorOpenMethod = 2; /*sposob otwarcia - 1: przesuwne, 2: obrotowe, 3: trójelementowe*/ float DoorCloseDelay { 0.f }; // delay (in seconds) before the door begin closing, once conditions to close are met double PlatformSpeed = 0.5; /*szybkosc stopnia*/ - double PlatformMaxShift { 45.0 }; /*wysuniecie stopnia*/ + double PlatformMaxShift { 0.0 }; /*wysuniecie stopnia*/ int PlatformOpenMethod { 2 }; /*sposob animacji stopnia*/ double MirrorMaxShift { 90.0 }; bool ScndS = false; /*Czy jest bocznikowanie na szeregowej*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 2d50436b..e06fd08d 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -4680,10 +4680,11 @@ double TMoverParameters::TractionForce(double dt) case DieselEngine: { - EnginePower = dmoment * enrot; - if (MainCtrlPos > 1) - dmoment -= - dizel_Mstand * (0.2 * enrot / dizel_nmax); // dodatkowe opory z powodu sprezarki} + EnginePower = ( 2 * dizel_Mstand + dmoment ) * enrot * ( 2.0 * M_PI / 1000.0 ); + if( MainCtrlPos > 1 ) { + // dodatkowe opory z powodu sprezarki} + dmoment -= dizel_Mstand * ( 0.2 * enrot / dizel_nmax ); + } Mm = dmoment; //bylo * dizel_engage Mw = Mm * dtrans; // dmoment i dtrans policzone przy okazji enginerotation Fw = Mw * 2.0 / WheelDiameter / NPoweredAxles; diff --git a/Model3d.cpp b/Model3d.cpp index b4325661..f2d4ecf8 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -331,12 +331,14 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic m_material = -4; iFlags |= (Opacity < 1.0) ? 8 : 0x10; // zmienna tekstura 4 } - else - { // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur + else { Name_Material(material); +/* if( material.find_first_of( "/" ) == material.npos ) { + // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur material.insert( 0, Global.asCurrentTexturePath ); } +*/ m_material = GfxRenderer.Fetch_Material( material ); // renderowanie w cyklu przezroczystych tylko jeśli: // 1. Opacity=0 (przejściowo <1, czy tam <100) oraz @@ -1672,9 +1674,11 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector *t, auto const materialindex = static_cast( iTexture ); if( materialindex < t->size() ) { m_materialname = t->at( materialindex ); +/* if( m_materialname.find_last_of( "/" ) == std::string::npos ) { m_materialname = Global.asCurrentTexturePath + m_materialname; } +*/ m_material = GfxRenderer.Fetch_Material( m_materialname ); if( ( iFlags & 0x30 ) == 0 ) { // texture-alpha based fallback if for some reason we don't have opacity flag set yet diff --git a/Texture.cpp b/Texture.cpp index f1d07a70..8f798c14 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -818,6 +818,11 @@ texture_manager::create( std::string Filename, bool const Loadnow ) { erase_extension( Filename ); + if( Filename[ 0 ] == '/' ) { + // filename can potentially begin with a slash, and we don't need it + Filename.erase( 0, 1 ); + } + std::vector extensions{ { ".dds" }, { ".tga" }, { ".png" }, { ".bmp" }, { ".ext" } }; // try to locate requested texture in the databank diff --git a/Train.cpp b/Train.cpp index 44ef2df1..d498c511 100644 --- a/Train.cpp +++ b/Train.cpp @@ -336,9 +336,7 @@ std::vector const TTrain::fPress_labels = { }; TTrain::TTrain() { -/* - Universal4Active = false; -*/ + ShowNextCurrent = false; // McZapkie-240302 - przyda sie do tachometru fTachoVelocity = 0; @@ -433,10 +431,10 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) PyObject *TTrain::GetTrainState() { - auto const &mover = DynamicObject->MoverParameters; + auto const *mover = DynamicObject->MoverParameters; auto *dict = PyDict_New(); if( ( dict == nullptr ) - || ( mover == nullptr ) ) { + || ( mover == nullptr ) ) { return nullptr; } @@ -759,7 +757,8 @@ void TTrain::OnCommand_secondcontrollerincrease( TTrain *Train, command_data con if( Command.action != GLFW_RELEASE ) { // on press or hold - if( Train->mvControlled->ShuntMode ) { + if( ( Train->mvControlled->EngineType == DieselElectric ) + && ( true == Train->mvControlled->ShuntMode ) ) { Train->mvControlled->AnPos = clamp( Train->mvControlled->AnPos + 0.025, 0.0, 1.0 ); @@ -774,7 +773,13 @@ void TTrain::OnCommand_secondcontrollerincreasefast( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // on press or hold - Train->mvControlled->IncScndCtrl( 2 ); + if( ( Train->mvControlled->EngineType == DieselElectric ) + && ( true == Train->mvControlled->ShuntMode ) ) { + Train->mvControlled->AnPos = 1.0; + } + else { + Train->mvControlled->IncScndCtrl( 2 ); + } } } @@ -820,12 +825,15 @@ void TTrain::OnCommand_secondcontrollerdecrease( TTrain *Train, command_data con if( Command.action != GLFW_RELEASE ) { // on press or hold - if( Train->mvControlled->ShuntMode ) { + if( ( Train->mvControlled->EngineType == DieselElectric ) + && ( true == Train->mvControlled->ShuntMode ) ) { Train->mvControlled->AnPos = clamp( Train->mvControlled->AnPos - 0.025, 0.0, 1.0 ); } - Train->mvControlled->DecScndCtrl( 1 ); + else { + Train->mvControlled->DecScndCtrl( 1 ); + } } } @@ -833,7 +841,13 @@ void TTrain::OnCommand_secondcontrollerdecreasefast( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // on press or hold - Train->mvControlled->DecScndCtrl( 2 ); + if( ( Train->mvControlled->EngineType == DieselElectric ) + && ( true == Train->mvControlled->ShuntMode ) ) { + Train->mvControlled->AnPos = 0.0; + } + else { + Train->mvControlled->DecScndCtrl( 2 ); + } } } @@ -5978,6 +5992,11 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) // bieżąca sciezka do tekstur to dynamic/... Global.asCurrentTexturePath = DynamicObject->asBaseDir; // szukaj kabinę jako oddzielny model + // name can contain leading slash, erase it to avoid creation of double slashes when the name is combined with current directory + replace_slashes( token ); + if( token[ 0 ] == '/' ) { + token.erase( 0, 1 ); + } TModel3d *kabina = TModelsManager::GetModel(DynamicObject->asBaseDir + token, true); // z powrotem defaultowa sciezka do tekstur Global.asCurrentTexturePath = szTexturePath; diff --git a/Train.h b/Train.h index 000ef5f2..616db454 100644 --- a/Train.h +++ b/Train.h @@ -312,11 +312,11 @@ class TTrain static void OnCommand_generictoggle( TTrain *Train, command_data const &Command ); // members - TDynamicObject *DynamicObject; // przestawia zmiana pojazdu [F5] - TMoverParameters *mvControlled; // człon, w którym sterujemy silnikiem - TMoverParameters *mvOccupied; // człon, w którym sterujemy hamulcem - TMoverParameters *mvSecond; // drugi człon (ET40, ET41, ET42, ukrotnienia) - TMoverParameters *mvThird; // trzeci człon (SN61) + TDynamicObject *DynamicObject { nullptr }; // przestawia zmiana pojazdu [F5] + TMoverParameters *mvControlled { nullptr }; // człon, w którym sterujemy silnikiem + TMoverParameters *mvOccupied { nullptr }; // człon, w którym sterujemy hamulcem + TMoverParameters *mvSecond { nullptr }; // drugi człon (ET40, ET41, ET42, ukrotnienia) + TMoverParameters *mvThird { nullptr }; // trzeci człon (SN61) // helper variable, to prevent immediate switch between closing and opening line breaker circuit int m_linebreakerstate { 0 }; // 0: open, 1: closed, 2: freshly closed (and yes this is awful way to go about it) static const commandhandler_map m_commandhandlers; diff --git a/material.cpp b/material.cpp index 5829dfc7..f4d194bc 100644 --- a/material.cpp +++ b/material.cpp @@ -115,6 +115,11 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) { erase_extension( filename ); + if( filename[ 0 ] == '/' ) { + // filename can potentially begin with a slash, and we don't need it + filename.erase( 0, 1 ); + } + filename += ".mat"; // try to locate requested material in the databank @@ -124,7 +129,6 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) { } // if this fails, try to look for it on disk opengl_material material; - material.name = filename; auto const disklookup = find_on_disk( filename ); if( disklookup != "" ) { cParser materialparser( disklookup, cParser::buffer_FILE ); @@ -132,6 +136,7 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) { // deserialization failed but the .mat file does exist, so we give up at this point return null_handle; } + material.name = disklookup; } else { // if there's no .mat file, this could be legacy method of referring just to diffuse texture directly, make a material out of it in such case @@ -140,6 +145,10 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) { // if there's also no texture, give up return null_handle; } + // use texture path and name to tell the newly created materials apart + filename = GfxRenderer.Texture( material.texture1 ).name; + erase_extension( filename ); + material.name = filename + ".mat"; material.has_alpha = GfxRenderer.Texture( material.texture1 ).has_alpha; } @@ -149,21 +158,23 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) { return handle; }; -// checks whether specified texture is in the texture bank. returns texture id, or npos. +// checks whether specified material is in the material bank. returns handle to the material, or a null handle material_handle material_manager::find_in_databank( std::string const &Materialname ) const { - auto lookup = m_materialmappings.find( Materialname ); - if( lookup != m_materialmappings.end() ) { - return lookup->second; - } - // jeszcze próba z dodatkową ścieżką - lookup = m_materialmappings.find( szTexturePath + Materialname ); + std::vector filenames { + Global.asCurrentTexturePath + Materialname, + Materialname, + szTexturePath + Materialname }; - return ( - lookup != m_materialmappings.end() ? - lookup->second : - null_handle ); + for( auto const &filename : filenames ) { + auto const lookup { m_materialmappings.find( filename ) }; + if( lookup != m_materialmappings.end() ) { + return lookup->second; + } + } + + return null_handle; } // checks whether specified file exists. @@ -172,6 +183,7 @@ std::string material_manager::find_on_disk( std::string const &Materialname ) const { return( + FileExists( Global.asCurrentTexturePath + Materialname ) ? Global.asCurrentTexturePath + Materialname : FileExists( Materialname ) ? Materialname : FileExists( szTexturePath + Materialname ) ? szTexturePath + Materialname : "" ); diff --git a/uilayer.cpp b/uilayer.cpp index 705f5b74..a1c5369c 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -747,7 +747,8 @@ ui_layer::update() { + " dist: " + to_string( vehicle->MoverParameters->DistCounter, 2 ) + " km" + "; pos: [" + to_string( vehicle->GetPosition().x, 2 ) + ", " + to_string( vehicle->GetPosition().y, 2 ) + ", " + to_string( vehicle->GetPosition().z, 2 ) + "]" + ", PM=" + to_string( vehicle->MoverParameters->WheelFlat, 1 ) - + " mm; enrot=" + to_string( vehicle->MoverParameters->enrot * 60, 0 ) + + " mm; enpwr=" + to_string( vehicle->MoverParameters->EnginePower, 1 ) + + "; enrot=" + to_string( vehicle->MoverParameters->enrot * 60, 0 ) + " tmrot=" + to_string( std::abs( vehicle->MoverParameters->nrot ) * vehicle->MoverParameters->Transmision.Ratio * 60, 0 ) + "; ventrot=" + to_string( vehicle->MoverParameters->RventRot * 60, 1 ) + "; fanrot=" + to_string( vehicle->MoverParameters->dizel_heat.rpmw, 1 ) + ", " + to_string( vehicle->MoverParameters->dizel_heat.rpmw2, 1 ); @@ -756,7 +757,7 @@ ui_layer::update() { "HamZ=" + to_string( vehicle->MoverParameters->fBrakeCtrlPos, 2 ) + "; HamP=" + std::to_string( vehicle->MoverParameters->LocalBrakePos ) + "/" + to_string( vehicle->MoverParameters->LocalBrakePosA, 2 ) + "; NasJ=" + std::to_string( vehicle->MoverParameters->MainCtrlPos ) + "(" + std::to_string( vehicle->MoverParameters->MainCtrlActualPos ) + ")" - + ( vehicle->MoverParameters->ShuntMode ? + + ( ( vehicle->MoverParameters->ShuntMode && vehicle->MoverParameters->EngineType == DieselElectric ) ? "; NasB=" + to_string( vehicle->MoverParameters->AnPos, 2 ) : "; NasB=" + std::to_string( vehicle->MoverParameters->ScndCtrlPos ) + "(" + std::to_string( vehicle->MoverParameters->ScndCtrlActualPos ) + ")" ) + "; I=" + @@ -965,7 +966,7 @@ ui_layer::render() { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT ); + glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT ); // blendfunc included since 3rd party gui doesn't play nice glDisable( GL_LIGHTING ); glDisable( GL_DEPTH_TEST ); glDisable( GL_ALPHA_TEST ); @@ -988,7 +989,7 @@ ui_layer::render() { render_panels(); render_tooltip(); - glPopAttrib(); + glPopAttrib(); } void diff --git a/version.h b/version.h index 8b50ec17..596a7ac7 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION_INFO "M7 19.05.2018, based on tmj 45e7513" +#define VERSION_INFO "M7 22.05.2018, based on tmj aa520aa3"