From d13221b2703ae5ea0cd37376fcd4ad0f4d3ab58a Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 3 Aug 2020 16:49:01 +0200 Subject: [PATCH] reflection map fidelity level --- Globals.cpp | 10 +++- Globals.h | 7 ++- Timer.cpp | 6 +++ Timer.h | 1 + driveruipanels.cpp | 5 +- opengl33renderer.cpp | 125 ++++++++++++++++++++++++++++++++++++++----- openglrenderer.cpp | 117 ++++++++++++++++++++++++++++++++++++---- 7 files changed, 244 insertions(+), 27 deletions(-) diff --git a/Globals.cpp b/Globals.cpp index 925973eb..d0380273 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -447,7 +447,12 @@ global_settings::ConfigParse(cParser &Parser) { else if( token == "gfx.reflections.framerate" ) { auto const updatespersecond { std::abs( Parser.getToken() ) }; - ReflectionUpdateInterval = 1.0 / updatespersecond; + reflectiontune.update_interval = 1.0 / updatespersecond; + } + else if( token == "gfx.reflections.fidelity" ) { + Parser.getTokens( 1, false ); + Parser >> reflectiontune.fidelity; + reflectiontune.fidelity = clamp( reflectiontune.fidelity, 0, 2 ); } else if( token == "timespeed" ) { // przyspieszenie czasu, zmienna do testów @@ -1032,7 +1037,8 @@ global_settings::export_as_text( std::ostream &Output ) const { export_as_text( Output, "createswitchtrackbeds", CreateSwitchTrackbeds ); export_as_text( Output, "gfx.resource.sweep", ResourceSweep ); export_as_text( Output, "gfx.resource.move", ResourceMove ); - export_as_text( Output, "gfx.reflections.framerate", 1.0 / ReflectionUpdateInterval ); + export_as_text( Output, "gfx.reflections.framerate", 1.0 / reflectiontune.update_interval ); + export_as_text( Output, "gfx.reflections.fidelity", reflectiontune.fidelity ); export_as_text( Output, "timespeed", fTimeSpeed ); export_as_text( Output, "multisampling", iMultisampling ); export_as_text( Output, "latitude", fLatitudeDeg ); diff --git a/Globals.h b/Globals.h index d8c33631..75803bab 100644 --- a/Globals.h +++ b/Globals.h @@ -127,7 +127,12 @@ struct global_settings { unsigned int map_size{ 2048 }; float range{ 250.f }; } shadowtune; - double ReflectionUpdateInterval{ 300.0 }; + struct reflectiontune_t { + double update_interval{ 300.0 }; + int fidelity{ 0 }; // 0: sections, 1: +static models, 2: +vehicles + float range_instances{ 750.f }; + float range_vehicles{ 250.f }; + } reflectiontune; bool bUseVBO{ true }; // czy jest VBO w karcie graficznej (czy użyć) 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 diff --git a/Timer.cpp b/Timer.cpp index fcd038a0..e0ed3f17 100644 --- a/Timer.cpp +++ b/Timer.cpp @@ -20,6 +20,7 @@ double fFPS{ 0.0f }; double fLastTime{ 0.0f }; DWORD dwFrames{ 0 }; double fSimulationTime{ 0.0 }; +double fRenderTime{ 0.0 }; double fSoundTimer{ 0.0 }; double fSinceStart{ 0.0 }; double override_delta = -1.0f; @@ -29,6 +30,10 @@ double GetTime() return fSimulationTime; } +double GetRenderTime() { + return fRenderTime; +} + double GetDeltaTime() { // czas symulacji (stoi gdy pauza) if (override_delta != -1.0f) @@ -67,6 +72,7 @@ void UpdateTimers(bool pause) fr = 1000000000; #endif DeltaRenderTime = double(count - oldCount) / double(fr); + fRenderTime += DeltaRenderTime; if (!pause) { DeltaTime = Global.fTimeSpeed * DeltaRenderTime; diff --git a/Timer.h b/Timer.h index b87e473b..b0f1870d 100644 --- a/Timer.h +++ b/Timer.h @@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/. namespace Timer { double GetTime(); +double GetRenderTime(); double GetDeltaTime(); double GetDeltaRenderTime(); diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 2074d091..d9d5e2be 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -572,7 +572,10 @@ debug_panel::render() { ImGui::Checkbox( "Debug Traction", &DebugTractionFlag ); } render_section( "Camera", m_cameralines ); - render_section( "Gfx Renderer", m_rendererlines ); + if( true == render_section( "Gfx Renderer", m_rendererlines ) ) { + // reflection fidelity + ImGui::SliderInt( ( to_string( Global.reflectiontune.fidelity ) + "###reflectionfidelity" ).c_str(), &Global.reflectiontune.fidelity, 0, 2, "Reflection fidelity" ); + } // toggles ImGui::Separator(); ImGui::Checkbox( "Debug Mode", &DebugModeFlag ); diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index 69f110a3..abb7a5d9 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -22,7 +22,8 @@ http://mozilla.org/MPL/2.0/. #include "application.h" #include "AnimModel.h" -int const EU07_PICKBUFFERSIZE{1024}; // size of (square) textures bound with the pick framebuffer +int const EU07_PICKBUFFERSIZE{ 1024 }; // size of (square) textures bound with the pick framebuffer +int const EU07_REFLECTIONFIDELITYOFFSET { 250 }; // artificial increase of range for reflection pass detail reduction auto const gammacorrection { glm::vec3( 2.2f ) }; @@ -1110,12 +1111,12 @@ bool opengl33_renderer::Render_coupler_adapter( TDynamicObject *Dynamic, float c // creates dynamic environment cubemap bool opengl33_renderer::Render_reflections(viewport_config &vp) { - if( Global.ReflectionUpdateInterval == 0 ) { return false; } + if( Global.reflectiontune.update_interval == 0 ) { return false; } - auto const timestamp{ Timer::GetTime() }; - if( ( timestamp - m_environmentupdatetime < Global.ReflectionUpdateInterval ) - && ( glm::length( m_renderpass.pass_camera.position() - m_environmentupdatelocation ) < 1000.0 ) ) { - // run update every 5+ mins of simulation time, or at least 1km from the last location + auto const timestamp{ Timer::GetRenderTime() }; + if( ( timestamp - m_environmentupdatetime < Global.reflectiontune.update_interval ) + && ( glm::length( m_renderpass.pass_camera.position() - m_environmentupdatelocation ) < 1000.0 ) ) { + // run update every 5+ mins of simulation time, or at least 1km from the last location return false; } m_environmentupdatetime = timestamp; @@ -2044,8 +2045,10 @@ void opengl33_renderer::Render(scene::basic_region *Region) } case rendermode::reflections: { - // for the time being reflections render only terrain geometry Render(std::begin(m_sectionqueue), std::end(m_sectionqueue)); + if( Global.reflectiontune.fidelity >= 1 ) { + Render(std::begin(m_cellqueue), std::end(m_cellqueue)); + } break; } case rendermode::pickcontrols: @@ -2120,6 +2123,7 @@ void opengl33_renderer::Render(section_sequence::iterator First, section_sequenc case rendermode::shadows: case rendermode::pickscenery: { + // TBD, TODO: refactor into a method to reuse below? for (auto &cell : section->m_cells) { if ((true == cell.m_active) && (m_renderpass.pass_camera.visible(cell.m_area))) @@ -2131,6 +2135,19 @@ void opengl33_renderer::Render(section_sequence::iterator First, section_sequenc break; } case rendermode::reflections: + { + // we can skip filling the cell queue if reflections pass isn't going to use it + if( Global.reflectiontune.fidelity == 0 ) { break; } + for (auto &cell : section->m_cells) + { + if ((true == cell.m_active) && (m_renderpass.pass_camera.visible(cell.m_area))) + { + // store visible cells with content as well as their current distance, for sorting later + m_cellqueue.emplace_back(glm::length2(m_renderpass.pass_camera.position() - cell.m_area.center), &cell); + } + } + break; + } case rendermode::pickcontrols: default: { @@ -2190,6 +2207,23 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite break; } + case rendermode::reflections: + { + // 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); + + // post-render cleanup + ::glPopMatrix(); + + break; + } case rendermode::shadows: { // since all shapes of the section share center point we can optimize out a few calls here @@ -2232,7 +2266,6 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite ::glPopMatrix(); break; } - case rendermode::reflections: case rendermode::pickcontrols: default: { @@ -2251,8 +2284,9 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite switch (m_renderpass.draw_mode) { case rendermode::color: - case rendermode::shadows: + case rendermode::shadows: { + // TBD, TODO: refactor in to a method to reuse in branch below? // opaque parts of instanced models for (auto *instance : cell->m_instancesopaque) { @@ -2268,7 +2302,25 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite } break; } - case rendermode::pickscenery: + case rendermode::reflections: + { + if( Global.reflectiontune.fidelity >= 1 ) { + // opaque parts of instanced models + for( auto *instance : cell->m_instancesopaque ) { + Render( instance ); + } + } + if( Global.reflectiontune.fidelity >= 2 ) { + // opaque parts of vehicles + for( auto *path : cell->m_paths ) { + for( auto *dynamic : path->Dynamics ) { + Render( dynamic ); + } + } + } + break; + } + case rendermode::pickscenery: { // opaque parts of instanced models // same procedure like with regular render, but each node receives custom colour used for picking @@ -2280,7 +2332,6 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite // vehicles aren't included in scenery picking for the time being break; } - case rendermode::reflections: case rendermode::pickcontrols: default: { @@ -2333,6 +2384,19 @@ void opengl33_renderer::Render(scene::shape_node const &Shape, bool const Ignore distancesquared = Math3D::SquareMagnitude((data.area.center - m_renderpass.viewport_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; break; } + case rendermode::reflections: + { + // reflection mode draws simplified version of the shapes, by artificially increasing view range + distancesquared = + // TBD, TODO: bind offset value with setting variable? + ( EU07_REFLECTIONFIDELITYOFFSET * EU07_REFLECTIONFIDELITYOFFSET ) + + glm::length2( ( data.area.center - m_renderpass.pass_camera.position() ) ); +/* + // TBD: take into account distance multipliers? + / Global.fDistanceFactor; +*/ + break; + } default: { distancesquared = glm::length2((data.area.center - m_renderpass.pass_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; @@ -2385,6 +2449,20 @@ void opengl33_renderer::Render(TAnimModel *Instance) distancesquared = glm::length2((Instance->location() - m_renderpass.viewport_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; break; } + case rendermode::reflections: + { + // reflection mode draws simplified version of the shapes, by artificially increasing view range + // it also ignores zoom settings and distance multipliers + // TBD: take into account distance multipliers? + distancesquared = glm::length2((Instance->location() - m_renderpass.pass_camera.position())) /* / Global.fDistanceFactor */; + // NOTE: arbitrary draw range limit + if( distancesquared > Global.reflectiontune.range_instances * Global.reflectiontune.range_instances ) { + return; + } + // TBD, TODO: bind offset value with setting variable? + distancesquared += ( EU07_REFLECTIONFIDELITYOFFSET * EU07_REFLECTIONFIDELITYOFFSET ); + break; + } default: { distancesquared = glm::length2((Instance->location() - m_renderpass.pass_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; @@ -2460,6 +2538,24 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) } break; } + case rendermode::reflections: + { + // reflection mode draws simplified version of the shapes, by artificially increasing view range + // it also ignores zoom settings and distance multipliers + squaredistance = + std::max( + 100.f * 100.f, + // TBD: take into account distance multipliers? + glm::length2( glm::vec3{ originoffset } ) /* / Global.fDistanceFactor */ ); + // NOTE: arbitrary draw range limit + if( squaredistance > Global.reflectiontune.range_vehicles * Global.reflectiontune.range_vehicles ) { + return false; + } + // TBD, TODO: bind offset value with setting variable? + // NOTE: combined 'squared' distance doesn't equal actual squared (distance + offset) but, eh + squaredistance += ( EU07_REFLECTIONFIDELITYOFFSET * EU07_REFLECTIONFIDELITYOFFSET ); + break; + } default: { squaredistance = glm::length2(glm::vec3{originoffset} / Global.ZoomFactor); @@ -2467,7 +2563,11 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) break; } } - + // crude way to reject early items too far to affect the output (mostly relevant for shadow and reflection passes) + auto const drawdistancethreshold{ m_renderpass.draw_range + 250 }; + if( squaredistance > drawdistancethreshold * drawdistancethreshold ) { + return false; + } // second stage visibility cull, reject vehicles too far away to be noticeable auto const squareradius{ Dynamic->radius() * Dynamic->radius() }; Dynamic->renderme = ( squareradius * Global.ZoomFactor / squaredistance > 0.003 * 0.003 ); @@ -2493,6 +2593,7 @@ bool opengl33_renderer::Render(TDynamicObject *Dynamic) { case rendermode::color: + case rendermode::reflections: { if (Dynamic->fShade > 0.0f) { diff --git a/openglrenderer.cpp b/openglrenderer.cpp index 0513bed1..601ee9a7 100644 --- a/openglrenderer.cpp +++ b/openglrenderer.cpp @@ -26,6 +26,7 @@ http://mozilla.org/MPL/2.0/. int const EU07_PICKBUFFERSIZE { 1024 }; // size of (square) textures bound with the pick framebuffer int const EU07_ENVIRONMENTBUFFERSIZE { 256 }; // size of (square) environmental cube map texture +int const EU07_REFLECTIONFIDELITYOFFSET { 250 }; // artificial increase of range for reflection pass detail reduction float const EU07_OPACITYDEFAULT { 0.5f }; @@ -871,10 +872,10 @@ bool opengl_renderer::Render_coupler_adapter( TDynamicObject *Dynamic, float con bool opengl_renderer::Render_reflections() { - if( Global.ReflectionUpdateInterval == 0 ) { return false; } + if( Global.reflectiontune.update_interval == 0 ) { return false; } - auto const timestamp { Timer::GetTime() }; - if( ( timestamp - m_environmentupdatetime < Global.ReflectionUpdateInterval ) + auto const timestamp { Timer::GetRenderTime() }; + if( ( timestamp - m_environmentupdatetime < Global.reflectiontune.update_interval ) && ( glm::length( m_renderpass.camera.position() - m_environmentupdatelocation ) < 1000.0 ) ) { // run update every 5+ mins of simulation time, or at least 1km from the last location return false; @@ -1847,8 +1848,10 @@ opengl_renderer::Render( scene::basic_region *Region ) { break; } case rendermode::reflections: { - // for the time being reflections render only terrain geometry Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); + if( Global.reflectiontune.fidelity >= 1 ) { + Render(std::begin(m_cellqueue), std::end(m_cellqueue)); + } break; } case rendermode::pickcontrols: @@ -1918,6 +1921,7 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite case rendermode::shadows: case rendermode::cabshadows: case rendermode::pickscenery: { + // TBD, TODO: refactor into a method to reuse below? for( auto &cell : section->m_cells ) { if( ( true == cell.m_active ) && ( m_renderpass.camera.visible( cell.m_area ) ) ) { @@ -1929,7 +1933,17 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite } break; } - case rendermode::reflections: + case rendermode::reflections: { + // we can skip filling the cell queue if reflections pass isn't going to use it + if( Global.reflectiontune.fidelity == 0 ) { break; } + for( auto &cell : section->m_cells ) { + if( ( true == cell.m_active ) && ( m_renderpass.camera.visible( cell.m_area ) ) ) { + // store visible cells with content as well as their current distance, for sorting later + m_cellqueue.emplace_back( glm::length2( m_renderpass.camera.position() - cell.m_area.center ), &cell ); + } + } + break; + } case rendermode::pickcontrols: default: { break; @@ -2011,6 +2025,22 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator break; } + case rendermode::reflections: + { + // 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.camera.position() }; + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + + // render + // opaque non-instanced shapes + for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } + + // post-render cleanup + ::glPopMatrix(); + + break; + } case rendermode::shadows: { // since all shapes of the section share center point we can optimize out a few calls here ::glPushMatrix(); @@ -2019,7 +2049,9 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // render // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } + for( auto const &shape : cell->m_shapesopaque ) { + Render( shape, false ); + } // tracks Render( std::begin( cell->m_paths ), std::end( cell->m_paths ) ); @@ -2036,7 +2068,9 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // render // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } + 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 @@ -2066,7 +2100,6 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator break; } - case rendermode::reflections: case rendermode::pickcontrols: default: { break; @@ -2084,8 +2117,11 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator case rendermode::color: case rendermode::shadows: case rendermode::cabshadows: { + // TBD, TODO: refactor in to a method to reuse in branch below? // opaque parts of instanced models - for( auto *instance : cell->m_instancesopaque ) { Render( instance ); } + for( auto *instance : cell->m_instancesopaque ) { + Render( instance ); + } // opaque parts of vehicles for( auto *path : cell->m_paths ) { for( auto *dynamic : path->Dynamics ) { @@ -2105,6 +2141,23 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator } break; } + case rendermode::reflections: { + if( Global.reflectiontune.fidelity >= 1 ) { + // opaque parts of instanced models + for( auto *instance : cell->m_instancesopaque ) { + Render( instance ); + } + } + if( Global.reflectiontune.fidelity >= 2 ) { + // opaque parts of vehicles + for( auto *path : cell->m_paths ) { + for( auto *dynamic : path->Dynamics ) { + Render( dynamic ); + } + } + } + break; + } case rendermode::pickscenery: { // opaque parts of instanced models // same procedure like with regular render, but each node receives custom colour used for picking @@ -2123,7 +2176,6 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // vehicles aren't included in scenery picking for the time being break; } - case rendermode::reflections: case rendermode::pickcontrols: default: { break; @@ -2180,6 +2232,15 @@ opengl_renderer::Render( scene::shape_node const &Shape, bool const Ignorerange distancesquared = Math3D::SquareMagnitude( ( data.area.center - Global.pCamera.Pos ) / Global.ZoomFactor ) / Global.fDistanceFactor; break; } + case rendermode::reflections: { + // reflection mode draws simplified version of the shapes, by artificially increasing view range + distancesquared = + // TBD, TODO: bind offset value with setting variable? + ( EU07_REFLECTIONFIDELITYOFFSET * EU07_REFLECTIONFIDELITYOFFSET ) + // TBD: take into account distance multipliers? + + glm::length2( ( data.area.center - m_renderpass.camera.position() ) ) /* / Global.fDistanceFactor */; + break; + } default: { distancesquared = glm::length2( ( data.area.center - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor; break; @@ -2235,6 +2296,19 @@ opengl_renderer::Render( TAnimModel *Instance ) { distancesquared = glm::length2((Instance->location() - m_renderpass.camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; break; } + case rendermode::reflections: { + // reflection mode draws simplified version of the shapes, by artificially increasing view range + // it also ignores zoom settings and distance multipliers + // TBD: take into account distance multipliers? + distancesquared = glm::length2( ( Instance->location() - m_renderpass.camera.position() ) ) /* / Global.fDistanceFactor */; + // NOTE: arbitrary draw range limit + if( distancesquared > Global.reflectiontune.range_instances * Global.reflectiontune.range_instances ) { + return; + } + // TBD, TODO: bind offset value with setting variable? + distancesquared += ( EU07_REFLECTIONFIDELITYOFFSET * EU07_REFLECTIONFIDELITYOFFSET ); + break; + } default: { distancesquared = glm::length2((Instance->location() - m_renderpass.camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; break; @@ -2308,13 +2382,34 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { squaredistance = std::max( 100.f * 100.f, squaredistance ); break; } + case rendermode::reflections: { + // reflection mode draws simplified version of the shapes, by artificially increasing view range + // it also ignores zoom settings and distance multipliers + squaredistance = + std::max( + 100.f * 100.f, + // TBD: take into account distance multipliers? + glm::length2( glm::vec3{ originoffset } ) /* / Global.fDistanceFactor */ ); + // NOTE: arbitrary draw range limit + if( squaredistance > Global.reflectiontune.range_vehicles * Global.reflectiontune.range_vehicles ) { + return false; + } + // TBD, TODO: bind offset value with setting variable? + // NOTE: combined 'squared' distance doesn't equal actual squared (distance + offset) but, eh + squaredistance += ( EU07_REFLECTIONFIDELITYOFFSET * EU07_REFLECTIONFIDELITYOFFSET ); + break; + } default: { squaredistance = glm::length2( glm::vec3{ originoffset } / Global.ZoomFactor ); // TODO: filter out small details based on fidelity setting break; } } - + // crude way to reject early items too far to affect the output (mostly relevant for shadow and reflection passes) + auto const drawdistancethreshold{ m_renderpass.draw_range + 250 }; + if( squaredistance > drawdistancethreshold * drawdistancethreshold ) { + return false; + } // second stage visibility cull, reject vehicles too far away to be noticeable Dynamic->renderme = ( Dynamic->radius() * Global.ZoomFactor / std::sqrt( squaredistance ) > 0.003 ); if( false == Dynamic->renderme ) {