From c8a70e528053e3bbd600e1e1b096286bb4dd0b16 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 1 Aug 2017 03:15:19 +0200 Subject: [PATCH] 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