diff --git a/Globals.cpp b/Globals.cpp index a4f9c169..5ff04bf6 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -77,6 +77,7 @@ global_settings::ConfigParse(cParser &Parser) { { Parser.getTokens(1, false); Parser >> fDistanceFactor; + fDistanceFactor = clamp( fDistanceFactor, 250.f, 10000.f ); // arbitrary limits to keep users from hurting themselves } else if (token == "targetfps") { @@ -93,6 +94,11 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens(); Parser >> bFullScreen; } + else if (token == "fullscreenmonitor") + { + Parser.getTokens(1, false); + Parser >> fullscreen_monitor; + } else if( token == "vsync" ) { Parser.getTokens(); @@ -734,6 +740,11 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens(1, false); Parser >> gfx_framebuffer_height; } + else if (token == "gfx.framebuffer.fidelity") + { + Parser.getTokens(1, false); + Parser >> gfx_framebuffer_fidelity; + } else if (token == "gfx.shadowmap.enabled") { Parser.getTokens(1); diff --git a/Globals.h b/Globals.h index d7863085..db249098 100644 --- a/Globals.h +++ b/Globals.h @@ -76,7 +76,7 @@ struct global_settings { bool bLiveTraction{ true }; float Overcast{ 0.1f }; // NOTE: all this weather stuff should be moved elsewhere glm::vec3 FogColor = { 0.6f, 0.7f, 0.8f }; - double fFogEnd{ 2000 }; + double fFogEnd{ 7500 }; std::string Season{}; // season of the year, based on simulation date std::string Weather{ "cloudy:" }; // current weather bool FullPhysics{ true }; // full calculations performed for each simulation step @@ -187,6 +187,7 @@ struct global_settings { bool render_cab = true; int gfx_framebuffer_width = -1; int gfx_framebuffer_height = -1; + int gfx_framebuffer_fidelity = -1; bool gfx_shadowmap_enabled = true; bool gfx_envmap_enabled = true; bool gfx_postfx_motionblur_enabled = true; @@ -198,6 +199,7 @@ struct global_settings { bool gfx_extraeffects = true; bool gfx_shadergamma = false; bool gfx_usegles = false; + std::string fullscreen_monitor; bool python_mipmaps = true; // methods diff --git a/PyInt.cpp b/PyInt.cpp index 41af5783..ef19721f 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -160,7 +160,8 @@ auto python_taskqueue::insert( task_request const &Task ) -> bool { if( ( Task.renderer.empty() ) || ( Task.input == nullptr ) - || ( Task.target == 0 ) ) { return false; } + || ( Task.target == 0 ) + || ( Task.target == (GLuint)-1 ) ) { return false; } auto *renderer { fetch_renderer( Task.renderer ) }; if( renderer == nullptr ) { return false; } diff --git a/application.cpp b/application.cpp index c5f29813..6cab8b3f 100644 --- a/application.cpp +++ b/application.cpp @@ -316,7 +316,7 @@ eu07_application::on_scroll( double const Xoffset, double const Yoffset ) { } GLFWwindow * -eu07_application::window( int const Windowindex ) { +eu07_application::window(int const Windowindex, bool visible, int width, int height, GLFWmonitor *monitor, bool keep_ownership , bool share_ctx) { if( Windowindex >= 0 ) { return ( @@ -325,15 +325,51 @@ eu07_application::window( int const Windowindex ) { nullptr ); } // for index -1 create a new child window - glfwWindowHint( GLFW_VISIBLE, GL_FALSE ); - auto *childwindow = glfwCreateWindow( 1, 1, "eu07helper", nullptr, m_windows.front() ); - if( childwindow != nullptr ) { - m_windows.emplace_back( childwindow ); - } + + auto const *vmode { glfwGetVideoMode( monitor ? monitor : glfwGetPrimaryMonitor() ) }; + + glfwWindowHint( GLFW_RED_BITS, vmode->redBits ); + glfwWindowHint( GLFW_GREEN_BITS, vmode->greenBits ); + glfwWindowHint( GLFW_BLUE_BITS, vmode->blueBits ); + glfwWindowHint( GLFW_REFRESH_RATE, vmode->refreshRate ); + + glfwWindowHint( GLFW_VISIBLE, visible ); + + auto *childwindow = glfwCreateWindow( width, height, "eu07window", monitor, + share_ctx ? m_windows.front() : nullptr); + if (!childwindow) + return nullptr; + + if (keep_ownership) + m_windows.emplace_back( childwindow ); + + glfwFocusWindow(m_windows.front()); // restore focus to main window + return childwindow; } // private: +GLFWmonitor* eu07_application::find_monitor(const std::string &str) const { + int monitor_count; + GLFWmonitor **monitors = glfwGetMonitors(&monitor_count); + + for (size_t i = 0; i < monitor_count; i++) { + if (describe_monitor(monitors[i]) == str) + return monitors[i]; + } + + return nullptr; +} + +std::string eu07_application::describe_monitor(GLFWmonitor *monitor) const { + std::string name(glfwGetMonitorName(monitor)); + std::replace(std::begin(name), std::end(name), ' ', '_'); + + int x, y; + glfwGetMonitorPos(monitor, &x, &y); + + return name + ":" + std::to_string(x) + "," + std::to_string(y); +} void eu07_application::init_debug() { @@ -342,12 +378,10 @@ eu07_application::init_debug() { // memory leaks _CrtSetDbgFlag( _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ) | _CRTDBG_LEAK_CHECK_DF ); // floating point operation errors - /* auto state { _clearfp() }; state = _control87( 0, 0 ); // this will turn on FPE for #IND and zerodiv state = _control87( state & ~( _EM_ZERODIVIDE | _EM_INVALID ), _MCW_EM ); - */ #endif #ifdef _WIN32 ::SetUnhandledExceptionFilter( unhandled_handler ); @@ -462,13 +496,19 @@ eu07_application::init_glfw() { } // match requested video mode to current to allow for // fullwindow creation when resolution is the same - auto *monitor { glfwGetPrimaryMonitor() }; - auto const *vmode { glfwGetVideoMode( monitor ) }; + { + int monitor_count; + GLFWmonitor **monitors = glfwGetMonitors(&monitor_count); - glfwWindowHint( GLFW_RED_BITS, vmode->redBits ); - glfwWindowHint( GLFW_GREEN_BITS, vmode->greenBits ); - glfwWindowHint( GLFW_BLUE_BITS, vmode->blueBits ); - glfwWindowHint( GLFW_REFRESH_RATE, vmode->refreshRate ); + WriteLog("available monitors:"); + for (size_t i = 0; i < monitor_count; i++) { + WriteLog(describe_monitor(monitors[i])); + } + } + + auto *monitor { find_monitor(Global.fullscreen_monitor) }; + if (!monitor) + monitor = glfwGetPrimaryMonitor(); glfwWindowHint( GLFW_AUTO_ICONIFY, GLFW_FALSE ); if( Global.iMultisampling > 0 ) { @@ -494,40 +534,24 @@ eu07_application::init_glfw() { } } - glfwWindowHint( GLFW_AUTO_ICONIFY, GLFW_FALSE ); + auto *mainwindow = window( + -1, true, Global.iWindowWidth, Global.iWindowHeight, Global.bFullScreen ? monitor : nullptr, true, false ); - if( Global.bFullScreen ) { - // match screen dimensions with selected monitor, for 'borderless window' in fullscreen mode - Global.iWindowWidth = vmode->width; - Global.iWindowHeight = vmode->height; - } - - auto *window { - glfwCreateWindow( - Global.iWindowWidth, - Global.iWindowHeight, - Global.AppName.c_str(), - ( Global.bFullScreen ? - monitor : - nullptr ), - nullptr ) }; - - if( window == nullptr ) { + if( mainwindow == nullptr ) { ErrorLog( "Bad init: failed to create glfw window" ); return -1; } - glfwMakeContextCurrent( window ); + glfwMakeContextCurrent( mainwindow ); glfwSwapInterval( Global.VSync ? 1 : 0 ); //vsync #ifdef _WIN32 // setup wrapper for base glfw window proc, to handle copydata messages - Hwnd = glfwGetWin32Window( window ); + Hwnd = glfwGetWin32Window( mainwindow ); BaseWindowProc = ( WNDPROC )::SetWindowLongPtr( Hwnd, GWLP_WNDPROC, (LONG_PTR)WndProc ); // switch off the topmost flag ::SetWindowPos( Hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); #endif - m_windows.emplace_back( window ); return 0; } diff --git a/application.h b/application.h index 815c0e91..58ea34d5 100644 --- a/application.h +++ b/application.h @@ -71,7 +71,7 @@ public: on_scroll( double const Xoffset, double const Yoffset ); // gives access to specified window, creates a new window if index == -1 GLFWwindow * - window( int const Windowindex = 0 ); + window( int const Windowindex = 0, bool visible = false, int width = 1, int height = 1, GLFWmonitor *monitor = nullptr, bool keep_ownership = true, bool share_ctx = true ); private: // types @@ -88,6 +88,8 @@ private: int init_audio(); int init_data(); int init_modes(); + GLFWmonitor * find_monitor( const std::string &str ) const; + std::string describe_monitor( GLFWmonitor *monitor ) const; // members modeptr_array m_modes { nullptr }; // collection of available application behaviour modes mode_stack m_modestack; // current behaviour mode diff --git a/color.h b/color.h index 1ec63e80..4260b1fb 100644 --- a/color.h +++ b/color.h @@ -4,7 +4,7 @@ namespace colors { glm::vec4 const none{ 0.f, 0.f, 0.f, 1.f }; glm::vec4 const white{ 1.f, 1.f, 1.f, 1.f }; -glm::vec4 const shadow{ 0.65f, 0.65f, 0.65f, 1.f }; +glm::vec4 const shadow{ 0.6f, 0.6f, 0.6f, 1.f }; inline glm::vec3 diff --git a/driveruipanels.cpp b/driveruipanels.cpp index df09a140..4d66241f 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -1092,7 +1092,7 @@ debug_panel::update_section_renderer( std::vector &Output ) { // gfx renderer data auto textline = "FoV: " + to_string( Global.FieldOfView / Global.ZoomFactor, 1 ) - + ", Draw range x " + to_string( Global.fDistanceFactor, 1 ) + + ", Draw range: " + to_string( Global.BaseDrawRange * Global.fDistanceFactor, 0 ) + "m" // + "; sectors: " + std::to_string( GfxRenderer->m_drawcount ) // + ", FPS: " + to_string( Timer::GetFPS(), 2 ); + ", FPS: " + std::to_string( static_cast(std::round(GfxRenderer->Framerate())) ); diff --git a/gl/glsl_common.cpp b/gl/glsl_common.cpp index 4215a475..299b3a12 100644 --- a/gl/glsl_common.cpp +++ b/gl/glsl_common.cpp @@ -58,6 +58,7 @@ void gl::glsl_common_setup() float emission; float fog_density; float alpha_mult; + float shadow_tone; }; layout (std140) uniform scene_ubo diff --git a/gl/ubo.h b/gl/ubo.h index c8ce5167..cd29cfb3 100644 --- a/gl/ubo.h +++ b/gl/ubo.h @@ -56,6 +56,7 @@ namespace gl float emission; float fog_density; float alpha_mult; + float shadow_tone; UBS_PAD(4); void set_modelview(const glm::mat4 &mv) @@ -65,7 +66,7 @@ namespace gl } }; - static_assert(sizeof(model_ubs) == 196 + 16 * MAX_PARAMS, "bad size of ubs"); + static_assert(sizeof(model_ubs) == 200 + 16 * MAX_PARAMS, "bad size of ubs"); struct light_element_ubs { diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index 62396c49..e5727d54 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -125,8 +125,15 @@ bool opengl33_renderer::Init(GLFWwindow *Window) m_viewports.push_back(std::make_unique()); viewport_config &default_viewport = *m_viewports.front().get(); - default_viewport.width = Global.gfx_framebuffer_width; - default_viewport.height = Global.gfx_framebuffer_height; + if( Global.gfx_framebuffer_fidelity >= 0 ) { + std::vector resolutions = { 480, 720, 1080, 1440 }; + default_viewport.height = resolutions[ clamp( Global.gfx_framebuffer_fidelity, 0, resolutions.size() - 1 ) ]; + default_viewport.width = quantize( Global.iWindowWidth * default_viewport.height / Global.iWindowHeight, 4 ); + } + else { + default_viewport.width = Global.gfx_framebuffer_width; + default_viewport.height = Global.gfx_framebuffer_height; + } default_viewport.main = true; default_viewport.window = m_window; default_viewport.draw_range = 1.0f; @@ -612,11 +619,18 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) if (!FreeFlyModeFlag && Global.Overcast <= 1.0f && Global.render_cab) { glDebug("render cab opaque"); - if (Global.gfx_shadowmap_enabled) - setup_shadow_map(m_cabshadows_tex.get(), m_cabshadowpass); - - auto const *vehicle = simulation::Train->Dynamic(); - Render_cab(vehicle, vehicle->InteriorLightLevel, false); + if( Global.gfx_shadowmap_enabled ) { + setup_shadow_map( m_cabshadows_tex.get(), m_cabshadowpass ); + } + // cache shadow colour in case we need to account for cab light + auto const *vehicle{ simulation::Train->Dynamic() }; + if( vehicle->InteriorLightLevel > 0.f ) { + setup_shadow_color( glm::min( colors::white, m_shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); + } + Render_cab( vehicle, vehicle->InteriorLightLevel, false ); + if( vehicle->InteriorLightLevel > 0.f ) { + setup_shadow_color( m_shadowcolor ); + } } glDebug("render opaque region"); @@ -644,10 +658,14 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) { glDebug("render translucent cab"); model_ubs.future = glm::mat4(); - if (Global.gfx_shadowmap_enabled) - setup_shadow_map(m_cabshadows_tex.get(), m_cabshadowpass); - // cache shadow colour in case we need to account for cab light - auto const *vehicle{simulation::Train->Dynamic()}; + if( Global.gfx_shadowmap_enabled ) { + setup_shadow_map( m_cabshadows_tex.get(), m_cabshadowpass ); + } + // cache shadow colour in case we need to account for cab light + auto *vehicle { simulation::Train->Dynamic() }; + if( vehicle->InteriorLightLevel > 0.f ) { + setup_shadow_color( glm::min( colors::white, m_shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); + } if (Global.Overcast > 1.0f) { // with active precipitation draw the opaque cab parts here to mask rain/snow placed 'inside' the cab @@ -656,7 +674,10 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) setup_drawing(true); } Render_cab(vehicle, vehicle->InteriorLightLevel, true); - } + if( vehicle->InteriorLightLevel > 0.f ) { + setup_shadow_color( m_shadowcolor ); + } + } Timer::subsystem.gfx_color.stop(); @@ -731,8 +752,10 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) scene_ubs.projection = OpenGLMatrices.data(GL_PROJECTION); scene_ubo->update(scene_ubs); - Render(simulation::Region); + if( m_shadowcolor != colors::white ) { + Render( simulation::Region ); + } m_shadowpass = m_renderpass; m_shadow_fb->unbind(); @@ -758,8 +781,13 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) scene_ubs.projection = OpenGLMatrices.data(GL_PROJECTION); scene_ubo->update(scene_ubs); - Render_cab(simulation::Train->Dynamic(), 0.0f, false); - Render_cab(simulation::Train->Dynamic(), 0.0f, true); + if( m_shadowcolor != colors::white ) { + if( Global.RenderCabShadowsRange > 0 ) { + Render( simulation::Region ); + } + Render_cab( simulation::Train->Dynamic(), 0.0f, false ); + Render_cab( simulation::Train->Dynamic(), 0.0f, true ); + } m_cabshadowpass = m_renderpass; m_cabshadows_fb->unbind(); @@ -1086,7 +1114,7 @@ void opengl33_renderer::setup_pass(viewport_config &Viewport, renderpass_config camera.position() = Global.pCamera.Pos - glm::dvec3{lightvector}; viewmatrix *= glm::lookAt(camera.position(), glm::dvec3{Global.pCamera.Pos}, glm::dvec3{0.f, 1.f, 0.f}); // projection - auto const maphalfsize{Config.draw_range * 0.5f}; + auto const maphalfsize { std::min( 10.f, Config.draw_range * 0.5f ) }; camera.projection() = ortho_projection(-maphalfsize, maphalfsize, -maphalfsize, maphalfsize, -Config.draw_range, Config.draw_range); frustumtest_proj = ortho_frustumtest_projection(-maphalfsize, maphalfsize, -maphalfsize, maphalfsize, -Config.draw_range, Config.draw_range); @@ -1221,19 +1249,28 @@ void opengl33_renderer::setup_shadow_map(opengl_texture *tex, renderpass_config 0.5, 0.5, 0.5, 1.0 // ); glm::mat4 depthproj = conf.pass_camera.projection(); - glm::mat4 depthcam = conf.pass_camera.modelview(); - glm::mat4 worldcam = m_renderpass.pass_camera.modelview(); + // NOTE: we strip transformations from camera projections to remove jitter that occurs + // with large (and unneded as we only need the offset) transformations back and forth + auto const depthcam{ glm::mat3{ conf.pass_camera.modelview()} }; + auto const worldcam{ glm::mat3{ m_renderpass.pass_camera.modelview()} }; scene_ubs.lightview = coordmove * depthproj - * depthcam - * glm::inverse( worldcam ); - + * glm::translate( + glm::mat4{ depthcam }, + glm::vec3{ m_renderpass.pass_camera.position() - conf.pass_camera.position() } ) + * glm::mat4{ glm::inverse( worldcam ) }; + scene_ubo->update(scene_ubs); } } +void opengl33_renderer::setup_shadow_color( glm::vec4 const &Shadowcolor ) { + + model_ubs.shadow_tone = glm::pow( Shadowcolor.x, 2.2f ); +} + void opengl33_renderer::setup_env_map(gl::cubemap *tex) { if (tex) @@ -1257,19 +1294,19 @@ void opengl33_renderer::setup_environment_light(TEnvironmentType const Environme { case e_flat: { - m_sunlight.apply_intensity(); + setup_sunlight_intensity(); // m_environment = Environment; break; } case e_canyon: { - m_sunlight.apply_intensity(0.4f); + setup_sunlight_intensity(0.4f); // m_environment = Environment; break; } case e_tunnel: { - m_sunlight.apply_intensity(0.2f); + setup_sunlight_intensity(0.2f); // m_environment = Environment; break; } @@ -1280,18 +1317,18 @@ void opengl33_renderer::setup_environment_light(TEnvironmentType const Environme } } +void opengl33_renderer::setup_sunlight_intensity( float const Factor ) { + + m_sunlight.apply_intensity( Factor ); + light_ubs.lights[ 0 ].intensity = m_sunlight.factor; + light_ubs.ambient = m_sunlight.ambient * m_sunlight.factor; + light_ubo->update( light_ubs ); +} + bool opengl33_renderer::Render(world_environment *Environment) { - - // calculate shadow tone, based on positions of celestial bodies - m_shadowcolor = interpolate(glm::vec4{colors::shadow}, glm::vec4{colors::white}, clamp(-Environment->m_sun.getAngle(), 0.f, 6.f) / 6.f); - if ((Environment->m_sun.getAngle() < -18.f) && (Environment->m_moon.getAngle() > 0.f)) - { - // turn on moon shadows after nautical twilight, if the moon is actually up - m_shadowcolor = colors::shadow; - } - // soften shadows depending on sky overcast factor - m_shadowcolor = glm::min(colors::white, m_shadowcolor + ((colors::white - colors::shadow) * Global.Overcast)); + m_shadowcolor = colors::white; // prevent shadow from affecting sky + setup_shadow_color( m_shadowcolor ); if (Global.bWireFrame) { @@ -1303,19 +1340,20 @@ bool opengl33_renderer::Render(world_environment *Environment) ::glDisable(GL_DEPTH_TEST); ::glPushMatrix(); - model_ubs.set_modelview(OpenGLMatrices.data(GL_MODELVIEW)); - model_ubo->update(model_ubs); - // skydome // drawn with 500m radius to blend in if the fog range is low glPushMatrix(); - glScalef(500.0f, 500.0f, 500.0f); - m_skydomerenderer.update(); - m_skydomerenderer.render(); - glPopMatrix(); + { + glScalef( 500.0f, 500.0f, 500.0f ); + model_ubs.set_modelview( OpenGLMatrices.data( GL_MODELVIEW ) ); + model_ubo->update( model_ubs ); - // skydome uses a custom vbo which could potentially confuse the main geometry system. hardly elegant but, eh - gfx::opengl_vbogeometrybank::reset(); + m_skydomerenderer.update(); + m_skydomerenderer.render(); + // skydome uses a custom vbo which could potentially confuse the main geometry system. hardly elegant but, eh + gfx::opengl_vbogeometrybank::reset(); + } + glPopMatrix(); ::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); @@ -1328,7 +1366,9 @@ bool opengl33_renderer::Render(world_environment *Environment) ::glRotatef(-std::fmod((float)Global.fTimeAngleDeg, 360.f), 0.f, 1.f, 0.f); // obrót dobowy osi OX // render + m_pointsize = ( 4.f ); // smaller points for stars visualization Render(Environment->m_stars.m_stars, nullptr, 1.0); + m_pointsize = ( 8.f ); // default point size // post-render cleanup ::glPopMatrix(); @@ -1461,6 +1501,18 @@ bool opengl33_renderer::Render(world_environment *Environment) m_sunlight.apply_angle(); m_sunlight.apply_intensity(); + // calculate shadow tone, based on positions of celestial bodies + m_shadowcolor = interpolate(glm::vec4{colors::shadow}, glm::vec4{colors::white}, clamp(-Environment->m_sun.getAngle(), 0.f, 6.f) / 6.f); + if ((Environment->m_sun.getAngle() < -18.f) && (Environment->m_moon.getAngle() > 0.f)) + { + // turn on moon shadows after nautical twilight, if the moon is actually up + m_shadowcolor = colors::shadow; + } + // soften shadows depending on sky overcast factor + m_shadowcolor = glm::min(colors::white, m_shadowcolor + ((colors::white - colors::shadow) * Global.Overcast)); + + setup_shadow_color( m_shadowcolor); + return true; } @@ -1727,7 +1779,8 @@ void opengl33_renderer::Render(scene::basic_region *Region) break; } case rendermode::shadows: - case rendermode::pickscenery: + case rendermode::cabshadows: + case rendermode::pickscenery: { // these render modes don't bother with lights Render(std::begin(m_sectionqueue), std::end(m_sectionqueue)); @@ -1758,7 +1811,8 @@ void opengl33_renderer::Render(section_sequence::iterator First, section_sequenc { case rendermode::color: case rendermode::reflections: - case rendermode::shadows: + case rendermode::cabshadows: + case rendermode::shadows: break; case rendermode::pickscenery: { @@ -1782,7 +1836,8 @@ void opengl33_renderer::Render(section_sequence::iterator First, section_sequenc case rendermode::color: case rendermode::reflections: case rendermode::shadows: - case rendermode::pickscenery: + case rendermode::cabshadows: + case rendermode::pickscenery: { if (false == section->m_shapes.empty()) { @@ -1812,7 +1867,8 @@ void opengl33_renderer::Render(section_sequence::iterator First, section_sequenc { case rendermode::color: case rendermode::shadows: - case rendermode::pickscenery: + case rendermode::cabshadows: + case rendermode::pickscenery: { for (auto &cell : section->m_cells) { @@ -1838,7 +1894,8 @@ void opengl33_renderer::Render(section_sequence::iterator First, section_sequenc switch (m_renderpass.draw_mode) { case rendermode::shadows: - { + case rendermode::cabshadows: + { break; } default: @@ -1903,6 +1960,24 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite break; } + case rendermode::cabshadows: + { + // since all shapes of the section share center point we can optimize out a few calls here + ::glPushMatrix(); + auto const originoffset{cell->m_area.center - m_renderpass.pass_camera.position()}; + ::glTranslated(originoffset.x, originoffset.y, originoffset.z); + + // render + // opaque non-instanced shapes + for (auto const &shape : cell->m_shapesopaque) + Render(shape, false); + // NOTE: tracks aren't likely to cast shadows into the cab, so we skip them in this pass + + // post-render cleanup + ::glPopMatrix(); + + break; + } case rendermode::pickscenery: { // same procedure like with regular render, but editor-enabled nodes receive custom colour used for picking @@ -1946,7 +2021,8 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite { case rendermode::color: case rendermode::shadows: - { + case rendermode::cabshadows: + { // opaque parts of instanced models for (auto *instance : cell->m_instancesopaque) { @@ -2022,7 +2098,8 @@ void opengl33_renderer::Render(scene::shape_node const &Shape, bool const Ignore switch (m_renderpass.draw_mode) { case rendermode::shadows: - { + case rendermode::cabshadows: + { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees distancesquared = Math3D::SquareMagnitude((data.area.center - m_renderpass.viewport_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; break; @@ -2047,7 +2124,8 @@ void opengl33_renderer::Render(scene::shape_node const &Shape, bool const Ignore Bind_Material(data.material); break; case rendermode::shadows: - Bind_Material_Shadow(data.material); + case rendermode::cabshadows: + Bind_Material_Shadow(data.material); break; case rendermode::pickscenery: case rendermode::pickcontrols: @@ -2076,7 +2154,8 @@ void opengl33_renderer::Render(TAnimModel *Instance) switch (m_renderpass.draw_mode) { case rendermode::shadows: - { + case rendermode::cabshadows: + { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees distancesquared = Math3D::SquareMagnitude((Instance->location() - m_renderpass.viewport_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; break; @@ -2140,8 +2219,18 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) case rendermode::shadows: { squaredistance = glm::length2(glm::vec3{glm::dvec3{Dynamic->vPosition - m_renderpass.viewport_camera.position()}} / Global.ZoomFactor) / Global.fDistanceFactor; + if( false == FreeFlyModeFlag ) { + // filter out small details if we're in vehicle cab + squaredistance = std::max( 100.f * 100.f, squaredistance ); + } break; } + case rendermode::cabshadows: { + squaredistance = glm::length2( glm::vec3{ glm::dvec3{ Dynamic->vPosition - m_renderpass.viewport_camera.position() } } / Global.ZoomFactor ) / Global.fDistanceFactor; + // filter out small details + squaredistance = std::max( 100.f * 100.f, squaredistance ); + break; + } default: { squaredistance = glm::length2(glm::vec3{originoffset} / Global.ZoomFactor) / Global.fDistanceFactor; @@ -2167,39 +2256,73 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) if (Dynamic->fShade > 0.0f) { // change light level based on light level of the occupied track - m_sunlight.apply_intensity(Dynamic->fShade); - } + setup_sunlight_intensity(Dynamic->fShade); + } // render - if (Dynamic->mdLowPolyInt) - Render(Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance); + 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 }, {} ); + } - if (Dynamic->mdModel) - Render(Dynamic->mdModel, Dynamic->Material(), squaredistance); - - 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 { + // HACK: if the model lacks low poly interior, we presume the load is placed outside + if( Dynamic->mdLoad ) { + // renderowanie nieprzezroczystego ładunku + Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); + } + } + if( Dynamic->mdModel ) { + // main model + Render( Dynamic->mdModel, Dynamic->Material(), squaredistance ); + } + // optional attached models + for( auto *attachment : Dynamic->mdAttachments ) { + Render( attachment, Dynamic->Material(), squaredistance ); + } // post-render cleanup - if (Dynamic->fShade > 0.0f) { // restore regular light level - m_sunlight.apply_intensity(); - } + setup_sunlight_intensity(); + } break; } case rendermode::shadows: + case rendermode::cabshadows: { - if (Dynamic->mdLowPolyInt) - { - // low poly interior - Render(Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance); - } - if (Dynamic->mdModel) - Render(Dynamic->mdModel, Dynamic->Material(), squaredistance); - if (Dynamic->mdLoad) // renderowanie nieprzezroczystego ładunku - Render(Dynamic->mdLoad, Dynamic->Material(), squaredistance, {0.f, Dynamic->LoadOffset, 0.f}, {}); + if( Dynamic->mdLowPolyInt ) { + // low poly interior + Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); + } + if( Dynamic->mdModel ) { + // main model + Render( Dynamic->mdModel, Dynamic->Material(), squaredistance ); + } + // optional attached models + for( auto *attachment : Dynamic->mdAttachments ) { + Render( attachment, Dynamic->Material(), squaredistance ); + } + if( Dynamic->mdLoad ) { + // renderowanie nieprzezroczystego ładunku + Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); + } // post-render cleanup break; } @@ -2265,8 +2388,8 @@ bool opengl33_renderer::Render_cab(TDynamicObject const *Dynamic, float const Li if (Dynamic->fShade > 0.0f) { // change light level based on light level of the occupied track - m_sunlight.apply_intensity(Dynamic->fShade); - } + setup_sunlight_intensity(Dynamic->fShade); + } // crude way to light the cabin, until we have something more complete in place glm::vec3 old_ambient = light_ubs.ambient; @@ -2288,8 +2411,8 @@ bool opengl33_renderer::Render_cab(TDynamicObject const *Dynamic, float const Li if (Dynamic->fShade > 0.0f) { // change light level based on light level of the occupied track - m_sunlight.apply_intensity(); - } + setup_sunlight_intensity(); + } // restore ambient light_ubs.ambient = old_ambient; @@ -2490,7 +2613,7 @@ void opengl33_renderer::Render(TSubModel *Submodel) Bind_Material(Submodel->m_material, Submodel); // main draw call - model_ubs.param[1].x = 2.0f * 2.0f; + model_ubs.param[1].x = m_pointsize; draw(Submodel->m_geometry); } @@ -2565,6 +2688,7 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator switch (m_renderpass.draw_mode) { case rendermode::shadows: + case rendermode::cabshadows: { // NOTE: roads-based platforms tend to miss parts of shadows if rendered with either back or front culling glDisable(GL_CULL_FACE); @@ -2614,7 +2738,8 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator break; } case rendermode::shadows: - { + case rendermode::cabshadows: + { if ((std::abs(track->fTexHeight1) < 0.35f) || (track->iCategoryFlag != 2)) { // shadows are only calculated for high enough roads, typically meaning track platforms @@ -2665,7 +2790,8 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator break; } case rendermode::shadows: - { + case rendermode::cabshadows: + { if ((std::abs(track->fTexHeight1) < 0.35f) || ((track->iCategoryFlag == 1) && (track->eType != tt_Normal))) { // shadows are only calculated for high enough trackbeds @@ -2722,7 +2848,8 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator break; } case rendermode::shadows: - { + case rendermode::cabshadows: + { if ((std::abs(track->fTexHeight1) < 0.35f) || ((track->iCategoryFlag == 1) && (track->eType != tt_Normal))) { // shadows are only calculated for high enough trackbeds @@ -2745,7 +2872,8 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator switch (m_renderpass.draw_mode) { case rendermode::shadows: - { + case rendermode::cabshadows: + { // restore standard face cull mode ::glEnable(GL_CULL_FACE); break; @@ -2771,7 +2899,8 @@ void opengl33_renderer::Render(TMemCell *Memcell) break; } case rendermode::shadows: - case rendermode::pickscenery: + case rendermode::cabshadows: + case rendermode::pickscenery: { break; } @@ -3099,27 +3228,32 @@ bool opengl33_renderer::Render_Alpha(TDynamicObject *Dynamic) if (Dynamic->fShade > 0.0f) { // change light level based on light level of the occupied track - m_sunlight.apply_intensity(Dynamic->fShade); + setup_sunlight_intensity(Dynamic->fShade); } - // render - if (Dynamic->mdLowPolyInt) - { - // low poly interior - Render_Alpha(Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance); - } - - if (Dynamic->mdModel) - Render_Alpha(Dynamic->mdModel, Dynamic->Material(), squaredistance); - - if (Dynamic->mdLoad) // renderowanie nieprzezroczystego ładunku - Render_Alpha(Dynamic->mdLoad, Dynamic->Material(), squaredistance); + // render + if( Dynamic->mdLowPolyInt ) { + // low poly interior + Render_Alpha( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); + } + if( Dynamic->mdModel ) { + // main model + Render_Alpha( Dynamic->mdModel, Dynamic->Material(), squaredistance ); + } + // optional attached models + for( auto *attachment : Dynamic->mdAttachments ) { + Render_Alpha( attachment, Dynamic->Material(), squaredistance ); + } + if( Dynamic->mdLoad ) { + // renderowanie nieprzezroczystego ładunku + Render_Alpha( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); + } // post-render cleanup if (Dynamic->fShade > 0.0f) { // restore regular light level - m_sunlight.apply_intensity(); + setup_sunlight_intensity(); } ::glPopMatrix(); @@ -3365,12 +3499,12 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel) { // fake fog halo float const fogfactor{interpolate(2.f, 1.f, clamp(Global.fFogEnd / 2000, 0.f, 1.f)) * std::max(1.f, Global.Overcast)}; - model_ubs.param[1].x = pointsize * resolutionratio * fogfactor * 2.0f; + model_ubs.param[1].x = pointsize * resolutionratio * fogfactor * 3.0f; model_ubs.param[0] = glm::vec4(glm::vec3(lightcolor), Submodel->fVisible * std::min(1.f, lightlevel) * 0.5f); draw(Submodel->m_geometry); } - model_ubs.param[1].x = pointsize * resolutionratio * 2.0f; + model_ubs.param[1].x = pointsize * resolutionratio * 3.0f; model_ubs.param[0] = glm::vec4(glm::vec3(lightcolor), Submodel->fVisible * std::min(1.f, lightlevel)); if (!Submodel->occlusion_query) @@ -3639,11 +3773,12 @@ void opengl33_renderer::Update(double const Deltatime) // TODO: it doesn't make much sense with vsync if( Global.targetfps == 0.0f ) { // automatic adjustment + auto const framerate = 1000.f / Timer::subsystem.gfx_color.average(); float targetfactor; - if( m_framerate > 90.0 ) { targetfactor = 3.0f; } - else if( m_framerate > 60.0 ) { targetfactor = 1.5f; } - else if( m_framerate > 30.0 ) { targetfactor = 1.25; } - else { targetfactor = 1.0f; } + if( framerate > 90.0 ) { targetfactor = 3.0f; } + else if( framerate > 60.0 ) { targetfactor = 1.5f; } + else if( framerate > 30.0 ) { targetfactor = 1.25; } + else { targetfactor = 1.0f; } if( targetfactor > Global.fDistanceFactor ) { Global.fDistanceFactor = std::min( targetfactor, Global.fDistanceFactor + 0.05f ); } @@ -3656,7 +3791,7 @@ void opengl33_renderer::Update(double const Deltatime) if( fps_diff > 0.5f ) { Global.fDistanceFactor = std::max( 1.0f, Global.fDistanceFactor - 0.05f ); } - else if( fps_diff < 0.5f ) { + else if( fps_diff < 1.0f ) { Global.fDistanceFactor = std::min( 3.0f, Global.fDistanceFactor + 0.05f ); } } @@ -3807,23 +3942,26 @@ void opengl33_renderer::Update_Lights(light_array &Lights) bool opengl33_renderer::Init_caps() { - WriteLog("MaSzyna OpenGL Renderer"); - WriteLog("Renderer: " + std::string((char *)glGetString(GL_RENDERER))); - WriteLog("Vendor: " + std::string((char *)glGetString(GL_VENDOR))); - WriteLog("GL version: " + std::string((char *)glGetString(GL_VERSION))); + WriteLog( + "Gfx Renderer: " + std::string( (char *)glGetString(GL_RENDERER)) + + "\nVendor: " + std::string( (char *)glGetString(GL_VENDOR)) + + "\nOpenGL Version: " + std::string((char *)glGetString(GL_VERSION)) ); WriteLog("--------"); + { + GLint extCount = 0; + glGetIntegerv( GL_NUM_EXTENSIONS, &extCount ); + std::string extensions; - GLint extCount = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &extCount); - - WriteLog("Supported extensions:"); - for (int i = 0; i < extCount; i++) - { - const char *ext = (const char *)glGetStringi(GL_EXTENSIONS, i); - WriteLog(ext); - } - WriteLog("--------"); + WriteLog( "Supported extensions:" ); + for( int i = 0; i < extCount; i++ ) { + const char *ext = (const char *)glGetStringi( GL_EXTENSIONS, i ); + extensions += ext; + extensions += " "; + } + WriteLog( extensions ); + } + WriteLog("--------"); if (!Global.gfx_usegles) { @@ -3860,20 +3998,20 @@ bool opengl33_renderer::Init_caps() } if (GLAD_GL_EXT_texture_filter_anisotropic) - WriteLog("EXT_texture_filter_anisotropic supported!"); + WriteLog("EXT_texture_filter_anisotropic supported."); if (GLAD_GL_EXT_clip_control) - WriteLog("EXT_clip_control supported!"); + WriteLog("EXT_clip_control supported."); if (GLAD_GL_EXT_geometry_shader) - WriteLog("EXT_geometry_shader supported!"); + WriteLog("EXT_geometry_shader supported."); } glGetError(); glLineWidth(2.0f); if (!glGetError()) { - WriteLog("wide lines supported!"); + WriteLog("wide lines supported."); m_widelines_supported = true; } else diff --git a/opengl33renderer.h b/opengl33renderer.h index bd7e2cb3..d712f198 100644 --- a/opengl33renderer.h +++ b/opengl33renderer.h @@ -204,8 +204,10 @@ class opengl33_renderer : public gfx_renderer { void setup_matrices(); void setup_drawing(bool const Alpha = false); void setup_shadow_map(opengl_texture *tex, renderpass_config conf); + void setup_shadow_color( glm::vec4 const &Shadowcolor ); void setup_env_map(gl::cubemap *tex); void setup_environment_light(TEnvironmentType const Environment = e_flat); + void setup_sunlight_intensity( float const Factor = 1.f); // runs jobs needed to generate graphics for specified render pass void Render_pass(viewport_config &vp, rendermode const Mode); // creates dynamic environment cubemap @@ -309,6 +311,7 @@ class opengl33_renderer : public gfx_renderer { GLuint64 m_gllasttime = 0; double m_precipitationrotation; + float m_pointsize{ 8.f }; glm::mat4 perspective_projection(float fov, float aspect, float z_near, float z_far); glm::mat4 perpsective_frustumtest_projection(float fov, float aspect, float z_near, float z_far); diff --git a/openglrenderer.cpp b/openglrenderer.cpp index 6bef883d..2a966ff5 100644 --- a/openglrenderer.cpp +++ b/openglrenderer.cpp @@ -895,7 +895,7 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f glm::dvec3 { Global.pCamera.Pos }, glm::dvec3 { 0.f, 1.f, 0.f } ); // projection - auto const maphalfsize { Config.draw_range * 0.5f }; + auto const maphalfsize { std::min( 10.f, Config.draw_range * 0.5f ) }; camera.projection() *= glm::ortho( -maphalfsize, maphalfsize, diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index cc4506ec..0215603a 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -184,7 +184,7 @@ state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratch Global.fFogEnd = clamp( Random( std::min( fograngestart, fograngeend ), std::max( fograngestart, fograngeend ) ), - 10.0, 2000.0 ); + 10.0, 25000.0 ); } std::string token { Input.getToken() };