diff --git a/Ground.cpp b/Ground.cpp index efe1721f..6025fbee 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -3493,18 +3493,6 @@ void TGround::Update_Lights() { m_lights.update(); - // arrange the light array from closest to farthest from current position of the camera - auto const camera = Global::pCameraPosition; - std::sort( - m_lights.data.begin(), - m_lights.data.end(), - [&]( light_array::light_record const &Left, light_array::light_record const &Right ) { - // move lights which are off at the end... - if( Left.intensity == 0.0f ) { return false; } - if( Right.intensity == 0.0f ) { return true; } - // ...otherwise prefer closer and/or brigher light sources - return ((camera - Left.position).LengthSquared() * (1.0f - Left.intensity)) < ((camera - Right.position).LengthSquared() * (1.0f - Right.intensity)); - } ); } // Winger 170204 - szukanie trakcji nad pantografami diff --git a/World.cpp b/World.cpp index a82dc753..922135c2 100644 --- a/World.cpp +++ b/World.cpp @@ -2220,10 +2220,12 @@ world_environment::update() { m_sun.update(); m_moon.update(); // ...determine source of key light and adjust global state accordingly... - auto const sunlightlevel = m_sun.getIntensity(); + // diffuse (sun) intensity goes down after twilight, and reaches minimum 18 degrees below horizon + float twilightfactor = clamp( -m_sun.getAngle(), 0.0f, 18.0f ) / 18.0f; + // NOTE: sun light receives extra padding to prevent moon from kicking in too soon + auto const sunlightlevel = m_sun.getIntensity() + 0.05f * ( 1.f - twilightfactor ); auto const moonlightlevel = m_moon.getIntensity() * 0.65f; // scaled down by arbitrary factor, it's pretty bright otherwise float keylightintensity; - float twilightfactor; glm::vec3 keylightcolor; if( moonlightlevel > sunlightlevel ) { // rare situations when the moon is brighter than the sun, typically at night @@ -2241,8 +2243,7 @@ world_environment::update() { Global::DayLight.set_position( m_sun.getPosition() ); Global::DayLight.direction = -1.0f * m_sun.getDirection(); keylightintensity = sunlightlevel; - // diffuse (sun) intensity goes down after twilight, and reaches minimum 18 degrees below horizon - twilightfactor = clamp( -Global::SunAngle, 0.0f, 18.0f ) / 18.0f; + // include 'golden hour' effect in twilight lighting float const duskfactor = 1.0f - clamp( Global::SunAngle, 0.0f, 18.0f ) / 18.0f; keylightcolor = interpolate( glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ), diff --git a/frustum.cpp b/frustum.cpp index f1b3eba1..a92f395f 100644 --- a/frustum.cpp +++ b/frustum.cpp @@ -83,14 +83,6 @@ cFrustum::calculate( glm::mat4 const &Projection, glm::mat4 const &Modelview ) { m_frustum[ side_FRONT ][ plane_C ] = clip[ 11 ] + clip[ 10 ]; m_frustum[ side_FRONT ][ plane_D ] = clip[ 15 ] + clip[ 14 ]; normalize_plane( side_FRONT ); - - m_inversetransformation = glm::inverse( Projection * glm::mat4{ glm::mat3{ Modelview } } ); - - // calculate frustum corners - m_frustumpoints = ndcfrustumshapepoints; - transform_to_world( - std::begin( m_frustumpoints ), - std::end( m_frustumpoints ) ); } bool @@ -177,19 +169,6 @@ cFrustum::cube_inside( float const X, float const Y, float const Z, float const return true; } -std::vector const frustumshapepoinstorder{ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 }; - -// debug helper, draws shape of frustum in world space -void -cFrustum::draw( glm::vec3 const &Offset ) const { - - ::glBegin( GL_LINES ); - for( auto const pointindex : frustumshapepoinstorder ) { - ::glVertex3fv( glm::value_ptr( glm::vec3{ m_frustumpoints[ pointindex ] } - Offset ) ); - } - ::glEnd(); -} - void cFrustum::normalize_plane( cFrustum::side const Side ) { float magnitude = diff --git a/frustum.h b/frustum.h index cd59dd85..2029a412 100644 --- a/frustum.h +++ b/frustum.h @@ -12,10 +12,12 @@ http://mozilla.org/MPL/2.0/. #include "float3d.h" #include "dumb3d.h" -std::vector const ndcfrustumshapepoints = { +std::vector const ndcfrustumshapepoints { { -1, -1, -1, 1 },{ 1, -1, -1, 1 },{ 1, 1, -1, 1 },{ -1, 1, -1, 1 }, // z-near { -1, -1, 1, 1 },{ 1, -1, 1, 1 },{ 1, 1, 1, 1 },{ -1, 1, 1, 1 } }; // z-far +std::vector const frustumshapepoinstorder { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 }; + // generic frustum class. used to determine if objects are inside current view area class cFrustum { @@ -65,23 +67,6 @@ public: cube_inside( Math3D::vector3 const &Center, float const Size ) const { return cube_inside( static_cast( Center.x ), static_cast( Center.y ), static_cast( Center.z ), Size ); } bool cube_inside( float const X, float const Y, float const Z, float const Size ) const; - // transforms provided set of clip space points to world space - template - void - transform_to_world( Iterator_ First, Iterator_ Last ) const { - std::for_each( - First, Last, - [this]( glm::vec4 &point ) { - // transform each point using the cached matrix... - point = this->m_inversetransformation * point; - // ...and scale by transformed w - point = glm::vec4{ glm::vec3{ point } / point.w, 1.f }; } ); } - // debug helper, draws shape of frustum in world space - void - draw( glm::vec3 const &Offset ) const; - -// members: - std::vector m_frustumpoints; // coordinates of corners for defined frustum, in world space private: // types: @@ -96,5 +81,4 @@ private: // members: float m_frustum[6][4]; // holds the A B C and D values (normal & distance) for each side of the frustum. - glm::mat4 m_inversetransformation; // cached inverse transformation matrix }; diff --git a/lightarray.cpp b/lightarray.cpp index 8a0412a3..f0486b7f 100644 --- a/lightarray.cpp +++ b/lightarray.cpp @@ -60,10 +60,10 @@ light_array::update() { if( ( true == light.owner->MoverParameters->Battery ) || ( true == light.owner->MoverParameters->ConverterFlag ) ) { // with power on, the intensity depends on the state of activated switches auto const &lightbits = light.owner->iLights[ light.index ]; - light.count = 0 + - ( ( lightbits & 1 ) ? 1 : 0 ) + - ( ( lightbits & 4 ) ? 1 : 0 ) + - ( ( lightbits & 16 ) ? 1 : 0 ); + light.count = 0 + + ( ( lightbits & TMoverParameters::light::headlight_left ) ? 1 : 0 ) + + ( ( lightbits & TMoverParameters::light::headlight_right ) ? 1 : 0 ) + + ( ( lightbits & TMoverParameters::light::headlight_upper ) ? 1 : 0 ); if( light.count > 0 ) { // TODO: intensity can be affected further by dim switch or other factors diff --git a/lightarray.h b/lightarray.h index 91e45ef0..ff7b694e 100644 --- a/lightarray.h +++ b/lightarray.h @@ -19,9 +19,9 @@ public: TDynamicObject const *owner; // the object in world which 'carries' the light int index{ -1 }; // 0: front lights, 1: rear lights - Math3D::vector3 position; // position of the light in 3d scene + glm::dvec3 position; // position of the light in 3d scene glm::vec3 direction; // direction of the light in 3d scene - float3 color{ 255.0f / 255.0f, 241.0f / 255.0f, 224.0f / 255.0f }; // color of the light, default is halogen light + glm::vec3 color{ 255.0f / 255.0f, 241.0f / 255.0f, 224.0f / 255.0f }; // color of the light, default is halogen light float intensity{ 0.0f }; // (combined) intensity of the light(s) int count{ 0 }; // number (or pattern) of active light(s) }; diff --git a/renderer.cpp b/renderer.cpp index bdcd5dee..f78f2c52 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -34,6 +34,20 @@ glm::vec4 const white{ 1.f, 1.f, 1.f, 1.f }; } // namespace colors +void +opengl_camera::update_frustum( glm::mat4 const &Projection, glm::mat4 const &Modelview ) { + + m_frustum.calculate( Projection, Modelview ); + // cache inverse tranformation matrix + // NOTE: transformation is done only to camera-centric space + m_inversetransformation = glm::inverse( Projection * glm::mat4{ glm::mat3{ Modelview } } ); + // calculate frustum corners + m_frustumpoints = ndcfrustumshapepoints; + transform_to_world( + std::begin( m_frustumpoints ), + std::end( m_frustumpoints ) ); +} + // returns true if specified object is within camera frustum, false otherwise bool opengl_camera::visible( bounding_area const &Area ) const { @@ -55,6 +69,17 @@ opengl_camera::visible( TDynamicObject const *Dynamic ) const { return ( m_frustum.sphere_inside( Dynamic->GetPosition(), radius ) > 0.0f ); } +// debug helper, draws shape of frustum in world space +void +opengl_camera::draw( glm::vec3 const &Offset ) const { + + ::glBegin( GL_LINES ); + for( auto const pointindex : frustumshapepoinstorder ) { + ::glVertex3fv( glm::value_ptr( glm::vec3{ m_frustumpoints[ pointindex ] } - Offset ) ); + } + ::glEnd(); +} + bool opengl_renderer::Init( GLFWwindow *Window ) { @@ -251,15 +276,16 @@ opengl_renderer::Render() { m_drawtime = std::max( 20.f, 0.95f * m_drawtime + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - m_drawstart ) ).count() / 1000.f ); } m_drawstart = std::chrono::steady_clock::now(); - auto const drawstartcolor = m_drawstart; + auto const drawstartcolorpass = m_drawstart; m_renderpass.draw_mode = rendermode::none; // force setup anew + m_debuginfo.clear(); Render_pass( rendermode::color ); m_drawcount = m_drawqueue.size(); // accumulate last 20 frames worth of render time (cap at 1000 fps to prevent calculations going awry) - m_drawtimecolor = std::max( 20.f, 0.95f * m_drawtimecolor + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - drawstartcolor ) ).count() / 1000.f ); - m_debuginfo += " frame total: " + to_string( m_drawtimecolor / 20.f, 2 ) + " msec (" + std::to_string( m_drawqueue.size() ) + " sectors)"; + m_drawtimecolorpass = std::max( 20.f, 0.95f * m_drawtimecolorpass + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - drawstartcolorpass ) ).count() / 1000.f ); + m_debuginfo += "frame total: " + to_string( m_drawtimecolorpass / 20.f, 2 ) + " msec (" + std::to_string( m_drawqueue.size() ) + " sectors) "; glfwSwapBuffers( m_window ); @@ -270,25 +296,26 @@ opengl_renderer::Render() { void opengl_renderer::Render_pass( rendermode const Mode ) { - m_renderpass.setup( Mode, m_framebuffersupport ); + setup_pass( m_renderpass, Mode ); switch( m_renderpass.draw_mode ) { case rendermode::color: { opengl_camera shadowcamera; // temporary helper, remove once ortho shadowmap code is done - if( Global::RenderShadows && World.InitPerformed() ) { + if( ( true == Global::RenderShadows ) + && ( true == World.InitPerformed() ) + && ( m_shadowcolor != colors::white ) ) { // run shadowmap pass before color Render_pass( rendermode::shadows ); #ifdef EU07_USE_DEBUG_SHADOWMAP UILayer.set_texture( m_shadowdebugtexture ); #endif shadowcamera = m_renderpass.camera; // cache shadow camera placement for visualization - - m_renderpass.setup( rendermode::color, m_framebuffersupport ); // restore draw mode. TBD, TODO: render mode stack + setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack #ifdef EU07_USE_DEBUG_CAMERA - m_worldcamera.setup( + setup_pass( + m_worldcamera, rendermode::color, - m_framebuffersupport, 0.f, std::min( 1.f, @@ -337,10 +364,10 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glColor4f( 1.f, 0.9f, 0.8f, 1.f ); ::glDisable( GL_LIGHTING ); ::glDisable( GL_TEXTURE_2D ); - shadowcamera.frustum().draw( m_renderpass.camera.position() - shadowcamera.position() ); + shadowcamera.draw( m_renderpass.camera.position() - shadowcamera.position() ); if( DebugCameraFlag ) { ::glColor4f( 0.8f, 1.f, 0.9f, 1.f ); - m_worldcamera.camera.frustum().draw( m_renderpass.camera.position() - m_worldcamera.camera.position() ); + m_worldcamera.camera.draw( m_renderpass.camera.position() - m_worldcamera.camera.position() ); } ::glLineWidth( 1.f ); ::glEnable( GL_LIGHTING ); @@ -397,8 +424,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target - m_drawtimeshadows = 0.95f * m_drawtimeshadows + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - shadowdrawstart ) ).count() / 1000.f; - m_debuginfo = "shadows: " + to_string( m_drawtimeshadows / 20.f, 2 ) + " msec (" + std::to_string( m_drawqueue.size() ) + " sectors)"; + m_drawtimeshadowpass = 0.95f * m_drawtimeshadowpass + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - shadowdrawstart ) ).count() / 1000.f; + m_debuginfo += "shadows: " + to_string( m_drawtimeshadowpass / 20.f, 2 ) + " msec (" + std::to_string( m_drawqueue.size() ) + " sectors) "; } break; } @@ -453,27 +480,29 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } void -opengl_renderer::renderpass_config::setup( rendermode const Mode, bool const Framebuffersupport, float const Znear, float const Zfar, bool const Ignoredebug ) { +opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, float const Znear, float const Zfar, bool const Ignoredebug ) { - draw_mode = Mode; + Config.draw_mode = Mode; if( false == World.InitPerformed() ) { return; } - - switch( draw_mode ) { - case rendermode::color: { draw_range = Global::BaseDrawRange; break; } - case rendermode::shadows: { draw_range = Global::BaseDrawRange * 0.5f; break; } - case rendermode::pickcontrols: { draw_range = 50.f; break; } - case rendermode::pickscenery: { draw_range = Global::BaseDrawRange * 0.5f; break; } - default: { draw_range = 0.f; break; } + // setup draw range + switch( Mode ) { + case rendermode::color: { Config.draw_range = Global::BaseDrawRange; break; } + case rendermode::shadows: { Config.draw_range = Global::BaseDrawRange * 0.5f; break; } + case rendermode::pickcontrols: { Config.draw_range = 50.f; break; } + case rendermode::pickscenery: { Config.draw_range = Global::BaseDrawRange * 0.5f; break; } + default: { Config.draw_range = 0.f; break; } } + // setup camera + auto &camera = Config.camera; camera.projection() = glm::mat4( 1.f ); glm::dmat4 viewmatrix( 1.0 ); - switch( draw_mode ) { + switch( Mode ) { case rendermode::color: { // projection - auto const zfar = draw_range * Global::fDistanceFactor * Zfar; + auto const zfar = Config.draw_range * Global::fDistanceFactor * Zfar; auto const znear = ( Znear > 0.f ? Znear * zfar : @@ -498,33 +527,29 @@ opengl_renderer::renderpass_config::setup( rendermode const Mode, bool const Fra case rendermode::shadows: { // calculate lightview boundaries based on relevant area of the world camera frustum: - // setup chunk of frustum we're interested in... + // ...setup chunk of frustum we're interested in... auto const znear = 0.f; auto const zfar = std::min( 1.f, Global::shadowtune.depth / ( Global::BaseDrawRange * Global::fDistanceFactor ) * std::max( 1.f, Global::ZoomFactor * 0.5f ) ); renderpass_config worldview; - worldview.setup( rendermode::color, Framebuffersupport, znear, zfar, true ); - // ...transform frustum shape to camera-centric world space... - auto frustumchunkshapepoints = ndcfrustumshapepoints; - worldview.camera.frustum().transform_to_world( std::begin( frustumchunkshapepoints ), std::end( frustumchunkshapepoints ) ); - // ...determine the centre of frustum chunk in world space... + setup_pass( worldview, rendermode::color, znear, zfar, true ); + auto &frustumchunkshapepoints = worldview.camera.frustum_points(); + // ...modelview matrix: determine the centre of frustum chunk in world space... glm::vec3 frustumchunkmin, frustumchunkmax; bounding_box( frustumchunkmin, frustumchunkmax, std::begin( frustumchunkshapepoints ), std::end( frustumchunkshapepoints ) ); auto const frustumchunkcentre = ( frustumchunkmin + frustumchunkmax ) * 0.5f; - auto const lighttarget = worldview.camera.position() + glm::dvec3{ frustumchunkcentre }; - + // ...cap the vertical angle to keep shadows from getting too long... auto const lightvector = glm::normalize( glm::vec3{ - -Global::DayLight.direction.x, - std::max( -Global::DayLight.direction.y, 0.15f ), - -Global::DayLight.direction.z } ); - // ...place the light source at the calculated centre... - camera.position() = lighttarget;// -glm::dvec3{ lightvector }; - // ...setup world space light view matrix... + Global::DayLight.direction.x, + std::min( Global::DayLight.direction.y, -0.15f ), + Global::DayLight.direction.z } ); + // ...place the light source at the calculated centre and setup world space light view matrix... + camera.position() = worldview.camera.position() + glm::dvec3{ frustumchunkcentre }; viewmatrix *= glm::lookAt( camera.position(), - camera.position() - glm::dvec3{ lightvector }, + camera.position() + glm::dvec3{ lightvector }, glm::dvec3{ 0.f, 1.f, 0.f } ); - // ...calculate boundaries of the frustum chunk in light space... + // ...projection matrix: calculate boundaries of the frustum chunk in light space... auto const lightviewmatrix = glm::translate( glm::mat4{ glm::mat3{ viewmatrix } }, @@ -547,7 +572,7 @@ opengl_renderer::renderpass_config::setup( rendermode const Mode, bool const Fra case rendermode::pickscenery: { // TODO: scissor test for pick modes // projection - if( true == Framebuffersupport ) { + if( true == m_framebuffersupport ) { auto const angle = Global::FieldOfView / Global::ZoomFactor; auto const height = std::max( 1.0f, (float)Global::iWindowWidth ) / std::max( 1.0f, (float)Global::iWindowHeight ) / ( Global::iWindowWidth / EU07_PICKBUFFERSIZE ); camera.projection() *= @@ -555,7 +580,7 @@ opengl_renderer::renderpass_config::setup( rendermode const Mode, bool const Fra glm::radians( Global::FieldOfView / Global::ZoomFactor ), std::max( 1.f, (float)Global::iWindowWidth ) / std::max( 1.0f, (float)Global::iWindowHeight ) / ( Global::iWindowWidth / EU07_PICKBUFFERSIZE ), 0.1f * Global::ZoomFactor, - draw_range * Global::fDistanceFactor ); + Config.draw_range * Global::fDistanceFactor ); } else { camera.projection() *= @@ -563,7 +588,7 @@ opengl_renderer::renderpass_config::setup( rendermode const Mode, bool const Fra glm::radians( Global::FieldOfView / Global::ZoomFactor ), std::max( 1.f, (float)Global::iWindowWidth ) / std::max( 1.f, (float)Global::iWindowHeight ), 0.1f * Global::ZoomFactor, - draw_range * Global::fDistanceFactor ); + Config.draw_range * Global::fDistanceFactor ); } // modelview camera.position() = Global::pCameraPosition; @@ -681,7 +706,9 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const // shadow texture unit. // interpolates between primary colour and the previous unit, which should hold darkened variant of the primary colour if( m_shadowtextureunit >= 0 ) { - if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + if( ( true == Global::RenderShadows ) + && ( true == Shadows ) + && ( m_shadowcolor != colors::white ) ) { ::glActiveTexture( m_shadowtextureunit ); ::glBindTexture( GL_TEXTURE_2D, m_shadowtexture ); @@ -756,7 +783,9 @@ void opengl_renderer::switch_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { // helper texture unit. if( m_helpertextureunit >= 0 ) { - if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + if( ( true == Global::RenderShadows ) + && ( true == Shadows ) + && ( m_shadowcolor != colors::white ) ) { ::glActiveTexture( m_helpertextureunit ); ::glEnable( GL_TEXTURE_2D ); } @@ -804,6 +833,17 @@ opengl_renderer::setup_shadow_color( glm::vec4 const &Shadowcolor ) { bool opengl_renderer::Render( world_environment *Environment ) { + // calculate shadow tone, based on positions of celestial bodies + m_shadowcolor = interpolate( + glm::vec4{ 0.5f, 0.5f, 0.5f, 1.f }, + 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 = glm::vec4{ 0.5f, 0.5f, 0.5f, 1.f }; + } + if( Global::bWireFrame ) { // bez nieba w trybie rysowania linii return false; @@ -888,7 +928,10 @@ opengl_renderer::Render( world_environment *Environment ) { ::glLoadIdentity(); // macierz jedynkowa ::glTranslatef( moonposition.x, moonposition.y, moonposition.z ); - float const size = 0.02f; // TODO: expose distance/scale factor from the moon object + float const size = interpolate( // TODO: expose distance/scale factor from the moon object + 0.0175f, + 0.015f, + clamp( Environment->m_moon.getAngle(), 0.f, 90.f ) / 90.f ); // choose the moon appearance variant, based on current moon phase // NOTE: implementation specific, 8 variants are laid out in 3x3 arrangement // from new moon onwards, top left to right bottom (last spot is left for future use, if any) @@ -2628,7 +2671,7 @@ opengl_renderer::Update( double const Deltatime ) { m_framerate = 1000.f / ( m_drawtime / 20.f ); // adjust draw ranges etc, based on recent performance - auto const framerate = 1000.0f / (m_drawtimecolor / 20.0f); + auto const framerate = 1000.0f / (m_drawtimecolorpass / 20.0f); float targetfactor; if( framerate > 90.0 ) { targetfactor = 3.0f; } @@ -2697,7 +2740,18 @@ opengl_renderer::Info() const { } void -opengl_renderer::Update_Lights( light_array const &Lights ) { +opengl_renderer::Update_Lights( light_array &Lights ) { + // arrange the light array from closest to farthest from current position of the camera + auto const camera = m_renderpass.camera.position(); + std::sort( + std::begin( Lights.data ), + std::end( Lights.data ), + [&camera]( light_array::light_record const &Left, light_array::light_record const &Right ) { + // move lights which are off at the end... + if( Left.intensity == 0.f ) { return false; } + if( Right.intensity == 0.f ) { return true; } + // ...otherwise prefer closer and/or brigher light sources + return ( glm::length2( camera - Left.position ) * ( 1.f - Left.intensity ) ) < ( glm::length2( camera - Right.position ) * ( 1.f - Right.intensity ) ); } ); size_t const count = std::min( m_lights.size(), Lights.data.size() ); if( count == 0 ) { return; } @@ -2710,40 +2764,35 @@ opengl_renderer::Update_Lights( light_array const &Lights ) { // we ran out of lights to assign break; } - if( scenelight.intensity == 0.0f ) { + if( scenelight.intensity == 0.f ) { // all lights past this one are bound to be off break; } - if( ( m_renderpass.camera.position() - scenelight.position ).Length() > 1000.0f ) { + auto const lightoffset = glm::vec3{ scenelight.position - camera }; + if( glm::length( lightoffset ) > 1000.f ) { // we don't care about lights past arbitrary limit of 1 km. // but there could still be weaker lights which are closer, so keep looking continue; } // if the light passed tests so far, it's good enough - renderlight->set_position( glm::make_vec3( (scenelight.position - m_renderpass.camera.position()).readArray() ) ); + renderlight->set_position( lightoffset ); renderlight->direction = scenelight.direction; - auto luminance = Global::fLuminance; // TODO: adjust this based on location, e.g. for tunnels + auto luminance = static_cast( Global::fLuminance ); + // adjust luminance level based on vehicle's location, e.g. tunnels auto const environment = scenelight.owner->fShade; - if( environment > 0.0f ) { + if( environment > 0.f ) { luminance *= environment; } - renderlight->diffuse[ 0 ] = static_cast( std::max( 0.0, scenelight.color.x - luminance ) ); - renderlight->diffuse[ 1 ] = static_cast( std::max( 0.0, scenelight.color.y - luminance ) ); - renderlight->diffuse[ 2 ] = static_cast( std::max( 0.0, scenelight.color.z - luminance ) ); - renderlight->ambient[ 0 ] = static_cast( std::max( 0.0, scenelight.color.x * scenelight.intensity - luminance) ); - renderlight->ambient[ 1 ] = static_cast( std::max( 0.0, scenelight.color.y * scenelight.intensity - luminance ) ); - renderlight->ambient[ 2 ] = static_cast( std::max( 0.0, scenelight.color.z * scenelight.intensity - luminance ) ); -/* - // NOTE: we have no simple way to determine whether the lights are falling on objects located in darker environment - // until this issue is resolved we're disabling reduction of light strenght based on the global luminance - renderlight->diffuse[ 0 ] = std::max( 0.0f, scenelight.color.x ); - renderlight->diffuse[ 1 ] = std::max( 0.0f, scenelight.color.y ); - renderlight->diffuse[ 2 ] = std::max( 0.0f, scenelight.color.z ); - renderlight->ambient[ 0 ] = std::max( 0.0f, scenelight.color.x * scenelight.intensity ); - renderlight->ambient[ 1 ] = std::max( 0.0f, scenelight.color.y * scenelight.intensity ); - renderlight->ambient[ 2 ] = std::max( 0.0f, scenelight.color.z * scenelight.intensity ); -*/ + renderlight->diffuse = + glm::vec4{ + glm::max( glm::vec3{ colors::none }, scenelight.color - glm::vec3{ luminance } ), + renderlight->diffuse[ 3 ] }; + renderlight->ambient = + glm::vec4{ + glm::max( glm::vec3{ colors::none }, scenelight.color * glm::vec3{ scenelight.intensity } - glm::vec3{ luminance } ), + renderlight->ambient[ 3 ] }; + ::glLightf( renderlight->id, GL_LINEAR_ATTENUATION, static_cast( (0.25 * scenelight.count) / std::pow( scenelight.count, 2 ) * (scenelight.owner->DimHeadlights ? 1.25 : 1.0) ) ); ::glEnable( renderlight->id ); diff --git a/renderer.h b/renderer.h index 001cf9b5..30e5dcad 100644 --- a/renderer.h +++ b/renderer.h @@ -87,10 +87,9 @@ public: // methods: inline void - update_frustum() { m_frustum.calculate( m_projection, m_modelview ); } - inline + update_frustum() { update_frustum( m_projection, m_modelview ); } void - update_frustum(glm::mat4 const &Projection, glm::mat4 const &Modelview) { m_frustum.calculate(Projection, Modelview); } + update_frustum( glm::mat4 const &Projection, glm::mat4 const &Modelview ); bool visible( bounding_area const &Area ) const; bool @@ -114,15 +113,31 @@ public: glm::mat4 & modelview() { return m_modelview; } inline - cFrustum const & - frustum() { return m_frustum; } + std::vector & + frustum_points() { return m_frustumpoints; } + // transforms provided set of clip space points to world space + template + void + transform_to_world( Iterator_ First, Iterator_ Last ) const { + std::for_each( + First, Last, + [this]( glm::vec4 &point ) { + // transform each point using the cached matrix... + point = this->m_inversetransformation * point; + // ...and scale by transformed w + point = glm::vec4{ glm::vec3{ point } / point.w, 1.f }; } ); } + // debug helper, draws shape of frustum in world space + void + draw( glm::vec3 const &Offset ) const; private: // members: cFrustum m_frustum; + std::vector m_frustumpoints; // visualization helper; corners of defined frustum, in world space glm::dvec3 m_position; glm::mat4 m_projection; glm::mat4 m_modelview; + glm::mat4 m_inversetransformation; // cached transformation to world space }; // bare-bones render controller, in lack of anything better yet @@ -207,9 +222,6 @@ private: opengl_camera camera; rendermode draw_mode { rendermode::none }; float draw_range { 0.0f }; - - void - setup( rendermode const Mode, bool const Framebuffersupport, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false ); }; typedef std::vector opengllight_array; @@ -220,6 +232,8 @@ private: // runs jobs needed to generate graphics for specified render pass void Render_pass( rendermode const Mode ); + void + setup_pass( renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false ); void setup_matrices(); void @@ -269,7 +283,7 @@ private: void Render_Alpha( TSubModel *Submodel ); void - Update_Lights( light_array const &Lights ); + Update_Lights( light_array &Lights ); glm::vec3 pick_color( std::size_t const Index ); std::size_t @@ -309,8 +323,8 @@ private: float m_drawtime { 1000.f / 30.f * 20.f }; // start with presumed 'neutral' average of 30 fps std::chrono::steady_clock::time_point m_drawstart; // cached start time of previous frame float m_framerate; - float m_drawtimecolor { 1000.f / 30.f * 20.f }; - float m_drawtimeshadows { 0.f }; + float m_drawtimecolorpass { 1000.f / 30.f * 20.f }; + float m_drawtimeshadowpass { 0.f }; double m_updateaccumulator { 0.0 }; std::string m_debuginfo; diff --git a/sun.cpp b/sun.cpp index dc65d471..ed08aa2b 100644 --- a/sun.cpp +++ b/sun.cpp @@ -251,21 +251,18 @@ void cSun::refract() { void cSun::irradiance() { - static double degrad = 57.295779513; // converts from radians to degrees - static double raddeg = 0.0174532925; // converts from degrees to radians - m_body.dayang = ( simulation::Time.year_day() - 1 ) * 360.0 / 365.0; - double sd = sin( raddeg * m_body.dayang ); // sine of the day angle - double cd = cos( raddeg * m_body.dayang ); // cosine of the day angle or delination + double sd = std::sin( glm::radians( m_body.dayang ) ); // sine of the day angle + double cd = std::cos( glm::radians( m_body.dayang ) ); // cosine of the day angle or delination m_body.erv = 1.000110 + 0.034221*cd + 0.001280*sd; double d2 = 2.0 * m_body.dayang; - double c2 = cos( raddeg * d2 ); - double s2 = sin( raddeg * d2 ); + double c2 = std::cos( glm::radians( d2 ) ); + double s2 = std::sin( glm::radians( d2 ) ); m_body.erv += 0.000719*c2 + 0.000077*s2; double solcon = 1367.0; // Solar constant, 1367 W/sq m - m_body.coszen = cos( raddeg * m_body.zenref ); + m_body.coszen = std::cos( glm::radians( m_body.zenref ) ); if( m_body.coszen > 0.0 ) { m_body.etrn = solcon * m_body.erv; m_body.etr = m_body.etrn * m_body.coszen;