diff --git a/Driver.cpp b/Driver.cpp index 28a7adc3..fbf9c981 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -4592,7 +4592,7 @@ TController::UpdateSituation(double dt) { } // uśrednione napięcie sieci: przy spadku poniżej wartości minimalnej opóźnić rozruch o losowy czas - fVoltage = 0.5 * (fVoltage + mvControlling->PantographVoltage); + fVoltage = 0.5 * (fVoltage + std::max( mvControlling->GetAnyTrainsetVoltage(), mvControlling->PantographVoltage ) ); if( fVoltage < mvControlling->EnginePowerSource.CollectorParameters.MinV ) { // gdy rozłączenie WS z powodu niskiego napięcia if( fActionTime >= 0 ) { diff --git a/DynObj.cpp b/DynObj.cpp index 80e0049a..ca51b6de 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -4185,9 +4185,9 @@ void TDynamicObject::RenderSounds() { && ( MoverParameters->Vel > 5.0 ) ) { // scale volume with curve radius and vehicle speed volume = - MoverParameters->AccN * MoverParameters->AccN + MoverParameters->AccN // * MoverParameters->AccN * interpolate( - 0.0, 1.0, + 0.5, 1.0, clamp( MoverParameters->Vel / 40.0, 0.0, 1.0 ) ) diff --git a/Globals.cpp b/Globals.cpp index 598c5c12..add04da6 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -293,15 +293,23 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens(1, false); int size; Parser >> size; - if (size <= 64) { iMaxTextureSize = 64; } - else if (size <= 128) { iMaxTextureSize = 128; } - else if (size <= 256) { iMaxTextureSize = 256; } - else if (size <= 512) { iMaxTextureSize = 512; } - else if (size <= 1024) { iMaxTextureSize = 1024; } - else if (size <= 2048) { iMaxTextureSize = 2048; } - else if (size <= 4096) { iMaxTextureSize = 4096; } - else if (size <= 8192) { iMaxTextureSize = 8192; } - else { iMaxTextureSize = 16384; } + auto p2size { 64 }; // start with 64px + while( (p2size <= 16384 ) && ( p2size <= size ) ) { + iMaxTextureSize = p2size; + p2size = p2size << 1; + } + } + else if (token == "maxcabtexturesize") + { + // wymuszenie przeskalowania tekstur + Parser.getTokens( 1, false ); + int size; + Parser >> size; + auto p2size { 64 }; // start with 64px + while( ( p2size <= 16384 ) && ( p2size <= size ) ) { + iMaxCabTextureSize = p2size; + p2size = p2size << 1; + } } else if (token == "movelight") { diff --git a/Globals.h b/Globals.h index 58d8ff0f..2924e542 100644 --- a/Globals.h +++ b/Globals.h @@ -53,6 +53,7 @@ struct global_settings { std::string asCurrentSceneryPath{ "scenery/" }; std::string asCurrentTexturePath{ szTexturePath }; std::string asCurrentDynamicPath; + int CurrentMaxTextureSize{ 4096 }; bool GfxFramebufferSRGB { true }; // settings // filesystem @@ -129,6 +130,7 @@ struct global_settings { float AnisotropicFiltering{ 8.f }; // requested level of anisotropic filtering. TODO: move it to renderer object float FieldOfView{ 45.f }; // vertical field of view for the camera. TODO: move it to the renderer GLint iMaxTextureSize{ 4096 }; // maksymalny rozmiar tekstury + GLint iMaxCabTextureSize{ 4096 }; // largest allowed texture in vehicle cab int iMultisampling{ 2 }; // tryb antyaliasingu: 0=brak,1=2px,2=4px,3=8px,4=16px bool bSmoothTraction{ true }; // wygładzanie drutów starym sposobem float SplineFidelity{ 1.f }; // determines segment size during conversion of splines to geometry diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 1b382294..657c11b7 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1544,7 +1544,7 @@ public: bool RunInternalCommand(); void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation); bool CabActivisation(void); - bool CabDeactivisation(void); + bool CabDeactivisation( bool const Force = false ); /*! funkcje zwiekszajace/zmniejszajace nastawniki*/ /*! glowny nastawnik:*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 73af24de..4137ef13 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1728,7 +1728,7 @@ void TMoverParameters::HeatingCheck( double const Timestep ) { break; } case TPowerSource::Main: { - voltage = ( true == Mains ? PantographVoltage : 0.0 ); + voltage = ( true == Mains ? std::max( GetAnyTrainsetVoltage(), PantographVoltage ) : 0.0 ); break; } default: { @@ -2355,11 +2355,11 @@ bool TMoverParameters::CabActivisation(void) // Q: 20160710 // wyłączenie rozrządu // ************************************************************************************************* -bool TMoverParameters::CabDeactivisation(void) +bool TMoverParameters::CabDeactivisation( bool const Force ) { bool OK = false; - OK = (CabNo == ActiveCab); // o ile obsada jest w kabinie ze sterowaniem + OK = Force || (CabNo == ActiveCab); // o ile obsada jest w kabinie ze sterowaniem if (OK) { CabNo = 0; @@ -3736,7 +3736,7 @@ void TMoverParameters::CompressorCheck(double dt) if( CompressorPower == 2 ) { CompressorAllow = ConverterAllow; } - + // TODO: clean up compressor CompressorFlag state code, large parts are cloned and an utter mess if (MaxCompressorF - MinCompressorF < 0.0001) { // TODO: investigate purpose of this branch and whether it can be removed as it duplicates later code if( ( true == CompressorAllow ) @@ -3802,10 +3802,9 @@ void TMoverParameters::CompressorCheck(double dt) CompressorFlag = ( ( ( CompressorAllow ) || ( CompressorStart == start_t::automatic ) ) && ( CompressorAllowLocal ) - && ( Mains ) - && ( ( ConverterFlag ) - || ( CompressorPower == 0 ) - || ( CompressorPower == 3 ) ) ); + && ( CompressorPower == 0 ? Mains : + CompressorPower == 3 ? Mains : + ConverterFlag ) ); if( Compressor > MaxCompressorF ) { // wyłącznik ciśnieniowy jest niezależny od sposobu zasilania @@ -3876,12 +3875,11 @@ void TMoverParameters::CompressorCheck(double dt) } else { CompressorFlag = ( - ( ( CompressorAllow ) || ( CompressorStart == start_t::automatic ) ) - && ( CompressorAllowLocal ) - && ( Mains ) - && ( ( ConverterFlag ) - || ( CompressorPower == 0 ) - || ( CompressorPower == 3 ) ) ); + ( ( CompressorAllow ) || ( CompressorStart == start_t::automatic ) ) + && ( CompressorAllowLocal ) + && ( CompressorPower == 0 ? Mains : + CompressorPower == 3 ? Mains : + ConverterFlag ) ); } // NOTE: crude way to enforce simultaneous activation of compressors in multi-unit setups diff --git a/Texture.cpp b/Texture.cpp index 23138417..4f2b0288 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -473,7 +473,7 @@ opengl_texture::load_DDS() { int blockSize = ( data_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16 ); int offset = 0; - while( ( data_width > Global.iMaxTextureSize ) || ( data_height > Global.iMaxTextureSize ) ) { + while( ( data_width > Global.CurrentMaxTextureSize ) || ( data_height > Global.CurrentMaxTextureSize ) ) { // pomijanie zbyt dużych mipmap, jeśli wymagane jest ograniczenie rozmiaru offset += ( ( data_width + 3 ) / 4 ) * ( ( data_height + 3 ) / 4 ) * blockSize; data_width /= 2; @@ -714,7 +714,7 @@ opengl_texture::load_TGA() { } downsize( GL_BGRA ); - if( ( data_width > Global.iMaxTextureSize ) || ( data_height > Global.iMaxTextureSize ) ) { + if( ( data_width > Global.CurrentMaxTextureSize ) || ( data_height > Global.CurrentMaxTextureSize ) ) { // for non-square textures there's currently possibility the scaling routine will have to abort // before it gets all work done data_state = resource_state::failed; @@ -1054,7 +1054,7 @@ opengl_texture::set_filtering() const { void opengl_texture::downsize( GLuint const Format ) { - while( ( data_width > Global.iMaxTextureSize ) || ( data_height > Global.iMaxTextureSize ) ) { + while( ( data_width > Global.CurrentMaxTextureSize ) || ( data_height > Global.CurrentMaxTextureSize ) ) { // scale down the base texture, if it's larger than allowed maximum // NOTE: scaling is uniform along both axes, meaning non-square textures can drop below the maximum // TODO: replace with proper scaling function once we have image middleware in place diff --git a/Train.cpp b/Train.cpp index f43bce83..e145db79 100644 --- a/Train.cpp +++ b/Train.cpp @@ -439,8 +439,15 @@ TTrain::TTrain() { bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) { // powiązanie ręcznego sterowania kabiną z pojazdem if( NewDynamicObject->Mechanik == nullptr ) { + /* ErrorLog( "Bad config: can't take control of inactive vehicle \"" + NewDynamicObject->asName + "\"" ); return false; + */ + auto const activecab { ( + NewDynamicObject->MoverParameters->ActiveCab > 0 ? "1" : + NewDynamicObject->MoverParameters->ActiveCab < 0 ? "2" : + "p" ) }; + NewDynamicObject->create_controller( activecab, NewDynamicObject->ctOwner != nullptr ); } DynamicSet(NewDynamicObject); @@ -452,11 +459,20 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) fMainRelayTimer = 0; // Hunter, do k...y nędzy, ustawiaj wartości początkowe zmiennych! - if( false == LoadMMediaFile( DynamicObject->asBaseDir + DynamicObject->MoverParameters->TypeName + ".mmd" ) ) { - return false; - } + iCabn = ( + mvOccupied->ActiveCab < 0 ? 0 : + mvOccupied->ActiveCab > 0 ? 1 : + 2 ); + + { + Global.CurrentMaxTextureSize = Global.iMaxCabTextureSize; + auto const result{ LoadMMediaFile( DynamicObject->asBaseDir + DynamicObject->MoverParameters->TypeName + ".mmd" ) }; + Global.CurrentMaxTextureSize = Global.iMaxTextureSize; + if( false == result ) { + return false; + } + } - iCabn = 0; // Ra: taka proteza - przesłanie kierunku do członów connected if (mvControlled->ActiveDir > 0) { // było do przodu @@ -4434,14 +4450,33 @@ void TTrain::OnCommand_generictoggle( TTrain *Train, command_data const &Command return; } */ + if( Command.action == GLFW_PRESS ) { - // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( item.GetDesiredValue() < 0.5 ) { + + if( item.type() == TGaugeType::push ) { + // impulse switch // turn on // visual feedback item.UpdateValue( 1.0 ); } else { + // two-state switch + if( item.GetDesiredValue() < 0.5 ) { + // turn on + // visual feedback + item.UpdateValue( 1.0 ); + } + else { + // turn off + // visual feedback + item.UpdateValue( 0.0 ); + } + } + } + else if( Command.action == GLFW_RELEASE ) { + + if( item.type() == TGaugeType::push ) { + // impulse switch // turn off // visual feedback item.UpdateValue( 0.0 ); diff --git a/audiorenderer.cpp b/audiorenderer.cpp index 5f9ed856..54b9addd 100644 --- a/audiorenderer.cpp +++ b/audiorenderer.cpp @@ -15,6 +15,8 @@ http://mozilla.org/MPL/2.0/. #include "Camera.h" #include "Logs.h" #include "utilities.h" +#include "simulation.h" +#include "Train.h" namespace audio { @@ -333,6 +335,7 @@ openal_renderer::update( double const Deltatime ) { ::alListenerfv( AL_ORIENTATION, reinterpret_cast( orientation ) ); // velocity if( Deltatime > 0 ) { +/* glm::dvec3 const listenerposition { Global.pCamera.Pos }; glm::dvec3 const listenermovement { listenerposition - m_listenerposition }; m_listenerposition = listenerposition; @@ -340,6 +343,40 @@ openal_renderer::update( double const Deltatime ) { glm::length( listenermovement ) < 1000.0 ? // large jumps are typically camera changes limit_velocity( listenermovement / Deltatime ) : glm::vec3() ); +*/ + auto cameramove{ glm::dvec3{ Global.pCamera.Pos - m_camerapos} }; + // intercept sudden user-induced camera jumps... + // ...from free fly mode change + if( m_freeflymode != FreeFlyModeFlag ) { + m_freeflymode = FreeFlyModeFlag; + if( true == m_freeflymode ) { + // cache last precipitation vector in the cab + m_cabcameramove = m_cameramove; + // don't carry previous precipitation vector to a new unrelated location + m_cameramove = glm::dvec3{ 0.0 }; + } + else { + // restore last cached precipitation vector + m_cameramove = m_cabcameramove; + } + cameramove = glm::dvec3{ 0.0 }; + } + // ...from jump between cab and window/mirror view + if( m_windowopen != Global.CabWindowOpen ) { + m_windowopen = Global.CabWindowOpen; + cameramove = glm::dvec3{ 0.0 }; + } + // ... from cab change + if( ( simulation::Train != nullptr ) && ( simulation::Train->iCabn != m_activecab ) ) { + m_activecab = simulation::Train->iCabn; + cameramove = glm::dvec3{ 0.0 }; + } + // ... from camera jump to another location + if( glm::length( cameramove ) > 100.0 ) { + cameramove = glm::dvec3{ 0.0 }; + } + m_listenervelocity = limit_velocity( cameramove / Deltatime ); + ::alListenerfv( AL_VELOCITY, reinterpret_cast( glm::value_ptr( m_listenervelocity ) ) ); } diff --git a/audiorenderer.h b/audiorenderer.h index 260d0850..3d577eb8 100644 --- a/audiorenderer.h +++ b/audiorenderer.h @@ -147,8 +147,16 @@ private: ALCdevice * m_device { nullptr }; ALCcontext * m_context { nullptr }; bool m_ready { false }; // renderer is initialized and functional +/* glm::dvec3 m_listenerposition; +*/ glm::vec3 m_listenervelocity; + glm::dvec3 m_camerapos{ 0.0 }; + glm::dvec3 m_cameramove{ 0.0 }; + glm::dvec3 m_cabcameramove{ 0.0 }; + bool m_freeflymode{ true }; + bool m_windowopen{ true }; + int m_activecab{ 0 }; buffer_manager m_buffers; // TBD: list of sources as vector, sorted by distance, for openal implementations with limited number of active sources? diff --git a/drivermode.cpp b/drivermode.cpp index fb0b6807..801c26bf 100644 --- a/drivermode.cpp +++ b/drivermode.cpp @@ -188,6 +188,14 @@ driver_mode::update() { if( Global.changeDynObj ) { // ABu zmiana pojazdu - przejście do innego ChangeDynamic(); + // move inside, but only if the human is in charge (otherwise we'll get pulled in when ai switches cabs) + if( ( simulation::Train != nullptr ) + && ( simulation::Train->Dynamic() != nullptr ) + && ( simulation::Train->Dynamic()->Mechanik != nullptr ) + && ( simulation::Train->Dynamic()->Mechanik->AIControllFlag == false ) + && ( true == FreeFlyModeFlag ) ) { + InOutKey(); + } } if( simulation::Train != nullptr ) { @@ -708,7 +716,6 @@ driver_mode::OnKeyDown(int cKey) { } break; } - case GLFW_KEY_F4: { if( Global.shiftState ) { ExternalView(); } // with Shift, cycle through external views @@ -722,46 +729,76 @@ driver_mode::OnKeyDown(int cKey) { break; } - TDynamicObject *tmp = std::get( simulation::Region->find_vehicle( Global.pCamera.Pos, 50, true, false ) ); + TDynamicObject *targetvehicle = std::get( simulation::Region->find_vehicle( Global.pCamera.Pos, 50, false, false ) ); - if( tmp != nullptr ) { + if( targetvehicle != nullptr ) { if( ( true == DebugModeFlag ) - || ( tmp->MoverParameters->Vel <= 5.0 ) ) { + || ( targetvehicle->MoverParameters->Vel <= 5.0 ) ) { // works always in debug mode, or for stopped/slow moving vehicles otherwise - if( simulation::Train ) { // jeśli mielismy pojazd - if( simulation::Train->Dynamic()->Mechanik ) { // na skutek jakiegoś błędu może czasem zniknąć - if( ( tmp->ctOwner == simulation::Train->Dynamic()->Mechanik ) - && ( true == Global.ctrlState ) ) { - // if the vehicle we left to the ai controlled the vehicle we're about to take over - // put the ai we left in charge of our old vehicle to sleep - // TODO: remove ctrl key mode once manual cab (de)activation is in place - simulation::Train->Dynamic()->Mechanik->primary( false ); - simulation::Train->Dynamic()->Mechanik->action() = TAction::actSleep; - simulation::Train->Dynamic()->MoverParameters->CabDeactivisation(); - } - simulation::Train->Dynamic()->Mechanik->TakeControl( true ); // oddajemy dotychczasowy AI - } - } - if( simulation::Train == nullptr ) { simulation::Train = new TTrain(); // jeśli niczym jeszcze nie jeździlismy } - if( simulation::Train->Init( tmp ) ) { - // przejmujemy sterowanie - if( true == Global.ctrlState ) { - // make sure we can take over the consist - // TODO: remove ctrl key mode once manual cab (de)activation is in place - simulation::Train->Dynamic()->Mechanik->primary( true ); - simulation::Train->Dynamic()->MoverParameters->CabActivisation(); + if( simulation::Train->Dynamic() != nullptr ) { + // jeśli mielismy pojazd + if( simulation::Train->Dynamic()->Mechanik ) { // na skutek jakiegoś błędu może czasem zniknąć + auto const *currentvehicle { simulation::Train->Dynamic() }; + auto const sameconsist { + ( targetvehicle->ctOwner == currentvehicle->Mechanik ) + || ( targetvehicle->ctOwner == currentvehicle->ctOwner ) }; + auto const isincharge { currentvehicle->Mechanik->primary() }; + auto const aidriveractive { currentvehicle->Mechanik->AIControllFlag }; + + if( !sameconsist && isincharge ) { + // oddajemy dotychczasowy AI + simulation::Train->Dynamic()->Mechanik->TakeControl( true ); + } + + if( ( !sameconsist ) // we leave behind an ai driver which should be preserved + || ( aidriveractive ) // we want to preserve existing ai driver + || ( targetvehicle->Mechanik != nullptr ) ) { // .changedynobj swaps drivers but we want a takeover not a swap + + if( sameconsist && !aidriveractive ) { + // we will be taking over controller in the target vehicle, so get rid of the old one + // unless it's an active ai in which case leave it running + SafeDelete( simulation::Train->Dynamic()->Mechanik ); + } + // HACK: by resetting owned vehicle we can reuse dynamic==nullptr code branch below + // TODO: refactor into utility method + simulation::Train->DynamicSet( nullptr ); + } + else { + // we can simply move the 'human' controller to the new vehicle + Global.changeDynObj = targetvehicle; + // TODO: choose active cab based on camera's location relative to vehicle's location +/* + Global.changeDynObj->MoverParameters->ActiveCab = ( + Train->DynamicObject->MoverParameters->Neighbours[ exitdirection ].vehicle_end ? + -1 : + 1 ); +*/ + } } - simulation::Train->Dynamic()->Mechanik->TakeControl( false, true ); } - else { - SafeDelete( simulation::Train ); // i nie ma czym sterować - } - if( simulation::Train ) { - InOutKey(); // do kabiny + if( simulation::Train->Dynamic() == nullptr ) { + // jeśli niczym jeszcze nie jeździlismy + if( simulation::Train->Init( targetvehicle ) ) { + // przejmujemy sterowanie + if( true == Global.ctrlState ) { + // make sure we can take over the consist + // TODO: remove ctrl key mode once manual cab (de)activation is in place + simulation::Train->Dynamic()->Mechanik->primary( true ); + } + simulation::Train->Dynamic()->Mechanik->TakeControl( false, true ); + if( true == simulation::Train->Dynamic()->Mechanik->primary() ) { + simulation::Train->Dynamic()->MoverParameters->CabDeactivisation( true ); // potentially left active + simulation::Train->Dynamic()->MoverParameters->CabActivisation(); + } + InOutKey(); // do kabiny + } + else { + SafeDelete( simulation::Train ); // i nie ma czym sterować + } } } } diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index 7545a65b..4442eba7 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -708,7 +708,9 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) // with active precipitation draw the opaque cab parts here to mask rain/snow placed 'inside' the cab setup_drawing(false); Render_cab(vehicle, vehicle->InteriorLightLevel, false); + Render_interior( false ); setup_drawing(true); + Render_interior( true ); } Render_cab(vehicle, vehicle->InteriorLightLevel, true); if( vehicle->InteriorLightLevel > 0.f ) { @@ -931,6 +933,104 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) } } + +bool opengl33_renderer::Render_interior( bool const Alpha ) { + // TODO: early frustum based cull, camera might be pointing elsewhere + std::vector< std::pair > dynamics; + auto *dynamic { simulation::Train->Dynamic() }; + // draw interiors of the occupied vehicle, and all vehicles behind it with permanent coupling, in case they're open-ended + while( dynamic != nullptr ) { + + glm::dvec3 const originoffset { dynamic->vPosition - m_renderpass.pass_camera.position() }; + float const squaredistance{ glm::length2( glm::vec3{ originoffset } / Global.ZoomFactor ) / Global.fDistanceFactor }; + dynamics.emplace_back( squaredistance, dynamic ); + dynamic = dynamic->NextC( coupling::permanent ); + } + // draw also interiors of permanently coupled vehicles in front, if there's any + dynamic = simulation::Train->Dynamic()->PrevC( coupling::permanent ); + while( dynamic != nullptr ) { + + glm::dvec3 const originoffset { dynamic->vPosition - m_renderpass.pass_camera.position() }; + float const squaredistance{ glm::length2( glm::vec3{ originoffset } / Global.ZoomFactor ) / Global.fDistanceFactor }; + dynamics.emplace_back( squaredistance, dynamic ); + dynamic = dynamic->PrevC( coupling::permanent ); + } + if( Alpha ) { + std::sort( + std::begin( dynamics ), std::end( dynamics ), + []( std::pair const &Left, std::pair const &Right ) { + return ( Left.first ) > ( Right.first ); } ); + } + for( auto &dynamic : dynamics ) { + Render_lowpoly( dynamic.second, dynamic.first, true, Alpha ); + } + + return true; +} + +bool opengl33_renderer::Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha ) { + + if( Dynamic->mdLowPolyInt == nullptr ) { return false; } + + // low poly interior + if( Setup ) { + + TSubModel::iInstance = reinterpret_cast( Dynamic ); //żeby nie robić cudzych animacji + glm::dvec3 const originoffset{ Dynamic->vPosition - m_renderpass.pass_camera.position() }; + + Dynamic->ABuLittleUpdate( Squaredistance ); // ustawianie zmiennych submodeli dla wspólnego modelu + + glm::mat4 mv = OpenGLMatrices.data( GL_MODELVIEW ); + + ::glPushMatrix(); + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + ::glMultMatrixd( Dynamic->mMatrix.readArray() ); + } + // HACK: reduce light level for vehicle interior if there's strong global lighting source + if( false == Alpha ) { + auto const luminance{ static_cast( 0.5 * ( std::max( 0.3, Global.fLuminance - Global.Overcast ) ) ) }; + setup_sunlight_intensity( + clamp( ( + Dynamic->fShade > 0.f ? + Dynamic->fShade : + 1.f ) + - luminance, + 0.f, 1.f ) ); + Render( Dynamic->mdLowPolyInt, Dynamic->Material(), Squaredistance ); + // HACK: if the model has low poly interior, we presume the load is placed inside and also affected by reduced light level + if( Dynamic->mdLoad ) { + // renderowanie nieprzezroczystego ładunku + Render( Dynamic->mdLoad, Dynamic->Material(), Squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); + } + setup_sunlight_intensity( Dynamic->fShade > 0.f ? Dynamic->fShade : 1.f ); + } + else { +// Render_Alpha( Dynamic->mdLowPolyInt, Dynamic->Material(), Squaredistance ); + // HACK: some models have windows included as part of the main model instead of lowpoly + if( Dynamic->mdModel ) { + // main model + Render_Alpha( Dynamic->mdModel, Dynamic->Material(), Squaredistance ); + } + } + + if( Setup ) { + + if( Dynamic->fShade > 0.0f ) { + // restore regular light level + setup_sunlight_intensity(); + } + + ::glPopMatrix(); + + // TODO: check if this reset is needed. In theory each object should render all parts based on its own instance data anyway? + if( Dynamic->btnOn ) { + Dynamic->TurnOff(); // przywrócenie domyślnych pozycji submodeli + } + } + + return true; +} + // creates dynamic environment cubemap bool opengl33_renderer::Render_reflections(viewport_config &vp) { @@ -2301,24 +2401,8 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) // render if( Dynamic->mdLowPolyInt ) { // low poly interior - // HACK: reduce light level for vehicle interior if there's strong global lighting source - auto const luminance { static_cast( 0.5 * ( std::max( 0.3, Global.fLuminance - Global.Overcast ) ) ) }; - setup_sunlight_intensity( - clamp( ( - Dynamic->fShade > 0.f ? - Dynamic->fShade : - 1.f ) - - luminance, - 0.f, 1.f ) ); - Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); - // HACK: if the model has low poly interior, we presume the load is placed inside and also affected by reduced light level - if( Dynamic->mdLoad ) { - // renderowanie nieprzezroczystego ładunku - Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); - } - - setup_sunlight_intensity( Dynamic->fShade > 0.f ? Dynamic->fShade : 1.f ); - } + Render_lowpoly( Dynamic, squaredistance, false ); + } else { // HACK: if the model lacks low poly interior, we presume the load is placed outside if( Dynamic->mdLoad ) { @@ -4069,10 +4153,12 @@ bool opengl33_renderer::Init_caps() GLint texturesize; ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texturesize); Global.iMaxTextureSize = std::min(Global.iMaxTextureSize, texturesize); - WriteLog("texture sizes capped at " + std::to_string(Global.iMaxTextureSize) + "px"); - m_shadowbuffersize = Global.shadowtune.map_size; + Global.iMaxCabTextureSize = std::min( Global.iMaxCabTextureSize, texturesize ); + WriteLog( "texture sizes capped at " + std::to_string(Global.iMaxTextureSize) + "p (" + std::to_string( Global.iMaxCabTextureSize ) + "p for cab textures)" ); + Global.CurrentMaxTextureSize = Global.iMaxTextureSize; + m_shadowbuffersize = Global.shadowtune.map_size; m_shadowbuffersize = std::min(m_shadowbuffersize, texturesize); - WriteLog("shadows map size capped at " + std::to_string(m_shadowbuffersize) + "px"); + WriteLog("shadows map size capped at " + std::to_string(m_shadowbuffersize) + "p"); } Global.DynamicLightCount = std::min(Global.DynamicLightCount, 8); diff --git a/opengl33renderer.h b/opengl33renderer.h index d712f198..e78af57b 100644 --- a/opengl33renderer.h +++ b/opengl33renderer.h @@ -225,6 +225,8 @@ class opengl33_renderer : public gfx_renderer { void Render(TTrack *Track); void Render(scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last); bool Render_cab(TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false); + bool Render_interior( bool const Alpha = false ); + bool Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false ); void Render(TMemCell *Memcell); void Render_particles(); void Render_precipitation(); diff --git a/openglrenderer.cpp b/openglrenderer.cpp index b4477df2..f46bd288 100644 --- a/openglrenderer.cpp +++ b/openglrenderer.cpp @@ -525,7 +525,9 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // with active precipitation draw the opaque cab parts here to mask rain/snow placed 'inside' the cab setup_drawing( false ); Render_cab( vehicle, vehicle->InteriorLightLevel, false ); + Render_interior( false); setup_drawing( true ); + Render_interior( true ); } Render_cab( vehicle, vehicle->InteriorLightLevel, true ); if( vehicle->InteriorLightLevel > 0.f ) { @@ -723,6 +725,107 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } } +bool opengl_renderer::Render_interior( bool const Alpha ) { + // NOTE: legacy renderer doesn't need to repaint translucent parts + // TODO: investigate reasons (depth writing?) and duplicate in opengl33 branch + if( Alpha ) { return false; } + // TODO: early frustum based cull, camera might be pointing elsewhere + std::vector< std::pair > dynamics; + auto *dynamic { simulation::Train->Dynamic() }; + // draw interiors of the occupied vehicle, and all vehicles behind it with permanent coupling, in case they're open-ended + while( dynamic != nullptr ) { + + glm::dvec3 const originoffset { dynamic->vPosition - m_renderpass.camera.position() }; + float const squaredistance{ glm::length2( glm::vec3{ originoffset } / Global.ZoomFactor ) / Global.fDistanceFactor }; + dynamics.emplace_back( squaredistance, dynamic ); + dynamic = dynamic->NextC( coupling::permanent ); + } + // draw also interiors of permanently coupled vehicles in front, if there's any + dynamic = simulation::Train->Dynamic()->PrevC( coupling::permanent ); + while( dynamic != nullptr ) { + + glm::dvec3 const originoffset { dynamic->vPosition - m_renderpass.camera.position() }; + float const squaredistance{ glm::length2( glm::vec3{ originoffset } / Global.ZoomFactor ) / Global.fDistanceFactor }; + dynamics.emplace_back( squaredistance, dynamic ); + dynamic = dynamic->PrevC( coupling::permanent ); + } + if( Alpha ) { + std::sort( + std::begin( dynamics ), std::end( dynamics ), + []( std::pair const &Left, std::pair const &Right ) { + return ( Left.first ) > ( Right.first ); } ); + } + for( auto &dynamic : dynamics ) { + Render_lowpoly( dynamic.second, dynamic.first, true, Alpha ); + } + + return true; +} + +bool opengl_renderer::Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha ) { + + if( Dynamic->mdLowPolyInt == nullptr ) { return false; } + + // low poly interior + glm::dvec3 const originoffset { Dynamic->vPosition - m_renderpass.camera.position() }; + + if( Setup ) { + + TSubModel::iInstance = reinterpret_cast( Dynamic ); //żeby nie robić cudzych animacji + Dynamic->ABuLittleUpdate( Squaredistance ); // ustawianie zmiennych submodeli dla wspólnego modelu + ::glPushMatrix(); + + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + ::glMultMatrixd( Dynamic->mMatrix.getArray() ); + + m_renderspecular = true; // vehicles are rendered with specular component. static models without, at least for the time being + } + // HACK: reduce light level for vehicle interior if there's strong global lighting source + if( false == Alpha ) { + auto const luminance{ static_cast( 0.5 * ( std::max( 0.3, Global.fLuminance - Global.Overcast ) ) ) }; + m_sunlight.apply_intensity( + clamp( ( + Dynamic->fShade > 0.f ? + Dynamic->fShade : + 1.f ) + - luminance, + 0.f, 1.f ) ); + Render( Dynamic->mdLowPolyInt, Dynamic->Material(), Squaredistance ); + // HACK: if the model has low poly interior, we presume the load is placed inside and also affected by reduced light level + if( Dynamic->mdLoad ) { + // renderowanie nieprzezroczystego ładunku + Render( Dynamic->mdLoad, Dynamic->Material(), Squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); + } + m_sunlight.apply_intensity( Dynamic->fShade > 0.f ? Dynamic->fShade : 1.f ); + } + else { + // Render_Alpha( Dynamic->mdLowPolyInt, Dynamic->Material(), Squaredistance ); + // HACK: some models have windows included as part of the main model instead of lowpoly + if( Dynamic->mdModel ) { + // main model + Render_Alpha( Dynamic->mdModel, Dynamic->Material(), Squaredistance ); + } + } + + if( Setup ) { + + m_renderspecular = false; + if( Dynamic->fShade > 0.0f ) { + // restore regular light level + m_sunlight.apply_intensity(); + } + + ::glPopMatrix(); + + // TODO: check if this reset is needed. In theory each object should render all parts based on its own instance data anyway? + if( Dynamic->btnOn ) { + Dynamic->TurnOff(); // przywrócenie domyślnych pozycji submodeli + } + } + + return true; +} + // creates dynamic environment cubemap bool opengl_renderer::Render_reflections() { @@ -2182,25 +2285,7 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { m_renderspecular = true; // vehicles are rendered with specular component. static models without, at least for the time being // render if( Dynamic->mdLowPolyInt ) { - // HACK: reduce light level for vehicle interior if there's strong global lighting source - auto const luminance { static_cast( 0.5 * ( std::max( 0.3, Global.fLuminance - Global.Overcast ) ) ) }; - m_sunlight.apply_intensity( - clamp( ( - Dynamic->fShade > 0.f ? - Dynamic->fShade : - 1.f ) - - luminance, - 0.f, 1.f ) ); - - // low poly interior - Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); - // HACK: if the model has low poly interior, we presume the load is placed inside and also affected by reduced light level - if( Dynamic->mdLoad ) { - // renderowanie nieprzezroczystego ładunku - Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); - } - - m_sunlight.apply_intensity( Dynamic->fShade > 0.f ? Dynamic->fShade : 1.f ); + Render_lowpoly( Dynamic, squaredistance, false ); } else { // HACK: if the model lacks low poly interior, we presume the load is placed outside @@ -4078,11 +4163,13 @@ opengl_renderer::Init_caps() { { GLint texturesize; ::glGetIntegerv( GL_MAX_TEXTURE_SIZE, &texturesize ); - Global.iMaxTextureSize = std::min( Global.iMaxTextureSize, texturesize ); - WriteLog( "Texture sizes capped at " + std::to_string( Global.iMaxTextureSize ) + " pixels" ); + Global.iMaxTextureSize = std::min(Global.iMaxTextureSize, texturesize); + Global.iMaxCabTextureSize = std::min( Global.iMaxCabTextureSize, texturesize ); + WriteLog( "texture sizes capped at " + std::to_string(Global.iMaxTextureSize) + "p (" + std::to_string( Global.iMaxCabTextureSize ) + "p for cab textures)" ); + Global.CurrentMaxTextureSize = Global.iMaxTextureSize; m_shadowbuffersize = Global.shadowtune.map_size; m_shadowbuffersize = std::min( m_shadowbuffersize, texturesize ); - WriteLog( "Shadows map size capped at " + std::to_string( m_shadowbuffersize ) + " pixels" ); + WriteLog( "Shadows map size capped at " + std::to_string( m_shadowbuffersize ) + "p" ); } // cap the number of supported lights based on hardware { diff --git a/openglrenderer.h b/openglrenderer.h index a0ccbc97..000a5465 100644 --- a/openglrenderer.h +++ b/openglrenderer.h @@ -218,6 +218,10 @@ private: Render( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last ); bool Render_cab( TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false ); + bool + Render_interior( bool const Alpha = false ); + bool + Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false ); void Render( TMemCell *Memcell ); void diff --git a/sound.cpp b/sound.cpp index c3da5ed7..22cc529a 100644 --- a/sound.cpp +++ b/sound.cpp @@ -333,7 +333,7 @@ sound_source::play( int const Flags ) { // initialize emitter-specific pitch variation if it wasn't yet set if( m_pitchvariation == 0.f ) { - m_pitchvariation = 0.01f * static_cast( Random( 97.5, 102.5 ) ); + m_pitchvariation = 1.f; // 0.01f * static_cast( Random( 97.5, 102.5 ) ); } /* if( ( ( m_flags & sound_flags::exclusive ) != 0 )