diff --git a/Globals.cpp b/Globals.cpp index a21a4cac..6073b44d 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -88,6 +88,7 @@ opengl_light Global::DayLight; int Global::DynamicLightCount { 3 }; bool Global::ScaleSpecularValues { true }; bool Global::RenderShadows { false }; +Global::shadowtune_t Global::shadowtune = { 2048, 200.0f, 150.0f, 100.0f }; bool Global::bRollFix = true; // czy wykonać przeliczanie przechyÅ‚ki bool Global::bJoinEvents = false; // czy grupować eventy o tych samych nazwach int Global::iHiddenEvents = 1; // czy łączyć eventy z torami poprzez nazwÄ™ toru @@ -550,6 +551,14 @@ void Global::ConfigParse(cParser &Parser) Parser.getTokens(); Parser >> Global::RenderShadows; } + else if( token == "shadowtune" ) { + Parser.getTokens( 4, false ); + Parser + >> Global::shadowtune.map_size + >> Global::shadowtune.width + >> Global::shadowtune.depth + >> Global::shadowtune.distance; + } else if (token == "smoothtraction") { // podwójna jasność ambient diff --git a/Globals.h b/Globals.h index 948eae86..752aca31 100644 --- a/Globals.h +++ b/Globals.h @@ -227,6 +227,12 @@ class Global static int DynamicLightCount; static bool ScaleSpecularValues; static bool RenderShadows; + static struct shadowtune_t { + unsigned int map_size; + float width; + float depth; + float distance; + } shadowtune; static int iSlowMotion; static TDynamicObject *changeDynObj; diff --git a/McZapkie/mctools.h b/McZapkie/mctools.h index 8f02ab11..55a65447 100644 --- a/McZapkie/mctools.h +++ b/McZapkie/mctools.h @@ -98,6 +98,11 @@ std::string to_string( glm::tvec3 const &Value ) { return to_string( Value.x, 2 ) + ", " + to_string( Value.y, 2 ) + ", " + to_string( Value.z, 2 ); } +template +std::string to_string( glm::tvec4 const &Value, int const Width = 2 ) { + return to_string( Value.x, Width ) + ", " + to_string( Value.y, Width ) + ", " + to_string( Value.z, Width ) + ", " + to_string( Value.w, Width ); +} + int stol_def(const std::string & str, const int & DefaultValue); std::string ToLower(std::string const &text); diff --git a/World.cpp b/World.cpp index 198f4ed6..f11c1e80 100644 --- a/World.cpp +++ b/World.cpp @@ -32,6 +32,8 @@ http://mozilla.org/MPL/2.0/. #include "uilayer.h" #include "translation.h" +//#define EU07_USE_DEBUG_SHADOWMAP + //--------------------------------------------------------------------------- TDynamicObject *Controlled = NULL; // pojazd, który prowadzimy @@ -632,8 +634,13 @@ void TWorld::OnKeyDown(int cKey) break; } case GLFW_KEY_F8: { +#ifdef EU07_USE_DEBUG_SHADOWMAP + if( Global::iTextMode == cKey ) { ++Global::iScreenMode[ cKey - GLFW_KEY_F1 ]; } + if( Global::iScreenMode[ cKey - GLFW_KEY_F1 ] > 1 ) { + Global::iScreenMode[ cKey - GLFW_KEY_F1 ] = 0; + } +#endif Global::iTextMode = cKey; - // FPS break; } case GLFW_KEY_F9: { @@ -1693,7 +1700,7 @@ TWorld::Update_UI() { std::to_string( int( tmp->MoverParameters->Im ) ) ) + "; U=" + to_string( int( tmp->MoverParameters->RunningTraction.TractionVoltage + 0.5 ) ) + "; R=" + - ( tmp->MoverParameters->RunningShape.R > 100000.0 ? + ( std::abs( tmp->MoverParameters->RunningShape.R ) > 10000.0 ? "~0.0" : to_string( tmp->MoverParameters->RunningShape.R, 1 ) ) + " An=" + to_string( tmp->MoverParameters->AccN, 2 ); // przyspieszenie poprzeczne diff --git a/openglmatrixstack.h b/openglmatrixstack.h index b57f545b..b75e37c7 100644 --- a/openglmatrixstack.h +++ b/openglmatrixstack.h @@ -55,6 +55,10 @@ public: multiply( glm::mat4 const &Matrix ) { m_stack.top() *= Matrix; upload(); } + void + ortho( float const Left, float const Right, float const Bottom, float const Top, float const Znear, float const Zfar ) { + m_stack.top() *= glm::ortho( Left, Right, Bottom, Top, Znear, Zfar ); + upload(); } void perspective( float const Fovy, float const Aspect, float const Znear, float const Zfar ) { m_stack.top() *= glm::perspective( Fovy, Aspect, Znear, Zfar ); @@ -132,10 +136,20 @@ public: m_stacks[ m_mode ].multiply( glm::make_mat4( Matrix ) ); } template + void + ortho( Type_ const Left, Type_ const Right, Type_ const Bottom, Type_ const Top, Type_ const Znear, Type_ const Zfar ) { + m_stacks[ m_mode ].ortho( + static_cast( Left ), + static_cast( Right ), + static_cast( Bottom ), + static_cast( Top ), + static_cast( Znear ), + static_cast( Zfar ) ); } + template void perspective( Type_ const Fovy, Type_ const Aspect, Type_ const Znear, Type_ const Zfar ) { m_stacks[ m_mode ].perspective( - static_cast(Fovy) * 0.0174532925f, // deg2rad + static_cast( glm::radians( Fovy ) ), static_cast( Aspect ), static_cast( Znear ), static_cast( Zfar ) ); } @@ -177,6 +191,7 @@ extern opengl_matrices OpenGLMatrices; // NOTE: no scale override as we aren't using it anywhere #define glMultMatrixd OpenGLMatrices.multiply #define glMultMatrixf OpenGLMatrices.multiply +#define glOrtho OpenGLMatrices.ortho #define gluPerspective OpenGLMatrices.perspective #define gluLookAt OpenGLMatrices.look_at diff --git a/renderer.cpp b/renderer.cpp index 07453165..c3ce5a93 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -25,9 +25,12 @@ http://mozilla.org/MPL/2.0/. opengl_renderer GfxRenderer; extern TWorld World; +//#define EU07_USE_ORTHO_SHADOWS + namespace colors { -glm::vec4 const none { 0.0f, 0.0f, 0.0f, 1.0f }; +glm::vec4 const none { 0.f, 0.f, 0.f, 1.f }; +glm::vec4 const white{ 1.f, 1.f, 1.f, 1.f }; } // namespace colors @@ -67,6 +70,10 @@ opengl_renderer::Init( GLFWwindow *Window ) { glEnable( GL_CULL_FACE ); // Cull back-facing triangles glShadeModel( GL_SMOOTH ); // Enable Smooth Shading + glActiveTexture( m_diffusetextureunit ); + glClientActiveTexture( m_diffusetextureunit ); + UILayer.set_unit( m_diffusetextureunit ); + glEnable( GL_DEPTH_TEST ); glAlphaFunc( GL_GREATER, 0.04f ); glEnable( GL_ALPHA_TEST ); @@ -152,22 +159,41 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target for now } #endif + + ::glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + // preload some common textures + WriteLog( "Loading common gfx data..." ); + m_glaretexture = Fetch_Texture( "fx\\lightglare" ); + m_suntexture = Fetch_Texture( "fx\\sun" ); + m_moontexture = Fetch_Texture( "fx\\moon" ); + if( m_helpertextureunit >= 0 ) { + m_reflectiontexture = Fetch_Texture( "fx\\reflections" ); + } + WriteLog( "...gfx data pre-loading done" ); + // shadowmap resources if( ( true == Global::RenderShadows ) && ( true == m_framebuffersupport ) ) { // texture: ::glGenTextures( 1, &m_shadowtexture ); ::glBindTexture( GL_TEXTURE_2D, m_shadowtexture ); + // allocate memory + ::glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_shadowbuffersize, m_shadowbuffersize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL ); + // setup parameters ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); // enable shadow comparison: true (ie not in shadow) if r<=texture... ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL ); - // ...shadow comparison should generate an INTENSITY result - ::glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY ); - ::glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_shadowbuffersize, m_shadowbuffersize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL ); + ::glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE ); + // eye_linear is the default, so we could probably skip it + ::glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR ); + ::glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR ); + ::glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR ); + ::glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR ); + ::glBindTexture( GL_TEXTURE_2D, 0 ); // create and assemble the framebuffer ::glGenFramebuffersEXT( 1, &m_shadowframebuffer ); ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); @@ -187,13 +213,6 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glDrawBuffer( GL_BACK ); } - ::glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); - // preload some common textures - WriteLog( "Loading common gfx data..." ); - m_glaretexture = Fetch_Texture( "fx\\lightglare" ); - m_suntexture = Fetch_Texture( "fx\\sun" ); - m_moontexture = Fetch_Texture( "fx\\moon" ); - WriteLog( "...gfx data pre-loading done" ); // prepare basic geometry chunks auto const geometrybank = m_geometry.create_bank(); float const size = 2.5f; @@ -217,6 +236,7 @@ opengl_renderer::Render() { auto const drawstart = std::chrono::steady_clock::now(); + m_renderpass.draw_mode = rendermode::none; // force setup anew Render_pass( rendermode::color ); glfwSwapBuffers( m_window ); @@ -248,7 +268,15 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glDrawBuffer( GL_BACK ); } - ::glViewport( 0, 0, Global::ScreenWidth, Global::ScreenHeight ); + if( ( Global::iTextMode == GLFW_KEY_F8 ) && ( Global::iScreenMode[ GLFW_KEY_F8 - GLFW_KEY_F1 ] == 1 ) ) { + // debug shadowmap + ::glViewport( 0, 0, Global::ScreenHeight, Global::ScreenHeight ); + m_renderpass.draw_range = 1250.f; // 1.0km square centered around camera + } + else { + ::glViewport( 0, 0, Global::ScreenWidth, Global::ScreenHeight ); + m_renderpass.draw_range = 2500.0f; // arbitrary base draw range + } if( World.InitPerformed() ) { auto const skydomecolour = World.Environment.m_skydome.GetAverageColor(); @@ -261,19 +289,22 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( World.InitPerformed() ) { // setup - m_renderpass.draw_range = 2500.0f; // arbitrary base draw range - Render_projection(); - Render_camera(); + setup_projection(); + setup_camera(); ::glDepthFunc( GL_LEQUAL ); // render - Render_setup( true ); + setup_drawing( true ); + setup_units( true, false, false ); Render( &World.Environment ); // opaque parts... - Render_setup(); + setup_drawing( false ); + setup_units( true, true, true ); Render( &World.Ground ); // ...translucent parts - Render_setup( true ); + setup_drawing( true ); Render_Alpha( &World.Ground ); + // cab render is performed without shadows, due to low resolution and number of models without windows :| + toggle_units( true, false, false ); // cab render is done in translucent phase to deal with badly configured vehicles if( World.Train != nullptr ) { Render_cab( World.Train->Dynamic() ); } } @@ -283,21 +314,34 @@ opengl_renderer::Render_pass( rendermode const Mode ) { case rendermode::shadows: { - ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); - - ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); - ::glClear( GL_DEPTH_BUFFER_BIT ); - if( World.InitPerformed() ) { // setup - m_renderpass.draw_range = 500.0f; // 1.0km square centered around camera - Render_projection(); - Render_camera(); + ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); + + ::glClear( GL_DEPTH_BUFFER_BIT ); + ::glScissor( 1, 1, m_shadowbuffersize - 2, m_shadowbuffersize - 2 ); + ::glEnable( GL_SCISSOR_TEST ); + + m_renderpass.draw_range = 1250.f; // 1.0km square centered around camera + m_shadowtexturematrix = + glm::mat4{ + 0.5f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f }; //bias from [-1, 1] to [0, 1] }; + setup_projection(); + setup_camera(); ::glDepthFunc( GL_LEQUAL ); + ::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting + ::glPolygonOffset( 4.f, 8.f ); // render // opaque parts... - Render_setup(); + setup_drawing( false ); + setup_units( false, false, false ); Render( &World.Ground ); + // post-render restore + ::glDisable( GL_POLYGON_OFFSET_FILL ); + ::glDisable( GL_SCISSOR_TEST ); } break; } @@ -307,19 +351,20 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #ifdef EU07_USE_PICKING_FRAMEBUFFER ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); #endif - ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - if( World.InitPerformed() ) { // setup + ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + m_pickcontrolsitems.clear(); m_renderpass.draw_range = 50.0f; // doesn't really matter for control picking - Render_projection(); - Render_camera(); + setup_projection(); + setup_camera(); ::glDepthFunc( GL_LEQUAL ); // render // opaque parts... - Render_setup(); + setup_drawing( false ); + setup_units( false, false, false ); // cab render skips translucent parts, so we can do it here if( World.Train != nullptr ) { Render_cab( World.Train->Dynamic() ); } } @@ -332,19 +377,20 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #ifdef EU07_USE_PICKING_FRAMEBUFFER ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); #endif - ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - if( World.InitPerformed() ) { // setup + ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + m_picksceneryitems.clear(); m_renderpass.draw_range = 1000.0f; // scenery picking is likely to focus on nearby nodes - Render_projection(); - Render_camera(); + setup_projection(); + setup_camera(); ::glDepthFunc( GL_LEQUAL ); // render // opaque parts... - Render_setup(); + setup_drawing( false ); + setup_units( false, false, false ); Render( &World.Ground ); } break; @@ -354,12 +400,11 @@ opengl_renderer::Render_pass( rendermode const Mode ) { break; } } - } // configures projection matrix for the current render pass void -opengl_renderer::Render_projection() { +opengl_renderer::setup_projection() { ::glMatrixMode( GL_PROJECTION ); ::glLoadIdentity(); @@ -371,11 +416,17 @@ opengl_renderer::Render_projection() { case rendermode::pickscenery: #endif case rendermode::color: { - ::gluPerspective( - Global::FieldOfView / Global::ZoomFactor, - std::max( 1.0f, (float)Global::ScreenWidth ) / std::max( 1.0f, (float)Global::ScreenHeight ), - 0.1f * Global::ZoomFactor, - m_renderpass.draw_range * Global::fDistanceFactor ); + if( ( Global::iTextMode == GLFW_KEY_F8 ) && ( Global::iScreenMode[ GLFW_KEY_F8 - GLFW_KEY_F1 ] == 1 ) ) { + // debug shadowmap +#ifdef EU07_USE_ORTHO_SHADOWS + setup_projection_light_ortho(); +#else + setup_projection_light_perspective(); +#endif + } + else { + setup_projection_world(); + } break; } #ifdef EU07_USE_PICKING_FRAMEBUFFER @@ -393,13 +444,12 @@ opengl_renderer::Render_projection() { } #endif case rendermode::shadows: { - // TODO: set parallel projection - ::gluPerspective( - Global::FieldOfView, - 1.f, -// std::max( 1.0f, (float)Global::ScreenWidth ) / std::max( 1.0f, (float)Global::ScreenHeight ), - 2.f, - m_renderpass.draw_range * Global::fDistanceFactor ); +#ifdef EU07_USE_ORTHO_SHADOWS + setup_projection_light_ortho(); +#else + setup_projection_light_perspective(); +#endif + m_shadowtexturematrix *= OpenGLMatrices.data( GL_PROJECTION ); break; } @@ -409,9 +459,38 @@ opengl_renderer::Render_projection() { } } +void +opengl_renderer::setup_projection_world() { + + ::gluPerspective( + Global::FieldOfView / Global::ZoomFactor, + std::max( 1.f, (float)Global::ScreenWidth ) / std::max( 1.f, (float)Global::ScreenHeight ), + 0.1f * Global::ZoomFactor, + m_renderpass.draw_range * Global::fDistanceFactor ); +} + +void +opengl_renderer::setup_projection_light_ortho() { + + ::glOrtho( + -250, 250, + -250, 250, + -2500, 2500 ); +} + +void +opengl_renderer::setup_projection_light_perspective() { + + ::gluPerspective( + 45.f, + 1.f, + 100.f, // light source is pulled back far enough we won't likely have anything too close to it, can get some z-range here + m_renderpass.draw_range * Global::fDistanceFactor ); +} + // configures modelview matrix for the current render pass void -opengl_renderer::Render_camera() { +opengl_renderer::setup_camera() { ::glMatrixMode( GL_MODELVIEW ); // Select The Modelview Matrix ::glLoadIdentity(); @@ -422,17 +501,29 @@ opengl_renderer::Render_camera() { case rendermode::color: case rendermode::pickcontrols: case rendermode::pickscenery: { - World.Camera.SetMatrix( viewmatrix ); - m_renderpass.camera.position() = Global::pCameraPosition; + if( ( Global::iTextMode == GLFW_KEY_F8 ) && ( Global::iScreenMode[ GLFW_KEY_F8 - GLFW_KEY_F1 ] == 1 ) ) { +#ifdef EU07_USE_ORTHO_SHADOWS + setup_camera_light_ortho( viewmatrix ); +#else + setup_camera_light_perspective( viewmatrix ); +#endif + } + else { + setup_camera_world( viewmatrix ); + } break; } case rendermode::shadows: { - m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * 250.f }; - m_renderpass.camera.position().y = std::max( 50.0, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low - viewmatrix = glm::lookAt( - m_renderpass.camera.position(), - glm::dvec3{ Global::pCameraPosition.x, 0.0, Global::pCameraPosition.z }, - glm::dvec3{ 0.f, 1.f, 0.f } ); +#ifdef EU07_USE_ORTHO_SHADOWS + setup_camera_light_ortho( viewmatrix ); +#else + setup_camera_light_perspective( viewmatrix ); +#endif + // during colour pass coordinates are moved from camera-centric to light-centric, essentially the difference between these two origins + m_shadowtexturematrix *= + glm::translate( + glm::mat4{ glm::dmat3{ viewmatrix } }, + glm::vec3{ glm::dvec3{ Global::pCameraPosition } - m_renderpass.camera.position() } ); break; } default: { @@ -445,7 +536,45 @@ opengl_renderer::Render_camera() { } void -opengl_renderer::Render_setup( bool const Alpha ) { +opengl_renderer::setup_camera_world( glm::dmat4 &Viewmatrix ) { + + World.Camera.SetMatrix( Viewmatrix ); + m_renderpass.camera.position() = Global::pCameraPosition; +} + +void +opengl_renderer::setup_camera_light_ortho( glm::dmat4 &Viewmatrix ) { +/* + m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * 250.f }; + m_renderpass.camera.position().y = std::max( 75.0, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low + Viewmatrix = glm::lookAt( + m_renderpass.camera.position(), + glm::dvec3{ Global::pCameraPosition.x, 0.0, Global::pCameraPosition.z }, + glm::dvec3{ 0.f, 1.f, 0.f } ); +*/ + m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction }; + if( m_renderpass.camera.position().y - Global::pCameraPosition.y < 0.15 ) { + m_renderpass.camera.position().y = Global::pCameraPosition.y + 0.15; + } + Viewmatrix = glm::lookAt( + m_renderpass.camera.position(), + glm::dvec3{ Global::pCameraPosition }, + glm::dvec3{ 0.f, 1.f, 0.f } ); +} + +void +opengl_renderer::setup_camera_light_perspective( glm::dmat4 &Viewmatrix ) { + + m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * m_renderpass.draw_range * 0.5f }; + m_renderpass.camera.position().y = std::max( 75.0, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low + Viewmatrix = glm::lookAt( + m_renderpass.camera.position(), + glm::dvec3{ Global::pCameraPosition.x, 0.0, Global::pCameraPosition.z }, + glm::dvec3{ 0.f, 1.f, 0.f } ); +} + +void +opengl_renderer::setup_drawing( bool const Alpha ) { if( true == Alpha ) { @@ -473,19 +602,6 @@ opengl_renderer::Render_setup( bool const Alpha ) { } else { ::glDisable( GL_FOG ); } - if( m_texenvmode != m_renderpass.draw_mode ) { - // texture colour and alpha - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); -/* - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); -*/ - m_texenvmode = m_renderpass.draw_mode; - } break; } case rendermode::shadows: @@ -498,19 +614,6 @@ opengl_renderer::Render_setup( bool const Alpha ) { } ::glDisable( GL_FOG ); - if( m_texenvmode != m_renderpass.draw_mode ) { - // solid colour with texture alpha - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR ); // or GL_PREVIOUS - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); -/* - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); -*/ - m_texenvmode = m_renderpass.draw_mode; - } break; } default: { @@ -519,6 +622,169 @@ opengl_renderer::Render_setup( bool const Alpha ) { } } +// configures, enables and disables specified texture units +void +opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { + // helper texture unit. + // darkens previous stage, preparing data for the shadow texture unit to select from + if( m_helpertextureunit >= 0 ) { + if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + // setup reflection texture unit + ::glActiveTexture( m_helpertextureunit ); + ::glBindTexture( GL_TEXTURE_2D, m_reflectiontexture ); // TODO: move to reflection unit setup + ::glEnable( GL_TEXTURE_2D ); +/* + glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); + glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); + glEnable( GL_TEXTURE_GEN_S ); + glEnable( GL_TEXTURE_GEN_T ); +*/ + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( m_shadowcolor ) ); // TODO: dynamically calculated shadow colour, based on sun height + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); // darken the previous stage + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // simply copy alpha from diffuse stage + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); + } + else { + ::glActiveTexture( m_helpertextureunit ); + ::glDisable( GL_TEXTURE_2D ); +/* + ::glDisable( GL_TEXTURE_GEN_S ); + ::glDisable( GL_TEXTURE_GEN_T ); + ::glDisable( GL_TEXTURE_GEN_R ); + ::glDisable( GL_TEXTURE_GEN_Q ); +*/ + } + } + // 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 ) ) { + + ::glActiveTexture( m_shadowtextureunit ); + ::glBindTexture( GL_TEXTURE_2D, m_shadowtexture ); + ::glEnable( GL_TEXTURE_2D ); + // s + ::glTexGenfv( GL_S, GL_EYE_PLANE, glm::value_ptr( glm::row( m_shadowtexturematrix, 0 ) ) ); + ::glEnable( GL_TEXTURE_GEN_S ); + // t + ::glTexGenfv( GL_T, GL_EYE_PLANE, glm::value_ptr( glm::row( m_shadowtexturematrix, 1 ) ) ); + ::glEnable( GL_TEXTURE_GEN_T ); + // r + ::glTexGenfv( GL_R, GL_EYE_PLANE, glm::value_ptr( glm::row( m_shadowtexturematrix, 2 ) ) ); + ::glEnable( GL_TEXTURE_GEN_R ); + // q + ::glTexGenfv( GL_Q, GL_EYE_PLANE, glm::value_ptr( glm::row( m_shadowtexturematrix, 3 ) ) ); + ::glEnable( GL_TEXTURE_GEN_Q ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); // choose between lit and lit * shadow colour, based on depth test + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // simply copy alpha from diffuse stage + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); + } + else { + // turn off shadow map tests + ::glActiveTexture( m_shadowtextureunit ); + + ::glDisable( GL_TEXTURE_2D ); + ::glDisable( GL_TEXTURE_GEN_S ); + ::glDisable( GL_TEXTURE_GEN_T ); + ::glDisable( GL_TEXTURE_GEN_R ); + ::glDisable( GL_TEXTURE_GEN_Q ); + } + } + // diffuse texture unit. + // NOTE: diffuse texture mapping is never fully disabled, alpha channel information is always included + ::glActiveTexture( m_diffusetextureunit ); + if( true == Diffuse ) { + // default behaviour, modulate with previous stage + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); +/* + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); +*/ + } + else { + // solid colour with texture alpha + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); +/* + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); +*/ + } +} + +// enables and disables specified texture units +void +opengl_renderer::toggle_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { + // helper texture unit. + if( m_helpertextureunit >= 0 ) { + if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + ::glActiveTexture( m_helpertextureunit ); + ::glEnable( GL_TEXTURE_2D ); + } + else { + ::glActiveTexture( m_helpertextureunit ); + ::glDisable( GL_TEXTURE_2D ); + } + } + // shadow texture unit. + if( m_shadowtextureunit >= 0 ) { + if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + + ::glActiveTexture( m_shadowtextureunit ); + ::glEnable( GL_TEXTURE_2D ); + } + else { + + ::glActiveTexture( m_shadowtextureunit ); + ::glDisable( GL_TEXTURE_2D ); + } + } + // diffuse texture unit. + // NOTE: toggle actually disables diffuse texture mapping, unlike setup counterpart + if( true == Diffuse ) { + + ::glActiveTexture( m_diffusetextureunit ); + ::glEnable( GL_TEXTURE_2D ); + } + else { + + ::glActiveTexture( m_diffusetextureunit ); + ::glDisable( GL_TEXTURE_2D ); + } +} + +void +opengl_renderer::setup_shadow_color( glm::vec4 const &Shadowcolor ) { + + ::glActiveTexture( m_helpertextureunit ); + ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( Shadowcolor ) ); // in-shadow colour multiplier + ::glActiveTexture( m_diffusetextureunit ); + +} + bool opengl_renderer::Render( world_environment *Environment ) { @@ -587,10 +853,10 @@ opengl_renderer::Render( world_environment *Environment ) { float const size = 0.045f; ::glBegin( GL_TRIANGLE_STRIP ); - ::glTexCoord2f( 1.0f, 1.0f ); ::glVertex3f( -size, size, 0.0f ); - ::glTexCoord2f( 1.0f, 0.0f ); ::glVertex3f( -size, -size, 0.0f ); - ::glTexCoord2f( 0.0f, 1.0f ); ::glVertex3f( size, size, 0.0f ); - ::glTexCoord2f( 0.0f, 0.0f ); ::glVertex3f( size, -size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, 1.f, 1.f ); ::glVertex3f( -size, size, 0.f ); + ::glMultiTexCoord2f( m_diffusetextureunit, 1.f, 0.f ); ::glVertex3f( -size, -size, 0.f ); + ::glMultiTexCoord2f( m_diffusetextureunit, 0.f, 1.f ); ::glVertex3f( size, size, 0.f ); + ::glMultiTexCoord2f( m_diffusetextureunit, 0.f, 0.f ); ::glVertex3f( size, -size, 0.f ); ::glEnd(); ::glPopMatrix(); @@ -623,10 +889,10 @@ opengl_renderer::Render( world_environment *Environment ) { else { moonv = 1.0f - 0.0f; moonu = 0.0f; } ::glBegin( GL_TRIANGLE_STRIP ); - ::glTexCoord2f( moonu, moonv ); ::glVertex3f( -size, size, 0.0f ); - ::glTexCoord2f( moonu, moonv - 0.333f ); ::glVertex3f( -size, -size, 0.0f ); - ::glTexCoord2f( moonu + 0.333f, moonv ); ::glVertex3f( size, size, 0.0f ); - ::glTexCoord2f( moonu + 0.333f, moonv - 0.333f ); ::glVertex3f( size, -size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, moonu, moonv ); ::glVertex3f( -size, size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, moonu, moonv - 0.333f ); ::glVertex3f( -size, -size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, moonu + 0.333f, moonv ); ::glVertex3f( size, size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, moonu + 0.333f, moonv - 0.333f ); ::glVertex3f( size, -size, 0.0f ); ::glEnd(); ::glPopMatrix(); @@ -831,21 +1097,23 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { case rendermode::pickscenery: { // non-interactive scenery elements get neutral colour ::glColor3fv( glm::value_ptr( colors::none ) ); - break; } case rendermode::shadows: - case rendermode::color: + case rendermode::color: { + if( Groundcell->nRenderRect != nullptr ) { + // nieprzezroczyste trójkÄ…ty kwadratu kilometrowego + for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { + Render( node ); + } + } + break; + } case rendermode::pickcontrols: default: { break; } } - if( Groundcell->nRenderRect != nullptr ) { - // nieprzezroczyste trójkÄ…ty kwadratu kilometrowego - for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { - Render( node ); - } - } + if( Groundcell->nTerrain ) { Render( Groundcell->nTerrain ); @@ -969,7 +1237,18 @@ opengl_renderer::Render( TGroundNode *Node ) { return true; } - double const distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need distance from actual camera + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ); + break; + } + default: { + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + break; + } + } if( ( distancesquared > ( Node->fSquareRadius * Global::fDistanceFactor ) ) || ( distancesquared < ( Node->fSquareMinRadius / Global::fDistanceFactor ) ) ) { return false; @@ -1015,8 +1294,30 @@ opengl_renderer::Render( TGroundNode *Node ) { #else Node->Model->RaAnimate(); // jednorazowe przeliczenie animacji Node->Model->RaPrepare(); - if( Node->Model->pModel ) // renderowanie rekurencyjne submodeli - Render( Node->Model->pModel, Node->Model->Material(), Node->pCenter - m_renderpass.camera.position(), Node->Model->vAngle ); + if( Node->Model->pModel ) { + // renderowanie rekurencyjne submodeli + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + Render( + Node->Model->pModel, + Node->Model->Material(), + SquareMagnitude( Node->pCenter - Global::pCameraPosition ), + Node->pCenter - m_renderpass.camera.position(), + Node->Model->vAngle ); + break; + } + default: { + auto const position = Node->pCenter - m_renderpass.camera.position(); + Render( + Node->Model->pModel, + Node->Model->Material(), + SquareMagnitude( position ), + position, + Node->Model->vAngle ); + break; + } + } + } #endif return true; } @@ -1154,11 +1455,20 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { if( false == Dynamic->renderme ) { return false; } - // setup TSubModel::iInstance = ( size_t )this; //żeby nie robić cudzych animacji auto const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); - double const squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + double squaredistance; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + squaredistance = SquareMagnitude( ( Dynamic->vPosition - Global::pCameraPosition ) / Global::ZoomFactor ); + break; + } + default: { + squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + break; + } + } Dynamic->ABuLittleUpdate( squaredistance ); // ustawianie zmiennych submodeli dla wspólnego modelu ::glPushMatrix(); @@ -1342,7 +1652,7 @@ opengl_renderer::Render( TModel3d *Model, material_data const *Material, double } bool -opengl_renderer::Render( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { +opengl_renderer::Render( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { ::glPushMatrix(); ::glTranslated( Position.x, Position.y, Position.z ); @@ -1353,7 +1663,7 @@ opengl_renderer::Render( TModel3d *Model, material_data const *Material, Math3D: if( Angle.z != 0.0 ) ::glRotated( Angle.z, 0.0, 0.0, 1.0 ); - auto const result = Render( Model, Material, SquareMagnitude( Position ) ); // position is effectively camera offset + auto const result = Render( Model, Material, Squaredistance ); ::glPopMatrix(); @@ -1417,6 +1727,8 @@ opengl_renderer::Render( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // zeby swiecilo na kolorowo ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); + // disable shadows so they don't obstruct self-lit items + setup_shadow_color( colors::white ); } // main draw call @@ -1429,6 +1741,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); + setup_shadow_color( m_shadowcolor ); } #ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { @@ -1524,10 +1837,14 @@ opengl_renderer::Render( TSubModel *Submodel ) { ::glLoadIdentity(); ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // poczÄ…tek ukÅ‚adu zostaje bez zmian + setup_shadow_color( colors::white ); + // main draw call m_geometry.draw( Submodel->m_geometry ); // post-draw reset + // re-enable shadows + setup_shadow_color( m_shadowcolor ); ::glPopMatrix(); ::glPopAttrib(); } @@ -1709,7 +2026,18 @@ opengl_renderer::Render_Alpha( TSubRect *Groundsubcell ) { bool opengl_renderer::Render_Alpha( TGroundNode *Node ) { - double const distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need distance from actual camera + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ); + break; + } + default: { + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + break; + } + } if( ( distancesquared > ( Node->fSquareRadius * Global::fDistanceFactor ) ) || ( distancesquared < ( Node->fSquareMinRadius / Global::fDistanceFactor ) ) ) { return false; @@ -1766,8 +2094,30 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { Node->Model->RenderAlpha( Node->pCenter - m_renderpass.camera.position() ); #else Node->Model->RaPrepare(); - if( Node->Model->pModel ) // renderowanie rekurencyjne submodeli - Render_Alpha( Node->Model->pModel, Node->Model->Material(), Node->pCenter - m_renderpass.camera.position(), Node->Model->vAngle ); + if( Node->Model->pModel ) { + // renderowanie rekurencyjne submodeli + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + Render_Alpha( + Node->Model->pModel, + Node->Model->Material(), + SquareMagnitude( Node->pCenter - Global::pCameraPosition ), + Node->pCenter - m_renderpass.camera.position(), + Node->Model->vAngle ); + break; + } + default: { + auto const position = Node->pCenter - m_renderpass.camera.position(); + Render_Alpha( + Node->Model->pModel, + Node->Model->Material(), + SquareMagnitude( position ), + position, + Node->Model->vAngle ); + break; + } + } + } #endif return true; } @@ -1848,7 +2198,17 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { // setup TSubModel::iInstance = ( size_t )this; //żeby nie robić cudzych animacji auto const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); - double const squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + double squaredistance; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + squaredistance = SquareMagnitude( ( Dynamic->vPosition - Global::pCameraPosition ) / Global::ZoomFactor ); + break; + } + default: { + squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + break; + } + } Dynamic->ABuLittleUpdate( squaredistance ); // ustawianie zmiennych submodeli dla wspólnego modelu ::glPushMatrix(); @@ -1936,7 +2296,7 @@ opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, d } bool -opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { +opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { ::glPushMatrix(); ::glTranslated( Position.x, Position.y, Position.z ); @@ -1947,7 +2307,7 @@ opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, M if( Angle.z != 0.0 ) ::glRotated( Angle.z, 0.0, 0.0, 1.0 ); - auto const result = Render_Alpha( Model, Material, SquareMagnitude( Position ) ); // position is effectively camera offset + auto const result = Render_Alpha( Model, Material, Squaredistance ); // position is effectively camera offset ::glPopMatrix(); @@ -2006,6 +2366,8 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // zeby swiecilo na kolorowo ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); + // disable shadows so they don't obstruct self-lit items + setup_shadow_color( colors::white ); } // main draw call @@ -2018,6 +2380,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); + setup_shadow_color( m_shadowcolor ); } #ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { @@ -2068,6 +2431,8 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { ::glLoadIdentity(); // macierz jedynkowa ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // poczÄ…tek ukÅ‚adu zostaje bez zmian ::glRotated( std::atan2( lightcenter.x, lightcenter.z ) * 180.0 / M_PI, 0.0, 1.0, 0.0 ); // jedynie obracamy w pionie o kÄ…t + // disable shadows so they don't obstruct self-lit items + setup_shadow_color( colors::white ); // main draw call m_geometry.draw( m_billboardgeometry ); @@ -2080,6 +2445,8 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // ...etc instead IF we had easy access to camera's forward and right vectors. TODO: check if Camera matrix is accessible */ // post-render cleanup + setup_shadow_color( m_shadowcolor ); + ::glPopMatrix(); ::glPopAttrib(); } @@ -2293,7 +2660,7 @@ opengl_renderer::Update( double const Deltatime ) { }; // debug performance string -std::string const & +std::string opengl_renderer::Info() const { return m_debuginfo; @@ -2397,15 +2764,36 @@ opengl_renderer::Init_caps() { else { WriteLog( "Framebuffer objects not supported, resorting to back buffer rendering where possible" ); } - if( Global::iMultisampling ) - WriteLog( "Using multisampling x" + std::to_string( 1 << Global::iMultisampling ) ); // ograniczenie maksymalnego rozmiaru tekstur - parametr dla skalowania tekstur { - GLint i; - glGetIntegerv( GL_MAX_TEXTURE_SIZE, &i ); - if( i < Global::iMaxTextureSize ) - Global::iMaxTextureSize = i; + GLint texturesize; + ::glGetIntegerv( GL_MAX_TEXTURE_SIZE, &texturesize ); + Global::iMaxTextureSize = std::min( Global::iMaxTextureSize, texturesize ); WriteLog( "Texture sizes capped at " + std::to_string( Global::iMaxTextureSize ) + " pixels" ); + m_shadowbuffersize = Global::shadowtune.map_size; + m_shadowbuffersize = std::min( m_shadowbuffersize, texturesize ); + WriteLog( "Shadows map size capped at " + std::to_string( m_shadowbuffersize ) + " pixels" ); + } + // cap the number of supported lights based on hardware + { + GLint maxlights; + ::glGetIntegerv( GL_MAX_LIGHTS, &maxlights ); + Global::DynamicLightCount = std::min( Global::DynamicLightCount, maxlights - 1 ); + WriteLog( "Dynamic light amount capped at " + std::to_string( Global::DynamicLightCount ) + " (" + std::to_string(maxlights) + " lights total supported by the gfx card)" ); + } + { + GLint maxtextureunits; + ::glGetIntegerv( GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits ); + if( maxtextureunits < 4 ) { + WriteLog( "Less than 4 texture units, shadow and reflection mapping will be disabled" ); + Global::RenderShadows = false; + m_diffusetextureunit = GL_TEXTURE0; + m_shadowtextureunit = -1; + m_helpertextureunit = -1; + } + } + if( Global::iMultisampling ) { + WriteLog( "Using multisampling x" + std::to_string( 1 << Global::iMultisampling ) ); } return true; diff --git a/renderer.h b/renderer.h index 8095a188..8aab1794 100644 --- a/renderer.h +++ b/renderer.h @@ -159,7 +159,7 @@ public: TGroundNode const * Update_Pick_Node(); // debug performance string - std::string const & + std::string Info() const; // members @@ -169,6 +169,7 @@ public: private: // types enum class rendermode { + none, color, shadows, pickcontrols, @@ -194,12 +195,30 @@ private: Render_pass( rendermode const Mode ); // configures projection matrix for the current render pass void - Render_projection(); + setup_projection(); + void + setup_projection_world(); + void + setup_projection_light_ortho(); + void + setup_projection_light_perspective(); // configures camera for the current render pass void - Render_camera(); + setup_camera(); void - Render_setup( bool const Alpha = false ); + setup_camera_world( glm::dmat4 &Viewmatrix ); + void + setup_camera_light_ortho( glm::dmat4 &Viewmatrix ); + void + setup_camera_light_perspective( glm::dmat4 &Viewmatrix ); + void + setup_drawing( bool const Alpha = false ); + void + setup_units( bool const Diffuse, bool const Shadows, bool const Reflections ); + void + setup_shadow_color( glm::vec4 const &Shadowcolor ); + void + toggle_units( bool const Diffuse, bool const Shadows, bool const Reflections ); bool Render( world_environment *Environment ); bool @@ -213,7 +232,7 @@ private: bool Render( TDynamicObject *Dynamic ); bool - Render( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); + Render( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); bool Render( TModel3d *Model, material_data const *Material, double const Squaredistance ); void @@ -233,7 +252,7 @@ private: bool Render_Alpha( TDynamicObject *Dynamic ); bool - Render_Alpha( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); + Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); bool Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance ); void @@ -257,13 +276,19 @@ private: #endif GLuint m_shadowframebuffer { 0 }; GLuint m_shadowtexture { 0 }; - int m_shadowbuffersize { 2048 }; + int m_shadowbuffersize { 4096 }; + glm::mat4 m_shadowtexturematrix; + glm::vec4 m_shadowcolor{ 0.5f, 0.5f, 0.5f, 1.f }; GLUquadricObj *m_quadric { nullptr }; // helper object for drawing debug mode scene elements + int m_shadowtextureunit { GL_TEXTURE1 }; + int m_helpertextureunit { GL_TEXTURE0 }; + int m_diffusetextureunit { GL_TEXTURE2 }; geometry_handle m_billboardgeometry { 0, 0 }; texture_handle m_glaretexture { -1 }; texture_handle m_suntexture { -1 }; texture_handle m_moontexture { -1 }; + texture_handle m_reflectiontexture { -1 }; float m_drawtime { 1000.0f / 30.0f * 20.0f }; // start with presumed 'neutral' average of 30 fps double m_updateaccumulator { 0.0 }; @@ -274,9 +299,7 @@ private: float m_speculartranslucentscalefactor { 1.0f }; bool m_framebuffersupport { false }; - rendermode m_texenvmode { rendermode::color }; // last configured texture environment renderpass_config m_renderpass; -// std::stack m_renderpasses; bool m_renderspecular { false }; // controls whether to include specular component in the calculations std::vector m_picksceneryitems; std::vector m_pickcontrolsitems; diff --git a/uilayer.cpp b/uilayer.cpp index 0b1474f9..d3ee278e 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -65,7 +65,7 @@ ui_layer::render() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho( 0.f, Global::ScreenWidth, Global::ScreenHeight, 0.f, -1.f, 1.f ); + glOrtho( 0, Global::ScreenWidth, Global::ScreenHeight, 0, -1, 1 ); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -272,10 +272,10 @@ ui_layer::quad( float4 const &Coordinates, float4 const &Color ) { glBegin( GL_TRIANGLE_STRIP ); - glTexCoord2f( 0.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio ); - glTexCoord2f( 0.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio ); - glTexCoord2f( 1.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio ); - glTexCoord2f( 1.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio ); + glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio ); + glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio ); + glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio ); + glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio ); glEnd(); } diff --git a/uilayer.h b/uilayer.h index b49c0598..40e851a1 100644 --- a/uilayer.h +++ b/uilayer.h @@ -41,6 +41,8 @@ public: // methods: bool init( GLFWwindow *Window ); + void + set_unit( GLint const Textureunit ) { m_textureunit = Textureunit; } // draws requested UI elements void render(); @@ -83,6 +85,7 @@ private: // members: GLFWwindow *m_window { nullptr }; + GLint m_textureunit{ GL_TEXTURE0 }; GLuint m_fontbase { (GLuint)-1 }; // numer DL dla znaków w napisach // progress bar config. TODO: put these together into an object diff --git a/version.h b/version.h index a54feaa0..3a7fa547 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 721 +#define VERSION_MINOR 724 #define VERSION_REVISION 0