diff --git a/Texture.cpp b/Texture.cpp index ca68d695..889aeb72 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -28,7 +28,7 @@ http://mozilla.org/MPL/2.0/. #define EU07_DEFERRED_TEXTURE_UPLOAD -std::array opengl_texture::units = { 0 }; +std::array opengl_texture::units = { 0 }; GLint opengl_texture::m_activeunit = -1; std::unordered_map opengl_texture::precompressed_formats = diff --git a/Texture.h b/Texture.h index d0741c65..2bc73f24 100644 --- a/Texture.h +++ b/Texture.h @@ -60,7 +60,7 @@ struct opengl_texture { GLint components_hint = 0; // components that material wants GLenum target = GL_TEXTURE_2D; - static std::array units; + static std::array units; static GLint m_activeunit; private: diff --git a/gl/glsl_common.cpp b/gl/glsl_common.cpp index 7ca89dce..fd1e761f 100644 --- a/gl/glsl_common.cpp +++ b/gl/glsl_common.cpp @@ -19,6 +19,7 @@ void gl::glsl_common_setup() const uint LIGHT_SPOT = 0U; const uint LIGHT_POINT = 1U; const uint LIGHT_DIR = 2U; + const uint LIGHT_HEADLIGHTS = 3U; struct light_s { @@ -36,6 +37,9 @@ void gl::glsl_common_setup() float intensity; float ambient; + + mat4 headlight_projection; + vec4 headlight_weights; }; layout(std140) uniform light_ubo diff --git a/gl/shader.cpp b/gl/shader.cpp index 462cb959..fe8f52ec 100644 --- a/gl/shader.cpp +++ b/gl/shader.cpp @@ -272,8 +272,9 @@ void gl::program::init() glUniform1i(loc, e.id); } - glUniform1i(glGetUniformLocation(*this, "shadowmap"), MAX_TEXTURES + 0); - glUniform1i(glGetUniformLocation(*this, "envmap"), MAX_TEXTURES + 1); + glUniform1i(glGetUniformLocation(*this, "shadowmap"), gl::SHADOW_TEX); + glUniform1i(glGetUniformLocation(*this, "envmap"), gl::ENV_TEX); + glUniform1i( glGetUniformLocation( *this, "headlightmap" ), gl::HEADLIGHT_TEX); GLuint index; diff --git a/gl/ubo.h b/gl/ubo.h index 2f6962e7..22aa2324 100644 --- a/gl/ubo.h +++ b/gl/ubo.h @@ -33,6 +33,10 @@ namespace gl const size_t MAX_TEXTURES = 8; const size_t ENVMAP_SIZE = 1024; const size_t MAX_CASCADES = 3; + const size_t HELPER_TEXTURES = 4; + const size_t SHADOW_TEX = MAX_TEXTURES + 0; + const size_t ENV_TEX = MAX_TEXTURES + 1; + const size_t HEADLIGHT_TEX = MAX_TEXTURES + 2; struct scene_ubs { @@ -76,7 +80,8 @@ namespace gl { SPOT = 0, POINT, - DIR + DIR, + HEADLIGHTS }; glm::vec3 pos; @@ -93,9 +98,12 @@ namespace gl float intensity; float ambient; + + glm::mat4 headlight_projection; + glm::vec4 headlight_weights; }; - static_assert(sizeof(light_element_ubs) == 64, "bad size of ubs"); + static_assert(sizeof(light_element_ubs) == 144, "bad size of ubs"); const size_t MAX_LIGHTS = 8; diff --git a/lightarray.cpp b/lightarray.cpp index aa22a957..8391c5e8 100644 --- a/lightarray.cpp +++ b/lightarray.cpp @@ -46,12 +46,12 @@ light_array::update() { // update light parameters to match current data of the owner if( light.index == end::front ) { // front light set - light.position = light.owner->GetPosition() + ( light.owner->VectorFront() * light.owner->GetLength() * 0.4 ); + light.position = light.owner->GetPosition() + ( light.owner->VectorFront() * ( std::max( 0.0, light.owner->GetLength() * 0.5 - 2.0 ) ) );// +( light.owner->VectorUp() * 0.25 ); light.direction = glm::make_vec3( light.owner->VectorFront().getArray() ); } else { // rear light set - light.position = light.owner->GetPosition() - ( light.owner->VectorFront() * light.owner->GetLength() * 0.4 ); + light.position = light.owner->GetPosition() - ( light.owner->VectorFront() * ( std::max( 0.0, light.owner->GetLength() * 0.5 - 2.0 ) ) );// +( light.owner->VectorUp() * 0.25 ); light.direction = glm::make_vec3( light.owner->VectorFront().getArray() ); light.direction.x = -light.direction.x; light.direction.z = -light.direction.z; @@ -72,9 +72,15 @@ light_array::update() { light.intensity = std::max( 0.0f, std::log( (float)light.count + 1.0f ) ); light.intensity *= ( light.owner->DimHeadlights ? 0.6f : 1.0f ); // TBD, TODO: intensity can be affected further by other factors + light.state = { + ( ( lights & light::headlight_left ) ? 1.f : 0.f ), + ( ( lights & light::headlight_upper ) ? 1.f : 0.f ), + ( ( lights & light::headlight_right ) ? 1.f : 0.f ) }; + light.state *= ( light.owner->DimHeadlights ? 0.6f : 1.0f ); } else { light.intensity = 0.0f; + light.state = glm::vec3{ 0.f }; } } else { diff --git a/lightarray.h b/lightarray.h index 0c22e13a..e826440f 100644 --- a/lightarray.h +++ b/lightarray.h @@ -21,6 +21,7 @@ public: 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) + glm::vec3 state{ 0.f }; // state of individual lights }; // methods diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index 8623db99..aeac2497 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -39,8 +39,7 @@ ErrorCallback( GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei l bool opengl33_renderer::Init(GLFWwindow *Window) { /* - if( false == Global.gfx_usegles ) { - // enable for gles after move to 3.2+ + if( GLAD_GL_KHR_debug ) { glEnable( GL_DEBUG_OUTPUT ); glDebugMessageCallback( ErrorCallback, 0 ); } @@ -82,6 +81,7 @@ bool opengl33_renderer::Init(GLFWwindow *Window) m_suntexture = Fetch_Texture("fx/sun"); m_moontexture = Fetch_Texture("fx/moon"); m_smoketexture = Fetch_Texture("fx/smoke"); + m_headlightstexture = Fetch_Texture("fx/headlights:st"); // prepare basic geometry chunks float const size = 2.5f / 2.0f; @@ -1329,7 +1329,7 @@ void opengl33_renderer::setup_drawing(bool const Alpha) void opengl33_renderer::setup_shadow_unbind_map() { - opengl_texture::unbind( gl::MAX_TEXTURES + 0 ); + opengl_texture::unbind( gl::SHADOW_TEX ); } // binds shadow map and updates shadow map uniform data @@ -1337,7 +1337,7 @@ void opengl33_renderer::setup_shadow_bind_map() { if( false == Global.gfx_shadowmap_enabled ) { return; } - m_shadow_tex->bind(gl::MAX_TEXTURES + 0); + m_shadow_tex->bind(gl::SHADOW_TEX); glm::mat4 coordmove; @@ -1386,12 +1386,12 @@ void opengl33_renderer::setup_env_map(gl::cubemap *tex) { if (tex) { - tex->bind(GL_TEXTURE0 + gl::MAX_TEXTURES + 1); + tex->bind(GL_TEXTURE0 + gl::ENV_TEX); glActiveTexture(GL_TEXTURE0); } else { - glActiveTexture(GL_TEXTURE0 + gl::MAX_TEXTURES + 1); + glActiveTexture(GL_TEXTURE0 + gl::ENV_TEX); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glActiveTexture(GL_TEXTURE0); } @@ -3958,43 +3958,56 @@ void opengl33_renderer::Update_Lights(light_array &Lights) { glDebug("Update_Lights"); + Bind_Texture( gl::HEADLIGHT_TEX, m_headlightstexture ); + // arrange the light array from closest to farthest from current position of the camera auto const camera = m_renderpass.pass_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)); - }); + 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) / Left.intensity) < (glm::length2(camera - Right.position) / Right.intensity); + }); - auto renderlight = m_lights.begin(); - size_t light_i = 1; + // set up helpers + glm::mat4 coordmove; + if (GLAD_GL_ARB_clip_control || GLAD_GL_EXT_clip_control) + // transform 1..-1 NDC xy coordinates to 1..0 + coordmove = glm::mat4( // + 0.5, 0.0, 0.0, 0.0, // + 0.0, 0.5, 0.0, 0.0, // + 0.0, 0.0, 1.0, 0.0, // + 0.5, 0.5, 0.0, 1.0 // + ); + else + // without clip_control we also need to transform z + coordmove = glm::mat4( // + 0.5, 0.0, 0.0, 0.0, // + 0.0, 0.5, 0.0, 0.0, // + 0.0, 0.0, 0.5, 0.0, // + 0.5, 0.5, 0.5, 1.0 // + ); + glm::mat4 mv = OpenGLMatrices.data( GL_MODELVIEW ); - glm::mat4 mv = OpenGLMatrices.data(GL_MODELVIEW); + // fill vehicle headlights data + auto renderlight = m_lights.begin(); + size_t light_i = 1; - for (auto const &scenelight : Lights.data) + for (auto const &scenelight : Lights.data) { - - if (renderlight == m_lights.end()) - { + if (renderlight == m_lights.end()) { // we ran out of lights to assign break; } - if (scenelight.intensity == 0.f) - { + if (scenelight.intensity == 0.f) { // all lights past this one are bound to be off break; } auto const lightoffset = glm::vec3{scenelight.position - camera}; - if (glm::length(lightoffset) > 1000.f) - { + 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; @@ -4016,39 +4029,89 @@ void opengl33_renderer::Update_Lights(light_array &Lights) renderlight->apply_intensity( ( scenelight.count * 0.5 ) * ( scenelight.owner->DimHeadlights ? 0.5 : 1.0 ) ); renderlight->apply_angle(); - gl::light_element_ubs *l = &light_ubs.lights[light_i]; - l->pos = mv * glm::vec4(renderlight->position, 1.0f); - l->dir = mv * glm::vec4(renderlight->direction, 0.0f); - l->type = gl::light_element_ubs::SPOT; - l->in_cutoff = headlight_config.in_cutoff; - l->out_cutoff = headlight_config.out_cutoff; - l->color = renderlight->diffuse * renderlight->factor; - l->linear = headlight_config.falloff_linear / 10.0f; - l->quadratic = headlight_config.falloff_quadratic / 100.0f; - l->ambient = headlight_config.ambient; - l->intensity = headlight_config.intensity; - light_i++; + gl::light_element_ubs *light = &light_ubs.lights[light_i]; + light->pos = mv * glm::vec4(renderlight->position, 1.0f); + light->dir = mv * glm::vec4(renderlight->direction, 0.0f); + light->type = gl::light_element_ubs::HEADLIGHTS; + light->in_cutoff = headlight_config.in_cutoff; + light->out_cutoff = headlight_config.out_cutoff; + light->color = renderlight->diffuse * renderlight->factor; + light->linear = headlight_config.falloff_linear / 10.0f; + light->quadratic = headlight_config.falloff_quadratic / 100.0f; + light->ambient = headlight_config.ambient; + light->intensity = headlight_config.intensity; + // headlights-to-world projection + { + // headlight projection is 1 km long box aligned with vehicle rotation and aimed slightly downwards + opengl_camera headlights; + auto const &ownerdimensions{ scenelight.owner->MoverParameters->Dim }; + auto const up{ static_cast( scenelight.owner->VectorUp() ) }; + auto const size{ static_cast( std::max( ownerdimensions.W, ownerdimensions.H ) * 1.0 ) }; // ensure square ratio + headlights.position() = + scenelight.owner->GetPosition() + - scenelight.direction * 150.f + + up * ( size * 0.5 ); +/* + headlights.projection() = ortho_projection( + -size, size, + -size, size, + ownerdimensions.L * 0.5 - 0.5, 1000.0f ); +*/ + headlights.projection() = perspective_projection( + glm::radians( 2.5 ), + 1.0, + ownerdimensions.L * 0.5 + 150.0 - 0.25, 1000.0f ); + glm::dmat4 viewmatrix{ 1.0 }; + viewmatrix *= + glm::lookAt( + headlights.position(), + headlights.position() + - up * 1.0 + + glm::dvec3{ scenelight.direction * 1000.f }, + glm::dvec3{ 0.f, 1.f, 0.f } ); + headlights.modelview() = viewmatrix; + // calculate world->headlight space projection matrix + glm::mat4 const depthproj{ headlights.projection() }; + // 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 lightcam{ glm::mat3{ headlights.modelview() } }; + auto const worldcam{ glm::mat3{ m_renderpass.pass_camera.modelview() } }; + + light->headlight_projection = + coordmove + * depthproj + * glm::translate( + glm::mat4{ lightcam }, + glm::vec3{ m_renderpass.pass_camera.position() - headlights.position() } ) + * glm::mat4{ glm::inverse( worldcam ) }; + } + // headlights weights + light->headlight_weights = { scenelight.state, 0.f }; + + ++light_i; ++renderlight; } - + light_ubs.lights_count = light_i; + // fill sunlight data light_ubs.ambient = m_sunlight.ambient * m_sunlight.factor;// *simulation::Environment.light_intensity(); light_ubs.lights[0].type = gl::light_element_ubs::DIR; light_ubs.lights[0].dir = mv * glm::vec4(m_sunlight.direction, 0.0f); light_ubs.lights[0].color = m_sunlight.diffuse * m_sunlight.factor * simulation::Environment.light_intensity(); light_ubs.lights[0].ambient = 0.0f; light_ubs.lights[0].intensity = 1.0f; - light_ubs.lights_count = light_i; - + // fill fog data light_ubs.fog_color = Global.FogColor; if (Global.fFogEnd > 0) { m_fogrange = Global.fFogEnd / std::max(1.f, Global.Overcast * 2.f); model_ubs.fog_density = 1.0f / m_fogrange; } - else - model_ubs.fog_density = 0.0f; - + else + { + model_ubs.fog_density = 0.0f; + } + // ship config data to the gpu model_ubo->update(model_ubs); light_ubo->update(light_ubs); } diff --git a/opengl33renderer.h b/opengl33renderer.h index 1a802fcc..e5fea87e 100644 --- a/opengl33renderer.h +++ b/opengl33renderer.h @@ -279,6 +279,7 @@ class opengl33_renderer : public gfx_renderer { texture_handle m_suntexture{-1}; texture_handle m_moontexture{-1}; texture_handle m_smoketexture{-1}; + texture_handle m_headlightstexture{-1}; // main shadowmap resources int m_shadowbuffersize{2048}; @@ -396,11 +397,11 @@ class opengl33_renderer : public gfx_renderer { float in_cutoff = 1.005f; float out_cutoff = 0.993f; - float falloff_linear = 0.069f; - float falloff_quadratic = 0.03f; + float falloff_linear = 0.15f; + float falloff_quadratic = 0.15f; float intensity = 1.0f; - float ambient = 0.184f; + float ambient = 0.0f; }; headlight_config_s headlight_config; diff --git a/openglrenderer.cpp b/openglrenderer.cpp index 1d1ccac2..84134dd9 100644 --- a/openglrenderer.cpp +++ b/openglrenderer.cpp @@ -4071,7 +4071,7 @@ opengl_renderer::Update_Lights( light_array &Lights ) { 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 ) ); } ); + return ( glm::length2( camera - Left.position ) / Left.intensity ) < ( glm::length2( camera - Right.position ) / Right.intensity ); } ); size_t const count = std::min( m_lights.size(), Lights.data.size() ); if( count == 0 ) { return; } diff --git a/version.h b/version.h index fd5923f2..f8f8f692 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once -#define VERSION_MAJOR 19 -#define VERSION_MINOR 1224 -#define VERSION_REVISION 1 +#define VERSION_MAJOR 20 +#define VERSION_MINOR 101 +#define VERSION_REVISION 0