From bb58156513e739e54a8c8504eac86ee395c1f0d2 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 27 Jul 2017 18:23:48 +0200 Subject: [PATCH 1/9] shadow visibilty fixes, more strict degenerate triangle detection --- Globals.cpp | 2 +- Model3d.cpp | 4 +- Texture.cpp | 2 - World.cpp | 8 -- renderer.cpp | 239 ++++++++++++++++++++++++++------------------------- renderer.h | 22 +++-- uilayer.cpp | 26 ++++++ uilayer.h | 5 ++ usefull.h | 2 +- version.h | 2 +- 10 files changed, 173 insertions(+), 139 deletions(-) diff --git a/Globals.cpp b/Globals.cpp index 6073b44d..4310ec3a 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -88,7 +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 }; +Global::shadowtune_t Global::shadowtune = { 2048, 250.f, 1250.f, 100.f }; 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 diff --git a/Model3d.cpp b/Model3d.cpp index e2168583..36715788 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -428,7 +428,9 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic std::vector sg; sg.resize( facecount ); // maski przynależności trójkątów do powierzchni std::vector wsp; wsp.resize( iNumVerts );// z którego wierzchołka kopiować wektor normalny int maska = 0; + int rawvertexcount = 0; // used to keep track of vertex indices in source file for (int i = 0; i < iNumVerts; ++i) { + ++rawvertexcount; // Ra: z konwersją na układ scenerii - będzie wydajniejsze wyświetlanie wsp[i] = -1; // wektory normalne nie są policzone dla tego wierzchołka if ((i % 3) == 0) { @@ -465,7 +467,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic --facecount; // o jeden trójkąt mniej iNumVerts -= 3; // czyli o 3 wierzchołki i -= 3; // wczytanie kolejnego w to miejsce - WriteLog("Bad model: degenerated triangle ignored in: \"" + pName + "\", vertices " + std::to_string(i) + "-" + std::to_string(i+2)); + WriteLog("Bad model: degenerated triangle ignored in: \"" + pName + "\", vertices " + std::to_string(rawvertexcount-2) + "-" + std::to_string(rawvertexcount)); } if (i > 0) { // jeśli pierwszy trójkąt będzie zdegenerowany, to zostanie usunięty i nie ma co sprawdzać diff --git a/Texture.cpp b/Texture.cpp index 3dfbdcc5..f6147f05 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -609,8 +609,6 @@ opengl_texture::release( bool const Backup ) { if( id == -1 ) { return; } - assert( is_ready ); - if( true == Backup ) { // query texture details needed to perform the backup... ::glBindTexture( GL_TEXTURE_2D, id ); diff --git a/World.cpp b/World.cpp index e6b5fea7..4ed9ac6e 100644 --- a/World.cpp +++ b/World.cpp @@ -32,8 +32,6 @@ http://mozilla.org/MPL/2.0/. #include "uilayer.h" #include "translation.h" -//#define EU07_USE_DEBUG_SHADOWMAP - //--------------------------------------------------------------------------- TDynamicObject *Controlled = NULL; // pojazd, który prowadzimy @@ -634,12 +632,6 @@ 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; break; } diff --git a/renderer.cpp b/renderer.cpp index a1243a02..9fbe6f33 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -62,6 +62,9 @@ opengl_renderer::Init( GLFWwindow *Window ) { m_window = Window; + ::glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + ::glPixelStorei( GL_PACK_ALIGNMENT, 1 ); + glClearDepth( 1.0f ); glClearColor( 51.0f / 255.0f, 102.0f / 255.0f, 85.0f / 255.0f, 1.0f ); // initial background Color @@ -126,6 +129,16 @@ opengl_renderer::Init( GLFWwindow *Window ) { m_lights.emplace_back( light ); } + // 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" ); + #ifdef EU07_USE_PICKING_FRAMEBUFFER // pick buffer resources if( true == m_framebuffersupport ) { @@ -159,18 +172,6 @@ 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 ) ) { @@ -188,16 +189,27 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL ); ::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 ); +#ifdef EU07_USE_DEBUG_SHADOWMAP + ::glGenTextures( 1, &m_shadowdebugtexture ); + ::glBindTexture( GL_TEXTURE_2D, m_shadowdebugtexture ); + // allocate memory + ::glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_shadowbuffersize, m_shadowbuffersize, 0, GL_BGRA, 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_LINEAR ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + ::glBindTexture( GL_TEXTURE_2D, 0 ); +#endif // create and assemble the framebuffer ::glGenFramebuffersEXT( 1, &m_shadowframebuffer ); ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); +#ifdef EU07_USE_DEBUG_SHADOWMAP + ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_shadowdebugtexture, 0 ); +#else ::glDrawBuffer( GL_NONE ); // we won't be rendering colour data, so can skip the colour attachment +#endif ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_shadowtexture, 0 ); // check if we got it working GLenum status = ::glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); @@ -210,7 +222,6 @@ opengl_renderer::Init( GLFWwindow *Window ) { Global::RenderShadows = false; } ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target for now - ::glDrawBuffer( GL_BACK ); } // prepare basic geometry chunks @@ -258,25 +269,15 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( Global::RenderShadows && World.InitPerformed() ) { // run shadowmap pass before color - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); - ::glDrawBuffer( GL_NONE ); // we won't be rendering colour data, so can skip the colour attachment - Render_pass( rendermode::shadows ); +#ifdef EU07_USE_DEBUG_SHADOWMAP + UILayer.set_texture( m_shadowdebugtexture ); +#endif m_renderpass.draw_mode = rendermode::color; // restore draw mode. TODO: render mode stack - - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target - ::glDrawBuffer( GL_BACK ); } - 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 - } + ::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(); @@ -304,7 +305,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { 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 ); + switch_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() ); } } @@ -316,13 +317,20 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( World.InitPerformed() ) { // setup + ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); + ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); - ::glClear( GL_DEPTH_BUFFER_BIT ); +#ifdef EU07_USE_DEBUG_SHADOWMAP + ::glClearColor( 0.f / 255.0f, 0.f / 255.0f, 0.f / 255.f, 1.f ); // initial background Color + ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); +#else + ::glClear( GL_DEPTH_BUFFER_BIT ); +#endif ::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_renderpass.draw_range = Global::shadowtune.depth; // 1.0km square centered around camera m_shadowtexturematrix = glm::mat4{ 0.5f, 0.0f, 0.0f, 0.0f, @@ -333,27 +341,32 @@ opengl_renderer::Render_pass( rendermode const Mode ) { setup_camera(); ::glDepthFunc( GL_LEQUAL ); ::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting - ::glPolygonOffset( 4.f, 8.f ); + ::glPolygonOffset( 2.f, 4.f ); // render // opaque parts... setup_drawing( false ); +#ifdef EU07_USE_DEBUG_SHADOWMAP + setup_units( true, false, false ); +#else setup_units( false, false, false ); +#endif Render( &World.Ground ); // post-render restore ::glDisable( GL_POLYGON_OFFSET_FILL ); ::glDisable( GL_SCISSOR_TEST ); + + ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target } break; } case rendermode::pickcontrols: { - -#ifdef EU07_USE_PICKING_FRAMEBUFFER - ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); -#endif if( World.InitPerformed() ) { // setup - ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); +#ifdef EU07_USE_PICKING_FRAMEBUFFER + ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); +#endif + ::glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); m_pickcontrolsitems.clear(); @@ -367,19 +380,18 @@ opengl_renderer::Render_pass( rendermode const Mode ) { 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() ); } + // post-render cleanup } - // post-render cleanup break; } case rendermode::pickscenery: { - -#ifdef EU07_USE_PICKING_FRAMEBUFFER - ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); -#endif if( World.InitPerformed() ) { // setup - ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); +#ifdef EU07_USE_PICKING_FRAMEBUFFER + ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); +#endif + ::glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); m_picksceneryitems.clear(); @@ -392,6 +404,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { setup_drawing( false ); setup_units( false, false, false ); Render( &World.Ground ); + // post-render cleanup } break; } @@ -416,17 +429,7 @@ opengl_renderer::setup_projection() { case rendermode::pickscenery: #endif case rendermode::color: { - 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(); - } + setup_projection_world(); break; } #ifdef EU07_USE_PICKING_FRAMEBUFFER @@ -471,11 +474,11 @@ opengl_renderer::setup_projection_world() { void opengl_renderer::setup_projection_light_ortho() { - + // TODO: calculate lightview boundaries based on area of the world camera frustum ::glOrtho( - -250, 250, - -250, 250, - -2500, 2500 ); + -Global::shadowtune.width, Global::shadowtune.width, + -Global::shadowtune.width, Global::shadowtune.width, + -Global::shadowtune.depth, Global::shadowtune.depth ); } void @@ -484,7 +487,7 @@ 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 * 0.1f, // 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 ); } @@ -501,16 +504,7 @@ opengl_renderer::setup_camera() { case rendermode::color: case rendermode::pickcontrols: case rendermode::pickscenery: { - 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 ); - } + setup_camera_world( viewmatrix ); break; } case rendermode::shadows: { @@ -544,19 +538,12 @@ opengl_renderer::setup_camera_world( glm::dmat4 &Viewmatrix ) { 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; + if( m_renderpass.camera.position().y - Global::pCameraPosition.y < 0.1 ) { + m_renderpass.camera.position().y = Global::pCameraPosition.y + 0.1; } - Viewmatrix = glm::lookAt( + Viewmatrix *= glm::lookAt( m_renderpass.camera.position(), glm::dvec3{ Global::pCameraPosition }, glm::dvec3{ 0.f, 1.f, 0.f } ); @@ -567,9 +554,9 @@ 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( m_renderpass.draw_range * 0.5f * 0.1f, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low - Viewmatrix = glm::lookAt( + Viewmatrix *= glm::lookAt( m_renderpass.camera.position(), - glm::dvec3{ Global::pCameraPosition.x, 0.0, Global::pCameraPosition.z }, + glm::dvec3{ Global::pCameraPosition }, glm::dvec3{ 0.f, 1.f, 0.f } ); } @@ -607,6 +594,7 @@ opengl_renderer::setup_drawing( bool const Alpha ) { case rendermode::shadows: case rendermode::pickcontrols: case rendermode::pickscenery: { + ::glColor4fv( glm::value_ptr( colors::white ) ); ::glDisable( GL_LIGHTING ); ::glShadeModel( GL_FLAT ); if( Global::iMultisampling ) { @@ -631,7 +619,7 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const 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 + Bind( m_reflectiontexture ); // TBD, TODO: move to reflection unit setup ::glEnable( GL_TEXTURE_2D ); /* glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); @@ -648,7 +636,7 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const ::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_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); } else { @@ -692,7 +680,7 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const ::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_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); } else { @@ -737,7 +725,7 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const // enables and disables specified texture units void -opengl_renderer::toggle_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { +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 ) ) { @@ -1098,7 +1086,6 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { // non-interactive scenery elements get neutral colour ::glColor3fv( glm::value_ptr( colors::none ) ); } - case rendermode::shadows: case rendermode::color: { if( Groundcell->nRenderRect != nullptr ) { // nieprzezroczyste trójkąty kwadratu kilometrowego @@ -1108,6 +1095,17 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { } break; } + case rendermode::shadows: { + if( Groundcell->nRenderRect != nullptr ) { + // experimental, for shadows render both back and front faces, to supply back faces of the 'forest strips' + ::glDisable( GL_CULL_FACE ); + // nieprzezroczyste trójkąty kwadratu kilometrowego + for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { + Render( node ); + } + ::glEnable( GL_CULL_FACE ); + } + } case rendermode::pickcontrols: default: { break; @@ -1240,7 +1238,7 @@ opengl_renderer::Render( TGroundNode *Node ) { 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 + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ); break; } @@ -1304,6 +1302,7 @@ opengl_renderer::Render( TGroundNode *Node ) { Render( Node->Model->pModel, Node->Model->Material(), + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees SquareMagnitude( Node->pCenter - Global::pCameraPosition ), Node->pCenter - m_renderpass.camera.position(), Node->Model->vAngle ); @@ -2032,7 +2031,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { 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 + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ); break; } @@ -2104,6 +2103,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { Render_Alpha( Node->Model->pModel, Node->Model->Material(), + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees SquareMagnitude( Node->pCenter - Global::pCameraPosition ), Node->pCenter - m_renderpass.camera.position(), Node->Model->vAngle ); @@ -2502,15 +2502,8 @@ opengl_renderer::Update_Pick_Control() { #ifdef EU07_USE_PICKING_FRAMEBUFFER if( true == m_framebuffersupport ) { ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_pickframebuffer ); - ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); } - else { - ::glReadBuffer( GL_BACK ); - } -#else - ::glReadBuffer( GL_BACK ); #endif - Render_pass( rendermode::pickcontrols ); // determine point to examine glm::dvec2 mousepos; @@ -2518,13 +2511,22 @@ opengl_renderer::Update_Pick_Control() { mousepos.y = Global::ScreenHeight - mousepos.y; // cursor coordinates are flipped compared to opengl #ifdef EU07_USE_PICKING_FRAMEBUFFER - glm::ivec2 pickbufferpos { - mousepos.x * m_pickbuffersize / Global::ScreenWidth, - mousepos.y * m_pickbuffersize / Global::ScreenHeight }; + glm::ivec2 pickbufferpos; + if( true == m_framebuffersupport ) { +// ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); + pickbufferpos = glm::ivec2{ + mousepos.x * m_pickbuffersize / Global::ScreenWidth, + mousepos.y * m_pickbuffersize / Global::ScreenHeight + }; + } + else { +// ::glReadBuffer( GL_BACK ); + pickbufferpos = glm::ivec2{ mousepos }; + } #else +// ::glReadBuffer( GL_BACK ); glm::ivec2 pickbufferpos{ mousepos }; #endif - unsigned char pickreadout[4]; ::glReadPixels( pickbufferpos.x, pickbufferpos.y, 1, 1, GL_BGRA, GL_UNSIGNED_BYTE, pickreadout ); auto const controlindex = pick_index( glm::ivec3{ pickreadout[ 2 ], pickreadout[ 1 ], pickreadout[ 0 ] } ); @@ -2548,15 +2550,8 @@ opengl_renderer::Update_Pick_Node() { #ifdef EU07_USE_PICKING_FRAMEBUFFER if( true == m_framebuffersupport ) { ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_pickframebuffer ); - ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); } - else { - ::glReadBuffer( GL_BACK ); - } -#else - ::glReadBuffer( GL_BACK ); #endif - Render_pass( rendermode::pickscenery ); // determine point to examine glm::dvec2 mousepos; @@ -2564,13 +2559,23 @@ opengl_renderer::Update_Pick_Node() { mousepos.y = Global::ScreenHeight - mousepos.y; // cursor coordinates are flipped compared to opengl #ifdef EU07_USE_PICKING_FRAMEBUFFER - glm::ivec2 pickbufferpos { - mousepos.x * m_pickbuffersize / Global::ScreenWidth, - mousepos.y * m_pickbuffersize / Global::ScreenHeight }; + glm::ivec2 pickbufferpos; + if( true == m_framebuffersupport ) { +// ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); + pickbufferpos = glm::ivec2{ + mousepos.x * m_pickbuffersize / Global::ScreenWidth, + mousepos.y * m_pickbuffersize / Global::ScreenHeight + }; + } + else { +// ::glReadBuffer( GL_BACK ); + pickbufferpos = glm::ivec2{ mousepos }; + } #else +// ::glReadBuffer( GL_BACK ); glm::ivec2 pickbufferpos{ mousepos }; #endif - + unsigned char pickreadout[4]; ::glReadPixels( pickbufferpos.x, pickbufferpos.y, 1, 1, GL_BGRA, GL_UNSIGNED_BYTE, pickreadout ); auto const nodeindex = pick_index( glm::ivec3{ pickreadout[ 2 ], pickreadout[ 1 ], pickreadout[ 0 ] } ); @@ -2663,7 +2668,7 @@ opengl_renderer::Update( double const Deltatime ) { }; // debug performance string -std::string +std::string const & opengl_renderer::Info() const { return m_debuginfo; diff --git a/renderer.h b/renderer.h index 8aab1794..05b5c6bc 100644 --- a/renderer.h +++ b/renderer.h @@ -18,6 +18,9 @@ http://mozilla.org/MPL/2.0/. #include "world.h" #include "memcell.h" +#define EU07_USE_PICKING_FRAMEBUFFER +//#define EU07_USE_DEBUG_SHADOWMAP + struct opengl_light { GLuint id{ (GLuint)-1 }; @@ -159,7 +162,7 @@ public: TGroundNode const * Update_Pick_Node(); // debug performance string - std::string + std::string const & Info() const; // members @@ -180,7 +183,7 @@ private: struct renderpass_config { opengl_camera camera; - rendermode draw_mode { rendermode::color }; + rendermode draw_mode { rendermode::none }; float draw_range { 0.0f }; std::vector draw_queue; // list of subcells to be drawn in current render pass }; @@ -218,7 +221,7 @@ private: void setup_shadow_color( glm::vec4 const &Shadowcolor ); void - toggle_units( bool const Diffuse, bool const Shadows, bool const Reflections ); + switch_units( bool const Diffuse, bool const Shadows, bool const Reflections ); bool Render( world_environment *Environment ); bool @@ -269,13 +272,16 @@ private: opengllight_array m_lights; GLFWwindow *m_window { nullptr }; #ifdef EU07_USE_PICKING_FRAMEBUFFER - GLuint m_pickframebuffer { 0 }; // TODO: refactor pick framebuffer stuff into an object - GLuint m_picktexture { 0 }; - GLuint m_pickdepthbuffer { 0 }; + GLuint m_pickframebuffer { NULL }; // TODO: refactor pick framebuffer stuff into an object + GLuint m_picktexture { NULL }; + GLuint m_pickdepthbuffer { NULL }; int m_pickbuffersize { 1024 }; // size of (square) textures bound with the pick framebuffer #endif - GLuint m_shadowframebuffer { 0 }; - GLuint m_shadowtexture { 0 }; + GLuint m_shadowframebuffer { NULL }; + GLuint m_shadowtexture { NULL }; +#ifdef EU07_USE_DEBUG_SHADOWMAP + GLuint m_shadowdebugtexture{ NULL }; +#endif int m_shadowbuffersize { 4096 }; glm::mat4 m_shadowtexturematrix; glm::vec4 m_shadowcolor{ 0.5f, 0.5f, 0.5f, 1.f }; diff --git a/uilayer.cpp b/uilayer.cpp index d4a574ca..67ca6c60 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -78,6 +78,7 @@ ui_layer::render() { // render code here render_background(); + render_texture(); render_progress(); render_panels(); render_tooltip(); @@ -235,6 +236,31 @@ ui_layer::render_background() { float4( 1.0f, 1.0f, 1.0f, 1.0f ) ); } +void +ui_layer::render_texture() { + + if( m_texture != NULL ) { + ::glColor4f( 1.f, 1.f, 1.f, 1.f ); + ::glDisable( GL_BLEND ); + + GfxRenderer.Bind( NULL ); + ::glBindTexture( GL_TEXTURE_2D, m_texture ); + + auto const size = 512.f; + + glBegin( GL_TRIANGLE_STRIP ); + + glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( 0.f, 256.f ); + glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( 0.f, 256.f + size ); + glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( size, 256.f ); + glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( size, 256.f + size ); + + glEnd(); + + ::glEnable( GL_BLEND ); + } +} + void ui_layer::print( std::string const &Text ) { diff --git a/uilayer.h b/uilayer.h index 40e851a1..8b3f31bb 100644 --- a/uilayer.h +++ b/uilayer.h @@ -54,6 +54,8 @@ public: // sets the ui background texture, if any void set_background( std::string const &Filename = "" ); + void + set_texture( GLuint Texture = NULL ) { m_texture = Texture; } void set_tooltip( std::string const &Tooltip ) { m_tooltip = Tooltip; } void @@ -68,6 +70,8 @@ private: // draws background quad with specified earlier texture void render_background(); + void + render_texture(); // draws a progress bar in defined earlier state void render_progress(); @@ -95,6 +99,7 @@ private: bool m_progressbottom { false }; // location of the progress bar texture_handle m_background { NULL }; // path to texture used as the background. size depends on mAspect. + GLuint m_texture { NULL }; std::vector > m_panels; std::string m_tooltip; }; diff --git a/usefull.h b/usefull.h index 6474da98..91c27795 100644 --- a/usefull.h +++ b/usefull.h @@ -77,7 +77,7 @@ bool degenerate( VecType_ const &Vertex1, VecType_ const &Vertex2, VecType_ const &Vertex3 ) { // degenerate( A, B, C, minarea ) = ( ( B - A ).cross( C - A ) ).lengthSquared() < ( 4.0f * minarea * minarea ); - return glm::length2( glm::cross( Vertex2 - Vertex1, Vertex3 - Vertex1 ) ) < std::numeric_limits::epsilon(); + return ( glm::length2( glm::cross( Vertex2 - Vertex1, Vertex3 - Vertex1 ) ) == 0.0 ); } //--------------------------------------------------------------------------- diff --git a/version.h b/version.h index 122d37e8..5060e104 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 725 +#define VERSION_MINOR 727 #define VERSION_REVISION 0 From 9b4dc83a2d5650c02e1017836d9f01315da81328 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 27 Jul 2017 18:24:58 +0200 Subject: [PATCH 2/9] pantograph switch functionality swapped for the rear cab --- Gauge.cpp | 2 +- Train.cpp | 393 ++++++++++++++++++++++++++++++++++++------------------ Train.h | 2 + 3 files changed, 264 insertions(+), 133 deletions(-) diff --git a/Gauge.cpp b/Gauge.cpp index 4c4da7b2..c52de766 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -180,7 +180,7 @@ void TGauge::DecValue(double fNewDesired) void TGauge::UpdateValue( double fNewDesired, PSound Fallbacksound ) { - auto const desiredtimes100 = static_cast( 100.0 * fNewDesired ); + auto const desiredtimes100 = static_cast( std::round( 100.0 * fNewDesired ) ); if( static_cast( std::round( 100.0 * ( fDesiredValue - fOffset ) / fScale ) ) == desiredtimes100 ) { return; } diff --git a/Train.cpp b/Train.cpp index 46dbb7b9..066e98e1 100644 --- a/Train.cpp +++ b/Train.cpp @@ -1361,6 +1361,14 @@ void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const &Command ) { + return ( + Train->mvControlled->ActiveCab < 0 ? + pantographtogglerear( Train, Command ) : + pantographtogglefront( Train, Command ) ); +} + +void TTrain::pantographtogglefront( TTrain *Train, command_data const &Command ) { + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->mvControlled->PantFrontUp ) { @@ -1368,60 +1376,99 @@ void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const Train->mvControlled->PantFrontSP = false; if( Train->mvControlled->PantFront( true ) ) { if( Train->mvControlled->PantFrontStart != 1 ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - Train->play_sound( Train->dsbSwitch ); -#endif // visual feedback - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->mvControlled->ActiveCab < 0 ) { + if( Train->ggPantRearButton.SubModel ) + Train->ggPantRearButton.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantRearButtonOff.SubModel ) + Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + } + else { + if( Train->ggPantFrontButton.SubModel ) + Train->ggPantFrontButton.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantFrontButtonOff.SubModel ) + Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + } } } } else { // ...or turn off if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( ( Train->ggPantFrontButtonOff.SubModel == nullptr ) - && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { - // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it - // then another control has to be used (like pantographlowerall) - // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels - return; + if( Train->mvControlled->ActiveCab < 0 ) { + if( ( Train->ggPantRearButtonOff.SubModel == nullptr ) + && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { + // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it + // then another control has to be used (like pantographlowerall) + // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels + return; + } + } + else { + if( ( Train->ggPantFrontButtonOff.SubModel == nullptr ) + && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { + // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it + // then another control has to be used (like pantographlowerall) + // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels + return; + } } } Train->mvControlled->PantFrontSP = false; if( false == Train->mvControlled->PantFront( false ) ) { if( Train->mvControlled->PantFrontStart != 0 ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - Train->play_sound( Train->dsbSwitch ); -#endif // visual feedback - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); + if( Train->mvControlled->ActiveCab < 0 ) { + // in rear cab switch functions are swapped + if( Train->ggPantRearButton.SubModel ) + Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantRearButtonOff.SubModel ) + Train->ggPantRearButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else { + // in front cab switches map normally + if( Train->ggPantFrontButton.SubModel ) + Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantFrontButtonOff.SubModel ) + Train->ggPantFrontButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); + } } } } @@ -1429,29 +1476,37 @@ void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const else if( Command.action == GLFW_RELEASE ) { // impulse switches return automatically to neutral position if( Train->mvOccupied->PantSwitchType == "impulse" ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->ggPantFrontButton.GetValue() > 0.35 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // also the switch off button, in cabs which have it -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->ggPantFrontButtonOff.GetValue() > 0.35 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - if( Train->ggPantSelectedDownButton.SubModel ) { + if( Train->mvControlled->ActiveCab < 0 ) { + if( Train->ggPantRearButton.SubModel ) + Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); // NOTE: currently we animate the selectable pantograph control based on standard key presses // TODO: implement actual selection control, and refactor handling this control setup in a separate method - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // also the switch off button, in cabs which have it + if( Train->ggPantRearButtonOff.SubModel ) + Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantSelectedDownButton.SubModel ) { + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + } + } + else { + if( Train->ggPantFrontButton.SubModel ) + Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // also the switch off button, in cabs which have it + if( Train->ggPantFrontButtonOff.SubModel ) + Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantSelectedDownButton.SubModel ) { + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + } } } } @@ -1459,6 +1514,14 @@ void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const void TTrain::OnCommand_pantographtogglerear( TTrain *Train, command_data const &Command ) { + return ( + Train->mvControlled->ActiveCab < 0 ? + pantographtogglefront( Train, Command ) : + pantographtogglerear( Train, Command ) ); +} + +void TTrain::pantographtogglerear( TTrain *Train, command_data const &Command ) { + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->mvControlled->PantRearUp ) { @@ -1466,60 +1529,100 @@ void TTrain::OnCommand_pantographtogglerear( TTrain *Train, command_data const & Train->mvControlled->PantRearSP = false; if( Train->mvControlled->PantRear( true ) ) { if( Train->mvControlled->PantRearStart != 1 ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - Train->play_sound( Train->dsbSwitch ); -#endif // visual feedback - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->mvControlled->ActiveCab < 0 ) { + // in rear cab switch functions are swapped + if( Train->ggPantFrontButton.SubModel ) + Train->ggPantFrontButton.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantFrontButtonOff.SubModel ) + Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + } + else { + if( Train->ggPantRearButton.SubModel ) + Train->ggPantRearButton.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantRearButtonOff.SubModel ) + Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + } } } } else { // ...or turn off if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( ( Train->ggPantRearButtonOff.SubModel == nullptr ) - && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { - // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it - // then another control has to be used (like pantographlowerall) - // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels - return; + if( Train->mvControlled->ActiveCab < 0 ) { + // in rear cab switch functions are swapped + if( ( Train->ggPantFrontButtonOff.SubModel == nullptr ) + && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { + // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it + // then another control has to be used (like pantographlowerall) + // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels + return; + } + } + else { + if( ( Train->ggPantRearButtonOff.SubModel == nullptr ) + && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { + // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it + // then another control has to be used (like pantographlowerall) + // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels + return; + } } } Train->mvControlled->PantRearSP = false; if( false == Train->mvControlled->PantRear( false ) ) { if( Train->mvControlled->PantRearStart != 0 ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - Train->play_sound( Train->dsbSwitch ); -#endif // visual feedback - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); + if( Train->mvControlled->ActiveCab < 0 ) { + // in rear cab switch functions are swapped + if( Train->ggPantFrontButton.SubModel ) + Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantFrontButtonOff.SubModel ) + Train->ggPantFrontButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else { + if( Train->ggPantRearButton.SubModel ) + Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantRearButtonOff.SubModel ) + Train->ggPantRearButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); + } } } } @@ -1527,29 +1630,38 @@ void TTrain::OnCommand_pantographtogglerear( TTrain *Train, command_data const & else if( Command.action == GLFW_RELEASE ) { // impulse switches return automatically to neutral position if( Train->mvOccupied->PantSwitchType == "impulse" ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->ggPantRearButton.GetValue() > 0.35 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // also the switch off button, in cabs which have it -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->ggPantRearButtonOff.GetValue() > 0.35 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - if( Train->ggPantSelectedDownButton.SubModel ) { + if( Train->mvControlled->ActiveCab < 0 ) { + // in rear cab switch functions are swapped + if( Train->ggPantFrontButton.SubModel ) + Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); // NOTE: currently we animate the selectable pantograph control based on standard key presses // TODO: implement actual selection control, and refactor handling this control setup in a separate method - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // also the switch off button, in cabs which have it + if( Train->ggPantFrontButtonOff.SubModel ) + Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantSelectedDownButton.SubModel ) { + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + } + } + else { + if( Train->ggPantRearButton.SubModel ) + Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // also the switch off button, in cabs which have it + if( Train->ggPantRearButtonOff.SubModel ) + Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantSelectedDownButton.SubModel ) { + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); + } } } } @@ -6329,12 +6441,21 @@ bool TTrain::Update( double const Deltatime ) || ( mvControlled->ConverterFlag ) ) { if( ggPantAllDownButton.GetValue() == 0.0 ) { // the 'lower all' button overrides state of switches, while active itself + // when in rear cab we treat switches as reversed, i.e. front controls physical rear + auto const frontpantographswitch = ( + mvControlled->ActiveCab < 0 ? + ggPantRearButton.GetValue() >= 1.0 : + ggPantFrontButton.GetValue() >= 1.0 ); + auto const rearpantographswitch = ( + mvControlled->ActiveCab < 0 ? + ggPantFrontButton.GetValue() >= 1.0 : + ggPantRearButton.GetValue() >= 1.0 ); if( ( false == mvControlled->PantFrontUp ) - && ( ggPantFrontButton.GetValue() >= 1.0 ) ) { + && ( frontpantographswitch ) ) { mvControlled->PantFront( true ); } if( ( false == mvControlled->PantRearUp ) - && ( ggPantRearButton.GetValue() >= 1.0 ) ) { + && ( rearpantographswitch ) ) { mvControlled->PantRear( true ); } } @@ -7205,42 +7326,50 @@ void TTrain::set_cab_controls() { } // pantographs if( mvOccupied->PantSwitchType != "impulse" ) { + auto const pantograph = ( + mvControlled->ActiveCab < 0 ? + mvControlled->PantRearUp : + mvControlled->PantFrontUp ); ggPantFrontButton.PutValue( - ( mvControlled->PantFrontUp ? + ( pantograph ? 1.0 : 0.0 ) ); ggPantFrontButtonOff.PutValue( - ( mvControlled->PantFrontUp ? + ( pantograph ? 0.0 : 1.0 ) ); // NOTE: currently we animate the selectable pantograph control for both pantographs // TODO: implement actual selection control, and refactor handling this control setup in a separate method ggPantSelectedButton.PutValue( - ( mvControlled->PantFrontUp ? + ( pantograph ? 1.0 : 0.0 ) ); ggPantSelectedDownButton.PutValue( - ( mvControlled->PantFrontUp ? + ( pantograph ? 0.0 : 1.0 ) ); } if( mvOccupied->PantSwitchType != "impulse" ) { + auto const pantograph = ( + mvControlled->ActiveCab < 0 ? + mvControlled->PantFrontUp : + mvControlled->PantRearUp ); ggPantRearButton.PutValue( - ( mvControlled->PantRearUp ? + ( pantograph ? 1.0 : 0.0 ) ); ggPantRearButtonOff.PutValue( - ( mvControlled->PantRearUp ? + ( pantograph ? 0.0 : 1.0 ) ); // NOTE: currently we animate the selectable pantograph control for both pantographs // TODO: implement actual selection control, and refactor handling this control setup in a separate method ggPantSelectedButton.PutValue( - ( mvControlled->PantRearUp ? + ( pantograph ? 1.0 : 0.0 ) ); ggPantSelectedDownButton.PutValue( - ( mvControlled->PantRearUp ? + ( pantograph ? 0.0 : 1.0 ) ); } diff --git a/Train.h b/Train.h index c9c9e6d2..5331386f 100644 --- a/Train.h +++ b/Train.h @@ -155,7 +155,9 @@ class TTrain static void OnCommand_pantographcompressorvalvetoggle( TTrain *Train, command_data const &Command ); static void OnCommand_pantographcompressoractivate( TTrain *Train, command_data const &Command ); static void OnCommand_pantographtogglefront( TTrain *Train, command_data const &Command ); + static void pantographtogglefront( TTrain *Train, command_data const &Command ); static void OnCommand_pantographtogglerear( TTrain *Train, command_data const &Command ); + static void pantographtogglerear( TTrain *Train, command_data const &Command ); static void OnCommand_pantographlowerall( TTrain *Train, command_data const &Command ); static void OnCommand_linebreakertoggle( TTrain *Train, command_data const &Command ); static void OnCommand_convertertoggle( TTrain *Train, command_data const &Command ); From 3a26ccb2fdd00b7657b2174a64bea35deb4b96bf Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Fri, 28 Jul 2017 20:28:08 +0200 Subject: [PATCH 3/9] reverted pantograph control changes, removed some of the type conversion warnings --- Camera.cpp | 32 +--- Camera.h | 1 - Driver.cpp | 6 +- DynObj.cpp | 126 +++++++--------- Float3d.h | 34 ++--- Ground.cpp | 18 +-- McZapkie/MOVER.h | 12 +- McZapkie/Mover.cpp | 6 +- Model3d.cpp | 26 ++-- Train.cpp | 357 ++++++++++++--------------------------------- Train.h | 2 - moon.cpp | 6 +- sun.cpp | 6 +- translation.h | 8 +- uilayer.h | 4 +- 15 files changed, 211 insertions(+), 433 deletions(-) diff --git a/Camera.cpp b/Camera.cpp index 2c59684f..6d3a3adb 100644 --- a/Camera.cpp +++ b/Camera.cpp @@ -379,35 +379,9 @@ void TCamera::Update() } } -vector3 TCamera::GetDirection() -{ - matrix4x4 mat; - vector3 Vec; - Vec = vector3(0, 0, 1); - Vec.RotateY(Yaw); +vector3 TCamera::GetDirection() { - return (Normalize(Vec)); -} - -bool TCamera::SetMatrix() -{ - glRotated( -Roll * 180.0 / M_PI, 0.0, 0.0, 1.0 ); // po wyłączeniu tego kręci się pojazd, a sceneria nie - glRotated( -Pitch * 180.0 / M_PI, 1.0, 0.0, 0.0 ); - glRotated( -Yaw * 180.0 / M_PI, 0.0, 1.0, 0.0 ); // w zewnętrznym widoku: kierunek patrzenia - - if( Type == tp_Follow ) - { - gluLookAt( - Pos.x, Pos.y, Pos.z, - LookAt.x, LookAt.y, LookAt.z, - vUp.x, vUp.y, vUp.z); // Ra: pOffset is zero - } - else { - glTranslated( -Pos.x, -Pos.y, -Pos.z ); // nie zmienia kierunku patrzenia - } - - Global::SetCameraPosition(Pos); // było +pOffset - return true; + return glm::normalize( glm::rotateY( glm::vec3{ 0.f, 0.f, 1.f }, Yaw ) ); } bool TCamera::SetMatrix( glm::dmat4 &Matrix ) { @@ -427,7 +401,6 @@ bool TCamera::SetMatrix( glm::dmat4 &Matrix ) { Matrix = glm::translate( Matrix, glm::dvec3{ -Pos } ); // nie zmienia kierunku patrzenia } - Global::SetCameraPosition( Pos ); // było +pOffset return true; } @@ -446,4 +419,3 @@ void TCamera::Stop() Type = tp_Follow; Velocity = vector3(0, 0, 0); }; - diff --git a/Camera.h b/Camera.h index f978047f..a6346f52 100644 --- a/Camera.h +++ b/Camera.h @@ -57,7 +57,6 @@ class TCamera void OnCommand( command_data const &Command ); void Update(); vector3 GetDirection(); - bool SetMatrix(); bool SetMatrix(glm::dmat4 &Matrix); void RaLook(); void Stop(); diff --git a/Driver.cpp b/Driver.cpp index 98709cc7..48aa14c4 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -507,7 +507,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) fCurrentDistance = lastspeedpoint.fDist; // nie doliczac dlugosci gdy: miniety początek lub jazda do konca toru fTrackLength = ( - ( lastspeedpoint.iFlags & ( spElapsed | spEnd ) != 0 ) ? + ( ( lastspeedpoint.iFlags & ( spElapsed | spEnd ) ) != 0 ) ? 0.0 : pTrack->Length() ); } @@ -1331,7 +1331,7 @@ void TController::TablePurger() for( std::size_t idx = 0; idx < sSpeedTable.size() - 1; ++idx ) { auto const &speedpoint = sSpeedTable[ idx ]; if( ( 0 == ( speedpoint.iFlags & spEnabled ) ) - || ( ( speedpoint.iFlags & ( spElapsed | spTrack | spCurve | spSwitch ) == ( spElapsed | spTrack | spCurve ) ) + || ( ( ( speedpoint.iFlags & ( spElapsed | spTrack | spCurve | spSwitch ) ) == ( spElapsed | spTrack | spCurve ) ) && ( speedpoint.fVelNext < 0.0 ) ) ) { // NOTE: we could break out early here, but running through entire thing gives us exact size needed for new table ++trimcount; @@ -1353,7 +1353,7 @@ void TController::TablePurger() } auto const &speedpoint = sSpeedTable[ idx ]; if( ( 0 == ( speedpoint.iFlags & spEnabled ) ) - || ( ( speedpoint.iFlags & ( spElapsed | spTrack | spCurve | spSwitch ) == ( spElapsed | spTrack | spCurve ) ) + || ( ( ( speedpoint.iFlags & ( spElapsed | spTrack | spCurve | spSwitch ) ) == ( spElapsed | spTrack | spCurve ) ) && ( speedpoint.fVelNext < 0.0 ) ) ) { // if the trimmed point happens to be currently active semaphor we need to invalidate their placements if( idx == SemNextIndex ) { diff --git a/DynObj.cpp b/DynObj.cpp index 2a5c9254..0f670004 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -185,9 +185,10 @@ float TDynamicObject::GetEPP() // od strony sprzegu (coupler_nr) obiektu (start) TDynamicObject *temp = this; int coupler_nr = 0; - float eq = 0, am = 0; + double eq = 0.0; + double am = 0.0; - for (int i = 0; i < 300; i++) // ograniczenie do 300 na wypadek zapętlenia składu + for (int i = 0; i < 300; ++i) // ograniczenie do 300 na wypadek zapętlenia składu { if (!temp) break; // Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów @@ -2732,26 +2733,20 @@ bool TDynamicObject::Update(double dt, double dt1) if (Mechanik) { // Ra 2F3F: do Driver.cpp to przenieść? MoverParameters->EqvtPipePress = GetEPP(); // srednie cisnienie w PG - if ((Mechanik->Primary()) && - (MoverParameters->EngineType == ElectricInductionMotor)) // jesli glowny i z - // asynchronami, to - // niech steruje - { // hamulcem lacznie dla calego pociagu/ezt - bool kier = (DirectionGet() * MoverParameters->ActiveCab > 0); - float FED = 0; - int np = 0; - float masa = 0; - float FrED = 0; - float masamax = 0; - float FmaxPN = 0; - float FfulED = 0; - float FmaxED = 0; - float Fzad = 0; - float FzadED = 0; - float FzadPN = 0; - float Frj = 0; - float amax = 0; - float osie = 0; + if( ( Mechanik->Primary() ) + && ( MoverParameters->EngineType == ElectricInductionMotor ) ) { + // jesli glowny i z asynchronami, to niech steruje hamulcem lacznie dla calego pociagu/ezt + auto const kier = (DirectionGet() * MoverParameters->ActiveCab > 0); + auto FED { 0.0 }; + auto np { 0 }; + auto masa { 0.0 }; + auto FrED { 0.0 }; + auto masamax { 0.0 }; + auto FmaxPN { 0.0 }; + auto FfulED { 0.0 }; + auto FmaxED { 0.0 }; + auto Frj { 0.0 }; + auto osie { 0 }; // 1. ustal wymagana sile hamowania calego pociagu // - opoznienie moze byc ustalane na podstawie charakterystyki // - opoznienie moze byc ustalane na podstawie mas i cisnien granicznych @@ -2762,11 +2757,14 @@ bool TDynamicObject::Update(double dt, double dt1) for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0, 4); p; (kier ? p = p->NextC(4) : p = p->PrevC(4))) { - np++; - masamax += p->MoverParameters->MBPM + - (p->MoverParameters->MBPM > 1 ? 0 : p->MoverParameters->Mass) + - p->MoverParameters->Mred; - float Nmax = ((p->MoverParameters->P2FTrans * p->MoverParameters->MaxBrakePress[0] - + ++np; + masamax += + p->MoverParameters->MBPM + + ( p->MoverParameters->MBPM > 1.0 ? + 0.0 : + p->MoverParameters->Mass ) + + p->MoverParameters->Mred; + auto const Nmax = ((p->MoverParameters->P2FTrans * p->MoverParameters->MaxBrakePress[0] - p->MoverParameters->BrakeCylSpring) * p->MoverParameters->BrakeCylMult[0] - p->MoverParameters->BrakeSlckAdj) * @@ -2780,18 +2778,19 @@ bool TDynamicObject::Update(double dt, double dt1) 0) ? p->MoverParameters->eimc[eimc_p_Fh] * 1000 : 0); // chwilowy max ED -> do rozdzialu sil - FED -= Min0R(p->MoverParameters->eimv[eimv_Fmax], 0) * + FED -= std::min(p->MoverParameters->eimv[eimv_Fmax], 0.0) * 1000; // chwilowy max ED -> do rozdzialu sil - FfulED = Min0R(p->MoverParameters->eimv[eimv_Fful], 0) * + FfulED = std::min(p->MoverParameters->eimv[eimv_Fful], 0.0) * 1000; // chwilowy max ED -> do rozdzialu sil - FrED -= Min0R(p->MoverParameters->eimv[eimv_Fr], 0) * + FrED -= std::min(p->MoverParameters->eimv[eimv_Fr], 0.0) * 1000; // chwilowo realizowane ED -> do pneumatyki - Frj += Max0R(p->MoverParameters->eimv[eimv_Fr], 0) * + Frj += std::max(p->MoverParameters->eimv[eimv_Fr], 0.0) * 1000;// chwilowo realizowany napęd -> do utrzymującego masa += p->MoverParameters->TotalMass; osie += p->MoverParameters->NAxles; } - amax = FmaxPN / masamax; + + auto const amax = FmaxPN / masamax; if ((MoverParameters->Vel < 0.5) && (MoverParameters->BrakePress > 0.2) || (dDoorMoveL > 0.001) || (dDoorMoveR > 0.001)) { @@ -2808,24 +2807,28 @@ bool TDynamicObject::Update(double dt, double dt1) MoverParameters->ShuntModeAllow = (MoverParameters->BrakePress > 0.2) && (MoverParameters->LocalBrakeRatio() < 0.01); } - Fzad = amax * MoverParameters->LocalBrakeRatio() * masa; + auto Fzad = amax * MoverParameters->LocalBrakeRatio() * masa; if ((MoverParameters->ScndS) && (MoverParameters->Vel > MoverParameters->eimc[eimc_p_Vh1]) && (FmaxED > 0)) { - Fzad = Min0R(MoverParameters->LocalBrakeRatio() * FmaxED, FfulED); + Fzad = std::min(MoverParameters->LocalBrakeRatio() * FmaxED, FfulED); } if (((MoverParameters->ShuntMode) && (Frj < 0.0015 * masa)) || (MoverParameters->V * MoverParameters->DirAbsolute < -0.2)) { - Fzad = Max0R(MoverParameters->StopBrakeDecc * masa, Fzad); + Fzad = std::max(MoverParameters->StopBrakeDecc * masa, Fzad); } - if (MoverParameters->BrakeHandle == MHZ_EN57?MoverParameters->BrakeOpModeFlag & bom_MED:MoverParameters->EpFuse) - FzadED = Min0R(Fzad, FmaxED); - else - FzadED = 0; - FzadPN = Fzad - FrED; + auto FzadED { 0.0 }; + if( ( MoverParameters->EpFuse ) + || ( ( MoverParameters->BrakeHandle == MHZ_EN57 ) + && ( MoverParameters->BrakeOpModeFlag & bom_MED ) ) ) { + FzadED = std::min( Fzad, FmaxED ); + } + auto const FzadPN = Fzad - FrED; //np = 0; + // BUG: likely memory leak, allocation per inner loop, deleted only once outside + // TODO: sort this shit out bool* PrzekrF = new bool[np]; float nPrzekrF = 0; bool test = true; @@ -2849,7 +2852,7 @@ bool TDynamicObject::Update(double dt, double dt1) for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0, 4); p; p = (kier == true ? p->NextC(4) : p->PrevC(4)) ) { - float Nmax = ((p->MoverParameters->P2FTrans * p->MoverParameters->MaxBrakePress[0] - + auto const Nmax = ((p->MoverParameters->P2FTrans * p->MoverParameters->MaxBrakePress[0] - p->MoverParameters->BrakeCylSpring) * p->MoverParameters->BrakeCylMult[0] - p->MoverParameters->BrakeSlckAdj) * @@ -2864,8 +2867,8 @@ bool TDynamicObject::Update(double dt, double dt1) FzED[i] = (FmaxED > 0 ? FzadED / FmaxED : 0); p->MoverParameters->AnPos = (MoverParameters->ScndS ? MoverParameters->LocalBrakeRatio() : FzED[i]); - FzEP[i] = FzadPN * p->MoverParameters->NAxles / osie; - i++; + FzEP[ i ] = static_cast( FzadPN * p->MoverParameters->NAxles ) / static_cast( osie ); + ++i; p->MoverParameters->ShuntMode = MoverParameters->ShuntMode; p->MoverParameters->ShuntModeAllow = MoverParameters->ShuntModeAllow; } @@ -2896,7 +2899,7 @@ bool TDynamicObject::Update(double dt, double dt1) FzEP[i] = 0; przek += przek1; } - i++; + ++i; } i = 0; przek = przek / (np - nPrzekrF); @@ -2907,7 +2910,7 @@ bool TDynamicObject::Update(double dt, double dt1) { FzEP[i] += przek; } - i++; + ++i; } } i = 0; @@ -2938,31 +2941,8 @@ bool TDynamicObject::Update(double dt, double dt1) p->MoverParameters->LocalBrakePosA = p->MoverParameters->LocalBrakePosA; else p->MoverParameters->LocalBrakePosA = 0; - i++; + ++i; } - /* ////ALGORYTM 1 - KAZDEMU PO ROWNO - for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0); p; - (iDirection > 0 ? p = p->NextC(4) : p = p->PrevC(4))) - { - - float Nmax = ((p->MoverParameters->P2FTrans * p->MoverParameters->MaxBrakePress[0] - - p->MoverParameters->BrakeCylSpring) * - p->MoverParameters->BrakeCylMult[0] - - p->MoverParameters->BrakeSlckAdj) * - p->MoverParameters->BrakeCylNo * p->MoverParameters->BrakeRigEff; - float FmaxPoj = Nmax * - p->MoverParameters->Hamulec->GetFC( - Nmax / (p->MoverParameters->NAxles * p->MoverParameters->NBpA), - p->MoverParameters->Vel) * - 1000; // sila hamowania pn - // Fpoj=(FED>0?-FzadED*p->MoverParameters->eimv[eimv_Fmax]*1000/FED:0); - // p->MoverParameters->AnPos=(p->MoverParameters->eimc[eimc_p_Fh]>1?0.001f*Fpoj/(p->MoverParameters->eimc[eimc_p_Fh]):0); - p->MoverParameters->AnPos = (FmaxED > 0 ? FzadED / FmaxED : 0); - // Fpoj = FzadPN * Min0R(p->MoverParameters->TotalMass / masa, 1); - // p->MoverParameters->LocalBrakePosA = - // (p->MoverParameters->SlippingWheels ? 0 : Min0R(Max0R(Fpoj / FmaxPoj, 0), 1)); - p->MoverParameters->LocalBrakePosA = (p->MoverParameters->SlippingWheels ? 0 : FzadPN / FmaxPN); - } */ MED[0][0] = masa*0.001; MED[0][1] = amax; @@ -2979,14 +2959,6 @@ bool TDynamicObject::Update(double dt, double dt1) delete[] FmaxEP; } - // yB: cos (AI) tu jest nie kompatybilne z czyms (hamulce) - // if (Controller!=Humandriver) - // if (Mechanik->LastReactionTime>0.5) - // { - // MoverParameters->BrakeCtrlPos=0; - // Mechanik->LastReactionTime=0; - // } - Mechanik->UpdateSituation(dt1); // przebłyski świadomości AI } diff --git a/Float3d.h b/Float3d.h index f7217a11..6afdaef3 100644 --- a/Float3d.h +++ b/Float3d.h @@ -241,7 +241,7 @@ public: e[i + 2] = f; // zamiana Y i Z } }; - inline float4x4 &Rotation(double angle, float3 axis); + inline float4x4 &Rotation(float const angle, float3 const &axis); inline bool IdentityIs() { // sprawdzenie jednostkowości for (int i = 0; i < 16; ++i) @@ -270,34 +270,30 @@ inline glm::vec3 operator*( const float4x4 &m, const glm::vec3 &v ) { // mnożen v.x * m[ 0 ][ 2 ] + v.y * m[ 1 ][ 2 ] + v.z * m[ 2 ][ 2 ] + m[ 3 ][ 2 ] ); } -inline float4x4 &float4x4::Rotation(double angle, float3 axis) +inline float4x4 &float4x4::Rotation(float const Angle, float3 const &Axis) { - double c = cos(angle); - double s = sin(angle); + auto const c = std::cos(Angle); + auto const s = std::sin(Angle); // One minus c (short name for legibility of formulai) - double omc = (1 - c); - if (axis.Length() != 1.0f) - axis = SafeNormalize(axis); - double x = axis.x; - double y = axis.y; - double z = axis.z; - double xs = x * s; - double ys = y * s; - double zs = z * s; - double xyomc = x * y * omc; - double xzomc = x * z * omc; - double yzomc = y * z * omc; - e[0] = x * x * omc + c; + auto const omc = (1.f - c); + auto const axis = SafeNormalize(Axis); + auto const xs = axis.x * s; + auto const ys = axis.y * s; + auto const zs = axis.z * s; + auto const xyomc = axis.x * axis.y * omc; + auto const xzomc = axis.x * axis.z * omc; + auto const yzomc = axis.y * axis.z * omc; + e[0] = axis.x * axis.x * omc + c; e[1] = xyomc + zs; e[2] = xzomc - ys; e[3] = 0; e[4] = xyomc - zs; - e[5] = y * y * omc + c; + e[5] = axis.y * axis.y * omc + c; e[6] = yzomc + xs; e[7] = 0; e[8] = xzomc + ys; e[9] = yzomc - xs; - e[10] = z * z * omc + c; + e[10] = axis.z * axis.z * omc + c; e[11] = 0; e[12] = 0; e[13] = 0; diff --git a/Ground.cpp b/Ground.cpp index 91f84e87..efe1721f 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -1352,12 +1352,12 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) >> vertex.normal.z >> vertex.texture.s >> vertex.texture.t; - vertex.position = glm::rotateZ( vertex.position, aRotate.z / 180 * M_PI ); - vertex.position = glm::rotateX( vertex.position, aRotate.x / 180 * M_PI ); - vertex.position = glm::rotateY( vertex.position, aRotate.y / 180 * M_PI ); - vertex.normal = glm::rotateZ( vertex.normal, static_cast( aRotate.z / 180 * M_PI ) ); - vertex.normal = glm::rotateX( vertex.normal, static_cast( aRotate.x / 180 * M_PI ) ); - vertex.normal = glm::rotateY( vertex.normal, static_cast( aRotate.y / 180 * M_PI ) ); + vertex.position = glm::rotateZ( vertex.position, glm::radians( aRotate.z ) ); + vertex.position = glm::rotateX( vertex.position, glm::radians( aRotate.x ) ); + vertex.position = glm::rotateY( vertex.position, glm::radians( aRotate.y ) ); + vertex.normal = glm::rotateZ( vertex.normal, glm::radians( aRotate.z ) ); + vertex.normal = glm::rotateX( vertex.normal, glm::radians( aRotate.x ) ); + vertex.normal = glm::rotateY( vertex.normal, glm::radians( aRotate.y ) ); vertex.position += glm::dvec3{ pOrigin }; if( true == clamps ) { vertex.texture.s = clamp( vertex.texture.s, 0.001f, 0.999f ); } if( true == clampt ) { vertex.texture.t = clamp( vertex.texture.t, 0.001f, 0.999f ); } @@ -1486,9 +1486,9 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) *parser >> vertex.position.y >> vertex.position.z; - vertex.position = glm::rotateZ( vertex.position, aRotate.z / 180 * M_PI ); - vertex.position = glm::rotateX( vertex.position, aRotate.x / 180 * M_PI ); - vertex.position = glm::rotateY( vertex.position, aRotate.y / 180 * M_PI ); + vertex.position = glm::rotateZ( vertex.position, glm::radians( aRotate.z ) ); + vertex.position = glm::rotateX( vertex.position, glm::radians( aRotate.x ) ); + vertex.position = glm::rotateY( vertex.position, glm::radians( aRotate.y ) ); vertex.position += glm::dvec3{ pOrigin }; // convert all data to gl_lines to allow data merge for matching nodes diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index a21608ba..8a83e79a 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -624,14 +624,14 @@ public: bool Signalling = false; /*Czy jest zalaczona sygnalizacja hamowania ostatniego wagonu*/ bool DoorSignalling = false; /*Czy jest zalaczona sygnalizacja blokady drzwi*/ bool Radio = true; /*Czy jest zalaczony radiotelefon*/ - double NominalBatteryVoltage = 0.0; /*Winger - baterie w elektrykach*/ + float NominalBatteryVoltage = 0.f; /*Winger - baterie w elektrykach*/ TDimension Dim; /*wymiary*/ double Cx = 0.0; /*wsp. op. aerodyn.*/ - double Floor = 0.96; //poziom podłogi dla ładunków - double WheelDiameter = 1.0; /*srednica kol napednych*/ - double WheelDiameterL = 0.9; //Ra: srednica kol tocznych przednich - double WheelDiameterT = 0.9; //Ra: srednica kol tocznych tylnych - double TrackW = 1.435; /*nominalna szerokosc toru [m]*/ + float Floor = 0.96f; //poziom podłogi dla ładunków + float WheelDiameter = 1.f; /*srednica kol napednych*/ + float WheelDiameterL = 0.9f; //Ra: srednica kol tocznych przednich + float WheelDiameterT = 0.9f; //Ra: srednica kol tocznych tylnych + float TrackW = 1.435f; /*nominalna szerokosc toru [m]*/ double AxleInertialMoment = 0.0; /*moment bezwladnosci zestawu kolowego*/ std::string AxleArangement; /*uklad osi np. Bo'Bo' albo 1'C*/ int NPoweredAxles = 0; /*ilosc osi napednych liczona z powyzszego*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b32c1e07..b24b268e 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -8178,7 +8178,7 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C else if (Command == "PantRear") /*Winger 160204, ABu 310105 i 030305*/ { // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów if ((TrainType == dt_EZT)) - { /*'ezt'*/ + { //'ezt' if ((CValue1 == 1)) { PantRearUp = true; @@ -8191,9 +8191,9 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C } } else - { /*nie 'ezt'*/ + { //nie 'ezt' if ((CValue1 == 1)) - /*if ostatni polaczony sprz. sterowania*/ + //if ostatni polaczony sprz. sterowania if ((TestFlag(Couplers[1].CouplingFlag, ctrain_controll) && (CValue2 == 1)) || (TestFlag(Couplers[0].CouplingFlag, ctrain_controll) && (CValue2 == -1))) { diff --git a/Model3d.cpp b/Model3d.cpp index 36715788..95880a02 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1667,16 +1667,22 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector *t, pName = ""; if (iTexture > 0) { // obsługa stałej tekstury - pTexture = t->at(iTexture); - if (pTexture.find_last_of("/\\") == std::string::npos) - pTexture.insert(0, Global::asCurrentTexturePath); - TextureID = GfxRenderer.Fetch_Texture(pTexture); - if( ( iFlags & 0x30 ) == 0 ) { - // texture-alpha based fallback if for some reason we don't have opacity flag set yet - iFlags |= - ( GfxRenderer.Texture( TextureID ).has_alpha ? - 0x20 : - 0x10 ); // 0x10-nieprzezroczysta, 0x20-przezroczysta + if( iTexture < t->size() ) { + pTexture = t->at( iTexture ); + if( pTexture.find_last_of( "/\\" ) == std::string::npos ) + pTexture.insert( 0, Global::asCurrentTexturePath ); + TextureID = GfxRenderer.Fetch_Texture( pTexture ); + if( ( iFlags & 0x30 ) == 0 ) { + // texture-alpha based fallback if for some reason we don't have opacity flag set yet + iFlags |= + ( GfxRenderer.Texture( TextureID ).has_alpha ? + 0x20 : + 0x10 ); // 0x10-nieprzezroczysta, 0x20-przezroczysta + } + } + else { + ErrorLog( "Bad model: reference to non-existent texture index in sub-model" + ( pName.empty() ? "" : " \"" + pName + "\"" ) ); + TextureID = NULL; } } else diff --git a/Train.cpp b/Train.cpp index 066e98e1..3db1a780 100644 --- a/Train.cpp +++ b/Train.cpp @@ -1361,14 +1361,6 @@ void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const &Command ) { - return ( - Train->mvControlled->ActiveCab < 0 ? - pantographtogglerear( Train, Command ) : - pantographtogglefront( Train, Command ) ); -} - -void TTrain::pantographtogglefront( TTrain *Train, command_data const &Command ) { - if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->mvControlled->PantFrontUp ) { @@ -1377,59 +1369,31 @@ void TTrain::pantographtogglefront( TTrain *Train, command_data const &Command ) if( Train->mvControlled->PantFront( true ) ) { if( Train->mvControlled->PantFrontStart != 1 ) { // visual feedback - if( Train->mvControlled->ActiveCab < 0 ) { - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - } - else { - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + if( Train->ggPantFrontButton.SubModel ) + Train->ggPantFrontButton.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantFrontButtonOff.SubModel ) + Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); } } } else { // ...or turn off if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( Train->mvControlled->ActiveCab < 0 ) { - if( ( Train->ggPantRearButtonOff.SubModel == nullptr ) - && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { - // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it - // then another control has to be used (like pantographlowerall) - // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels - return; - } - } - else { - if( ( Train->ggPantFrontButtonOff.SubModel == nullptr ) - && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { - // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it - // then another control has to be used (like pantographlowerall) - // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels - return; - } + if( ( Train->ggPantFrontButtonOff.SubModel == nullptr ) + && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { + // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it + // then another control has to be used (like pantographlowerall) + // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels + return; } } @@ -1437,38 +1401,19 @@ void TTrain::pantographtogglefront( TTrain *Train, command_data const &Command ) if( false == Train->mvControlled->PantFront( false ) ) { if( Train->mvControlled->PantFrontStart != 0 ) { // visual feedback - if( Train->mvControlled->ActiveCab < 0 ) { - // in rear cab switch functions are swapped - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); - } - else { - // in front cab switches map normally - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); - } + if( Train->ggPantFrontButton.SubModel ) + Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantFrontButtonOff.SubModel ) + Train->ggPantFrontButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); } } } @@ -1476,37 +1421,19 @@ void TTrain::pantographtogglefront( TTrain *Train, command_data const &Command ) else if( Command.action == GLFW_RELEASE ) { // impulse switches return automatically to neutral position if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( Train->mvControlled->ActiveCab < 0 ) { - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantFrontButton.SubModel ) + Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // also the switch off button, in cabs which have it + if( Train->ggPantFrontButtonOff.SubModel ) + Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantSelectedDownButton.SubModel ) { // NOTE: currently we animate the selectable pantograph control based on standard key presses // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // also the switch off button, in cabs which have it - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - if( Train->ggPantSelectedDownButton.SubModel ) { - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - } - } - else { - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // also the switch off button, in cabs which have it - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - if( Train->ggPantSelectedDownButton.SubModel ) { - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); } } } @@ -1514,14 +1441,6 @@ void TTrain::pantographtogglefront( TTrain *Train, command_data const &Command ) void TTrain::OnCommand_pantographtogglerear( TTrain *Train, command_data const &Command ) { - return ( - Train->mvControlled->ActiveCab < 0 ? - pantographtogglefront( Train, Command ) : - pantographtogglerear( Train, Command ) ); -} - -void TTrain::pantographtogglerear( TTrain *Train, command_data const &Command ) { - if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->mvControlled->PantRearUp ) { @@ -1530,61 +1449,31 @@ void TTrain::pantographtogglerear( TTrain *Train, command_data const &Command ) if( Train->mvControlled->PantRear( true ) ) { if( Train->mvControlled->PantRearStart != 1 ) { // visual feedback - if( Train->mvControlled->ActiveCab < 0 ) { - // in rear cab switch functions are swapped - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - } - else { - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + if( Train->ggPantRearButton.SubModel ) + Train->ggPantRearButton.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantRearButtonOff.SubModel ) + Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); } } } else { // ...or turn off if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( Train->mvControlled->ActiveCab < 0 ) { - // in rear cab switch functions are swapped - if( ( Train->ggPantFrontButtonOff.SubModel == nullptr ) - && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { - // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it - // then another control has to be used (like pantographlowerall) - // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels - return; - } - } - else { - if( ( Train->ggPantRearButtonOff.SubModel == nullptr ) - && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { - // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it - // then another control has to be used (like pantographlowerall) - // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels - return; - } + if( ( Train->ggPantRearButtonOff.SubModel == nullptr ) + && ( Train->ggPantSelectedDownButton.SubModel == nullptr ) ) { + // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it + // then another control has to be used (like pantographlowerall) + // TODO: we should have a way to define presense of cab controls without having to bind these to 3d submodels + return; } } @@ -1592,37 +1481,19 @@ void TTrain::pantographtogglerear( TTrain *Train, command_data const &Command ) if( false == Train->mvControlled->PantRear( false ) ) { if( Train->mvControlled->PantRearStart != 0 ) { // visual feedback - if( Train->mvControlled->ActiveCab < 0 ) { - // in rear cab switch functions are swapped - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); - } - else { - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedDownButton.SubModel ) - Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); - } + if( Train->ggPantRearButton.SubModel ) + Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // pantograph control can have two-button setup + if( Train->ggPantRearButtonOff.SubModel ) + Train->ggPantRearButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedDownButton.SubModel ) + Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); } } } @@ -1630,38 +1501,19 @@ void TTrain::pantographtogglerear( TTrain *Train, command_data const &Command ) else if( Command.action == GLFW_RELEASE ) { // impulse switches return automatically to neutral position if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( Train->mvControlled->ActiveCab < 0 ) { - // in rear cab switch functions are swapped - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantRearButton.SubModel ) + Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); + // NOTE: currently we animate the selectable pantograph control based on standard key presses + // TODO: implement actual selection control, and refactor handling this control setup in a separate method + if( Train->ggPantSelectedButton.SubModel ) + Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); + // also the switch off button, in cabs which have it + if( Train->ggPantRearButtonOff.SubModel ) + Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + if( Train->ggPantSelectedDownButton.SubModel ) { // NOTE: currently we animate the selectable pantograph control based on standard key presses // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // also the switch off button, in cabs which have it - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - if( Train->ggPantSelectedDownButton.SubModel ) { - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - } - } - else { - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - if( Train->ggPantSelectedButton.SubModel ) - Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); - // also the switch off button, in cabs which have it - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - if( Train->ggPantSelectedDownButton.SubModel ) { - // NOTE: currently we animate the selectable pantograph control based on standard key presses - // TODO: implement actual selection control, and refactor handling this control setup in a separate method - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); } } } @@ -6441,21 +6293,12 @@ bool TTrain::Update( double const Deltatime ) || ( mvControlled->ConverterFlag ) ) { if( ggPantAllDownButton.GetValue() == 0.0 ) { // the 'lower all' button overrides state of switches, while active itself - // when in rear cab we treat switches as reversed, i.e. front controls physical rear - auto const frontpantographswitch = ( - mvControlled->ActiveCab < 0 ? - ggPantRearButton.GetValue() >= 1.0 : - ggPantFrontButton.GetValue() >= 1.0 ); - auto const rearpantographswitch = ( - mvControlled->ActiveCab < 0 ? - ggPantFrontButton.GetValue() >= 1.0 : - ggPantRearButton.GetValue() >= 1.0 ); if( ( false == mvControlled->PantFrontUp ) - && ( frontpantographswitch ) ) { + && ( ggPantFrontButton.GetValue() >= 1.0 ) ) { mvControlled->PantFront( true ); } if( ( false == mvControlled->PantRearUp ) - && ( rearpantographswitch ) ) { + && ( ggPantRearButton.GetValue() >= 1.0 ) ) { mvControlled->PantRear( true ); } } @@ -7326,50 +7169,42 @@ void TTrain::set_cab_controls() { } // pantographs if( mvOccupied->PantSwitchType != "impulse" ) { - auto const pantograph = ( - mvControlled->ActiveCab < 0 ? - mvControlled->PantRearUp : - mvControlled->PantFrontUp ); ggPantFrontButton.PutValue( - ( pantograph ? + ( mvControlled->PantFrontUp ? 1.0 : 0.0 ) ); ggPantFrontButtonOff.PutValue( - ( pantograph ? + ( mvControlled->PantFrontUp ? 0.0 : 1.0 ) ); // NOTE: currently we animate the selectable pantograph control for both pantographs // TODO: implement actual selection control, and refactor handling this control setup in a separate method ggPantSelectedButton.PutValue( - ( pantograph ? + ( mvControlled->PantFrontUp ? 1.0 : 0.0 ) ); ggPantSelectedDownButton.PutValue( - ( pantograph ? + ( mvControlled->PantFrontUp ? 0.0 : 1.0 ) ); } if( mvOccupied->PantSwitchType != "impulse" ) { - auto const pantograph = ( - mvControlled->ActiveCab < 0 ? - mvControlled->PantFrontUp : - mvControlled->PantRearUp ); ggPantRearButton.PutValue( - ( pantograph ? + ( mvControlled->PantRearUp ? 1.0 : 0.0 ) ); ggPantRearButtonOff.PutValue( - ( pantograph ? + ( mvControlled->PantRearUp ? 0.0 : 1.0 ) ); // NOTE: currently we animate the selectable pantograph control for both pantographs // TODO: implement actual selection control, and refactor handling this control setup in a separate method ggPantSelectedButton.PutValue( - ( pantograph ? + ( mvControlled->PantRearUp ? 1.0 : 0.0 ) ); ggPantSelectedDownButton.PutValue( - ( pantograph ? + ( mvControlled->PantRearUp ? 0.0 : 1.0 ) ); } diff --git a/Train.h b/Train.h index 5331386f..c9c9e6d2 100644 --- a/Train.h +++ b/Train.h @@ -155,9 +155,7 @@ class TTrain static void OnCommand_pantographcompressorvalvetoggle( TTrain *Train, command_data const &Command ); static void OnCommand_pantographcompressoractivate( TTrain *Train, command_data const &Command ); static void OnCommand_pantographtogglefront( TTrain *Train, command_data const &Command ); - static void pantographtogglefront( TTrain *Train, command_data const &Command ); static void OnCommand_pantographtogglerear( TTrain *Train, command_data const &Command ); - static void pantographtogglerear( TTrain *Train, command_data const &Command ); static void OnCommand_pantographlowerall( TTrain *Train, command_data const &Command ); static void OnCommand_linebreakertoggle( TTrain *Train, command_data const &Command ); static void OnCommand_convertertoggle( TTrain *Train, command_data const &Command ); diff --git a/moon.cpp b/moon.cpp index a6566d5e..feb72ba3 100644 --- a/moon.cpp +++ b/moon.cpp @@ -36,9 +36,9 @@ void cMoon::update() { move(); - glm::vec3 position( 0.0f, 0.0f, -2000.0f * Global::fDistanceFactor ); - position = glm::rotateX( position, (float)( m_body.elevref * ( M_PI / 180.0 ) ) ); - position = glm::rotateY( position, (float)( -m_body.hrang * ( M_PI / 180.0 ) ) ); + glm::vec3 position( 0.f, 0.f, -2000.f * Global::fDistanceFactor ); + position = glm::rotateX( position, glm::radians( m_body.elevref ) ); + position = glm::rotateY( position, glm::radians( -m_body.hrang ) ); m_position = position; } diff --git a/sun.cpp b/sun.cpp index 55db2f00..dc65d471 100644 --- a/sun.cpp +++ b/sun.cpp @@ -33,9 +33,9 @@ void cSun::update() { move(); - glm::vec3 position( 0.0f, 0.0f, -2000.0f * Global::fDistanceFactor ); - position = glm::rotateX( position, (float)( m_body.elevref * ( M_PI / 180.0 ) ) ); - position = glm::rotateY( position, (float)( -m_body.hrang * ( M_PI / 180.0 ) ) ); + glm::vec3 position( 0.f, 0.f, -2000.f * Global::fDistanceFactor ); + position = glm::rotateX( position, glm::radians( m_body.elevref ) ); + position = glm::rotateY( position, glm::radians( -m_body.hrang ) ); m_position = position; } diff --git a/translation.h b/translation.h index 8ce582e0..c18a865b 100644 --- a/translation.h +++ b/translation.h @@ -62,10 +62,10 @@ static std::unordered_map m_cabcontrols = { { "converteroff_sw:", "converter" }, { "main_sw:", "line breaker" }, { "radio_sw:", "radio" }, - { "pantfront_sw:", "front pantograph" }, - { "pantrear_sw:", "rear pantograph" }, - { "pantfrontoff_sw:", "front pantograph" }, - { "pantrearoff_sw:", "rear pantograph" }, + { "pantfront_sw:", "pantograph A" }, + { "pantrear_sw:", "pantograph B" }, + { "pantfrontoff_sw:", "pantograph A" }, + { "pantrearoff_sw:", "pantograph B" }, { "pantalloff_sw:", "all pantographs" }, { "pantselected_sw:", "selected pantograph" }, { "pantselectedoff_sw:", "selected pantograph" }, diff --git a/uilayer.h b/uilayer.h index 8b3f31bb..79863a38 100644 --- a/uilayer.h +++ b/uilayer.h @@ -24,8 +24,8 @@ struct ui_panel { {} std::vector text_lines; - float origin_x; - float origin_y; + int origin_x; + int origin_y; }; class ui_layer { From 4357919272d58c78d4c15d638e2a639aaa66255f Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 29 Jul 2017 00:28:37 +0200 Subject: [PATCH 4/9] mvp matrices cached separately for each render pass, eliminated duplicate screen width/height variables --- EU07.cpp | 14 +- Globals.cpp | 6 +- Globals.h | 4 +- World.cpp | 34 +++-- frustum.cpp | 25 ++++ frustum.h | 18 ++- maszyna.vcxproj.filters | 6 - moon.cpp | 2 +- mouseinput.cpp | 2 +- openglmatrixstack.h | 21 ++- renderer.cpp | 284 +++++++++++++++++++++++----------------- renderer.h | 107 +++++++++------ uilayer.cpp | 15 +-- 13 files changed, 322 insertions(+), 216 deletions(-) diff --git a/EU07.cpp b/EU07.cpp index c226a225..00852213 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -78,8 +78,8 @@ void screenshot_save_thread( char *img ) png_image png; memset(&png, 0, sizeof(png_image)); png.version = PNG_IMAGE_VERSION; - png.width = Global::ScreenWidth; - png.height = Global::ScreenHeight; + png.width = Global::iWindowWidth; + png.height = Global::iWindowHeight; png.format = PNG_FORMAT_RGB; char datetime[64]; @@ -95,7 +95,7 @@ void screenshot_save_thread( char *img ) std::string filename = "screenshots/" + std::string(datetime) + "_" + std::to_string(perf) + ".png"; - if (png_image_write_to_file(&png, filename.c_str(), 0, img, -Global::ScreenWidth * 3, nullptr) == 1) + if (png_image_write_to_file(&png, filename.c_str(), 0, img, -Global::iWindowWidth * 3, nullptr) == 1) WriteLog("saved " + filename + "."); else WriteLog("failed to save screenshot."); @@ -105,8 +105,8 @@ void screenshot_save_thread( char *img ) void make_screenshot() { - char *img = new char[Global::ScreenWidth * Global::ScreenHeight * 3]; - glReadPixels(0, 0, Global::ScreenWidth, Global::ScreenHeight, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)img); + char *img = new char[Global::iWindowWidth * Global::iWindowHeight * 3]; + glReadPixels(0, 0, Global::iWindowWidth, Global::iWindowHeight, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)img); std::thread t(screenshot_save_thread, img); t.detach(); @@ -117,8 +117,8 @@ void window_resize_callback(GLFWwindow *window, int w, int h) { // NOTE: we have two variables which basically do the same thing as we don't have dynamic fullscreen toggle // TBD, TODO: merge them? - Global::ScreenWidth = Global::iWindowWidth = w; - Global::ScreenHeight = Global::iWindowHeight = h; + Global::iWindowWidth = w; + Global::iWindowHeight = h; Global::fDistanceFactor = std::max( 0.5f, h / 768.0f ); // not sure if this is really something we want to use glViewport(0, 0, w, h); } diff --git a/Globals.cpp b/Globals.cpp index 4310ec3a..7aa9094f 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -66,9 +66,6 @@ cParser *Global::pParser = NULL; int Global::iSegmentsRendered = 90; // ilość segmentów do regulacji wydajności TCamera *Global::pCamera = NULL; // parametry kamery TDynamicObject *Global::pUserDynamic = NULL; // pojazd użytkownika, renderowany bez trzęsienia -/* -std::string Global::asTranscript[5]; // napisy na ekranie (widoczne) -*/ TTranscripts Global::tranTexts; // obiekt obsługujący stenogramy dźwięków na ekranie float4 Global::UITextColor = float4( 225.0 / 255.0f, 225.0f / 255.0f, 225.0f / 255.0f, 1.0f ); @@ -84,6 +81,7 @@ GLfloat Global::FogColor[] = {0.6f, 0.7f, 0.8f}; double Global::fFogStart = 1700; double Global::fFogEnd = 2000; float Global::Overcast { 0.1f }; // NOTE: all this weather stuff should be moved elsewhere +float Global::BaseDrawRange { 2500.f }; opengl_light Global::DayLight; int Global::DynamicLightCount { 3 }; bool Global::ScaleSpecularValues { true }; @@ -98,7 +96,7 @@ int Global::Keys[MaxKeys]; bool Global::RealisticControlMode{ false }; int Global::iWindowWidth = 800; int Global::iWindowHeight = 600; -float Global::fDistanceFactor = Global::ScreenHeight / 768.0; // baza do przeliczania odległości dla LoD +float Global::fDistanceFactor = Global::iWindowHeight / 768.0; // baza do przeliczania odległości dla LoD int Global::iFeedbackMode = 1; // tryb pracy informacji zwrotnej int Global::iFeedbackPort = 0; // dodatkowy adres dla informacji zwrotnych bool Global::InputGamepad{ true }; diff --git a/Globals.h b/Globals.h index 752aca31..485a8a2f 100644 --- a/Globals.h +++ b/Globals.h @@ -223,6 +223,7 @@ class Global // static bool bTimeChange; // TODO: put these things in the renderer + static float BaseDrawRange; static opengl_light DayLight; static int DynamicLightCount; static bool ScaleSpecularValues; @@ -315,9 +316,6 @@ class Global // informacje podczas kalibracji static double fBrakeStep; // krok zmiany hamulca dla klawiszy [Num3] i [Num9] static bool bJoinEvents; // czy grupować eventy o tych samych nazwach -/* - static std::string asTranscript[5]; // napisy na ekranie (widoczne) -*/ static TTranscripts tranTexts; // obiekt obsługujący stenogramy dźwięków na ekranie static float4 UITextColor; // base color of UI text static std::string asLang; // domyślny język - http://tools.ietf.org/html/bcp47 diff --git a/World.cpp b/World.cpp index 4ed9ac6e..c4c1d5fa 100644 --- a/World.cpp +++ b/World.cpp @@ -489,9 +489,8 @@ void TWorld::OnKeyDown(int cKey) } else // również przeskakiwanie { // Ra: to z tą kamerą (Camera.Pos i Global::pCameraPosition) jest trochę bez sensu - Global::SetCameraPosition( - Global::FreeCameraInit[i]); // nowa pozycja dla generowania obiektów - Ground.Silence(Camera.Pos); // wyciszenie wszystkiego z poprzedniej pozycji + Ground.Silence( Global::pCameraPosition ); // wyciszenie wszystkiego z poprzedniej pozycji + Global::SetCameraPosition( Global::FreeCameraInit[i] ); // nowa pozycja dla generowania obiektów Camera.Init(Global::FreeCameraInit[i], Global::FreeCameraInitAngle[i]); // przestawienie } @@ -1136,25 +1135,25 @@ bool TWorld::Update() void TWorld::Update_Camera( double const Deltatime ) { - // Console::Update(); //tu jest zależne od FPS, co nie jest korzystne if( false == Global::ControlPicking ) { if( glfwGetMouseButton( window, GLFW_MOUSE_BUTTON_LEFT ) == GLFW_PRESS ) { Camera.Reset(); // likwidacja obrotów - patrzy horyzontalnie na południe - // if (!FreeFlyModeFlag) //jeśli wewnątrz - patrzymy do tyłu - // Camera.LookAt=Train->pMechPosition-Normalize(Train->GetDirection())*10; - if( Controlled && LengthSquared3( Controlled->GetPosition() - Camera.Pos ) < ( 1500 * 1500 ) ) // gdy bliżej niż 1.5km + if( Controlled && LengthSquared3( Controlled->GetPosition() - Camera.Pos ) < ( 1500 * 1500 ) ) { + // gdy bliżej niż 1.5km Camera.LookAt = Controlled->GetPosition(); + } else { TDynamicObject *d = Ground.DynamicNearest( Camera.Pos, 300 ); // szukaj w promieniu 300m if( !d ) - d = Ground.DynamicNearest( Camera.Pos, - 1000 ); // dalej szukanie, jesli bliżej nie ma - if( d && pDynamicNearest ) // jeśli jakiś jest znaleziony wcześniej - if( 100.0 * LengthSquared3( d->GetPosition() - Camera.Pos ) > - LengthSquared3( pDynamicNearest->GetPosition() - Camera.Pos ) ) + d = Ground.DynamicNearest( Camera.Pos, 1000 ); // dalej szukanie, jesli bliżej nie ma + if( d && pDynamicNearest ) { + // jeśli jakiś jest znaleziony wcześniej + if( 100.0 * LengthSquared3( d->GetPosition() - Camera.Pos ) > LengthSquared3( pDynamicNearest->GetPosition() - Camera.Pos ) ) { d = pDynamicNearest; // jeśli najbliższy nie jest 10 razy bliżej niż + } + } // poprzedni najbliższy, zostaje poprzedni if( d ) pDynamicNearest = d; // zmiana na nowy, jeśli coś znaleziony niepusty @@ -1164,7 +1163,7 @@ TWorld::Update_Camera( double const Deltatime ) { if( FreeFlyModeFlag ) Camera.RaLook(); // jednorazowe przestawienie kamery } - else if( glfwGetMouseButton( window, GLFW_MOUSE_BUTTON_RIGHT ) == GLFW_PRESS ) { //||Console::Pressed(VK_F4)) + else if( glfwGetMouseButton( window, GLFW_MOUSE_BUTTON_RIGHT ) == GLFW_PRESS ) { FollowView( false ); // bez wyciszania dźwięków } else if( glfwGetMouseButton( window, GLFW_MOUSE_BUTTON_MIDDLE ) == GLFW_PRESS ) { @@ -1179,12 +1178,7 @@ TWorld::Update_Camera( double const Deltatime ) { } Camera.Update(); // uwzględnienie ruchu wywołanego klawiszami -/* - if( Camera.Type == tp_Follow ) { - if( Train ) { // jeśli jazda w kabinie, przeliczyć trzeba parametry kamery - Train->UpdateMechPosition( Deltatime / - Global::fTimeSpeed ); // ograniczyć telepanie po przyspieszeniu -*/ + if( (Train != nullptr) && (Camera.Type == tp_Follow )) { // jeśli jazda w kabinie, przeliczyć trzeba parametry kamery @@ -1248,6 +1242,8 @@ TWorld::Update_Camera( double const Deltatime ) { else { // kamera nieruchoma Global::SetCameraRotation( Camera.Yaw - M_PI ); } + // all done, update camera position to the new value + Global::SetCameraPosition( Camera.Pos ); } void TWorld::Update_Environment() { diff --git a/frustum.cpp b/frustum.cpp index 0f4624b7..c2420c89 100644 --- a/frustum.cpp +++ b/frustum.cpp @@ -10,6 +10,10 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "frustum.h" +std::vector 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 + void cFrustum::calculate() { @@ -83,6 +87,14 @@ 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 * Modelview ); + + // calculate frustum corners + m_frustumpoints = ndcfrustumshapepoints; + transform_to_world( + std::begin( m_frustumpoints ), + std::end( m_frustumpoints ) ); } bool @@ -169,6 +181,19 @@ 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 360f8a5a..4333e25a 100644 --- a/frustum.h +++ b/frustum.h @@ -61,6 +61,20 @@ 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 ) { + 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; protected: // types: @@ -74,5 +88,7 @@ protected: normalize_plane( cFrustum::side const Side ); // normalizes a plane (A side) from the frustum // members: - float m_frustum[6][4]; // holds the A B C and D values (normal & distance) for each side of the frustum. + 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 + std::vector m_frustumpoints; // coordinates of corners for defined frustum, in world space }; diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index 67cb75d1..b375a4c0 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -120,9 +120,6 @@ Source Files - - Source Files - Source Files @@ -374,9 +371,6 @@ Header Files - - Header Files - Header Files diff --git a/moon.cpp b/moon.cpp index feb72ba3..02db79f1 100644 --- a/moon.cpp +++ b/moon.cpp @@ -55,7 +55,7 @@ cMoon::render() { glEnd(); glPushMatrix(); glTranslatef( position.x, position.y, position.z ); - gluSphere( moonsphere, /* (float)( Global::ScreenHeight / Global::FieldOfView ) * 0.5 * */ ( m_body.distance / 60.2666 ) * 9.037461, 12, 12 ); + gluSphere( moonsphere, /* (float)( Global::iWindowHeight / Global::FieldOfView ) * 0.5 * */ ( m_body.distance / 60.2666 ) * 9.037461, 12, 12 ); glPopMatrix(); } diff --git a/mouseinput.cpp b/mouseinput.cpp index 97e1f6dc..e5b8c1de 100644 --- a/mouseinput.cpp +++ b/mouseinput.cpp @@ -168,7 +168,7 @@ mouse_input::poll() { auto updaterate { m_updaterate }; if( m_varyingpollrate ) { - updaterate /= std::max( 0.15, 2.0 * glm::length( m_cursorposition - m_commandstartcursor ) / std::max( 1, Global::ScreenHeight ) ); + updaterate /= std::max( 0.15, 2.0 * glm::length( m_cursorposition - m_commandstartcursor ) / std::max( 1, Global::iWindowHeight ) ); } if( m_updateaccumulator < updaterate ) { diff --git a/openglmatrixstack.h b/openglmatrixstack.h index b75e37c7..27a875ab 100644 --- a/openglmatrixstack.h +++ b/openglmatrixstack.h @@ -31,17 +31,23 @@ public: // methods: glm::mat4 const & - data() const { return m_stack.top(); } + data() const { + return m_stack.top(); } void - push_matrix() { m_stack.emplace(m_stack.top()); } + push_matrix() { + m_stack.emplace( m_stack.top() ); } void pop_matrix() { m_stack.pop(); - if( m_stack.empty() ) { m_stack.emplace(1.0f); } + if( m_stack.empty() ) { m_stack.emplace( 1.f ); } upload(); } void load_identity() { - m_stack.top() = glm::mat4( 1.0f ); + m_stack.top() = glm::mat4( 1.f ); + upload(); } + void + load_matrix( glm::mat4 const &Matrix ) { + m_stack.top() = Matrix; upload(); } void rotate( float const Angle, glm::vec3 const &Axis ) { @@ -113,6 +119,11 @@ public: pop_matrix() { m_stacks[ m_mode ].pop_matrix(); } void load_identity() { m_stacks[ m_mode ].load_identity(); } + void + load_matrix( glm::mat4 const &Matrix ) { m_stacks[ m_mode ].load_matrix( Matrix ); } + template + void + load_matrix( Type_ const *Matrix ) { load_matrix( glm::make_mat4( Matrix ) ); } template void rotate( Type_ const Angle, Type_ const X, Type_ const Y, Type_ const Z ) { @@ -184,6 +195,8 @@ extern opengl_matrices OpenGLMatrices; #define glPushMatrix OpenGLMatrices.push_matrix #define glPopMatrix OpenGLMatrices.pop_matrix #define glLoadIdentity OpenGLMatrices.load_identity +#define glLoadMatrixf OpenGLMatrices.load_matrix +#define glLoadMatrixd OpenGLMatrices.load_matrix #define glRotated OpenGLMatrices.rotate #define glRotatef OpenGLMatrices.rotate #define glTranslated OpenGLMatrices.translate diff --git a/renderer.cpp b/renderer.cpp index 9fbe6f33..ee935681 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -27,6 +27,8 @@ extern TWorld World; //#define EU07_USE_ORTHO_SHADOWS +int const EU07_PICKBUFFERSIZE { 1024 }; // size of (square) textures bound with the pick framebuffer + namespace colors { glm::vec4 const none { 0.f, 0.f, 0.f, 1.f }; @@ -77,6 +79,7 @@ opengl_renderer::Init( GLFWwindow *Window ) { m_geometry.units().texture = m_diffusetextureunit; UILayer.set_unit( m_diffusetextureunit ); + ::glDepthFunc( GL_LEQUAL ); glEnable( GL_DEPTH_TEST ); glAlphaFunc( GL_GREATER, 0.04f ); glEnable( GL_ALPHA_TEST ); @@ -150,11 +153,11 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::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 ); - ::glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_pickbuffersize, m_pickbuffersize, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); + ::glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); // depth buffer ::glGenRenderbuffersEXT( 1, &m_pickdepthbuffer ); ::glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_pickdepthbuffer ); - ::glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, m_pickbuffersize, m_pickbuffersize ); + ::glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE ); // create and assemble the framebuffer ::glGenFramebuffersEXT( 1, &m_pickframebuffer ); ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_pickframebuffer ); @@ -229,10 +232,10 @@ opengl_renderer::Init( GLFWwindow *Window ) { float const size = 2.5f; m_billboardgeometry = m_geometry.create_chunk( vertex_array{ - { { -size, size, 0.0f }, glm::vec3(), { 1.0f, 1.0f } }, - { { size, size, 0.0f }, glm::vec3(), { 0.0f, 1.0f } }, - { { -size, -size, 0.0f }, glm::vec3(), { 1.0f, 0.0f } }, - { { size, -size, 0.0f }, glm::vec3(), { 0.0f, 0.0f } } }, + { { -size, size, 0.f }, glm::vec3(), { 1.f, 1.f } }, + { { size, size, 0.f }, glm::vec3(), { 0.f, 1.f } }, + { { -size, -size, 0.f }, glm::vec3(), { 1.f, 0.f } }, + { { size, -size, 0.f }, glm::vec3(), { 0.f, 0.f } } }, geometrybank, GL_TRIANGLE_STRIP ); // prepare debug mode objects @@ -251,9 +254,9 @@ opengl_renderer::Render() { Render_pass( rendermode::color ); glfwSwapBuffers( m_window ); - m_drawcount = m_renderpass.draw_queue.size(); + m_drawcount = m_drawqueue.size(); // accumulate last 20 frames worth of render time (cap at 1000 fps to prevent calculations going awry) - m_drawtime = std::max( 20.0f, 0.95f * m_drawtime + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - drawstart ) ).count() ); + m_drawtime = std::max( 20.f, 0.95f * m_drawtime + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - drawstart ) ).count() ); return true; // for now always succeed } @@ -262,37 +265,46 @@ opengl_renderer::Render() { void opengl_renderer::Render_pass( rendermode const Mode ) { - m_renderpass.draw_mode = Mode; + m_renderpass.setup( Mode ); switch( m_renderpass.draw_mode ) { case rendermode::color: { + opengl_camera shadowcamera; if( Global::RenderShadows && World.InitPerformed() ) { // run shadowmap pass before color Render_pass( rendermode::shadows ); #ifdef EU07_USE_DEBUG_SHADOWMAP UILayer.set_texture( m_shadowdebugtexture ); #endif - m_renderpass.draw_mode = rendermode::color; // restore draw mode. TODO: render mode stack + shadowcamera = m_renderpass.camera; // cache shadow camera placement for visualization + + m_renderpass.setup( rendermode::color ); // restore draw mode. TBD, TODO: render mode stack + // setup shadowmap matrix + m_shadowtexturematrix = + //bias from [-1, 1] to [0, 1] }; + 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 } + * shadowcamera.projection() + // during colour pass coordinates are moved from camera-centric to light-centric, essentially the difference between these two origins + * glm::translate( + glm::mat4{ glm::mat3{ shadowcamera.modelview() } }, + glm::vec3{ m_renderpass.camera.position() - shadowcamera.position() } ); } - ::glViewport( 0, 0, Global::ScreenWidth, Global::ScreenHeight ); - m_renderpass.draw_range = 2500.0f; // arbitrary base draw range + ::glViewport( 0, 0, Global::iWindowWidth, Global::iWindowHeight ); if( World.InitPerformed() ) { auto const skydomecolour = World.Environment.m_skydome.GetAverageColor(); - ::glClearColor( skydomecolour.x, skydomecolour.y, skydomecolour.z, 0.0f ); // kolor nieba + ::glClearColor( skydomecolour.x, skydomecolour.y, skydomecolour.z, 0.f ); // kolor nieba } else { - ::glClearColor( 51.0f / 255.0f, 102.0f / 255.0f, 85.0f / 255.0f, 1.0f ); // initial background Color + ::glClearColor( 51.0f / 255.f, 102.0f / 255.f, 85.0f / 255.f, 1.f ); // initial background Color } ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); if( World.InitPerformed() ) { // setup - setup_projection(); - setup_camera(); - ::glDepthFunc( GL_LEQUAL ); + setup_matrices(); // render setup_drawing( true ); setup_units( true, false, false ); @@ -300,6 +312,19 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // opaque parts... setup_drawing( false ); setup_units( true, true, true ); + + if( DebugModeFlag ) { + // draw light frustum + ::glLineWidth( 2.f ); + ::glColor4f( 1.f, 0.9f, 0.8f, 1.f ); + ::glDisable( GL_LIGHTING ); + ::glDisable( GL_TEXTURE_2D ); + shadowcamera.frustum().draw( m_renderpass.camera.position() ); + ::glLineWidth( 1.f ); + ::glEnable( GL_LIGHTING ); + ::glEnable( GL_TEXTURE_2D ); + } + Render( &World.Ground ); // ...translucent parts setup_drawing( true ); @@ -322,31 +347,34 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); #ifdef EU07_USE_DEBUG_SHADOWMAP - ::glClearColor( 0.f / 255.0f, 0.f / 255.0f, 0.f / 255.f, 1.f ); // initial background Color + ::glClearColor( 0.f / 255.f, 0.f / 255.f, 0.f / 255.f, 1.f ); // initial background Color ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + opengl_camera worldcamera{ m_renderpass.camera }; // cache shadow camera placement for visualization #else ::glClear( GL_DEPTH_BUFFER_BIT ); #endif ::glScissor( 1, 1, m_shadowbuffersize - 2, m_shadowbuffersize - 2 ); ::glEnable( GL_SCISSOR_TEST ); - - m_renderpass.draw_range = Global::shadowtune.depth; // 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 ); + setup_matrices(); ::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting - ::glPolygonOffset( 2.f, 4.f ); + ::glPolygonOffset( 4.f, 8.f ); // render // opaque parts... setup_drawing( false ); #ifdef EU07_USE_DEBUG_SHADOWMAP setup_units( true, false, false ); + + if( DebugModeFlag ) { + // draw camera frustum + ::glLineWidth( 2.f ); + ::glColor4f( 1.f, 0.9f, 0.8f, 1.f ); + ::glDisable( GL_LIGHTING ); + ::glDisable( GL_TEXTURE_2D ); + worldcamera.frustum().draw( m_renderpass.camera.position() ); + ::glLineWidth( 1.f ); + ::glEnable( GL_LIGHTING ); + ::glEnable( GL_TEXTURE_2D ); + } #else setup_units( false, false, false ); #endif @@ -364,16 +392,13 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( World.InitPerformed() ) { // setup #ifdef EU07_USE_PICKING_FRAMEBUFFER - ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); + ::glViewport( 0, 0, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE ); #endif - ::glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + ::glClearColor( 0.f, 0.f, 0.f, 1.f ); ::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 - setup_projection(); - setup_camera(); - ::glDepthFunc( GL_LEQUAL ); + setup_matrices(); // render // opaque parts... setup_drawing( false ); @@ -388,17 +413,14 @@ opengl_renderer::Render_pass( rendermode const Mode ) { case rendermode::pickscenery: { if( World.InitPerformed() ) { // setup + m_picksceneryitems.clear(); #ifdef EU07_USE_PICKING_FRAMEBUFFER - ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); + ::glViewport( 0, 0, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE ); #endif - ::glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + ::glClearColor( 0.f, 0.f, 0.f, 1.f ); ::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 - setup_projection(); - setup_camera(); - ::glDepthFunc( GL_LEQUAL ); + setup_matrices(); // render // opaque parts... setup_drawing( false ); @@ -415,14 +437,32 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } } +void +opengl_renderer::renderpass_config::setup( rendermode const Mode ) { + + 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::shadowtune.depth; 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_projection(); + setup_modelview(); +} + // configures projection matrix for the current render pass void -opengl_renderer::setup_projection() { +opengl_renderer::renderpass_config::setup_projection() { - ::glMatrixMode( GL_PROJECTION ); - ::glLoadIdentity(); + camera.projection() = glm::mat4( 1.f ); - switch( m_renderpass.draw_mode ) { + switch( draw_mode ) { #ifndef EU07_USE_PICKING_FRAMEBUFFER case rendermode::pickcontrols: @@ -437,12 +477,13 @@ opengl_renderer::setup_projection() { case rendermode::pickscenery: { // TODO: scissor test for pick modes auto const angle = Global::FieldOfView / Global::ZoomFactor; - auto const height = std::max( 1.0f, (float)Global::ScreenWidth ) / std::max( 1.0f, (float)Global::ScreenHeight ) / ( Global::ScreenWidth / m_pickbuffersize ); - ::gluPerspective( - Global::FieldOfView / Global::ZoomFactor, - std::max( 1.0f, (float)Global::ScreenWidth ) / std::max( 1.0f, (float)Global::ScreenHeight ) / ( Global::ScreenWidth / m_pickbuffersize ), - 0.1f * Global::ZoomFactor, - m_renderpass.draw_range * Global::fDistanceFactor ); + auto const height = std::max( 1.0f, (float)Global::iWindowWidth ) / std::max( 1.0f, (float)Global::iWindowHeight ) / ( Global::iWindowWidth / EU07_PICKBUFFERSIZE ); + camera.projection() *= + glm::perspective( + glm::radians( Global::FieldOfView / Global::ZoomFactor ), + std::max( 1.0f, (float)Global::iWindowWidth ) / std::max( 1.0f, (float)Global::iWindowHeight ) / ( Global::iWindowWidth / EU07_PICKBUFFERSIZE ), + 0.1f * Global::ZoomFactor, + draw_range * Global::fDistanceFactor ); break; } #endif @@ -452,7 +493,6 @@ opengl_renderer::setup_projection() { #else setup_projection_light_perspective(); #endif - m_shadowtexturematrix *= OpenGLMatrices.data( GL_PROJECTION ); break; } @@ -463,103 +503,114 @@ opengl_renderer::setup_projection() { } void -opengl_renderer::setup_projection_world() { +opengl_renderer::renderpass_config::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 ); + 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, + draw_range * Global::fDistanceFactor ); } void -opengl_renderer::setup_projection_light_ortho() { +opengl_renderer::renderpass_config::setup_projection_light_ortho() { // TODO: calculate lightview boundaries based on area of the world camera frustum - ::glOrtho( - -Global::shadowtune.width, Global::shadowtune.width, - -Global::shadowtune.width, Global::shadowtune.width, - -Global::shadowtune.depth, Global::shadowtune.depth ); + camera.projection() *= + glm::ortho( + -Global::shadowtune.width, Global::shadowtune.width, + -Global::shadowtune.width, Global::shadowtune.width, + -Global::shadowtune.depth, Global::shadowtune.depth ); } void -opengl_renderer::setup_projection_light_perspective() { +opengl_renderer::renderpass_config::setup_projection_light_perspective() { - ::gluPerspective( - 45.f, - 1.f, - m_renderpass.draw_range * 0.1f, // 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 ); + camera.projection() *= + glm::perspective( + glm::radians( 45.f ), + 1.f, + draw_range * 0.1f, // light source is pulled back far enough we won't likely have anything too close to it, can get some z-range here + draw_range * Global::fDistanceFactor ); } // configures modelview matrix for the current render pass void -opengl_renderer::setup_camera() { +opengl_renderer::renderpass_config::setup_modelview() { - ::glMatrixMode( GL_MODELVIEW ); // Select The Modelview Matrix - ::glLoadIdentity(); + camera.modelview() = glm::mat4( 1.f ); glm::dmat4 viewmatrix; - switch( m_renderpass.draw_mode ) { + switch( draw_mode ) { case rendermode::color: case rendermode::pickcontrols: case rendermode::pickscenery: { - setup_camera_world( viewmatrix ); + setup_modelview_world( viewmatrix ); break; } case rendermode::shadows: { #ifdef EU07_USE_ORTHO_SHADOWS - setup_camera_light_ortho( viewmatrix ); + setup_modelview_light_ortho( viewmatrix ); #else - setup_camera_light_perspective( viewmatrix ); + setup_modelview_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: { break; } } - +#ifdef EU07_USE_ORTHO_SHADOWS m_renderpass.camera.update_frustum( OpenGLMatrices.data( GL_PROJECTION ), viewmatrix ); // frustum tests are performed in 'world space' but after we set up frustum we no longer need camera translation, only rotation ::glMultMatrixd( glm::value_ptr( glm::dmat4( glm::dmat3( viewmatrix ) ) ) ); +#else + camera.modelview() = viewmatrix; + camera.update_frustum(); +#endif } void -opengl_renderer::setup_camera_world( glm::dmat4 &Viewmatrix ) { +opengl_renderer::renderpass_config::setup_modelview_world( glm::dmat4 &Viewmatrix ) { + camera.position() = Global::pCameraPosition; World.Camera.SetMatrix( Viewmatrix ); - m_renderpass.camera.position() = Global::pCameraPosition; } void -opengl_renderer::setup_camera_light_ortho( glm::dmat4 &Viewmatrix ) { +opengl_renderer::renderpass_config::setup_modelview_light_ortho( glm::dmat4 &Viewmatrix ) { - m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction }; - if( m_renderpass.camera.position().y - Global::pCameraPosition.y < 0.1 ) { - m_renderpass.camera.position().y = Global::pCameraPosition.y + 0.1; + camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction }; + if( camera.position().y - Global::pCameraPosition.y < 0.1 ) { + camera.position().y = Global::pCameraPosition.y + 0.1; } Viewmatrix *= glm::lookAt( - m_renderpass.camera.position(), + camera.position(), glm::dvec3{ Global::pCameraPosition }, glm::dvec3{ 0.f, 1.f, 0.f } ); } void -opengl_renderer::setup_camera_light_perspective( glm::dmat4 &Viewmatrix ) { +opengl_renderer::renderpass_config::setup_modelview_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( m_renderpass.draw_range * 0.5f * 0.1f, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low + camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * draw_range * 0.5f }; + camera.position().y = std::max( draw_range * 0.5f * 0.1f, camera.position().y ); // prevent shadow source from dipping too low Viewmatrix *= glm::lookAt( - m_renderpass.camera.position(), + camera.position(), glm::dvec3{ Global::pCameraPosition }, glm::dvec3{ 0.f, 1.f, 0.f } ); } +void +opengl_renderer::setup_matrices() { + + ::glMatrixMode( GL_PROJECTION ); + OpenGLMatrices.load_matrix( m_renderpass.camera.projection() ); + // trim modelview matrix just to rotation, since rendering is done in camera-centric world space + ::glMatrixMode( GL_MODELVIEW ); + OpenGLMatrices.load_matrix( glm::mat4( glm::mat3( m_renderpass.camera.modelview() ) ) ); +} + void opengl_renderer::setup_drawing( bool const Alpha ) { @@ -982,7 +1033,7 @@ opengl_renderer::Render( TGround *Ground ) { ++TGroundRect::iFrameNumber; // zwięszenie licznika ramek (do usuwniania nadanimacji) - m_renderpass.draw_queue.clear(); + m_drawqueue.clear(); switch( m_renderpass.draw_mode ) { case rendermode::color: { @@ -1034,12 +1085,12 @@ opengl_renderer::Render( TGround *Ground ) { } // draw queue was filled while rendering content of ground cells. now sort the nodes based on their distance to viewer... std::sort( - std::begin( m_renderpass.draw_queue ), - std::end( m_renderpass.draw_queue ), + std::begin( m_drawqueue ), + std::end( m_drawqueue ), []( distancesubcell_pair const &Left, distancesubcell_pair const &Right ) { return ( Left.first ) < ( Right.first ); } ); // ...then render the opaque content of the visible subcells. - for( auto subcellpair : m_renderpass.draw_queue ) { + for( auto subcellpair : m_drawqueue ) { Render( subcellpair.second ); } break; @@ -1058,7 +1109,7 @@ opengl_renderer::Render( TGround *Ground ) { } // they can also skip queue sorting, as they only deal with opaque geometry // NOTE: there's benefit from rendering front-to-back, but is it significant enough? TODO: investigate - for( auto subcellpair : m_renderpass.draw_queue ) { + for( auto subcellpair : m_drawqueue ) { Render( subcellpair.second ); } break; @@ -1125,7 +1176,7 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { auto subcell = Groundcell->pSubRects + subcellindex; if( subcell->iNodeCount ) { // o ile są jakieś obiekty, bo po co puste sektory przelatywać - m_renderpass.draw_queue.emplace_back( + m_drawqueue.emplace_back( glm::length2( m_renderpass.camera.position() - glm::dvec3( subcell->m_area.center ) ), subcell ); } @@ -1290,9 +1341,6 @@ opengl_renderer::Render( TGroundNode *Node ) { break; } } -#ifdef EU07_USE_OLD_RENDERCODE - Node->Model->Render( Node->pCenter - m_renderpass.camera.position() ); -#else Node->Model->RaAnimate(); // jednorazowe przeliczenie animacji Node->Model->RaPrepare(); if( Node->Model->pModel ) { @@ -1320,7 +1368,6 @@ opengl_renderer::Render( TGroundNode *Node ) { } } } -#endif return true; } @@ -1981,21 +2028,21 @@ opengl_renderer::Render_Alpha( TGround *Ground ) { TGroundNode *node; TSubRect *tmp; // Ra: renderowanie progresywne - zależne od FPS oraz kierunku patrzenia - for( auto subcellpair = std::rbegin( m_renderpass.draw_queue ); subcellpair != std::rend( m_renderpass.draw_queue ); ++subcellpair ) { + for( auto subcellpair = std::rbegin( m_drawqueue ); subcellpair != std::rend( m_drawqueue ); ++subcellpair ) { // przezroczyste trójkąty w oddzielnym cyklu przed modelami tmp = subcellpair->second; for( node = tmp->nRenderRectAlpha; node; node = node->nNext3 ) { Render_Alpha( node ); } } - for( auto subcellpair = std::rbegin( m_renderpass.draw_queue ); subcellpair != std::rend( m_renderpass.draw_queue ); ++subcellpair ) + for( auto subcellpair = std::rbegin( m_drawqueue ); subcellpair != std::rend( m_drawqueue ); ++subcellpair ) { // renderowanie przezroczystych modeli oraz pojazdów Render_Alpha( subcellpair->second ); } ::glDisable( GL_LIGHTING ); // linie nie powinny świecić - for( auto subcellpair = std::rbegin( m_renderpass.draw_queue ); subcellpair != std::rend( m_renderpass.draw_queue ); ++subcellpair ) { + for( auto subcellpair = std::rbegin( m_drawqueue ); subcellpair != std::rend( m_drawqueue ); ++subcellpair ) { // druty na końcu, żeby się nie robiły białe plamy na tle lasu tmp = subcellpair->second; for( node = tmp->nRenderWires; node; node = node->nNext3 ) { @@ -2092,9 +2139,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { } } case TP_MODEL: { -#ifdef EU07_USE_OLD_RENDERCODE - Node->Model->RenderAlpha( Node->pCenter - m_renderpass.camera.position() ); -#else + Node->Model->RaPrepare(); if( Node->Model->pModel ) { // renderowanie rekurencyjne submodeli @@ -2121,7 +2166,6 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { } } } -#endif return true; } @@ -2508,15 +2552,15 @@ opengl_renderer::Update_Pick_Control() { // determine point to examine glm::dvec2 mousepos; glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y ); - mousepos.y = Global::ScreenHeight - mousepos.y; // cursor coordinates are flipped compared to opengl + mousepos.y = Global::iWindowHeight - mousepos.y; // cursor coordinates are flipped compared to opengl #ifdef EU07_USE_PICKING_FRAMEBUFFER glm::ivec2 pickbufferpos; if( true == m_framebuffersupport ) { // ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); pickbufferpos = glm::ivec2{ - mousepos.x * m_pickbuffersize / Global::ScreenWidth, - mousepos.y * m_pickbuffersize / Global::ScreenHeight + mousepos.x * EU07_PICKBUFFERSIZE / Global::iWindowWidth, + mousepos.y * EU07_PICKBUFFERSIZE / Global::iWindowHeight }; } else { @@ -2556,15 +2600,15 @@ opengl_renderer::Update_Pick_Node() { // determine point to examine glm::dvec2 mousepos; glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y ); - mousepos.y = Global::ScreenHeight - mousepos.y; // cursor coordinates are flipped compared to opengl + mousepos.y = Global::iWindowHeight - mousepos.y; // cursor coordinates are flipped compared to opengl #ifdef EU07_USE_PICKING_FRAMEBUFFER glm::ivec2 pickbufferpos; if( true == m_framebuffersupport ) { // ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); pickbufferpos = glm::ivec2{ - mousepos.x * m_pickbuffersize / Global::ScreenWidth, - mousepos.y * m_pickbuffersize / Global::ScreenHeight + mousepos.x * EU07_PICKBUFFERSIZE / Global::iWindowWidth, + mousepos.y * EU07_PICKBUFFERSIZE / Global::iWindowHeight }; } else { @@ -2611,8 +2655,8 @@ opengl_renderer::Update( double const Deltatime ) { float targetfactor; if( framerate > 90.0 ) { targetfactor = 3.0f; } else if( framerate > 60.0 ) { targetfactor = 1.5f; } - else if( framerate > 30.0 ) { targetfactor = Global::ScreenHeight / 768.0f; } - else { targetfactor = Global::ScreenHeight / 768.0f * 0.75f; } + else if( framerate > 30.0 ) { targetfactor = Global::iWindowHeight / 768.0f; } + else { targetfactor = Global::iWindowHeight / 768.0f * 0.75f; } if( targetfactor > Global::fDistanceFactor ) { diff --git a/renderer.h b/renderer.h index 05b5c6bc..81a85d19 100644 --- a/renderer.h +++ b/renderer.h @@ -26,10 +26,10 @@ struct opengl_light { GLuint id{ (GLuint)-1 }; glm::vec3 direction; glm::vec4 - position { 0.0f, 0.0f, 0.0f, 1.0f }, // 4th parameter specifies directional(0) or omni-directional(1) light source - ambient { 0.0f, 0.0f, 0.0f, 1.0f }, - diffuse { 1.0f, 1.0f, 1.0f, 1.0f }, - specular { 1.0f, 1.0f, 1.0f, 1.0f }; + position { 0.f, 0.f, 0.f, 1.f }, // 4th parameter specifies directional(0) or omni-directional(1) light source + ambient { 0.f, 0.f, 0.f, 1.f }, + diffuse { 1.f, 1.f, 1.f, 1.f }, + specular { 1.f, 1.f, 1.f, 1.f }; inline void apply_intensity( float const Factor = 1.0f ) { @@ -54,7 +54,7 @@ struct opengl_light { void apply_angle() { glLightfv( id, GL_POSITION, glm::value_ptr(position) ); - if( position.w == 1.0f ) { + if( position.w == 1.f ) { glLightfv( id, GL_SPOT_DIRECTION, glm::value_ptr(direction) ); } } @@ -86,7 +86,7 @@ public: // methods: inline void - update_frustum() { m_frustum.calculate(); } + update_frustum() { m_frustum.calculate( m_projection, m_modelview ); } inline void update_frustum(glm::mat4 const &Projection, glm::mat4 const &Modelview) { m_frustum.calculate(Projection, Modelview); } @@ -100,11 +100,28 @@ public: inline glm::dvec3 & position() { return m_position; } + inline + glm::mat4 const & + projection() const { return m_projection; } + inline + glm::mat4 & + projection() { return m_projection; } + inline + glm::mat4 const & + modelview() const { return m_modelview; } + inline + glm::mat4 & + modelview() { return m_modelview; } + inline + cFrustum const & + frustum() { return m_frustum; } private: // members: cFrustum m_frustum; glm::dvec3 m_position; + glm::mat4 m_projection; + glm::mat4 m_modelview; }; // bare-bones render controller, in lack of anything better yet @@ -185,7 +202,28 @@ private: opengl_camera camera; rendermode draw_mode { rendermode::none }; float draw_range { 0.0f }; - std::vector draw_queue; // list of subcells to be drawn in current render pass + + void + setup( rendermode const Mode ); + private: + // configures projection matrix for the current render pass + void + setup_projection(); + void + setup_projection_world(); + void + setup_projection_light_ortho(); + void + setup_projection_light_perspective(); + // configures camera for the current render pass + void + setup_modelview(); + void + setup_modelview_world( glm::dmat4 &Viewmatrix ); + void + setup_modelview_light_ortho( glm::dmat4 &Viewmatrix ); + void + setup_modelview_light_perspective( glm::dmat4 &Viewmatrix ); }; typedef std::vector opengllight_array; @@ -196,24 +234,8 @@ private: // runs jobs needed to generate graphics for specified render pass void Render_pass( rendermode const Mode ); - // configures projection matrix for the current render pass void - setup_projection(); - void - setup_projection_world(); - void - setup_projection_light_ortho(); - void - setup_projection_light_perspective(); - // configures camera for the current render pass - void - setup_camera(); - void - setup_camera_world( glm::dmat4 &Viewmatrix ); - void - setup_camera_light_ortho( glm::dmat4 &Viewmatrix ); - void - setup_camera_light_perspective( glm::dmat4 &Viewmatrix ); + setup_matrices(); void setup_drawing( bool const Alpha = false ); void @@ -267,46 +289,49 @@ private: std::size_t pick_index( glm::ivec3 const &Color ); // members + GLFWwindow *m_window { nullptr }; geometrybank_manager m_geometry; texture_manager m_textures; opengllight_array m_lights; - GLFWwindow *m_window { nullptr }; + + geometry_handle m_billboardgeometry { NULL, NULL }; + texture_handle m_glaretexture { -1 }; + texture_handle m_suntexture { -1 }; + texture_handle m_moontexture { -1 }; + texture_handle m_reflectiontexture { -1 }; + GLUquadricObj *m_quadric { nullptr }; // helper object for drawing debug mode scene elements + + bool m_framebuffersupport { false }; #ifdef EU07_USE_PICKING_FRAMEBUFFER GLuint m_pickframebuffer { NULL }; // TODO: refactor pick framebuffer stuff into an object GLuint m_picktexture { NULL }; GLuint m_pickdepthbuffer { NULL }; - int m_pickbuffersize { 1024 }; // size of (square) textures bound with the pick framebuffer #endif + int m_shadowbuffersize { 4096 }; GLuint m_shadowframebuffer { NULL }; GLuint m_shadowtexture { NULL }; #ifdef EU07_USE_DEBUG_SHADOWMAP GLuint m_shadowdebugtexture{ NULL }; #endif - 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 + glm::mat4 m_shadowtexturematrix; // conversion from camera-centric world space to light-centric clip space + 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 + float m_drawtime { 1000.f / 30.f * 20.f }; // start with presumed 'neutral' average of 30 fps double m_updateaccumulator { 0.0 }; std::string m_debuginfo; glm::vec4 m_baseambient { 0.0f, 0.0f, 0.0f, 1.0f }; - float m_specularopaquescalefactor { 1.0f }; - float m_speculartranslucentscalefactor { 1.0f }; + glm::vec4 m_shadowcolor { 0.5f, 0.5f, 0.5f, 1.f }; + float m_specularopaquescalefactor { 1.f }; + float m_speculartranslucentscalefactor { 1.f }; + bool m_renderspecular{ false }; // controls whether to include specular component in the calculations - bool m_framebuffersupport { false }; renderpass_config m_renderpass; - bool m_renderspecular { false }; // controls whether to include specular component in the calculations + std::vector m_drawqueue; // list of subcells to be drawn in current render pass + std::vector m_picksceneryitems; std::vector m_pickcontrolsitems; TSubModel const *m_pickcontrolitem { nullptr }; diff --git a/uilayer.cpp b/uilayer.cpp index 67ca6c60..4680a728 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -65,7 +65,7 @@ ui_layer::render() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho( 0, std::max( 1, Global::ScreenWidth ), std::max( 1, Global::ScreenHeight ), 0, -1, 1 ); + glOrtho( 0, std::max( 1, Global::iWindowWidth ), std::max( 1, Global::iWindowHeight ), 0, -1, 1 ); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -107,11 +107,7 @@ ui_layer::set_background( std::string const &Filename ) { m_progressbottom = ( texture.width() != texture.height() ); } } -/* -void cGuiLayer::setNote( const std::string Note ) { mNote = Note; } -std::string cGuiLayer::getNote() { return mNote; } -*/ void ui_layer::render_progress() { @@ -247,13 +243,14 @@ ui_layer::render_texture() { ::glBindTexture( GL_TEXTURE_2D, m_texture ); auto const size = 512.f; + auto const offset = 64.f; glBegin( GL_TRIANGLE_STRIP ); - glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( 0.f, 256.f ); - glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( 0.f, 256.f + size ); - glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( size, 256.f ); - glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( size, 256.f + size ); + glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( offset, Global::iWindowHeight - offset - size ); + glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( offset, Global::iWindowHeight - offset ); + glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( offset + size, Global::iWindowHeight - offset - size ); + glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( offset + size, Global::iWindowHeight - offset ); glEnd(); From 01ef6b3887ed3ee1e408012f3c70552070938049 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 29 Jul 2017 02:02:02 +0200 Subject: [PATCH 5/9] minor c++ standard compliance fixes --- McZapkie/MOVER.h | 16 ++++----- ResourceManager.h | 2 +- Texture.cpp | 5 +-- parser.cpp | 36 +++++++++++++++++++ parser.h | 91 +++++++++++++++++------------------------------ 5 files changed, 78 insertions(+), 72 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 8a83e79a..2fe15ace 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -384,14 +384,14 @@ struct TBoilerType { }; /*rodzaj odbieraka pradu*/ struct TCurrentCollector { - long CollectorsNo{ 0 }; //musi być tu, bo inaczej się kopie - double MinH{ 0.0 }; double MaxH{ 0.0 }; //zakres ruchu pantografu, nigdzie nie używany - double CSW{ 0.0 }; //szerokość części roboczej (styku) ślizgacza - double MinV{ 0.0 }; double MaxV{ 0.0 }; //minimalne i maksymalne akceptowane napięcie - double OVP{ 0.0 }; //czy jest przekaznik nadnapieciowy - double InsetV{ 0.0 }; //minimalne napięcie wymagane do załączenia - double MinPress{ 0.0 }; //minimalne ciśnienie do załączenia WS - double MaxPress{ 0.0 }; //maksymalne ciśnienie za reduktorem + long CollectorsNo; //musi być tu, bo inaczej się kopie + double MinH; double MaxH; //zakres ruchu pantografu, nigdzie nie używany + double CSW; //szerokość części roboczej (styku) ślizgacza + double MinV; double MaxV; //minimalne i maksymalne akceptowane napięcie + double OVP; //czy jest przekaznik nadnapieciowy + double InsetV; //minimalne napięcie wymagane do załączenia + double MinPress; //minimalne ciśnienie do załączenia WS + double MaxPress; //maksymalne ciśnienie za reduktorem //inline TCurrentCollector() { // CollectorsNo = 0; // MinH, MaxH, CSW, MinV, MaxV = 0.0; diff --git a/ResourceManager.h b/ResourceManager.h index 50e65600..ae890b57 100644 --- a/ResourceManager.h +++ b/ResourceManager.h @@ -110,7 +110,7 @@ private: std::chrono::nanoseconds const m_unusedresourcetimetolive; typename Container_::size_type const m_unusedresourcesweepsize; std::string const m_resourcename; - typename Container_ &m_container; + Container_ &m_container; typename Container_::size_type m_resourcesweepindex { 0 }; std::chrono::steady_clock::time_point m_resourcetimestamp { std::chrono::steady_clock::now() }; }; diff --git a/Texture.cpp b/Texture.cpp index f6147f05..228d314d 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -592,10 +592,7 @@ opengl_texture::create() { } } - data.swap( std::vector() ); // TBD, TODO: keep the texture data if we start doing some gpu data cleaning down the road -/* - data_state = resource_state::none; -*/ + data = std::vector(); data_state = resource_state::none; is_ready = true; } diff --git a/parser.cpp b/parser.cpp index 68355475..c7bbebd5 100644 --- a/parser.cpp +++ b/parser.cpp @@ -72,6 +72,42 @@ cParser::~cParser() mComments.clear(); } +template<> +cParser& +cParser::operator>>( std::string &Right ) { + + if( true == this->tokens.empty() ) { return *this; } + + Right = this->tokens.front(); + this->tokens.pop_front(); + + return *this; +} + +template<> +cParser& +cParser::operator>>( bool &Right ) { + + if( true == this->tokens.empty() ) { return *this; } + + Right = ( ( this->tokens.front() == "true" ) + || ( this->tokens.front() == "yes" ) + || ( this->tokens.front() == "1" ) ); + this->tokens.pop_front(); + + return *this; +} + +template <> +bool +cParser::getToken( bool const ToLower, const char *Break ) { + + auto const token = getToken( true, Break ); + return ( ( token == "true" ) + || ( token == "yes" ) + || ( token == "1" ) ); +} + // methods bool cParser::getTokens(unsigned int Count, bool ToLower, const char *Break) { diff --git a/parser.h b/parser.h index b8176c0a..982959ef 100644 --- a/parser.h +++ b/parser.h @@ -35,12 +35,6 @@ class cParser //: public std::stringstream template cParser & operator>>( Type_ &Right ); - template <> - cParser & - operator>>( std::string &Right ); - template <> - cParser & - operator>>( bool &Right ); template Output_ getToken( bool const ToLower = true, const char *Break = "\n\r\t ;" ) { @@ -48,41 +42,34 @@ class cParser //: public std::stringstream Output_ output; *this >> output; return output; }; - template <> - bool - getToken( bool const ToLower, const char *Break ) { - auto const token = getToken( true, Break ); - return ( ( token == "true" ) - || ( token == "yes" ) - || ( token == "1" ) ); } - inline void ignoreToken() - { - readToken(); - }; - inline void ignoreTokens(int count) - { - for (int i = 0; i < count; i++) - readToken(); - }; - inline bool expectToken(std::string const &value) - { - return readToken() == value; - }; - bool eof() - { - return mStream->eof(); - }; - bool ok() - { - return !mStream->fail(); - }; - bool getTokens(unsigned int Count = 1, bool ToLower = true, char const *Break = "\n\r\t ;"); + inline + void + ignoreToken() { + readToken(); }; + inline + void + ignoreTokens(int count) { + for( int i = 0; i < count; ++i ) { + readToken(); } }; + inline + bool + expectToken( std::string const &Value ) { + return readToken() == Value; }; + bool + eof() { + return mStream->eof(); }; + bool + ok() { + return !mStream->fail(); }; + bool + getTokens( unsigned int Count = 1, bool ToLower = true, char const *Break = "\n\r\t ;" ); // returns next incoming token, if any, without removing it from the set - std::string peek() const { - return ( - false == tokens.empty() ? - tokens.front() : - "" ); } + std::string + peek() const { + return ( + false == tokens.empty() ? + tokens.front() : + "" ); } // returns percentage of file processed so far int getProgress() const; int getFullProgress() const; @@ -129,26 +116,12 @@ cParser::operator>>( Type_ &Right ) { template<> cParser& -cParser::operator>>( std::string &Right ) { - - if( true == this->tokens.empty() ) { return *this; } - - Right = this->tokens.front(); - this->tokens.pop_front(); - - return *this; -} +cParser::operator>>( std::string &Right ); template<> cParser& -cParser::operator>>( bool &Right ) { +cParser::operator>>( bool &Right ); - if( true == this->tokens.empty() ) { return *this; } - - Right = ( ( this->tokens.front() == "true" ) - || ( this->tokens.front() == "yes" ) - || ( this->tokens.front() == "1" ) ); - this->tokens.pop_front(); - - return *this; -} +template<> +bool +cParser::getToken( bool const ToLower, const char *Break ); From c8a70e528053e3bbd600e1e1b096286bb4dd0b16 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 1 Aug 2017 03:15:19 +0200 Subject: [PATCH 6/9] build 170731. cascade shadow maps stub, support for additional debug camera --- Camera.cpp | 8 +- Globals.cpp | 3 +- Globals.h | 1 + McZapkie/mctools.cpp | 1 + McZapkie/mctools.h | 1 + World.cpp | 57 ++++----- World.h | 1 + frustum.cpp | 10 +- frustum.h | 12 +- renderer.cpp | 282 ++++++++++++++++++++----------------------- renderer.h | 34 +++--- usefull.h | 15 +++ version.h | 2 +- 13 files changed, 213 insertions(+), 214 deletions(-) diff --git a/Camera.cpp b/Camera.cpp index 6d3a3adb..0c7c21ca 100644 --- a/Camera.cpp +++ b/Camera.cpp @@ -352,7 +352,8 @@ void TCamera::Update() } */ if( ( Type == tp_Free ) - || ( false == Global::ctrlState ) ) { + || ( false == Global::ctrlState ) + || ( true == DebugCameraFlag) ) { // ctrl is used for mirror view, so we ignore the controls when in vehicle if ctrl is pressed if( m_keys.up ) Velocity.y = clamp( Velocity.y + m_moverate.y * 10.0 * deltatime, -m_moverate.y, m_moverate.y ); @@ -371,7 +372,8 @@ void TCamera::Update() } #endif - if( Type == tp_Free ) { + if( ( Type == tp_Free ) + || ( true == DebugCameraFlag ) ) { // free movement position update is handled here, movement while in vehicle is handled by train update vector3 Vec = Velocity; Vec.RotateY( Yaw ); @@ -390,7 +392,7 @@ bool TCamera::SetMatrix( glm::dmat4 &Matrix ) { Matrix = glm::rotate( Matrix, -Pitch, glm::dvec3( 1.0, 0.0, 0.0 ) ); Matrix = glm::rotate( Matrix, -Yaw, glm::dvec3( 0.0, 1.0, 0.0 ) ); // w zewnętrznym widoku: kierunek patrzenia - if( Type == tp_Follow ) { + if( ( Type == tp_Follow ) && ( false == DebugCameraFlag ) ) { Matrix *= glm::lookAt( glm::dvec3{ Pos }, diff --git a/Globals.cpp b/Globals.cpp index 7aa9094f..80b4fa98 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -71,6 +71,7 @@ float4 Global::UITextColor = float4( 225.0 / 255.0f, 225.0f / 255.0f, 225.0f / 2 // parametry scenerii vector3 Global::pCameraPosition; +vector3 Global::DebugCameraPosition; double Global::pCameraRotation; double Global::pCameraRotationDeg; std::vector Global::FreeCameraInit; @@ -86,7 +87,7 @@ opengl_light Global::DayLight; int Global::DynamicLightCount { 3 }; bool Global::ScaleSpecularValues { true }; bool Global::RenderShadows { false }; -Global::shadowtune_t Global::shadowtune = { 2048, 250.f, 1250.f, 100.f }; +Global::shadowtune_t Global::shadowtune = { 2048, 250.f, 250.f, 500.f }; 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 diff --git a/Globals.h b/Globals.h index 485a8a2f..376a45a0 100644 --- a/Globals.h +++ b/Globals.h @@ -170,6 +170,7 @@ class Global static int Keys[MaxKeys]; static bool RealisticControlMode; // controls ability to steer the vehicle from outside views static Math3D::vector3 pCameraPosition; // pozycja kamery w świecie + static Math3D::vector3 DebugCameraPosition; // pozycja kamery w świecie static double pCameraRotation; // kierunek bezwzględny kamery w świecie: 0=północ, 90°=zachód (-azymut) static double pCameraRotationDeg; // w stopniach, dla animacji billboard diff --git a/McZapkie/mctools.cpp b/McZapkie/mctools.cpp index 33744897..01a92684 100644 --- a/McZapkie/mctools.cpp +++ b/McZapkie/mctools.cpp @@ -19,6 +19,7 @@ Copyright (C) 2007-2014 Maciej Cierniak bool DebugModeFlag = false; bool FreeFlyModeFlag = false; +bool DebugCameraFlag = false; double Max0R(double x1, double x2) { diff --git a/McZapkie/mctools.h b/McZapkie/mctools.h index 55a65447..3611dcf2 100644 --- a/McZapkie/mctools.h +++ b/McZapkie/mctools.h @@ -20,6 +20,7 @@ http://mozilla.org/MPL/2.0/. extern bool DebugModeFlag; extern bool FreeFlyModeFlag; +extern bool DebugCameraFlag; /*funkcje matematyczne*/ double Max0R(double x1, double x2); diff --git a/World.cpp b/World.cpp index c4c1d5fa..a82dc753 100644 --- a/World.cpp +++ b/World.cpp @@ -288,24 +288,16 @@ bool TWorld::Init( GLFWwindow *Window ) { "ShaXbee, Oli_EU, youBy, KURS90, Ra, hunter, szociu, Stele, Q, firleju and others\n" ); UILayer.set_background( "logo" ); -/* - std::shared_ptr initpanel = std::make_shared(85, 600); -*/ + TSoundsManager::Init( glfwGetWin32Window( window ) ); WriteLog("Sound Init OK"); TModelsManager::Init(); WriteLog("Models init OK"); -/* - initpanel->text_lines.emplace_back( "Loading scenery / Wczytywanie scenerii:", float4( 0.0f, 0.0f, 0.0f, 1.0f ) ); - initpanel->text_lines.emplace_back( Global::SceneryFile.substr(0, 40), float4( 0.0f, 0.0f, 0.0f, 1.0f ) ); - UILayer.push_back( initpanel ); -*/ + glfwSetWindowTitle( window, ( Global::AppName + " (" + Global::SceneryFile + ")" ).c_str() ); // nazwa scenerii UILayer.set_progress(0.01); UILayer.set_progress( "Loading scenery / Wczytywanie scenerii" ); - GfxRenderer.Render(); - WriteLog( "Ground init" ); if( true == Ground.Init( Global::SceneryFile ) ) { WriteLog( "Ground init OK" ); @@ -315,13 +307,8 @@ bool TWorld::Init( GLFWwindow *Window ) { Environment.init(); Camera.Init(Global::FreeCameraInit[0], Global::FreeCameraInitAngle[0]); -/* - initpanel->text_lines.clear(); - initpanel->text_lines.emplace_back( "Preparing train / Przygotowanie kabiny:", float4( 0.0f, 0.0f, 0.0f, 1.0f ) ); -*/ + UILayer.set_progress( "Preparing train / Przygotowanie kabiny" ); - GfxRenderer.Render(); - WriteLog( "Player train init: " + Global::asHumanCtrlVehicle ); TGroundNode *nPlayerTrain = NULL; @@ -631,12 +618,18 @@ void TWorld::OnKeyDown(int cKey) break; } case GLFW_KEY_F8: { - Global::iTextMode = cKey; + if( Global::ctrlState + && Global::shiftState ) { + DebugCameraFlag = !DebugCameraFlag; // taka opcjonalna funkcja, może się czasem przydać + } + else { + Global::iTextMode = cKey; + } break; } case GLFW_KEY_F9: { Global::iTextMode = cKey; - // wersja, typ wyświetlania, błędy OpenGL + // wersja break; } case GLFW_KEY_F10: { @@ -1074,8 +1067,9 @@ bool TWorld::Update() } // fixed step part of the camera update - if( (Train != nullptr) - && (Camera.Type == tp_Follow )) { + if( ( Train != nullptr ) + && ( Camera.Type == tp_Follow ) + && ( false == DebugCameraFlag ) ) { // jeśli jazda w kabinie, przeliczyć trzeba parametry kamery Train->UpdateMechPosition( m_secondaryupdaterate ); } @@ -1112,11 +1106,16 @@ bool TWorld::Update() while( fTime50Hz >= 1.0 / 50.0 ) { Console::Update(); // to i tak trzeba wywoływać Update_UI(); - + // decelerate camera Camera.Velocity *= 0.65; if( std::abs( Camera.Velocity.x ) < 0.01 ) { Camera.Velocity.x = 0.0; } if( std::abs( Camera.Velocity.y ) < 0.01 ) { Camera.Velocity.y = 0.0; } if( std::abs( Camera.Velocity.z ) < 0.01 ) { Camera.Velocity.z = 0.0; } + // decelerate debug camera too + DebugCamera.Velocity *= 0.65; + if( std::abs( DebugCamera.Velocity.x ) < 0.01 ) { DebugCamera.Velocity.x = 0.0; } + if( std::abs( DebugCamera.Velocity.y ) < 0.01 ) { DebugCamera.Velocity.y = 0.0; } + if( std::abs( DebugCamera.Velocity.z ) < 0.01 ) { DebugCamera.Velocity.z = 0.0; } fTime50Hz -= 1.0 / 50.0; } @@ -1177,10 +1176,12 @@ TWorld::Update_Camera( double const Deltatime ) { } } - Camera.Update(); // uwzględnienie ruchu wywołanego klawiszami + if( DebugCameraFlag ) { DebugCamera.Update(); } + else { Camera.Update(); } // uwzględnienie ruchu wywołanego klawiszami - if( (Train != nullptr) - && (Camera.Type == tp_Follow )) { + if( ( Train != nullptr ) + && ( Camera.Type == tp_Follow ) + && ( false == DebugCameraFlag ) ) { // jeśli jazda w kabinie, przeliczyć trzeba parametry kamery vector3 tempangle = Controlled->VectorFront() * ( Controlled->MoverParameters->ActiveCab == -1 ? -1 : 1 ); double modelrotate = atan2( -tempangle.x, tempangle.z ); @@ -1243,7 +1244,8 @@ TWorld::Update_Camera( double const Deltatime ) { Global::SetCameraRotation( Camera.Yaw - M_PI ); } // all done, update camera position to the new value - Global::SetCameraPosition( Camera.Pos ); + Global::pCameraPosition = Camera.Pos; + Global::DebugCameraPosition = DebugCamera.Pos; } void TWorld::Update_Environment() { @@ -1597,8 +1599,9 @@ TWorld::Update_UI() { uitextline1 = "FoV: " + to_string( Global::FieldOfView / Global::ZoomFactor, 1 ) + ", Draw range x " + to_string( Global::fDistanceFactor, 1 ) - + "; sectors: " + std::to_string( GfxRenderer.m_drawcount ) - + ", FPS: " + to_string( Timer::GetFPS(), 2 ); +// + "; sectors: " + std::to_string( GfxRenderer.m_drawcount ) +// + ", FPS: " + to_string( Timer::GetFPS(), 2 ); + + ", FPS: " + std::to_string( static_cast(std::round(GfxRenderer.Framerate())) ); if( Global::iSlowMotion ) { uitextline1 += " (slowmotion " + to_string( Global::iSlowMotion ) + ")"; } diff --git a/World.h b/World.h index a7a26fea..54517e7c 100644 --- a/World.h +++ b/World.h @@ -118,6 +118,7 @@ private: void ResourceSweep(); TCamera Camera; + TCamera DebugCamera; TGround Ground; world_environment Environment; TTrain *Train; diff --git a/frustum.cpp b/frustum.cpp index c2420c89..f1b3eba1 100644 --- a/frustum.cpp +++ b/frustum.cpp @@ -10,10 +10,6 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "frustum.h" -std::vector 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 - void cFrustum::calculate() { @@ -26,8 +22,8 @@ cFrustum::calculate() { void cFrustum::calculate( glm::mat4 const &Projection, glm::mat4 const &Modelview ) { - float const *proj = &Projection[ 0 ][ 0 ]; - float const *modl = &Modelview[ 0 ][ 0 ]; + float const *proj = glm::value_ptr( Projection ); + float const *modl = glm::value_ptr( Modelview ); float clip[ 16 ]; // multiply the matrices to retrieve clipping planes @@ -88,7 +84,7 @@ cFrustum::calculate( glm::mat4 const &Projection, glm::mat4 const &Modelview ) { m_frustum[ side_FRONT ][ plane_D ] = clip[ 15 ] + clip[ 14 ]; normalize_plane( side_FRONT ); - m_inversetransformation = glm::inverse( Projection * Modelview ); + m_inversetransformation = glm::inverse( Projection * glm::mat4{ glm::mat3{ Modelview } } ); // calculate frustum corners m_frustumpoints = ndcfrustumshapepoints; diff --git a/frustum.h b/frustum.h index 4333e25a..cd59dd85 100644 --- a/frustum.h +++ b/frustum.h @@ -12,6 +12,10 @@ http://mozilla.org/MPL/2.0/. #include "float3d.h" #include "dumb3d.h" +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 + // generic frustum class. used to determine if objects are inside current view area class cFrustum { @@ -64,7 +68,7 @@ public: // transforms provided set of clip space points to world space template void - transform_to_world( Iterator_ First, Iterator_ Last ) { + transform_to_world( Iterator_ First, Iterator_ Last ) const { std::for_each( First, Last, [this]( glm::vec4 &point ) { @@ -76,7 +80,10 @@ public: void draw( glm::vec3 const &Offset ) const; -protected: +// members: + std::vector m_frustumpoints; // coordinates of corners for defined frustum, in world space + +private: // types: // planes of the frustum enum side { side_RIGHT = 0, side_LEFT = 1, side_BOTTOM = 2, side_TOP = 3, side_BACK = 4, side_FRONT = 5 }; @@ -90,5 +97,4 @@ protected: // 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 - std::vector m_frustumpoints; // coordinates of corners for defined frustum, in world space }; diff --git a/renderer.cpp b/renderer.cpp index ee935681..bdcd5dee 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -25,8 +25,6 @@ http://mozilla.org/MPL/2.0/. opengl_renderer GfxRenderer; extern TWorld World; -//#define EU07_USE_ORTHO_SHADOWS - int const EU07_PICKBUFFERSIZE { 1024 }; // size of (square) textures bound with the pick framebuffer namespace colors { @@ -212,6 +210,7 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_shadowdebugtexture, 0 ); #else ::glDrawBuffer( GL_NONE ); // we won't be rendering colour data, so can skip the colour attachment + ::glReadBuffer( GL_NONE ); #endif ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_shadowtexture, 0 ); // check if we got it working @@ -248,15 +247,21 @@ opengl_renderer::Init( GLFWwindow *Window ) { bool opengl_renderer::Render() { - auto const drawstart = std::chrono::steady_clock::now(); + if( m_drawstart != std::chrono::steady_clock::time_point() ) { + 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; m_renderpass.draw_mode = rendermode::none; // force setup anew Render_pass( rendermode::color ); - glfwSwapBuffers( m_window ); m_drawcount = m_drawqueue.size(); // accumulate last 20 frames worth of render time (cap at 1000 fps to prevent calculations going awry) - m_drawtime = std::max( 20.f, 0.95f * m_drawtime + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - drawstart ) ).count() ); + 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)"; + + glfwSwapBuffers( m_window ); return true; // for now always succeed } @@ -265,12 +270,12 @@ opengl_renderer::Render() { void opengl_renderer::Render_pass( rendermode const Mode ) { - m_renderpass.setup( Mode ); + m_renderpass.setup( Mode, m_framebuffersupport ); switch( m_renderpass.draw_mode ) { case rendermode::color: { - opengl_camera shadowcamera; + opengl_camera shadowcamera; // temporary helper, remove once ortho shadowmap code is done if( Global::RenderShadows && World.InitPerformed() ) { // run shadowmap pass before color Render_pass( rendermode::shadows ); @@ -279,7 +284,20 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #endif shadowcamera = m_renderpass.camera; // cache shadow camera placement for visualization - m_renderpass.setup( rendermode::color ); // restore draw mode. TBD, TODO: render mode stack + m_renderpass.setup( rendermode::color, m_framebuffersupport ); // restore draw mode. TBD, TODO: render mode stack +#ifdef EU07_USE_DEBUG_CAMERA + m_worldcamera.setup( + rendermode::color, + m_framebuffersupport, + 0.f, + std::min( + 1.f, + Global::shadowtune.depth / ( Global::BaseDrawRange * Global::fDistanceFactor ) + * std::max( + 1.f, + Global::ZoomFactor * 0.5f ) ), + true ); +#endif // setup shadowmap matrix m_shadowtexturematrix = //bias from [-1, 1] to [0, 1] }; @@ -312,19 +330,23 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // opaque parts... setup_drawing( false ); setup_units( true, true, true ); - +#ifdef EU07_USE_DEBUG_CAMERA if( DebugModeFlag ) { // draw light frustum ::glLineWidth( 2.f ); ::glColor4f( 1.f, 0.9f, 0.8f, 1.f ); ::glDisable( GL_LIGHTING ); ::glDisable( GL_TEXTURE_2D ); - shadowcamera.frustum().draw( m_renderpass.camera.position() ); + shadowcamera.frustum().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() ); + } ::glLineWidth( 1.f ); ::glEnable( GL_LIGHTING ); ::glEnable( GL_TEXTURE_2D ); } - +#endif Render( &World.Ground ); // ...translucent parts setup_drawing( true ); @@ -342,6 +364,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( World.InitPerformed() ) { // setup + auto const shadowdrawstart = std::chrono::steady_clock::now(); + ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); @@ -363,18 +387,6 @@ opengl_renderer::Render_pass( rendermode const Mode ) { setup_drawing( false ); #ifdef EU07_USE_DEBUG_SHADOWMAP setup_units( true, false, false ); - - if( DebugModeFlag ) { - // draw camera frustum - ::glLineWidth( 2.f ); - ::glColor4f( 1.f, 0.9f, 0.8f, 1.f ); - ::glDisable( GL_LIGHTING ); - ::glDisable( GL_TEXTURE_2D ); - worldcamera.frustum().draw( m_renderpass.camera.position() ); - ::glLineWidth( 1.f ); - ::glEnable( GL_LIGHTING ); - ::glEnable( GL_TEXTURE_2D ); - } #else setup_units( false, false, false ); #endif @@ -384,6 +396,9 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glDisable( GL_SCISSOR_TEST ); ::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)"; } break; } @@ -438,7 +453,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } void -opengl_renderer::renderpass_config::setup( rendermode const Mode ) { +opengl_renderer::renderpass_config::setup( rendermode const Mode, bool const Framebuffersupport, float const Znear, float const Zfar, bool const Ignoredebug ) { draw_mode = Mode; @@ -446,159 +461,121 @@ opengl_renderer::renderpass_config::setup( rendermode const Mode ) { switch( draw_mode ) { case rendermode::color: { draw_range = Global::BaseDrawRange; break; } - case rendermode::shadows: { draw_range = Global::shadowtune.depth; 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_projection(); - setup_modelview(); -} - -// configures projection matrix for the current render pass -void -opengl_renderer::renderpass_config::setup_projection() { - camera.projection() = glm::mat4( 1.f ); + glm::dmat4 viewmatrix( 1.0 ); switch( draw_mode ) { - -#ifndef EU07_USE_PICKING_FRAMEBUFFER - case rendermode::pickcontrols: - case rendermode::pickscenery: -#endif case rendermode::color: { - setup_projection_world(); - break; - } -#ifdef EU07_USE_PICKING_FRAMEBUFFER - case rendermode::pickcontrols: - case rendermode::pickscenery: { - // TODO: scissor test for pick modes - 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 ); + // projection + auto const zfar = draw_range * Global::fDistanceFactor * Zfar; + auto const znear = ( + Znear > 0.f ? + Znear * zfar : + 0.1f * Global::ZoomFactor ); camera.projection() *= glm::perspective( glm::radians( Global::FieldOfView / Global::ZoomFactor ), - std::max( 1.0f, (float)Global::iWindowWidth ) / std::max( 1.0f, (float)Global::iWindowHeight ) / ( Global::iWindowWidth / EU07_PICKBUFFERSIZE ), - 0.1f * Global::ZoomFactor, - draw_range * Global::fDistanceFactor ); + std::max( 1.f, (float)Global::iWindowWidth ) / std::max( 1.f, (float)Global::iWindowHeight ), + znear, + zfar ); + // modelview + if( ( false == DebugCameraFlag ) || ( true == Ignoredebug ) ) { + camera.position() = Global::pCameraPosition; + World.Camera.SetMatrix( viewmatrix ); + } + else { + camera.position() = Global::DebugCameraPosition; + World.DebugCamera.SetMatrix( viewmatrix ); + } break; } -#endif + case rendermode::shadows: { -#ifdef EU07_USE_ORTHO_SHADOWS - setup_projection_light_ortho(); -#else - setup_projection_light_perspective(); -#endif + // calculate lightview boundaries based on relevant area of the world camera frustum: + // 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... + 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 }; + + 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... + viewmatrix *= glm::lookAt( + camera.position(), + camera.position() - glm::dvec3{ lightvector }, + glm::dvec3{ 0.f, 1.f, 0.f } ); + // ...calculate boundaries of the frustum chunk in light space... + auto const lightviewmatrix = + glm::translate( + glm::mat4{ glm::mat3{ viewmatrix } }, + -frustumchunkcentre ); + for( auto &point : frustumchunkshapepoints ) { + point = lightviewmatrix * point; + } + bounding_box( frustumchunkmin, frustumchunkmax, std::begin( frustumchunkshapepoints ), std::end( frustumchunkshapepoints ) ); + // ...use the dimensions to set up light projection boundaries + // NOTE: since we only have one cascade map stage, we extend the chunk forward/back to catch areas normally covered by other stages + camera.projection() *= + glm::ortho( + frustumchunkmin.x, frustumchunkmax.x, + frustumchunkmin.y, frustumchunkmax.y, + frustumchunkmin.z - 500.f, frustumchunkmax.z + 500.f ); break; } - default: { - break; - } - } -} - -void -opengl_renderer::renderpass_config::setup_projection_world() { - - 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, - draw_range * Global::fDistanceFactor ); -} - -void -opengl_renderer::renderpass_config::setup_projection_light_ortho() { - // TODO: calculate lightview boundaries based on area of the world camera frustum - camera.projection() *= - glm::ortho( - -Global::shadowtune.width, Global::shadowtune.width, - -Global::shadowtune.width, Global::shadowtune.width, - -Global::shadowtune.depth, Global::shadowtune.depth ); -} - -void -opengl_renderer::renderpass_config::setup_projection_light_perspective() { - - camera.projection() *= - glm::perspective( - glm::radians( 45.f ), - 1.f, - draw_range * 0.1f, // light source is pulled back far enough we won't likely have anything too close to it, can get some z-range here - draw_range * Global::fDistanceFactor ); -} - -// configures modelview matrix for the current render pass -void -opengl_renderer::renderpass_config::setup_modelview() { - - camera.modelview() = glm::mat4( 1.f ); - - glm::dmat4 viewmatrix; - - switch( draw_mode ) { - case rendermode::color: case rendermode::pickcontrols: case rendermode::pickscenery: { - setup_modelview_world( viewmatrix ); - break; - } - case rendermode::shadows: { -#ifdef EU07_USE_ORTHO_SHADOWS - setup_modelview_light_ortho( viewmatrix ); -#else - setup_modelview_light_perspective( viewmatrix ); -#endif + // TODO: scissor test for pick modes + // projection + if( true == 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() *= + glm::perspective( + 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 ); + } + else { + 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, + draw_range * Global::fDistanceFactor ); + } + // modelview + camera.position() = Global::pCameraPosition; + World.Camera.SetMatrix( viewmatrix ); break; } default: { - break; } + break; + } } -#ifdef EU07_USE_ORTHO_SHADOWS - m_renderpass.camera.update_frustum( OpenGLMatrices.data( GL_PROJECTION ), viewmatrix ); - // frustum tests are performed in 'world space' but after we set up frustum we no longer need camera translation, only rotation - ::glMultMatrixd( glm::value_ptr( glm::dmat4( glm::dmat3( viewmatrix ) ) ) ); -#else camera.modelview() = viewmatrix; camera.update_frustum(); -#endif -} - -void -opengl_renderer::renderpass_config::setup_modelview_world( glm::dmat4 &Viewmatrix ) { - - camera.position() = Global::pCameraPosition; - World.Camera.SetMatrix( Viewmatrix ); -} - -void -opengl_renderer::renderpass_config::setup_modelview_light_ortho( glm::dmat4 &Viewmatrix ) { - - camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction }; - if( camera.position().y - Global::pCameraPosition.y < 0.1 ) { - camera.position().y = Global::pCameraPosition.y + 0.1; - } - Viewmatrix *= glm::lookAt( - camera.position(), - glm::dvec3{ Global::pCameraPosition }, - glm::dvec3{ 0.f, 1.f, 0.f } ); -} - -void -opengl_renderer::renderpass_config::setup_modelview_light_perspective( glm::dmat4 &Viewmatrix ) { - - camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * draw_range * 0.5f }; - camera.position().y = std::max( draw_range * 0.5f * 0.1f, camera.position().y ); // prevent shadow source from dipping too low - Viewmatrix *= glm::lookAt( - camera.position(), - glm::dvec3{ Global::pCameraPosition }, - glm::dvec3{ 0.f, 1.f, 0.f } ); } void @@ -2648,9 +2625,10 @@ opengl_renderer::Update( double const Deltatime ) { } m_updateaccumulator = 0.0; + m_framerate = 1000.f / ( m_drawtime / 20.f ); // adjust draw ranges etc, based on recent performance - auto const framerate = 1000.0f / (m_drawtime / 20.0f); + auto const framerate = 1000.0f / (m_drawtimecolor / 20.0f); float targetfactor; if( framerate > 90.0 ) { targetfactor = 3.0f; } diff --git a/renderer.h b/renderer.h index 81a85d19..001cf9b5 100644 --- a/renderer.h +++ b/renderer.h @@ -20,6 +20,7 @@ http://mozilla.org/MPL/2.0/. #define EU07_USE_PICKING_FRAMEBUFFER //#define EU07_USE_DEBUG_SHADOWMAP +//#define EU07_USE_DEBUG_CAMERA struct opengl_light { @@ -139,6 +140,9 @@ public: // main draw call. returns false on error bool Render(); + inline + float + Framerate() { return m_framerate; } // geometry methods // NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself // creates a new geometry bank. returns: handle to the bank or NULL @@ -199,31 +203,13 @@ private: typedef std::pair< double, TSubRect * > distancesubcell_pair; struct renderpass_config { + opengl_camera camera; rendermode draw_mode { rendermode::none }; float draw_range { 0.0f }; void - setup( rendermode const Mode ); - private: - // configures projection matrix for the current render pass - void - setup_projection(); - void - setup_projection_world(); - void - setup_projection_light_ortho(); - void - setup_projection_light_perspective(); - // configures camera for the current render pass - void - setup_modelview(); - void - setup_modelview_world( glm::dmat4 &Viewmatrix ); - void - setup_modelview_light_ortho( glm::dmat4 &Viewmatrix ); - void - setup_modelview_light_perspective( glm::dmat4 &Viewmatrix ); + 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; @@ -288,6 +274,7 @@ private: pick_color( std::size_t const Index ); std::size_t pick_index( glm::ivec3 const &Color ); + // members GLFWwindow *m_window { nullptr }; geometrybank_manager m_geometry; @@ -320,6 +307,10 @@ private: int m_diffusetextureunit { GL_TEXTURE2 }; 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 }; double m_updateaccumulator { 0.0 }; std::string m_debuginfo; @@ -336,6 +327,9 @@ private: std::vector m_pickcontrolsitems; TSubModel const *m_pickcontrolitem { nullptr }; TGroundNode const *m_picksceneryitem { nullptr }; +#ifdef EU07_USE_DEBUG_CAMERA + renderpass_config m_worldcamera; // debug item +#endif }; extern opengl_renderer GfxRenderer; diff --git a/usefull.h b/usefull.h index 91c27795..1e38c215 100644 --- a/usefull.h +++ b/usefull.h @@ -80,4 +80,19 @@ degenerate( VecType_ const &Vertex1, VecType_ const &Vertex2, VecType_ const &Ve return ( glm::length2( glm::cross( Vertex2 - Vertex1, Vertex3 - Vertex1 ) ) == 0.0 ); } +// calculates bounding box for provided set of points +template +void +bounding_box( VecType_ &Mincorner, VecType_ &Maxcorner, Iterator_ First, Iterator_ Last ) { + + Mincorner = VecType_( typename std::numeric_limits::max() ); + Maxcorner = VecType_( typename std::numeric_limits::min() ); + + std::for_each( + First, Last, + [&]( typename Iterator_::value_type &point ) { + Mincorner = glm::min( Mincorner, VecType_{ point } ); + Maxcorner = glm::max( Maxcorner, VecType_{ point } ); } ); +} + //--------------------------------------------------------------------------- diff --git a/version.h b/version.h index 5060e104..68f79535 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 727 +#define VERSION_MINOR 731 #define VERSION_REVISION 0 From 238ea7547d22259145827c926197f0614f182a3f Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 3 Aug 2017 16:00:48 +0200 Subject: [PATCH 7/9] minor refactoring, shadows colour influenced by sun/moon positions --- Ground.cpp | 12 ---- World.cpp | 9 +-- frustum.cpp | 21 ------ frustum.h | 22 +----- lightarray.cpp | 8 +-- lightarray.h | 4 +- renderer.cpp | 187 +++++++++++++++++++++++++++++++------------------ renderer.h | 36 +++++++--- sun.cpp | 13 ++-- 9 files changed, 162 insertions(+), 150 deletions(-) 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; From b783cd38a945d0e995d0e93798b34cf4feb5d202 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 5 Aug 2017 18:28:46 +0200 Subject: [PATCH 8/9] replaced basic array in the model manager with stl containers --- Event.cpp | 2 +- MdlMngr.cpp | 180 ++++++++++++------------------------------- MdlMngr.h | 41 +++++----- Texture.cpp | 2 +- World.cpp | 3 - openglgeometrybank.h | 4 +- renderer.cpp | 12 +-- version.h | 2 +- 8 files changed, 81 insertions(+), 165 deletions(-) diff --git a/Event.cpp b/Event.cpp index ee34478a..a56c5cdf 100644 --- a/Event.cpp +++ b/Event.cpp @@ -223,7 +223,7 @@ void TEvent::Load(cParser *parser, vector3 *org) parser->getTokens(1, false); // case sensitive *parser >> token; // str = AnsiString(token.c_str()); - Params[0].asText = new char[token.length() + 1]; + Params[0].asText = new char[token.length() + 1]; // BUG: source of memory leak strcpy(Params[0].asText, token.c_str()); if (token != "*") // czy ma zostać bez zmian? iFlags |= update_memstring; diff --git a/MdlMngr.cpp b/MdlMngr.cpp index fb55dfe9..fa1f6236 100644 --- a/MdlMngr.cpp +++ b/MdlMngr.cpp @@ -17,64 +17,45 @@ http://mozilla.org/MPL/2.0/. #include "MdlMngr.h" #include "Globals.h" -#include "Logs.h" #include "McZapkie/mctools.h" -//#define SeekFiles std::string("*.t3d") +// wczytanie modelu do kontenerka +TModel3d * +TMdlContainer::LoadModel(std::string const &Name, bool const Dynamic) { -TModel3d * TMdlContainer::LoadModel(std::string const &NewName, bool dynamic) -{ // wczytanie modelu do kontenerka - SafeDelete(Model); - Name = NewName; - Model = new TModel3d(); - if (!Model->LoadFromFile(Name, dynamic)) // np. "models\\pkp/head1-y.t3d" - SafeDelete(Model); - return Model; -}; - -TMdlContainer *TModelsManager::Models; -int TModelsManager::Count; -int const MAX_MODELS = 1000; - -void TModelsManager::Init() -{ - Models = new TMdlContainer[MAX_MODELS]; - Count = 0; -} -/* - TModelsManager::TModelsManager() -{ -// Models= NULL; - Models= new TMdlContainer[MAX_MODELS]; - Count= 0; -}; - - TModelsManager::~TModelsManager() -{ - Free(); -}; - */ -void TModelsManager::Free() -{ - delete[] Models; - Models = nullptr; -} - -TModel3d * TModelsManager::LoadModel(std::string const &Name, bool dynamic) -{ // wczytanie modelu do tablicy - TModel3d *mdl = NULL; - if (Count >= MAX_MODELS) - Error("FIXME: Too many models, program will now crash :)"); - else - { - mdl = Models[Count].LoadModel(Name, dynamic); - if (mdl) - Count++; // jeśli błąd wczytania modelu, to go nie wliczamy + Model = std::make_shared(); + if( true == Model->LoadFromFile( Name, Dynamic ) ) { + m_name = Name; + return Model.get(); + } + else { + m_name.clear(); + Model = nullptr; + return nullptr; + } +}; + +TModelsManager::modelcontainer_sequence TModelsManager::m_models; +TModelsManager::stringmodelcontainerindex_map TModelsManager::m_modelsmap; + +// wczytanie modelu do tablicy +TModel3d * +TModelsManager::LoadModel(std::string const &Name, bool dynamic) { + + m_models.emplace_back(); + auto model = m_models.back().LoadModel( Name, dynamic ); + if( model != nullptr ) { + m_modelsmap.emplace( Name, m_models.size() - 1 ); + return model; + } + else { + m_models.pop_back(); + return nullptr; } - return mdl; } -TModel3d * TModelsManager::GetModel(std::string const &Name, bool dynamic) +TModel3d * +TModelsManager::GetModel(std::string const &Name, bool const Dynamic) { // model może być we wpisie "node...model" albo "node...dynamic", a także być dodatkowym w dynamic // (kabina, wnętrze, ładunek) // dla "node...dynamic" mamy podaną ścieżkę w "\dynamic\" i musi być co najmniej 1 poziom, zwkle @@ -97,53 +78,10 @@ TModel3d * TModelsManager::GetModel(std::string const &Name, bool dynamic) // - niebo animowane, ścieżka brana ze wpisu, tekstury nieokreślone // - wczytanie modelu animowanego - Init() - sprawdzić std::string buf; - std::string buftp = Global::asCurrentTexturePath; // zapamiętanie aktualnej ścieżki do tekstur, - // bo będzie tyczmasowo zmieniana - /* - // Ra: niby tak jest lepiej, ale działa gorzej, więc przywrócone jest oryginalne - //nawet jeśli model będzie pobrany z tablicy, to trzeba ustalić ścieżkę dla tekstur - if (dynamic) //na razie tak, bo nie wiadomo, jaki może mieć wpływ na pozostałe modele - {//dla pojazdów podana jest zawsze pełna ścieżka do modelu - strcpy(buf,Name); - if (strchr(Name,'/')!=NULL) - {//pobieranie tekstur z katalogu, w którym jest model - Global::asCurrentTexturePath=Global::asCurrentTexturePath+AnsiString(Name); - Global::asCurrentTexturePath.Delete(Global::asCurrentTexturePath.Pos("/")+1,Global::asCurrentTexturePath.Length()); - } - } - else - {//dla modeli scenerii trzeba ustalić ścieżkę - if (strchr(Name,'\\')==NULL) - {//jeśli nie ma lewego ukośnika w ścieżce, a jest prawy, to zmienić ścieżkę dla tekstur na tę - z modelem - strcpy(buf,"models\\"); //Ra: było by lepiej katalog dodać w parserze - //strcpy(buf,"scenery\\"); //Ra: było by lepiej katalog dodać w parserze - strcat(buf,Name); - if (strchr(Name,'/')!=NULL) - {//jeszcze musi być prawy ukośnik - Global::asCurrentTexturePath=Global::asCurrentTexturePath+AnsiString(Name); - Global::asCurrentTexturePath.Delete(Global::asCurrentTexturePath.Pos("/")+1,Global::asCurrentTexturePath.Length()); - } - } - else - {//jeśli jest lewy ukośnik, to ścieżkę do tekstur zmienić tylko dla pojazdów - strcpy(buf,Name); - } - } - StrLower(buf); - for (int i=0;isecond ].Model.get() ); + } + + auto model = LoadModel(buf, Dynamic); // model nie znaleziony, to wczytać Global::asCurrentTexturePath = buftp; // odtworzenie ścieżki do tekstur - return (tmpModel); // NULL jeśli błąd + return model; // NULL jeśli błąd }; -/* -TModel3d TModelsManager::GetModel(char *Name, AnsiString asReplacableTexture) -{ - GLuint ReplacableTextureID= 0; - TModel3d NewModel; - - NewModel= *GetNextModel(Name); - - if (asReplacableTexture!=AnsiString("none")) - ReplacableTextureID= TTexturesManager::GetTextureID(asReplacableTexture.c_str()); - - NewModel.ReplacableSkinID=ReplacableTextureID; - - return NewModel; -}; -*/ - //--------------------------------------------------------------------------- diff --git a/MdlMngr.h b/MdlMngr.h index 2162bf30..b8f53687 100644 --- a/MdlMngr.h +++ b/MdlMngr.h @@ -9,33 +9,28 @@ http://mozilla.org/MPL/2.0/. #pragma once #include "Model3d.h" -#include "usefull.h" -class TMdlContainer -{ +class TMdlContainer { friend class TModelsManager; - ~TMdlContainer() - { - delete Model; - }; - TModel3d * LoadModel(std::string const &NewName, bool dynamic); - TModel3d *Model{ nullptr }; - std::string Name; +private: + TModel3d *LoadModel( std::string const &Name, bool const Dynamic ); + std::shared_ptr Model { nullptr }; + std::string m_name; }; -class TModelsManager -{ // klasa statyczna, nie ma obiektu - private: - static TMdlContainer *Models; - static int Count; - static TModel3d * LoadModel(std::string const &Name, bool dynamic); - - public: - // TModelsManager(); - // ~TModelsManager(); - static void Init(); - static void Free(); +// klasa statyczna, nie ma obiektu +class TModelsManager { +// types: + typedef std::deque modelcontainer_sequence; + typedef std::unordered_map stringmodelcontainerindex_map; +// members: + static modelcontainer_sequence m_models; + static stringmodelcontainerindex_map m_modelsmap; +// methods: + static TModel3d *LoadModel( std::string const &Name, bool const Dynamic ); +public: // McZapkie: dodalem sciezke, notabene Path!=Patch :) - static TModel3d * GetModel(std::string const &Name, bool dynamic = false); + static TModel3d *GetModel( std::string const &Name, bool dynamic = false ); }; + //--------------------------------------------------------------------------- diff --git a/Texture.cpp b/Texture.cpp index 228d314d..ce557936 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -909,7 +909,7 @@ texture_manager::info() const { } return - "Textures: " + "; textures: " #ifdef EU07_DEFERRED_TEXTURE_UPLOAD + std::to_string( readytexturecount ) + " (" diff --git a/World.cpp b/World.cpp index 922135c2..89ca524a 100644 --- a/World.cpp +++ b/World.cpp @@ -203,7 +203,6 @@ TWorld::~TWorld() TrainDelete(); // Ground.Free(); //Ra: usunięcie obiektów przed usunięciem dźwięków - sypie się TSoundsManager::Free(); - TModelsManager::Free(); } void TWorld::TrainDelete(TDynamicObject *d) @@ -291,8 +290,6 @@ bool TWorld::Init( GLFWwindow *Window ) { TSoundsManager::Init( glfwGetWin32Window( window ) ); WriteLog("Sound Init OK"); - TModelsManager::Init(); - WriteLog("Models init OK"); glfwSetWindowTitle( window, ( Global::AppName + " (" + Global::SceneryFile + ")" ).c_str() ); // nazwa scenerii UILayer.set_progress(0.01); diff --git a/openglgeometrybank.h b/openglgeometrybank.h index 736f3d66..90bec5a0 100644 --- a/openglgeometrybank.h +++ b/openglgeometrybank.h @@ -25,8 +25,8 @@ struct basic_vertex { glm::vec2 texture; // uv space basic_vertex() = default; - basic_vertex( glm::vec3 const&Position, glm::vec3 const &Normal, glm::vec2 const &Texture ) : - position( Position ), normal( Normal ), texture( Texture ) + basic_vertex( glm::vec3 const &Position, glm::vec3 const &Normal, glm::vec2 const &Texture ) : + position( Position ), normal( Normal ), texture( Texture ) {} void serialize( std::ostream& ) const; void deserialize( std::istream& ); diff --git a/renderer.cpp b/renderer.cpp index f78f2c52..e738a0c9 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -408,7 +408,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glEnable( GL_SCISSOR_TEST ); setup_matrices(); ::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting - ::glPolygonOffset( 4.f, 8.f ); + ::glPolygonOffset( 2.f, 2.f ); // render // opaque parts... setup_drawing( false ); @@ -560,6 +560,11 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f bounding_box( frustumchunkmin, frustumchunkmax, std::begin( frustumchunkshapepoints ), std::end( frustumchunkshapepoints ) ); // ...use the dimensions to set up light projection boundaries // NOTE: since we only have one cascade map stage, we extend the chunk forward/back to catch areas normally covered by other stages + auto const quantizationstep = ( Global::shadowtune.depth + 1000.f ) / m_shadowbuffersize; + frustumchunkmin.x -= std::remainder( frustumchunkmin.x, quantizationstep ); + frustumchunkmin.y -= std::remainder( frustumchunkmin.y, quantizationstep ); + frustumchunkmax.x -= std::remainder( frustumchunkmax.x, quantizationstep ); + frustumchunkmax.y -= std::remainder( frustumchunkmax.y, quantizationstep ); camera.projection() *= glm::ortho( frustumchunkmin.x, frustumchunkmax.x, @@ -2708,10 +2713,7 @@ opengl_renderer::Update( double const Deltatime ) { } if( true == DebugModeFlag ) { - m_debuginfo = m_textures.info(); - } - else { - m_debuginfo.clear(); + m_debuginfo += m_textures.info(); } if( ( true == Global::ControlPicking ) diff --git a/version.h b/version.h index 68f79535..60e9869e 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 731 +#define VERSION_MINOR 803 #define VERSION_REVISION 0 From 7e5b2d09cb194aba7004d94ce2f47c92eb71ba5d Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 5 Aug 2017 18:41:39 +0200 Subject: [PATCH 9/9] conversion of legacy model-based terrain to camera-centric render system --- AnimModel.cpp | 10 +- AnimModel.h | 2 +- Ground.cpp | 245 ++++++++++++++++++----------- Ground.h | 7 +- Model3d.cpp | 76 +++++++-- Model3d.h | 6 +- Train.h | 1 - World.cpp | 23 +-- maszyna.vcxproj.filters | 6 - FadeSound.cpp => old/FadeSound.cpp | 0 FadeSound.h => old/FadeSound.h | 0 renderer.cpp | 7 +- usefull.h | 2 +- version.h | 2 +- 14 files changed, 248 insertions(+), 139 deletions(-) rename FadeSound.cpp => old/FadeSound.cpp (100%) rename FadeSound.h => old/FadeSound.h (100%) diff --git a/AnimModel.cpp b/AnimModel.cpp index 58d8ecb0..02e33d9c 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -472,8 +472,10 @@ bool TAnimModel::Load(cParser *parser, bool ter) if( name.substr( name.rfind( '.' ) ) == ".t3d" ) { name[ name.length() - 3 ] = 'e'; } +#ifdef EU07_USE_OLD_TERRAINCODE Global::asTerrainModel = name; WriteLog("Terrain model \"" + name + "\" will be created."); +#endif } else ErrorLog("Missed file: " + name); @@ -632,13 +634,7 @@ TSubModel * TAnimModel::TerrainSquare(int n) { // pobieranie wskaźników do pierwszego submodelu return pModel ? pModel->TerrainSquare(n) : 0; }; -#ifdef EU07_USE_OLD_RENDERCODE -void TAnimModel::TerrainRenderVBO(int n) -{ // renderowanie terenu z VBO - if (pModel) - pModel->TerrainRenderVBO(n); -}; -#endif + //--------------------------------------------------------------------------- void TAnimModel::Advanced() diff --git a/AnimModel.h b/AnimModel.h index 69866f56..73bad8f7 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -144,7 +144,7 @@ class TAnimModel { material_data m_materialdata; std::string asText; // tekst dla wyświetlacza znakowego - TAnimAdvanced *pAdvanced; + TAnimAdvanced *pAdvanced { nullptr }; void Advanced(); TLightState lsLights[iMaxNumLights]; float fDark; // poziom zapalanie światła (powinno być chyba powiązane z danym światłem?) diff --git a/Ground.cpp b/Ground.cpp index 6025fbee..57b76e36 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -153,73 +153,30 @@ TGroundNode::TGroundNode( TGroundNodeType t ) : } } -void TGroundNode::InitNormals() -{ // obliczenie wektorów normalnych - glm::dvec3 v1, v2, v3, v4, v5; - glm::vec3 n1, n2, n3, n4; +// obliczenie wektorów normalnych +void TGroundNode::InitNormals() { + + glm::dvec3 v1, v2; + glm::vec3 n1; glm::vec2 t1; - int i; - switch (iType) - { - case GL_TRIANGLE_STRIP: - v1 = Piece->vertices[0].position - Piece->vertices[1].position; - v2 = Piece->vertices[1].position - Piece->vertices[2].position; - n1 = glm::normalize(glm::cross(v1, v2)); - if (Piece->vertices[0].normal == glm::vec3()) - Piece->vertices[0].normal = n1; - v3 = Piece->vertices[2].position - Piece->vertices[3].position; - n2 = glm::normalize(glm::cross(v3, v2)); - if (Piece->vertices[1].normal == glm::vec3()) - Piece->vertices[1].normal = (n1 + n2) * 0.5f; - for ( i = 2; i < iNumVerts - 2; i += 2) - { - v4 = Piece->vertices[i - 1].position - Piece->vertices[i].position; - v5 = Piece->vertices[i].position - Piece->vertices[i + 1].position; - n3 = glm::normalize(glm::cross(v3, v4)); - n4 = glm::normalize(glm::cross(v5, v4)); - if (Piece->vertices[i].normal == glm::vec3()) - Piece->vertices[i].normal = (n1 + n2 + n3) / 3.0f; - if (Piece->vertices[i + 1].normal == glm::vec3()) - Piece->vertices[i + 1].normal = (n2 + n3 + n4) / 3.0f; - n1 = n3; - n2 = n4; - v3 = v5; - } - if (Piece->vertices[i].normal == glm::vec3()) - Piece->vertices[i].normal = (n1 + n2) / 2.0f; - if (i + 1 < iNumVerts) - { - if (Piece->vertices[i + 1].normal == glm::vec3()) - Piece->vertices[i + 1].normal = n2; - } - else - WriteLog("odd number of vertices, normals may be wrong!"); + for( auto i = 0; i < iNumVerts; i += 3 ) { - break; - case GL_TRIANGLE_FAN: - - break; - case GL_TRIANGLES: - for (i = 0; i < iNumVerts; i += 3) - { - v1 = Piece->vertices[i + 0].position - Piece->vertices[i + 1].position; - v2 = Piece->vertices[i + 1].position - Piece->vertices[i + 2].position; - n1 = glm::normalize(glm::cross(v1, v2)); - if( Piece->vertices[i + 0].normal == glm::vec3() ) - Piece->vertices[i + 0].normal = (n1); - if( Piece->vertices[i + 1].normal == glm::vec3() ) - Piece->vertices[i + 1].normal = (n1); - if( Piece->vertices[i + 2].normal == glm::vec3() ) - Piece->vertices[i + 2].normal = (n1); - t1 = glm::vec2( - std::floor( Piece->vertices[ i + 0 ].texture.s ), - std::floor( Piece->vertices[ i + 0 ].texture.t ) ); - Piece->vertices[ i + 1 ].texture -= t1; - Piece->vertices[ i + 2 ].texture -= t1; - Piece->vertices[ i + 0 ].texture -= t1; - } - break; + v1 = Piece->vertices[ i + 0 ].position - Piece->vertices[ i + 1 ].position; + v2 = Piece->vertices[ i + 1 ].position - Piece->vertices[ i + 2 ].position; + n1 = glm::normalize( glm::cross( v1, v2 ) ); + if( Piece->vertices[ i + 0 ].normal == glm::vec3() ) + Piece->vertices[ i + 0 ].normal = ( n1 ); + if( Piece->vertices[ i + 1 ].normal == glm::vec3() ) + Piece->vertices[ i + 1 ].normal = ( n1 ); + if( Piece->vertices[ i + 2 ].normal == glm::vec3() ) + Piece->vertices[ i + 2 ].normal = ( n1 ); + t1 = glm::vec2( + std::floor( Piece->vertices[ i + 0 ].texture.s ), + std::floor( Piece->vertices[ i + 0 ].texture.t ) ); + Piece->vertices[ i + 1 ].texture -= t1; + Piece->vertices[ i + 2 ].texture -= t1; + Piece->vertices[ i + 0 ].texture -= t1; } } @@ -704,6 +661,42 @@ TGround::GetRect( double x, double z ) { } }; +// convert tp_terrain model to a series of triangle nodes +void +TGround::convert_terrain( TGroundNode const *Terrain ) { + + TSubModel *submodel { nullptr }; + for( auto cellindex = 1; cellindex < Terrain->iCount; ++cellindex ) { + // go through all submodels for individual terrain cells... + submodel = Terrain->nNode[ cellindex ].smTerrain; + convert_terrain( submodel ); + // if there's more than one group of triangles in the cell they're held as children of the primary submodel + submodel = submodel->ChildGet(); + while( submodel != nullptr ) { + convert_terrain( submodel ); + submodel = submodel->NextGet(); + } + } +} + +void +TGround::convert_terrain( TSubModel const *Submodel ) { + + auto groundnode = new TGroundNode( GL_TRIANGLES ); + Submodel->convert( *groundnode ); + + if( groundnode->iNumVerts > 0 ) { + // jeśli nie jest pojazdem ostatni dodany dołączamy na końcu nowego + groundnode->nNext = nRootOfType[ groundnode->iType ]; + // ustawienie nowego na początku listy + nRootOfType[ groundnode->iType ] = groundnode; + ++iNumNodes; + } + else { + delete groundnode; + } +} + double fTrainSetVel = 0; double fTrainSetDir = 0; double fTrainSetDist = 0; // odległość składu od punktu 1 w stronę punktu 2 @@ -1219,61 +1212,123 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } break; - case TP_MODEL: - if (rmin < 0) - { + case TP_MODEL: { +#ifdef EU07_USE_OLD_TERRAINCODE + if( rmin < 0 ) { tmp->iType = TP_TERRAIN; tmp->fSquareMinRadius = 0; // to w ogóle potrzebne? } - parser->getTokens(3); + parser->getTokens( 3 ); *parser >> tmp->pCenter.x >> tmp->pCenter.y >> tmp->pCenter.z; parser->getTokens(); *parser >> tf1; // OlO_EU&KAKISH-030103: obracanie punktow zaczepien w modelu - tmp->pCenter.RotateY(aRotate.y / 180.0 * M_PI); + tmp->pCenter.RotateY( aRotate.y / 180.0 * M_PI ); // McZapkie-260402: model tez ma wspolrzedne wzgledne tmp->pCenter += pOrigin; tmp->Model = new TAnimModel(); - tmp->Model->RaAnglesSet(aRotate.x, tf1 + aRotate.y, aRotate.z); // dostosowanie do pochylania linii + tmp->Model->RaAnglesSet( aRotate.x, tf1 + aRotate.y, aRotate.z ); // dostosowanie do pochylania linii if( tmp->Model->Load( parser, tmp->iType == TP_TERRAIN ) ) { // wczytanie modelu, tekstury i stanu świateł... tmp->iFlags = tmp->Model->Flags() | 0x200; // ustalenie, czy przezroczysty; flaga usuwania } - else if (tmp->iType != TP_TERRAIN) - { // model nie wczytał się - ignorowanie node + else if( tmp->iType != TP_TERRAIN ) { // model nie wczytał się - ignorowanie node delete tmp; tmp = NULL; // nie może być tu return break; // nie może być tu return? } - if (tmp->iType == TP_TERRAIN) - { // jeśli model jest terenem, trzeba utworzyć dodatkowe obiekty + if( tmp->iType == TP_TERRAIN ) { // jeśli model jest terenem, trzeba utworzyć dodatkowe obiekty // po wczytaniu model ma już utworzone DL albo VBO Global::pTerrainCompact = tmp->Model; // istnieje co najmniej jeden obiekt terenu tmp->pCenter = Math3D::vector3( 0.0, 0.0, 0.0 ); // enforce placement in the world center tmp->iCount = Global::pTerrainCompact->TerrainCount() + 1; // zliczenie submodeli - tmp->nNode = new TGroundNode[tmp->iCount]; // sztuczne node dla kwadratów - tmp->nNode[0].iType = TP_MODEL; // pierwszy zawiera model (dla delete) - tmp->nNode[0].Model = Global::pTerrainCompact; - tmp->nNode[0].iFlags = 0x200; // nie wyświetlany, ale usuwany - for (int i = 1; i < tmp->iCount; ++i) - { // a reszta to submodele - tmp->nNode[i].iType = TP_SUBMODEL; - tmp->nNode[i].smTerrain = Global::pTerrainCompact->TerrainSquare(i - 1); - tmp->nNode[i].iFlags = 0x10; // nieprzezroczyste; nie usuwany - tmp->nNode[i].bVisible = true; - tmp->nNode[i].pCenter = tmp->pCenter; // nie przesuwamy w inne miejsce + tmp->nNode = new TGroundNode[ tmp->iCount ]; // sztuczne node dla kwadratów + tmp->nNode[ 0 ].iType = TP_MODEL; // pierwszy zawiera model (dla delete) + tmp->nNode[ 0 ].Model = Global::pTerrainCompact; + tmp->nNode[ 0 ].iFlags = 0x200; // nie wyświetlany, ale usuwany + for( int i = 1; i < tmp->iCount; ++i ) { // a reszta to submodele + tmp->nNode[ i ].iType = TP_SUBMODEL; + tmp->nNode[ i ].smTerrain = Global::pTerrainCompact->TerrainSquare( i - 1 ); + tmp->nNode[ i ].iFlags = 0x10; // nieprzezroczyste; nie usuwany + tmp->nNode[ i ].bVisible = true; + tmp->nNode[ i ].pCenter = tmp->pCenter; // nie przesuwamy w inne miejsce } } - else if (!tmp->asName.empty()) // jest pusta gdy "none" + else if( !tmp->asName.empty() ) // jest pusta gdy "none" { // dodanie do wyszukiwarki if( false == m_trackmap.Add( TP_MODEL, tmp->asName, tmp ) ) { // przy zdublowaniu wskaźnik zostanie podmieniony w drzewku na późniejszy (zgodność wsteczna) ErrorLog( "Duplicated model: " + tmp->asName ); // to zgłaszać duplikat } } - // str=Parser->GetNextSymbol().LowerCase(); +#else + if( rmin < 0 ) { + // legacy leftover: special case, terrain provided as 3d model + tmp->iType = TP_TERRAIN; + tmp->fSquareMinRadius = 0; // to w ogóle potrzebne? + // we ignore center and rotation for terrain; they should be set to 0 anyway + parser->getTokens( 4 ); + + tmp->iFlags = 0x200; // flaga usuwania + tmp->Model = new TAnimModel(); + if( false == tmp->Model->Load( parser, true ) ) { + delete tmp; + tmp = nullptr; + break; + } + tmp->iFlags |= tmp->Model->Flags(); // ustalenie, czy przezroczysty + tmp->pCenter = Math3D::vector3(); // enforce placement in the world center + // jeśli model jest terenem, trzeba utworzyć dodatkowe obiekty + tmp->iCount = tmp->Model->TerrainCount() + 1; // zliczenie submodeli + Global::pTerrainCompact = tmp->Model; + tmp->nNode = new TGroundNode[ tmp->iCount ]; // sztuczne node dla kwadratów + tmp->nNode[ 0 ].iType = TP_MODEL; // pierwszy zawiera model (dla delete) + tmp->nNode[ 0 ].Model = Global::pTerrainCompact; + tmp->nNode[ 0 ].iFlags = 0x200; // nie wyświetlany, ale usuwany + for( auto i = 1; i < tmp->iCount; ++i ) { // a reszta to submodele + tmp->nNode[ i ].iType = TP_SUBMODEL; + tmp->nNode[ i ].smTerrain = Global::pTerrainCompact->TerrainSquare( i - 1 ); + tmp->nNode[ i ].iFlags = 0x10; // nieprzezroczyste; nie usuwany + tmp->nNode[ i ].bVisible = true; + tmp->nNode[ i ].pCenter = tmp->pCenter; // nie przesuwamy w inne miejsce + } + } + else { + // regular 3d model + parser->getTokens( 3 ); + *parser + >> tmp->pCenter.x + >> tmp->pCenter.y + >> tmp->pCenter.z; + parser->getTokens(); + *parser >> tf1; + // OlO_EU&KAKISH-030103: obracanie punktow zaczepien w modelu + tmp->pCenter.RotateY( glm::radians( aRotate.y ) ); + // McZapkie-260402: model tez ma wspolrzedne wzgledne + tmp->pCenter += pOrigin; + + tmp->iFlags = 0x200; // flaga usuwania + tmp->Model = new TAnimModel(); + tmp->Model->RaAnglesSet( aRotate.x, tf1 + aRotate.y, aRotate.z ); // dostosowanie do pochylania linii + if( false == tmp->Model->Load( parser, false ) ) { + // model nie wczytał się - ignorowanie node + delete tmp; + tmp = nullptr; // nie może być tu return + break; // nie może być tu return? + } + tmp->iFlags |= tmp->Model->Flags(); // ustalenie, czy przezroczysty + if( false == tmp->asName.empty() ) { // jest pusta gdy "none" + // dodanie do wyszukiwarki + if( false == m_trackmap.Add( TP_MODEL, tmp->asName, tmp ) ) { + // przy zdublowaniu wskaźnik zostanie podmieniony w drzewku na późniejszy (zgodność wsteczna) + ErrorLog( "Duplicated model: " + tmp->asName ); // to zgłaszać duplikat + } + } + } +#endif break; + } // case TP_GEOMETRY : case GL_TRIANGLES: case GL_TRIANGLE_STRIP: @@ -1620,7 +1675,8 @@ void TGround::FirstInit() for (int type = 0; type < TP_LAST; ++type) { for (TGroundNode *Current = nRootOfType[type]; Current != nullptr; Current = Current->nNext) { - Current->InitNormals(); + if( type == GL_TRIANGLES ) { Current->InitNormals(); } + if (Current->iType != TP_DYNAMIC) { // pojazdów w ogóle nie dotyczy dodawanie do mapy if( ( type == TP_EVLAUNCH ) @@ -1628,6 +1684,7 @@ void TGround::FirstInit() // dodanie do globalnego obiektu srGlobal.NodeAdd( Current ); } +#ifdef EU07_USE_OLD_TERRAINCODE else if (type == TP_TERRAIN) { // specjalne przetwarzanie terenu wczytanego z pliku E3D TGroundRect *gr; @@ -1640,6 +1697,7 @@ void TGround::FirstInit() gr->nTerrain = Current->nNode + j; // zapamiętanie } } +#endif else { TSubRect *targetcell { nullptr }; // test whether we can add the node to a ground cell, or a subcell @@ -1743,6 +1801,15 @@ bool TGround::Init(std::string File) } break; } +#ifndef EU07_SCENERY_EDITOR + case TP_TERRAIN: { + // convert legacy terrain model to a series of triangle nodes, to take advantage of camera-centric render and geometry merging + // NOTE: this leaves us with a large model we don't use loaded and taking space. it'll sort of solve itself when the binary scenery file is in place + convert_terrain( LastNode ); + SafeDelete( LastNode ); + Global::pTerrainCompact = nullptr; + } +#endif default: { break; } @@ -2120,8 +2187,10 @@ bool TGround::Init(std::string File) if (!bInitDone) FirstInit(); // jeśli nie było w scenerii +#ifdef EU07_USE_OLD_TERRAINCODE if (Global::pTerrainCompact) TerrainWrite(); // Ra: teraz można zapisać teren w jednym pliku +#endif Global::iPause &= ~0x10; // koniec pauzy wczytywania return true; } diff --git a/Ground.h b/Ground.h index 1005ab3f..d8b02ad9 100644 --- a/Ground.h +++ b/Ground.h @@ -175,10 +175,6 @@ class TSubRect : /*public Resource,*/ public CMesh TTrack **tTracks = nullptr; // tory do renderowania pojazdów protected: TTrack *tTrackAnim = nullptr; // obiekty do przeliczenia animacji -#ifdef EU07_USE_OLD_RENDERCODE - TGroundNode *nRootMesh = nullptr; // obiekty renderujące wg tekstury (wtórne, lista po nNext2) - TGroundNode *nMeshed = nullptr; // lista obiektów dla których istnieją obiekty renderujące grupowo -#endif public: TGroundNode *nRootNode = nullptr; // wszystkie obiekty w sektorze, z wyjątkiem renderujących i pojazdów (nNext2) TGroundNode *nRenderHidden = nullptr; // lista obiektów niewidocznych, "renderowanych" również z tyłu (nNext3) @@ -333,6 +329,9 @@ class TGround void TrackJoin(TGroundNode *Current); private: + // convert tp_terrain model to a series of triangle nodes + void convert_terrain( TGroundNode const *Terrain ); + void convert_terrain( TSubModel const *Submodel ); void RaTriangleDivider(TGroundNode *node); void Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam); diff --git a/Model3d.cpp b/Model3d.cpp index 95880a02..e9f3a8fc 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -19,6 +19,7 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others #include "logs.h" #include "mczapkie/mctools.h" #include "Usefull.h" +#include "ground.h" #include "renderer.h" #include "Timer.h" #include "mtable.h" @@ -1037,7 +1038,7 @@ void TSubModel::RaAnimation(TAnimType a) //--------------------------------------------------------------------------- -void TSubModel::serialize_geometry( std::ostream &Output ) { +void TSubModel::serialize_geometry( std::ostream &Output ) const { if( Child ) { Child->serialize_geometry( Output ); @@ -1076,6 +1077,65 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const & Next->create_geometry( Dataoffset, Bank ); } +// places contained geometry in provided ground node +void +TSubModel::convert( TGroundNode &Groundnode ) const { + + Groundnode.asName = pName; + Groundnode.Ambient = f4Ambient; + Groundnode.Diffuse = f4Diffuse; + Groundnode.Specular = f4Specular; + Groundnode.TextureID = TextureID; + Groundnode.iFlags = ( + ( true == GfxRenderer.Texture( TextureID ).has_alpha ) ? + 0x20 : + 0x10 ); + + if( m_geometry == NULL ) { return; } + + std::size_t vertexcount { 0 }; + std::vector importedvertices; + TGroundVertex vertex, vertex1, vertex2; + for( auto const &sourcevertex : GfxRenderer.Vertices( m_geometry ) ) { + vertex.position = sourcevertex.position; + vertex.normal = sourcevertex.normal; + vertex.texture = sourcevertex.texture; + if( vertexcount == 0 ) { vertex1 = vertex; } + else if( vertexcount == 1 ) { vertex2 = vertex; } + else if( vertexcount >= 2 ) { + if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) { + importedvertices.emplace_back( vertex1 ); + importedvertices.emplace_back( vertex2 ); + importedvertices.emplace_back( vertex ); + } + } + ++vertexcount; + if( vertexcount > 2 ) { vertexcount = 0; } // start new triangle if needed + } + if( Groundnode.Piece == nullptr ) { + Groundnode.Piece = new piece_node(); + } + Groundnode.iNumVerts = importedvertices.size(); + if( Groundnode.iNumVerts > 0 ) { + + Groundnode.Piece->vertices.swap( importedvertices ); + + for( auto const &vertex : Groundnode.Piece->vertices ) { + Groundnode.pCenter += vertex.position; + } + Groundnode.pCenter /= Groundnode.iNumVerts; + + double r { 0.0 }; + double tf; + for( auto const &vertex : Groundnode.Piece->vertices ) { + tf = glm::length2( vertex.position - glm::dvec3{ Groundnode.pCenter } ); + if( tf > r ) + r = tf; + } + Groundnode.fSquareRadius += r; + } +} + // NOTE: leftover from static distance factor adjustment. // TODO: get rid of it, once we have the dynamic adjustment code in place void TSubModel::AdjustDist() @@ -1861,17 +1921,3 @@ TSubModel *TModel3d::TerrainSquare(int n) r->UnFlagNext(); // blokowanie wyświetlania po Next głównej listy return r; }; -#ifdef EU07_USE_OLD_RENDERCODE -void TModel3d::TerrainRenderVBO(int n) -{ // renderowanie terenu z VBO - ::glPushMatrix(); - ::glTranslated( -Global::pCameraPosition.x, -Global::pCameraPosition.y, -Global::pCameraPosition.z ); - TSubModel *r = Root; - while( r ) { - if( r->iVisible == n ) // tylko jeśli ma być widoczny w danej ramce (problem dla 0==false) - GfxRenderer.Render( r ); // sub kolejne (Next) się nie wyrenderują - r = r->NextGet(); - } - ::glPopMatrix(); -}; -#endif \ No newline at end of file diff --git a/Model3d.h b/Model3d.h index 4a569ef4..4e72b3c4 100644 --- a/Model3d.h +++ b/Model3d.h @@ -50,6 +50,8 @@ enum TAnimType // rodzaj animacji at_Undefined = 0x800000FF // animacja chwilowo nieokreślona }; +class TGroundNode; + class TSubModel { // klasa submodelu - pojedyncza siatka, punkt świetlny albo grupa punktów //m7todo: zrobić normalną serializację @@ -213,7 +215,9 @@ public: std::vector&, std::vector&, std::vector&); - void serialize_geometry( std::ostream &Output ); + void serialize_geometry( std::ostream &Output ) const; + // places contained geometry in provided ground node + void convert( TGroundNode &Groundnode ) const; }; diff --git a/Train.h b/Train.h index c9c9e6d2..2b4b4595 100644 --- a/Train.h +++ b/Train.h @@ -15,7 +15,6 @@ http://mozilla.org/MPL/2.0/. #include "Gauge.h" #include "Spring.h" #include "AdvSound.h" -#include "FadeSound.h" #include "PyInt.h" #include "command.h" diff --git a/World.cpp b/World.cpp index 89ca524a..363defe2 100644 --- a/World.cpp +++ b/World.cpp @@ -294,6 +294,7 @@ bool TWorld::Init( GLFWwindow *Window ) { glfwSetWindowTitle( window, ( Global::AppName + " (" + Global::SceneryFile + ")" ).c_str() ); // nazwa scenerii UILayer.set_progress(0.01); UILayer.set_progress( "Loading scenery / Wczytywanie scenerii" ); + GfxRenderer.Render(); WriteLog( "Ground init" ); if( true == Ground.Init( Global::SceneryFile ) ) { @@ -1039,17 +1040,17 @@ bool TWorld::Update() // NOTE: experimentally changing this to prevent the desync. // TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps Ground.Update(dt / updatecount, updatecount); // tu zrobić tylko coklatkową aktualizację przesunięć -/* - if (DebugModeFlag) - if (Global::bActive) // nie przyspieszać, gdy jedzie w tle :) - if( Console::Pressed( GLFW_KEY_ESCAPE ) ) { - // yB dodał przyspieszacz fizyki - Ground.Update(dt, n); - Ground.Update(dt, n); - Ground.Update(dt, n); - Ground.Update(dt, n); // 5 razy - } -*/ + + // yB dodał przyspieszacz fizyki + if( (true == DebugModeFlag) + && (true == Global::bActive) // nie przyspieszać, gdy jedzie w tle :) + && ( glfwGetKey( window, GLFW_KEY_PAUSE ) == GLFW_PRESS ) ) { + + Ground.Update( dt, updatecount ); + Ground.Update( dt, updatecount ); + Ground.Update( dt, updatecount ); + Ground.Update( dt, updatecount ); // 5 razy + } // secondary fixed step simulation time routines while( m_secondaryupdateaccumulator >= m_secondaryupdaterate ) { diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index b375a4c0..e4762bc0 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -63,9 +63,6 @@ Source Files - - Source Files - Source Files @@ -338,9 +335,6 @@ Header Files - - Header Files - Header Files diff --git a/FadeSound.cpp b/old/FadeSound.cpp similarity index 100% rename from FadeSound.cpp rename to old/FadeSound.cpp diff --git a/FadeSound.h b/old/FadeSound.h similarity index 100% rename from FadeSound.h rename to old/FadeSound.h diff --git a/renderer.cpp b/renderer.cpp index e738a0c9..e55a781d 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1187,11 +1187,12 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { break; } } - +#ifdef EU07_USE_OLD_TERRAINCODE if( Groundcell->nTerrain ) { Render( Groundcell->nTerrain ); } +#endif Groundcell->iLastDisplay = Groundcell->iFrameNumber; // drugi raz nie potrzeba result = true; @@ -1298,7 +1299,7 @@ opengl_renderer::Render( TSubRect *Groundsubcell ) { bool opengl_renderer::Render( TGroundNode *Node ) { - +#ifdef EU07_USE_OLD_TERRAINCODE switch (Node->iType) { // obiekty renderowane niezależnie od odległości case TP_SUBMODEL: @@ -1310,7 +1311,7 @@ opengl_renderer::Render( TGroundNode *Node ) { ::glPopMatrix(); return true; } - +#endif double distancesquared; switch( m_renderpass.draw_mode ) { case rendermode::shadows: { diff --git a/usefull.h b/usefull.h index 1e38c215..0c4c398d 100644 --- a/usefull.h +++ b/usefull.h @@ -86,7 +86,7 @@ void bounding_box( VecType_ &Mincorner, VecType_ &Maxcorner, Iterator_ First, Iterator_ Last ) { Mincorner = VecType_( typename std::numeric_limits::max() ); - Maxcorner = VecType_( typename std::numeric_limits::min() ); + Maxcorner = VecType_( typename std::numeric_limits::lowest() ); std::for_each( First, Last, diff --git a/version.h b/version.h index 60e9869e..a572b462 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 803 +#define VERSION_MINOR 805 #define VERSION_REVISION 0