diff --git a/EU07.cpp b/EU07.cpp index 6a0f80d5..2d38edc2 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -322,6 +322,7 @@ int main(int argc, char *argv[]) glfwWindowHint(GLFW_GREEN_BITS, vmode->greenBits); glfwWindowHint(GLFW_BLUE_BITS, vmode->blueBits); glfwWindowHint(GLFW_REFRESH_RATE, vmode->refreshRate); + glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE); glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE); if( Global.iMultisampling > 0 ) { @@ -448,17 +449,17 @@ int main(int argc, char *argv[]) glfwPollEvents(); - if (take_screenshot) - { - make_screenshot(); - take_screenshot = false; - } - if (!World.Update()) break; if (!GfxRenderer.Render()) break; + if (take_screenshot) + { + make_screenshot(); + take_screenshot = false; + } + if (input::motiontelemetry) input::motiontelemetry->update(); input::Keyboard.poll(); diff --git a/Texture.cpp b/Texture.cpp index 111e3394..f3fa7b41 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -609,9 +609,6 @@ opengl_texture::create() { } } - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ( wraps == true ? GL_REPEAT : GL_CLAMP_TO_EDGE ) ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ( wrapt == true ? GL_REPEAT : GL_CLAMP_TO_EDGE ) ); - // upload texture data int dataoffset = 0, datasize = 0, @@ -619,12 +616,22 @@ opengl_texture::create() { dataheight = data_height; if (is_rendertarget) { - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + if (data_components == GL_DEPTH_COMPONENT) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + } glTexImage2D(GL_TEXTURE_2D, 0, data_format, data_width, data_height, 0, data_components, data_type, nullptr); } else { + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (wraps == true ? GL_REPEAT : GL_CLAMP_TO_EDGE)); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (wrapt == true ? GL_REPEAT : GL_CLAMP_TO_EDGE)); set_filtering(); for( int maplevel = 0; maplevel < data_mapcount; ++maplevel ) { diff --git a/imgui/imgui_impl_opengl3.cpp b/imgui/imgui_impl_opengl3.cpp index 38916c1b..48c9a5ad 100644 --- a/imgui/imgui_impl_opengl3.cpp +++ b/imgui/imgui_impl_opengl3.cpp @@ -98,6 +98,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + GLboolean last_enable_srgb = glIsEnabled(GL_FRAMEBUFFER_SRGB); // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill glEnable(GL_BLEND); @@ -107,6 +108,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glDisable(GL_DEPTH_TEST); glEnable(GL_SCISSOR_TEST); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glEnable(GL_FRAMEBUFFER_SRGB); // Setup viewport, orthographic projection matrix // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. @@ -192,6 +194,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); + if (last_enable_srgb) glEnable(GL_FRAMEBUFFER_SRGB); else glDisable(GL_FRAMEBUFFER_SRGB); glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); @@ -254,7 +257,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() "void main()\n" "{\n" " Frag_UV = UV;\n" - " Frag_Color = Color;\n" + " Frag_Color = vec4(pow(Color.rgb, vec3(2.2)), Color.a);\n" " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" "}\n"; diff --git a/renderer.cpp b/renderer.cpp index 004b0bf3..e33383d0 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -175,6 +175,14 @@ opengl_renderer::Init( GLFWwindow *Window ) { m_shadow_shader = std::unique_ptr(prog); } + { + gl::shader vert("pick.vert"); + gl::shader frag("pick.frag"); + gl::program *prog = new gl::program_mvp({ vert, frag }); + prog->init(); + m_pick_shader = std::unique_ptr(prog); + } + m_invalid_material = Fetch_Material("invalid"); m_main_fb = std::make_unique(); @@ -199,6 +207,14 @@ opengl_renderer::Init( GLFWwindow *Window ) { if (!m_shadow_fb->is_complete()) return false; + m_pick_tex = std::make_unique(); + m_pick_tex->alloc_rendertarget(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE); + m_pick_rb = std::make_unique(); + m_pick_rb->alloc(GL_DEPTH_COMPONENT24, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE); + m_pick_fb = std::make_unique(); + m_pick_fb->attach(*m_pick_tex, GL_COLOR_ATTACHMENT0); + m_pick_fb->attach(*m_pick_rb, GL_DEPTH_ATTACHMENT); + return true; } @@ -284,10 +300,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) { glDebug("rendermode::color"); { -glActiveTexture(GL_TEXTURE10); -glBindTexture(GL_TEXTURE_2D, 0); -glActiveTexture(GL_TEXTURE0); -m_textures.reset_unit_cache(); + setup_shadow_map(nullptr); + glDebug("render shadowmap start"); Timer::subsystem.gfx_shadows.start(); @@ -351,21 +365,13 @@ m_textures.reset_unit_cache(); scene_ubo->update(scene_ubs); // opaque parts... setup_drawing( false ); - setup_shadow_map(*m_shadow_tex); + + setup_shadow_map(m_shadow_tex.get()); if( false == FreeFlyModeFlag ) { glDebug("render cab opaque"); - //setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix ); - // cache shadow colour in case we need to account for cab light - auto const shadowcolor { m_shadowcolor }; - auto const *vehicle{ World.Train->Dynamic() }; - if( vehicle->InteriorLightLevel > 0.f ) { - setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); - } + auto const *vehicle{ World.Train->Dynamic() }; Render_cab( vehicle, false ); - if( vehicle->InteriorLightLevel > 0.f ) { - setup_shadow_color( shadowcolor ); - } } glDebug("render opaque region"); @@ -381,15 +387,8 @@ m_textures.reset_unit_cache(); // cab render is performed without shadows, due to low resolution and number of models without windows :| //setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix ); // cache shadow colour in case we need to account for cab light - auto const shadowcolor{ m_shadowcolor }; auto const *vehicle{ World.Train->Dynamic() }; - if( vehicle->InteriorLightLevel > 0.f ) { - setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); - } Render_cab( vehicle, true ); - if( vehicle->InteriorLightLevel > 0.f ) { - setup_shadow_color( shadowcolor ); - } } /* @@ -403,7 +402,6 @@ m_textures.reset_unit_cache(); } */ - glDebug("color pass done"); } glEnable(GL_FRAMEBUFFER_SRGB); @@ -412,7 +410,10 @@ m_textures.reset_unit_cache(); m_textures.reset_unit_cache(); glDisable(GL_FRAMEBUFFER_SRGB); + glDebug("uilayer render"); UILayer.render(); + + glDebug("rendermode::color end"); break; } @@ -428,7 +429,7 @@ m_textures.reset_unit_cache(); glViewport(0, 0, m_shadowbuffersize, m_shadowbuffersize); m_shadow_fb->bind(); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + m_shadow_fb->clear(); setup_matrices(); setup_drawing(false); @@ -446,6 +447,8 @@ m_textures.reset_unit_cache(); m_shadow_fb->unbind(); + glDebug("rendermode::end"); + break; } @@ -458,6 +461,27 @@ m_textures.reset_unit_cache(); } case rendermode::pickcontrols: { + if (!World.InitPerformed() || !World.Train) + break; + + glDebug("rendermode::pickcontrols"); + + glViewport(0, 0, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE); + m_pick_fb->bind(); + m_pick_fb->clear(); + + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + + m_pickcontrolsitems.clear(); + setup_matrices(); + setup_drawing(false); + + Render_cab(World.Train->Dynamic()); + + m_pick_fb->unbind(); + + glDebug("rendermode::pickcontrols end"); break; } @@ -607,6 +631,21 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f break; } + case rendermode::pickcontrols: + case rendermode::pickscenery: + { + // modelview + camera.position() = Global.pCameraPosition; + World.Camera.SetMatrix(viewmatrix); + // projection + camera.projection() *= + glm::perspective( + glm::radians(Global.FieldOfView / Global.ZoomFactor), + std::max(1.f, (float)Global.iWindowWidth) / std::max(1.f, (float)Global.iWindowHeight), + 0.1f * Global.ZoomFactor, + Config.draw_range * Global.fDistanceFactor); + break; + } default: { break; } @@ -649,6 +688,8 @@ opengl_renderer::setup_drawing( bool const Alpha ) { switch( m_renderpass.draw_mode ) { case rendermode::color: case rendermode::reflections: { + glCullFace(GL_BACK); + // setup fog if( Global.fFogEnd > 0 ) { // m7t setup fog ubo @@ -657,6 +698,10 @@ opengl_renderer::setup_drawing( bool const Alpha ) { break; } case rendermode::shadows: + { + glCullFace(GL_FRONT); + break; + } case rendermode::cabshadows: case rendermode::pickcontrols: case rendermode::pickscenery: @@ -671,17 +716,16 @@ opengl_renderer::setup_drawing( bool const Alpha ) { // configures shadow texture unit for specified shadow map and conersion matrix void -opengl_renderer::setup_shadow_map( opengl_texture &tex ) { +opengl_renderer::setup_shadow_map( opengl_texture *tex ) { glActiveTexture(GL_TEXTURE10); - tex.bind(); + if (tex) + tex->bind(); + else + glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); m_textures.reset_unit_cache(); } -void -opengl_renderer::setup_shadow_color( glm::vec4 const &Shadowcolor ) { -} - void opengl_renderer::setup_environment_light( TEnvironmentType const Environment ) { @@ -940,8 +984,6 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite break; } case rendermode::shadows: { - // for shadows it is better to cull front faces - glCullFace(GL_FRONT); break; } case rendermode::pickscenery: { break; @@ -1007,8 +1049,6 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite switch( m_renderpass.draw_mode ) { case rendermode::shadows: { - // restore standard face cull mode - ::glCullFace( GL_BACK ); break; } default: { break; } @@ -1362,7 +1402,12 @@ opengl_renderer::Render_cab( TDynamicObject const *Dynamic, bool const Alpha ) { break; } case rendermode::cabshadows: + break; case rendermode::pickcontrols: + { + Render(Dynamic->mdKabina, Dynamic->Material(), 0.0); + break; + } default: { break; } @@ -1522,7 +1567,18 @@ opengl_renderer::Render( TSubModel *Submodel ) { } case rendermode::cabshadows: case rendermode::pickscenery: + break; case rendermode::pickcontrols: + { + m_pick_shader->bind(); + // control picking applies individual colour for each submodel + m_pickcontrolsitems.emplace_back(Submodel); + model_ubs.param[0] = glm::vec4(pick_color(m_pickcontrolsitems.size()), 1.0f); + model_ubs.set_modelview(OpenGLMatrices.data(GL_MODELVIEW)); + model_ubo->update(model_ubs); + m_geometry.draw(Submodel->m_geometry); + break; + } default: { break; } @@ -1687,6 +1743,7 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, switch( m_renderpass.draw_mode ) { case rendermode::shadows: { // NOTE: roads-based platforms tend to miss parts of shadows if rendered with either back or front culling + glDisable(GL_CULL_FACE); break; } default: { @@ -2329,7 +2386,34 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // utility methods TSubModel const * opengl_renderer::Update_Pick_Control() { - return nullptr; + Render_pass(rendermode::pickcontrols); + + // determine point to examine + glm::dvec2 mousepos; + glfwGetCursorPos(m_window, &mousepos.x, &mousepos.y); + mousepos.y = Global.iWindowHeight - mousepos.y; // cursor coordinates are flipped compared to opengl + + glm::ivec2 pickbufferpos; + pickbufferpos = glm::ivec2{ + mousepos.x * EU07_PICKBUFFERSIZE / std::max(1, Global.iWindowWidth), + mousepos.y * EU07_PICKBUFFERSIZE / std::max(1, Global.iWindowHeight) }; + + unsigned char pickreadout[3]; + + //m7t: ! replace with PBO and wait frame or two to improve performance + m_pick_fb->bind(); + ::glReadPixels(pickbufferpos.x, pickbufferpos.y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pickreadout); + m_pick_fb->unbind(); + + auto const controlindex = pick_index(glm::ivec3{ pickreadout[0], pickreadout[1], pickreadout[2] }); + TSubModel const *control{ nullptr }; + if ((controlindex > 0) + && (controlindex <= m_pickcontrolsitems.size())) { + control = m_pickcontrolsitems[controlindex - 1]; + } + + m_pickcontrolitem = control; + return control; } scene::basic_node const * @@ -2575,20 +2659,6 @@ opengl_renderer::Init_caps() { glm::vec3 opengl_renderer::pick_color( std::size_t const Index ) { -/* - // pick colours are set with step of 4 for some slightly easier visual debugging. not strictly needed but, eh - int const colourstep = 4; - int const componentcapacity = 256 / colourstep; - auto const redgreen = std::div( Index, componentcapacity * componentcapacity ); - auto const greenblue = std::div( redgreen.rem, componentcapacity ); - auto const blue = Index % componentcapacity; - return - glm::vec3 { - redgreen.quot * colourstep / 255.0f, - greenblue.quot * colourstep / 255.0f, - greenblue.rem * colourstep / 255.0f }; -*/ - // alternatively return glm::vec3{ ( ( Index & 0xff0000 ) >> 16 ) / 255.0f, @@ -2599,13 +2669,6 @@ opengl_renderer::pick_color( std::size_t const Index ) { std::size_t opengl_renderer::pick_index( glm::ivec3 const &Color ) { -/* - return ( - std::floor( Color.b / 4 ) - + std::floor( Color.g / 4 ) * 64 - + std::floor( Color.r / 4 ) * 64 * 64 ); -*/ - // alternatively return Color.b + ( Color.g * 256 ) diff --git a/renderer.h b/renderer.h index 5bf019a0..2e9610f4 100644 --- a/renderer.h +++ b/renderer.h @@ -237,9 +237,7 @@ private: void setup_drawing( bool const Alpha = false ); void - setup_shadow_map(opengl_texture &tex); - void - setup_shadow_color( glm::vec4 const &Shadowcolor ); + setup_shadow_map(opengl_texture *tex); void setup_environment_light( TEnvironmentType const Environment = e_flat ); // runs jobs needed to generate graphics for specified render pass @@ -401,6 +399,11 @@ private: std::unique_ptr m_shadow_tex; std::unique_ptr m_shadow_shader; + std::unique_ptr m_pick_fb; + std::unique_ptr m_pick_tex; + std::unique_ptr m_pick_rb; + std::unique_ptr m_pick_shader; + material_handle m_invalid_material; bool m_widelines_supported; diff --git a/shaders/mat_default.frag b/shaders/mat_default.frag index f422fa4e..696e6a16 100644 --- a/shaders/mat_default.frag +++ b/shaders/mat_default.frag @@ -12,12 +12,25 @@ uniform sampler2DShadow shadowmap; float calc_shadow() { - vec3 coords = f_light_pos.xyz; - //float bias = clamp(0.0025*tan(acos(clamp(dot(f_normal, -lights[0].dir), 0.0, 1.0))), 0, 0.01); - float bias = 0.0f; + vec3 coords = f_light_pos.xyz / f_light_pos.w; - float shadow = texture(shadowmap, vec3(coords.xy, coords.z - bias)); + // do something better + float bias = 0.0001f; + //sampler PCF + //float shadow = texture(shadowmap, vec3(coords.xy, coords.z - bias)); + + //sampler PCF + PCF + float shadow = 0.0; + vec2 texel = 1.0 / textureSize(shadowmap, 0); + for (float y = -1.5; y <= 1.5; y += 1.0) + for (float x = -1.5; x <= 1.5; x += 1.0) + shadow += texture(shadowmap, coords.xyz + vec3(vec2(x, y) * texel, -bias)); + shadow /= 16.0; + + if (coords.z > 1.0f) + shadow = 1.0f; + return shadow; } diff --git a/shaders/pick.frag b/shaders/pick.frag new file mode 100644 index 00000000..2b030e1a --- /dev/null +++ b/shaders/pick.frag @@ -0,0 +1,8 @@ +#version 330 + +#include + +void main() +{ + gl_FragColor = vec4(param[0].rgb, 1.0); +} diff --git a/shaders/pick.vert b/shaders/pick.vert new file mode 100644 index 00000000..363bc11c --- /dev/null +++ b/shaders/pick.vert @@ -0,0 +1,10 @@ +#version 330 + +layout(location = 0) in vec3 v_vert; + +#include + +void main() +{ + gl_Position = (projection * modelview) * vec4(v_vert, 1.0f); +} diff --git a/uilayer.cpp b/uilayer.cpp index 03100c89..e24b6fe9 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -1078,14 +1078,9 @@ ui_layer::render_tooltip() { glm::dvec2 mousepos; glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y ); - ImGui::SetNextWindowPos(ImVec2(mousepos.x, mousepos.y)); - ImGui::SetNextWindowSize(ImVec2(0, 0)); - ImGui::Begin("Tooltip", nullptr, - ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | - ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoBringToFrontOnFocus); - ImGui::TextUnformatted(m_tooltip.c_str()); - ImGui::End(); + ImGui::BeginTooltip(); + ImGui::TextUnformatted(m_tooltip.c_str()); + ImGui::EndTooltip(); } void